ビットマップイメージからAVIファイルを作成する

開発途中のゲームのプレイ画面をAVIの動画ファイルとして保存する方法についてのメモ書きです。

1.AVIファイルの作成
AVIFileOpen()関数にてAVIファイルを作成します。

    PAVIFILE pAviFile = NULL;
    AVIFileOpen(&pAviFile, TEXT("test.avi"), OF_CREATE | OF_WRITE, NULL);

第1引数に作成したAVIファイルの情報を格納する構造体ポインタ変数へのポインタ、
第2引数ひは作成するAVIファイル名、第3引数には作成オプションを指定します。
作成する場合はOF_CREATEを指定し、今回はファイルに書き込む必要があるのでOF_WRITEを指定しています。
第4引数はとりあえずNULLで問題ありません。

2.AVIストリームの作成
AVIFileCreateStream()関数にてAVIストリームを作成する必要があります。
この関数を呼び出す前に、まずAVISTREAMINFO構造体に必要な情報をセットする必要があります。

    AVISTREAMINFO AviStreamInfo;
    AviStreamInfo.fccType               = streamtypeVIDEO; // 動画
    AviStreamInfo.fccHandler            = comptypeDIB;     // デバイス非依存ビットマップ
    AviStreamInfo.dwFlags               = 0;
    AviStreamInfo.dwCaps                = 0;
    AviStreamInfo.wPriority             = 0;
    AviStreamInfo.wLanguage             = 0;
    AviStreamInfo.dwScale               = 1;
    AviStreamInfo.dwRate                = dwRate;     // フレームレート
    AviStreamInfo.dwStart               = 0;
    AviStreamInfo.dwLength              = dwFrameNum; // フレーム数
    AviStreamInfo.dwInitialFrames       = 0;
    AviStreamInfo.dwSuggestedBufferSize = 0;
    AviStreamInfo.dwQuality             = static_cast<dword>(-1); // クオリティはデフォルトで
    AviStreamInfo.dwSampleSize          = 0;
    AviStreamInfo.rcFrame.left          = 0;
    AviStreamInfo.rcFrame.top           = 0;
    AviStreamInfo.rcFrame.right         = dwWidth;  // 画面の幅
    AviStreamInfo.rcFrame.bottom        = dwHeight; // 画面の高さ
    AviStreamInfo.dwEditCount           = 0;
    AviStreamInfo.dwFormatChangeCount   = 0;
    AviStreamInfo.szName[0]             = TEXT('');

    PAVISTREAM pAviStream = NULL;
    if ( AVIFileCreateStream(pAviFile, &pAviStream, &AviStreamInfo) )
        throw std::runtime_error("AVIストリームの作成に失敗しました。");

構造体の詳細については、MSDNのリファレンスをご覧ください。
指定が必要な情報は上記ソースコード上にコメントで記しておきました。

3.ビットマップフォーマットの指定
AVIの1枚1枚のフレームはビットマップからなり、このフォーマットを指定します。

    BITMAPINFOHEADER BmpInfoHeader;
    BmpInfoHeader.biSize            = sizeof(BITMAPINFOHEADER);
    BmpInfoHeader.biWidth           = dwWidth;  // 画面の幅
    BmpInfoHeader.biHeight          = dwHeight; // 画面の高さ
    BmpInfoHeader.biPlanes          = 1;
    BmpInfoHeader.biBitCount        = byBitCount; //
    BmpInfoHeader.biCompression     = BI_RGB;
    BmpInfoHeader.biSizeImage       = dwWidth * dwHeight * byBitCount;
    BmpInfoHeader.biXPelsPerMeter   = 0;
    BmpInfoHeader.biYPelsPerMeter   = 0;
    BmpInfoHeader.biClrUsed         = 0;
    BmpInfoHeader.biClrImportant    = 0;

    if ( AVIStreamSetFormat(pAviStream, 0, &BmpInfoHeader, sizeof(BITMAPINFOHEADER)) )
        throw std::runtime_error("ビットマップフォーマットの指定に失敗しました。");

BITMAPINFOHEADER構造体にビットマップのフォーマットを指定し、AVIStreamSetFormat()関数に渡しています。

4.ビットマップイメージの書き込み
AVIStreamWrite()関数でイメージをAVIファイルに書き込みます。

    if ( AVIStreamWrite(
            pAviStream,
            dwIndex++, // イメージを書き込んだらインデックスを次にする
            1,
            pImage,    // ビットマップイメージのデータ
            dwWidth * dwHeight * byBitCount / 8,
            AVIIF_KEYFRAME,
            NULL,
            NULL
    ) )
        throw std::runtime_error("ビットマップイメージの書き込みに失敗しました。");

こちらも特に何も変哲の無い処理です。
注意すべきところは、AVIStreamWrite()の第3引数のdwIndex++です。
どのインデックスにビットマップイメージを書き込む必要があるのかを指定しなければいけないため、dwIndex変数で次に書き込むべきインデックスを保持しています。

5.後始末
AVIファイルの書き込みが終了したら、後始末を行います。

    if ( pAviStream ) {
        AVIStreamRelease(pAviStream);
        pAviStream = NULL;
    }
    if ( pAviFile ) {
        AVIFileRelease(pAviFile);
        pAviFile = NULL;
    }

注意すべきなのは、AVIストリームを作成していたらまずこちらから開放する必要があるところです。

以上駆け足になりましたが、AVIファイルの作成方法についてのメモ書きでした。