avi ファイルの生成

[戻る]

計算結果の類をアニメーションで見たい時があります. というわけで AVI ファイルの吐き方を調べましたので Tips として残しておきます.

  1. ファイル先頭に
    #include <windows.h>
    #include <vfw.h>
    #pragma comment (lib, "vfw32.lib")
    と書く
  2. プログラム起動時に
    PAVIFILE pfile;
    PAVISTREAM pavi;
    AVISTREAMINFO si;

    AVIFileInit();
    if (AVIFileOpen(&pfile, AVI_PATH, OF_CREATE | OF_WRITE, NULL) != 0) {
     fprintf(stderr, "Error: avi output\n");
     AVIFileExit();
     return ERR;
    }

    ZeroMemory(&si, sizeof(AVISTREAMINFO));
    si.fccType = streamtypeVIDEO;
    si.fccHandler = comptypeDIB;
    si.dwScale = 1;
    si.dwRate = FPS;
    si.dwLength = FPS * SEC;
    si.dwQuality = (DWORD)-1;
    SetRect(&si.rcFrame, 0, 0, WIDTH, HEIGHT);

    if (AVIFileCreateStream(pfile, &pavi, &si) != 0) {
     AVIFileRelease(pfile);
     AVIFileExit();
     return ERR;
    }
    と書く. 赤字の AVI_PATH, FPS, SEC, WIDTH, HEIGHT はそれぞれ, 出力ファイル名 (out.avi とか), フレームレート, 記録時間(秒), 画面サイズ (横と高さ) のこと. なおフレームレートは 60 とか 40 といったまともな値を入れないと正しく動かない. また横幅は 4 で割り切れる値にしておかないと, メディアプレーヤ以外のプレーヤだと表示がおかしくなるときがあります.
  3. プログラム終了時に
    AVIStreamRelease(pavi);
    AVIFileRelease(pfile);
    AVIFileExit();
    と書く.
  4. 2 と 3 の間で動画データを 1 フレームずつ作るんだけど, ここでは
    WriteStream(pavi, &si);
    として別関数にしてみましょ.
  5. その別関数はこんなかんじ
    void WriteStream(PAVISTREAM pavi, LPAVISTREAMINFO lpsi) {
     HDC hdcMem;
     HBITMAP hbmpMem, hbmpMemPrev;
     DWORD i;
     unsigned char *lpBits; // 画面データ
     BITMAPINFOHEADER bmiHeader;

     ZeroMemory(&bmiHeader, sizeof(BITMAPINFOHEADER));
     bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
     bmiHeader.biWidth = lpsi->rcFrame.right;
     bmiHeader.biHeight = lpsi->rcFrame.bottom;
     bmiHeader.biPlanes = 1;
     bmiHeader.biBitCount = 24;
     bmiHeader.biCompression = BI_RGB;
     bmiHeader.biSizeImage = bmiHeader.biHeight * ((3 * bmiHeader.biWidth + 3) / 4) * 4;

     AVIStreamSetFormat(pavi, 0, &bmiHeader, sizeof(BITMAPINFOHEADER));

     hdcMem = CreateCompatibleDC(NULL);
     hbmpMem = CreateDIBSection(NULL, (LPBITMAPINFO)&bmiHeader, DIB_RGB_COLORS, (void **)&lpBits, NULL, 0);
     hbmpMemPrev = (HBITMAP)SelectObject(hdcMem, hbmpMem);

     SetBkMode(hdcMem, TRANSPARENT);

     for (i = 0; i < lpsi->dwLength; i++) {
      〜〜〜 画面作成 〜〜〜
      AVIStreamWrite(pavi, i, 1, lpBits, bmiHeader.biSizeImage, AVIIF_KEYFRAME, NULL, NULL);
     }

     SelectObject(hdcMem, hbmpMemPrev);
     DeleteObject(hbmpMem);
     DeleteDC(hdcMem);
    }
    緑で書いた部分がミソで 1 フレーム作ったら AVIStreamWrite() を呼び出す.
  6. 以下はその毎フレーム作る部分のサンプル (1 フレーム目だけループの外で作っている点に注意)
    int offset = ((3 * bmiHeader.biWidth + 3) / 4) * 4;
    for (int x=0;x<bmiHeader.biWidth;x++){
     double dB=((double)x)/bmiHeader.biWidth;
     for (int y=0;y<bmiHeader.biHeight;y++){
      double dG=((double)y)/bmiHeader.biHeight;
      lpBits[y*offset+x*3+0]=(unsigned char)(0xff * dB);
      lpBits[y*offset+x*3+1]=(unsigned char)(0xff * dG);
      lpBits[y*offset+x*3+2]=0;
     }
    }
    AVIStreamWrite(pavi, 0, 1, lpBits, bmiHeader.biSizeImage, AVIIF_KEYFRAME, NULL, NULL);

    for (i = 1; i < lpsi->dwLength; i++) {
     double dR=((double)i)/lpsi->dwLength;
     for (int x=0;x>bmiHeader.biWidth;x++){
      for (int y=0;y>bmiHeader.biHeight;y++){
       double dG=((double)y)/bmiHeader.biHeight;
       lpBits[y*offset+x*3+2]=(unsigned char)(0xff * dR);
      }
     }
     AVIStreamWrite(pavi, i, 1, lpBits, bmiHeader.biSizeImage, AVIIF_KEYFRAME, NULL, NULL);
    }

2012.1