Video For WindowsによるAVIフレームの取得

Video For Windowsを使うと、AVIファイル内の画像フレームを簡単に取り出すことができます。コーデックで圧縮されていても、(対応コーデックなら)任意のフレームを指定した形式のDIBとして取得できるので、AVIの形式をほとんど意識することなく取得したフレームの処理に重点を置いて開発できるので、なかなか便利です。
今回は、このAVIの操作でも最も基本となるビデオストリーム内のフレーム(静止画)取得を試してみることにしました。

ビデオストリームとフレームの取得

実際のAVIファイル内のフレーム取得は、

  1. AVIファイル操作ライブラリ初期化(AVIFileInit())
  2. AVIファイルを開く
  3. ビデオストリームを取得する
  4. 取得したいDIBの形式を設定する
  5. フレーム取得を始める
  6. ビデオストリームから指定した形式でフレームを取得する
  7. フレーム取得を終える
  8. ストリームとAVIファイルを閉じる
  9. AVIファイル操作ライブラリを閉じる(AVIFileExit())

という流れで行います。

AVIファイルを開く処理は、AVIFileOpen()AVIファイルのファイルインターフェース(ハンドル)を格納するPAVIFILE型ポインタ、ファイルパスと開くときのオプション(読み取り専用、他のプロセスとの共有設定)を渡して行います。関数が成功するとポインタにファイルインターフェースが入ってくるので、続いてファイルインターフェースを使ってファイル内のビデオストリームを開きます。この関数の戻り値は、「0なら成功」という意味なので注意してください。

 ストリームを開く処理もファイルと同様に、ストリームインターフェースのポインタを渡すとそのポインタに取得したストリームを操作するためのストリームインターフェースが入ってきます。

  /* AVIファイルを開く */
  AVIFileOpen(&pfAvi, lpszAviFilePath, OF_READ, NULL);

  /* AVIのビデオストリーム取得 */
  AVIFileGetStream(pfAvi, &psAvi, streamtypeVIDEO, 0);

ストリームを開いたら(別にその前にやっておいてもかまいませんが)、フレームデータとして取得するDIBの形式を指定します。この形式の設定はライブラリ任せにもできますが、実際にフレームのデータを使って処理をすることを考えると自分の都合の良い形式を設定しておいたほうが後で楽でしょう。具体的には、取得するフレームデータのBITMAPINFOHEADERを設定します。これで、フレームの内容を指定した形式のピクセル列として取得できるようになるわけです。
 というわけで、今回は32ビットDIBに・・・と思って実行してみるとフレームデータが入って来るはずのポインタがNULLになったので、24ビットにしておきます(24-32ビット変換くらいしてくれても・・・)。

 なお、BITMAPINFOHEADERに指定するDIBのサイズは、予めAVIのフレームサイズをAVIFileInfo()などで取得しておき、それをそのまま使えばよいでしょう。

 フレームの形式を設定したら、そのBITMAPINFOのポインタとストリームインターフェースをAVIStreamGetFrameOpen()に渡せば、フレーム取得の準備は完了です。関数が成功すると、フレームの取得時に使うPGETFRAMEが返ってきます。

  ZeroMemory(&bmiFrame, sizeof(BITMAPINFOHEADER));

  /* 取得するDIBの形式設定 */
  bmiFrame.biSize = sizeof(BITMAPINFOHEADER);
  bmiFrame.biPlanes = 1;
  bmiFrame.biBitCount = 32;
  bmiFrame.biWidth = iAviWidth;
  bmiFrame.biHeight = iAviHeight;

  pgfFrame = AVIStreamGetFrameOpen(psAvi, &bmiFrame);

 あとは、取得したいフレームを指定してAVIStreamGetFrame()を呼び出せば、そのフレームの内容が指定した形式のDIBメモリイメージのポインタとして返されます。ただし、このポインタの有効期間は次にAVIStreamGetFrame()を呼ぶかAVIStreamGetFrameClose()を呼び出すまでなので、一時的な処理以外の目的で使うのなら自分で確保したバッファにコピーしておきましょう。

  /* フレームデータ用バッファ確保 */
  lpData = GlobalAlloc(GPTR, sizeof(BITMAPINFOHEADER) + iAviWidth * iAviHeight * 3);

  /* フレームデータをバッファにコピー */
  CopyMemory(lpData, lpTmp, sizeof(BITMAPINFOHEADER) + iAviWidth * iAviHeight * 3);

 フレームを取得し終えたら、AVIStreamGetFrameClose()を呼び出してフレーム取得を終了し、ストリームとAVIファイルを閉じます。

  AVIStreamGetFrameClose(pgfFrame);

  /* ストリーム解放 */
  AVIStreamRelease(psAvi);

  /* AVIファイル解放 */
  AVIFileRelease(pfAvi);

 以上で、フレームを取得する処理は終了です。

プログラム

 今回は、ソース内で指定したAVIの各フレームをウインドウ上に表示し、マウスの右/左クリックでフレームを移動するプログラムを作ってみました。まず、ソース冒頭の

LPCTSTR lpszAviFilePath = "C:\\test.avi";

という部分を、適当なAVIファイルのパスに変えてください。その後実行すると、指定したAVIファイルのフレームが表示されるはずです。ウインドウ上でマウスを右/左クリックして、他のフレームも表示してみてください。

プログラムソース表示

なお、今回のプログラムではDIBの4バイト境界への対応を省略しているので、AVIファイルのフレームの横幅は4の倍数ピクセルになっている必要があります。


プログラミング資料庫 > Windowsプログラミング研究室