Skip to content

Instantly share code, notes, and snippets.

@kimoto
Created May 27, 2011 10:51
Show Gist options
  • Save kimoto/995031 to your computer and use it in GitHub Desktop.
Save kimoto/995031 to your computer and use it in GitHub Desktop.
無圧縮AVIを、任意のコーデックで圧縮するコード
/*
* 無圧縮AVIファイル"input.avi"を
* GUIで選択した圧縮形式で圧縮して、"output.avi" に出力するだけのプログラム
*/
#include <Windows.h>
#include <tchar.h>
#include <Vfw.h>
#pragma comment(lib, "vfw32.lib")
#include <GdiPlus.h>
#pragma comment(lib, "gdiplus.lib")
using namespace ::Gdiplus;
/*
* デバッグ情報出力用関数
*/
#define TRACE_BUFFER_SIZE 256
void trace(LPCTSTR format, ...)
{
#ifdef _DEBUG
va_list arg;
va_start(arg, format);
TCHAR buffer[TRACE_BUFFER_SIZE];
::_vsnwprintf_s(buffer, TRACE_BUFFER_SIZE, _TRUNCATE, format, arg);
::OutputDebugString(buffer);
va_end(arg);
#endif
}
/*
* エラー出力用のメッセージボックス
* 可変引数で任意のフォーマットで出力できます
*/
#define ERRMSGBOX_BUFFER_SIZE 256
void ErrorMessageBox(LPCTSTR format, ...)
{
va_list arg;
va_start(arg, format);
TCHAR buffer[ERRMSGBOX_BUFFER_SIZE];
::_vsnwprintf_s(buffer, ERRMSGBOX_BUFFER_SIZE, _TRUNCATE, format, arg);
::MessageBox(NULL, buffer, L"Error", MB_OK);
va_end(arg);
}
/*
* 最後に発生したエラーコードのエラー内容を
* メッセージボックスで表示します
*/
void ShowLastError(void){
LPVOID lpMessageBuffer;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // デフォルト ユーザー言語
(LPTSTR) &lpMessageBuffer,
0,
NULL );
MessageBox(NULL, (LPCWSTR)lpMessageBuffer, TEXT("Error"), MB_OK);
//... 文字列が表示されます。
// システムによって確保されたバッファを開放します。
LocalFree( lpMessageBuffer );
}
class AVIFile
{
private:
PAVIFILE pavi;
PAVISTREAM pstm;
LPBITMAPINFOHEADER pbmih;
PGETFRAME pfrm;
public:
AVIFile(LPCTSTR filename)
{
::AVIFileInit();
// AVIファイルのオープン
if( ::AVIFileOpen(&this->pavi, filename, OF_READ | OF_SHARE_DENY_NONE, NULL) != 0 ){
trace(L"AVIFileOpen Error!\n");
return;
}
// AVIファイルから、streamを取得します
if( ::AVIFileGetStream(pavi, &pstm, 0, 0) != 0 ){
trace(L"AVIFileGetStream Error!\n");
return;
}
// stream内にはbmpが圧縮されてるので
// 展開する必要があります
// そのフォーマットなんかを取得する関数
pfrm = ::AVIStreamGetFrameOpen(pstm, NULL);
if(pfrm == NULL){
trace(L"AVIStreamGetFrameOpen Error!\n");
return;
}
}
~AVIFile() {
if(pfrm){
if( ::AVIStreamGetFrameClose(pfrm) != 0 ){
::trace(L"AVIStreamGetFrameClose Error!\n");
return;
}
}
if(pstm)
::AVIStreamRelease(pstm);
if(pavi)
::AVIFileRelease(pavi);
::AVIFileExit();
}
LPBITMAPINFOHEADER getFrame(long pos)
{
return (LPBITMAPINFOHEADER)::AVIStreamGetFrame(pfrm, pos);
}
// 指定されたフレームをHBITMAP形式で返却します
// 不要になったら::DeleteObjectしてください
HBITMAP getFrameBitmap(long pos)
{
LPBITMAPINFOHEADER lpbmi = this->getFrame(pos);
HWND hWnd = ::GetDesktopWindow();
HDC hdc = ::GetDC(hWnd); // 汎用のDCを取得
HDC myhdc = ::CreateCompatibleDC(hdc);
HBITMAP bitmap = ::CreateCompatibleBitmap(hdc, lpbmi->biWidth, lpbmi->biHeight);
HBITMAP oldBitmap = (HBITMAP)::SelectObject(myhdc, bitmap);
// 転送
::StretchDIBits(myhdc, 0, 0, lpbmi->biWidth, lpbmi->biHeight,
0, 0, lpbmi->biWidth, lpbmi->biHeight,
lpbmi + 1, (LPBITMAPINFO)lpbmi, DIB_RGB_COLORS, SRCCOPY);
::SelectObject(myhdc, oldBitmap);
//::DeleteObject(bitmap);
::DeleteDC(myhdc);
::ReleaseDC(hWnd, hdc);
return bitmap;
}
long length()
{
return ::AVIStreamLength(pstm);
}
long getStartIndex()
{
return ::AVIStreamStart(pstm);
}
AVIFILEINFO getInfo()
{
AVIFILEINFO info;
::AVIFileInfo(pavi, &info, sizeof(AVIFILEINFO));
return info;
}
};
// AVIファイルのすべてのフレームをPNG画像に変換します
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
::AVIFileInit();
// 既存の無圧縮AVIを読み込んで
// ファイル情報を元に、圧縮方法について選定します
AVIFile avifile(L"input.avi");
AVIFILEINFO info = avifile.getInfo();
int width = info.dwWidth;
int height = info.dwHeight;
int sizeimage = height * ((3 * width + 3) / 4) * 4;
BITMAPINFOHEADER bmih={sizeof(BITMAPINFOHEADER),width,height,1,24,BI_RGB,sizeimage,0,0,0,0};
AVISTREAMINFO si;
ZeroMemory(&si, sizeof(AVISTREAMINFO));
si.fccType = streamtypeVIDEO;
si.fccHandler = comptypeDIB;
si.dwScale = info.dwScale;
si.dwRate = info.dwRate;
si.dwLength = info.dwLength;
si.dwQuality = -1;
// ユーザーに圧縮フォーマットを選択させます
::AVICOMPRESSOPTIONS opt;
::COMPVARS cv;
memset(&cv, 0, sizeof(COMPVARS));
cv.cbSize = sizeof(COMPVARS);
cv.dwFlags = ICMF_COMPVARS_VALID;
cv.fccHandler = comptypeDIB;
cv.lQ = ICQUALITY_DEFAULT;
if( !::ICCompressorChoose(NULL, ICMF_CHOOSE_DATARATE | ICMF_CHOOSE_KEYFRAME,
&bmih, NULL, &cv, NULL) ){
::ErrorMessageBox(L"選択されていません");
return 0;
}
// 選択された圧縮フォーマットに基づく設定を変更します
si.fccHandler = cv.fccHandler;
opt.fccType = streamtypeVIDEO;
opt.fccHandler = cv.fccHandler;
opt.dwKeyFrameEvery = cv.lKey;
opt.dwQuality = cv.lQ;
opt.dwBytesPerSecond = cv.lDataRate;
opt.dwFlags=(cv.lDataRate>0?AVICOMPRESSF_DATARATE:0)
|(cv.lKey>0?AVICOMPRESSF_KEYFRAMES:0);
opt.lpFormat=NULL;
opt.cbFormat=0;
opt.lpParms=cv.lpState;
opt.cbParms=cv.cbState;
opt.dwInterleaveEvery=0;
// =====================
// AVIの書き込み処理
// =====================
PAVIFILE pavi;
PAVISTREAM pstm;
// AVIファイルをオープンします
if( ::AVIFileOpen(&pavi, L"output.avi", OF_CREATE | OF_WRITE | OF_SHARE_DENY_NONE,NULL) != 0)
return 0;
// AVIファイル書き込み用のVIDEOストリームを作成します
if( ::AVIFileCreateStream(pavi, &pstm, &si) != 0)
return 0;
trace(L"create stream\n");
// 圧縮済みデータ格納用のストリームを作成します(ptmp)
PAVISTREAM ptmp;
if( ::AVIMakeCompressedStream(&ptmp, pstm, &opt, NULL) != AVIERR_OK)
return 0;
// 圧縮済みデータ格納用のストリームのデータ情報を設定します
if( ::AVIStreamSetFormat(ptmp, 0, &bmih, sizeof(BITMAPINFOHEADER)) != 0){
return 0;
}
// 入力AVIデータのすべてのフレームを、
// 圧縮済みAVIストリームにすべて書き込みます
trace(L"avistream set format\n");
for (long i=0;i<avifile.length();i++) {
LPBITMAPINFOHEADER lpbmi = avifile.getFrame(i);
if (AVIStreamWrite(ptmp,i,1,lpbmi + 1,lpbmi->biSizeImage,AVIIF_KEYFRAME,NULL,NULL)!=0)
return 0;
}
// 使用済みストリームや、ハンドルをすべて解放します
::AVIStreamRelease(ptmp);
::AVIStreamRelease(pstm);
::AVIFileRelease(pavi);
::ICCompressorFree(&cv);
::AVIFileExit();
trace(L"exit\n");
::ErrorMessageBox(L"出力しました");
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment