連載2 画像転送システム その3
コーデックを使うにはvfw.hとaviriff.hの2つのヘッダーファイルをインクルードすることと、vfw32.libのリンクが必要です。
コーデックの処理はIC~というAPIを駆使して行きますが、ややっこしいのは入力フォーマット(圧縮前のフォーマット)と出力フォーマット(圧縮後のフォーマット)の扱いでしょうか。これらは基本的にBITMAPINFOという構造体を使いますが、コーデックによってはこの構造体が拡張される場合があるので構造体のサイズは不定となります。なので、変数の定義としてはポインタにしておき、必要に応じてその都度バッファを確保するようにします。
先ずは、入力フォーマット(圧縮前のフォーマット=DIBセクションと同じ)を用意します。これはBITMAPINFOでいいので簡単です。また設定内容はDIBセクションと同じにして下さい。
次に出力フォーマットを用意しますが、これはコーデックから取得する必要があり少々面倒です。先にコーデックへアクセスする為のハンドルを取得しましょう。
ハンドルの取得はICOpen()の二番目の引数に使いたいコーデックのFourCCを指定することで得られます。今回はAMV3ビデオコーデックを使うので、この部分はFCC('AMV3')と記述します。FCC()はマクロでaviriff.hの中で定義されています。これを使うと文字列を簡単にFourCCコードに変換してくれます。
ハンドルの取得が出来たら次の3段階に分けて出力フォーマットを取得します。
(1)出力フォーマットのサイズを得る。
出力フォーマットは拡張されている可能性があるので、構造体のバイト数が幾つになるか問い合わせましょう。ICCompressGetFormat()の3番目の引数に0を指定すると戻り値として必要なバイト数が返されます。
(2)出力フォーマットを格納するバッファを確保する。
1で取得したバイト数分だけバッファを確保します。
(3)出力フォーマットを得る。
ICCompressGetFormat()の3番目の引数に2のバッファを指定することで出力フォーマットを得ることが出来ます。
入力と出力のフォーマットが用意できたらICCompressBegin()で圧縮処理の初期化、ICCompress()で圧縮、ICCompressEnd()で圧縮終了となります。
また、これとは別に圧縮後のデータを格納するのに最大何バイトのバッファが必要か調べます。多くの場合ICCompressGetFormat()で得た出力フォーマットのglpbiOut->bmiHeader.biSizeImageに格納されていますが、この部分が0のコーデックもありますので、その場合はICCompressGetSize()で取得しましょう。私の場合は両方の値を比較して大きい方を使うことにしています。バッファサイズが決まったらすかさずバッファを確保してこれで準備完了です。
なお、FourCCと出力フォーマットはクライアント側の復元処理(デコンプレス)の際に必要となりますので、クライアント側へ通知しておきます。
#include <vfw.h> #include <aviriff.h> #pragma comment(lib,"vfw32") // コーデック情報の送信につけるヘッダー struct NETWORK_CODEC_HEADER { DWORD dwbiSize; // コーデック情報のバイト数 DWORD dwFcc; // コーデックのFourCC DWORD dwReserve[2]; // 未使用 }; // コーデック処理で使う変数 HIC ghIC = NULL; // コーデックのハンドル LPVOID glpCompressBuff = NULL; // 圧縮された画像を格納するバッファへのポインタ BITMAPINFO *glpbiIn = NULL; // 圧縮前の画像フォーマット(ビットマップインフォ) BITMAPINFO *glpbiOut = NULL; // 圧縮後の画像フォーマット(実態はコーデックにより異なる) // コーデック処理の初期化 BOOL CodecInit( SOCKET sock ) { // 圧縮元のフォーマットを設定(DIBと同じ設定にする) glpbiIn = (BITMAPINFO*)malloc( sizeof(BITMAPINFO) ); if (glpbiIn == NULL) return FALSE; ZeroMemory(glpbiIn, sizeof(BITMAPINFO) ); glpbiIn->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); glpbiIn->bmiHeader.biBitCount = 32; glpbiIn->bmiHeader.biPlanes = 1; glpbiIn->bmiHeader.biWidth = gdwSizeW; glpbiIn->bmiHeader.biHeight = gdwSizeH; glpbiIn->bmiHeader.biSizeImage = gdwSizeW*gdwSizeH*4; // コーデックのハンドルを取得(FourCCでコーデックの選択ができます) ghIC = ICOpen( ICTYPE_VIDEO, FCC('AMV3'), ICMODE_COMPRESS ); if (ghIC == NULL) return FALSE; // 圧縮後のフォーマットをコーデックから取得する // (1)圧縮後のフォーマットを格納するバイト数を取得する DWORD size = ICCompressGetFormat( ghIC, // コーデックのハンドル glpbiIn, // 圧縮元フォーマット 0 // 圧縮後のフォーマット部を0にすると、バイト数を戻り値として返す ); if (size == 0) return FALSE; // (2)圧縮後のフォーマットを格納するバッファを確保 glpbiOut = (BITMAPINFO*)malloc( size ); if (glpbiOut == NULL) return FALSE; // (3)圧縮後のフォーマットを取得 DWORD ret = ICCompressGetFormat( ghIC, glpbiIn, glpbiOut // このバッファへ圧縮後のフォーマットが格納される ); if (ret != ICERR_OK) return FALSE; // コーデックの圧縮処理を初期化 ret = ICCompressBegin( ghIC, glpbiIn, glpbiOut ); if (ret != ICERR_OK) return FALSE; // 圧縮後のデータの最大バイト数を取得する DWORD dwSizeImage = ICCompressGetSize( ghIC, glpbiIn, glpbiOut ); if (glpbiOut->bmiHeader.biSizeImage < dwSizeImage) glpbiOut->bmiHeader.biSizeImage = dwSizeImage; // 圧縮後のデータを格納するバッファを確保 glpCompressBuff = malloc(glpbiOut->bmiHeader.biSizeImage ); if (glpCompressBuff == NULL) return FALSE; // コーデック情報(圧縮後のフォーマット)をクライアント側へ送信(復元に使います) NETWORK_CODEC_HEADER network_codec_header; ZeroMemory( &network_codec_header, sizeof(network_codec_header) ); network_codec_header.dwbiSize = glpbiOut->bmiHeader.biSize; // コーデック情報のバイト数 network_codec_header.dwFcc = FCC('AMV3'); // コーデックのFourCC size = send_all( sock, (char*)&network_codec_header, sizeof(network_codec_header), 0 ); if (size == SOCKET_ERROR) return FALSE; // コーデック情報を送信 size = send_all( sock, (char*) glpbiOut, glpbiOut->bmiHeader.biSize, 0 ); if (size == SOCKET_ERROR) return FALSE; return TRUE; } |
この連載の中で一番シンドイ部分ですが、出力フォーマットと圧縮後の最大バイト数の扱いだけ気をつければ何とかなると思います。逆に出力フォーマットをBITMAPINFO構造体に固定してしまい上手く行かないケースが多そうですね。次回は圧縮処理の本編を紹介します。
- 関連記事
trackback
コメントの投稿