連載2画像転送システム■サンプルプログラム画像転送システム(サンプルプログラム)ライセンスはフリーウエアとなっています。ソースファイルは変更の有無、コンテンツの有料/無料を問わず、ご自身の責任の元自由にご利用頂けます。コーデックを使ったエンコード(画像の圧縮)及び、デコード(画像の復元)の参考になれば幸いです。【使い方】プログラムはサーバーとクライアントに分かれています。設定は付属のiniファイルをメモ帳で編集して下さい。サーバー側はポート番号の設定のみです。クライアント側はポート番号とサーバーのアドレスを設定してください。設定が終わったらサーバー側を先に起動します。次にクライアントを起動すると勝手に接続されサーバー側のデスクトップ画像がどんどん転送されてきて表示します。AMV3ビデオコーデックがインストールされていないと利用できません。他のコーデックを利用する場合はプログラム内のFourCCを書き換えて下さい。普通にコンパイルできると思いますが、サーバー側のみマルチスレッドライブラリを利用しています。USE_THREADをコメントアウトして下さい。コメントアウトするとマルチスレッド系のAPIは使いません。■連載2-1:画像転送システムその1(2009.2.13)ファンタジーリモートのメイン処理とも言える画像転送システムについて、コーデックの使い方を中心に紹介していこうと思います。プログラムは画像を送る側の「サーバー」と、受け取った画像を表示する「クライアント」の2つからなり、主な処理は次のようになります。デスクトップキャプチャーにはGDIのBitBlt()を使いDIBデータを取得します、それをコーデックのコンプレッサーで圧縮して、最後にTCPのソケット通信で送信します。この処理を延々と繰り返すのがサーバー側のプログラムです。クライアント側はソケット通信でデータを受信するところから始まります。受信したデータはコーデックのデコンプレッサーを使ってDIBデータを復元します。復元されたDIBはGDIのStretchBlt()でウインドウいっぱいに表示します。クライアント側はこの処理を延々と繰り返します。実際にはサーバー側の処理が早すぎるとクライアントの処理が間に合わず通信データが溜まって行ってしまうので、サーバー側にはウエイトを入れてフレームレートをコントロールします。ちなみに、アマレココの場合はキャプチャーと圧縮までがサーバー側プログラムと同じで後はaviのヘッダーを付けてファイルへ保存する処理となります。送信部分がファイル処理に置き換わるだけなので殆ど同じ仕組みで動いていると言えますね。次回からは各々の処理についてもう少し詳しく書いていきたいと思います。■連載2-2:画像転送システムその2(2009.2.15)今回は、サーバー側のキャプチャー部です。すべてGDIのAPIで処理しますので簡単です。キャプチャー処理はオフスクリーンのデバイスコンテキスト(ghOffDC)を一つ用意して、そこにデスクトップのデバイスコンテキスト(ghDC)から画像をブリットすることで行います。取り込んだ画像はコーデックに送る必要があるので、オフスクリーンにはDIBセクションをアタッチし、画像データへアクセスするためのアドレス(glpOff)を取得しておきます。DIBセクションでは色数等を自由に設定できますが、コーデックが対応している色数にする必要があるので注意して下さい。AMV3コーデックの場合はRGB32かRGB24が使えます。デスクトップの色数も同じにしておくと、最も効率良く処理することが出来ます。言うよりも色数が違うとGDIによる変換処理が加わりものすごく遅くなりますので、そういった使い方は意図的に出来ないようにしておいた方が良いでしょう。最近はデスクトップの色数をRGB24にすることはマレ(若しくはできない?)なので、殆どの場合はRGB32がベストと言うことになります。・コーデックはこのDIB(glpOff)を元に圧縮します。加えると半透明ウインドウも一緒にキャプチャーします。);returnTRUE;}基本的にキャプチャー処理はBitBlt()で行いますが、GDI処理の遅いパソコンの場合は一度で画面全体を取り込もうとするとゲームなどのfpsがガクッと落ちてしまいますので、画面を何分割かして少しずつ取り込むようにした方が好ましい場合もあります。アマレココやファンタジーリモートの分散処理オプションがこれに相当します。■連載2-3:画像転送システムその3(2009.2.17)今回はサーバー側のコーデックによる圧縮処理(準備編)を紹介します。コーデックを使うには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と出力フォーマットはクライアント側の復元処理(デコンプレス)の際に必要となりますので、クライアント側へ通知しておきます。FALSE;returnTRUE;}この連載の中で一番シンドイ部分ですが、出力フォーマットと圧縮後の最大バイト数の扱いだけ気をつければ何とかなると思います。逆に出力フォーマットをBITMAPINFO構造体に固定してしまい上手く行かないケースが多そうですね。次回は圧縮処理の本編を紹介します。■連載2-4:画像転送システムその4(2009.2.18)今回はコーデックを使って圧縮する処理を紹介します。圧縮処理はICCompress()を一つ使うだけなので簡単。引数が多いですがAMVビデオコーデックをはじめ最近のコーデックの場合は殆どの引数を無視しますので0にしておきましょう(もちろんコーデックによっては個々の引数が意味を持つ場合もあるのでそういったコーデックを利用する場合はしっかり調べましょう)。結局、重要な引数は入力フォーマットと、入力バッファのアドレス、出力フォーマットと出力バッファのアドレスと非常にシンプルです。各フォーマットは準備編で用意したものを使いますが、ICCompress()の引数の型がなぜかLPBITMAPINFOHEADERとなっているので念のためヘッダーの方を指定しています。入力バッファのアドレスはDIBセクションのアドレスを指定します。出力バッファのアドレスは準備編で用意したものを使いましょう。//圧縮動作の制御フラグ(キーフレームなど)//AMVコーデックでは使いません。//予約されています。//AMVコーデックでは使いません。//目標ビットレート、AMVコーデックでは使いません。//画質、AMVコーデックでは使いません。//前フレームのフォーマット、AMVコーデックでは使いません。//前フレームを格納するバッファ、AMVコーデックでは使いません。//キーフレームとして圧縮されたreturnTRUE;}ICCompress()が正常に終わると戻り値としてICERR_OKがセットされ、出力バッファに圧縮後のデータがセットされています。圧縮後のデータが何バイトになるかは出力フォーマットのメンバー変数glpbiOut->bmiHeader.biSizeImageで確認できます。処理したフレームがキーフレームになる場合はdwFlagsOutにAVIIF_KEYFRAMEがセットされます。他のフラグと一緒にセットされることもあるようなので、イコールで判定するのではなく論理andを使って該当するビットが立っているかどうかで確認するようにしましょう。以上で、コーデックを使った圧縮処理は終わりとなりますが、APIの中には他にもICSeqCompressFrame()というコーデックを使って動画を圧縮出来るものがあります。こちらの方が細かい部分をAPIの方で管理してもらえるので扱いが楽なのですが、出力バッファを指定できないと言う欠点がありまして、処理速度が求められる場合などには向きません。アマレココの3.00まではICSeqCompressFrame()を使っていたのでコーデックにより圧縮されたデータはAPIが管理するバッファへ格納されてしまい、それをワザワザ自分で用意した出力バッファ(ファイルバッファ)へコピーしていました。このコピー処理がまるまる無駄となりコーデックを使った場合のオーバーヘッドとなっていましたが、3.01からICCompress()を使うようにしたので直接ファイルバッファへ書き出すことが出来(コピー処理が無くなった分)高速化となっています。次回はサーバー編の最終回、ネットワークの送信処理を紹介します。■連載2-5:画像転送システムその5(2009.2.20)ネットワーク処理に関してはWinsockを使ってTCPで行います。普通に作れば良いと思うので準備及び接続は省略して要点のみ書きます。送信処理で重要なのが一度に送信するバイト数で、大きすぎても小さすぎてもパフォーマンスは低下します(なんだかハードディスクドライブのアクセスのコツと似ていますね)。);の様に4096バイトずつこまめに転送する方が早く転送できるようです。私の場合には6kByteくらいで分割すると最も良い結果がでました。理由は不明ですがジャンボフレームなどが影響しているのかな?と言う事で、次のような送信関数を使っています。最大送信サイズ(これより大きいデータは、このサイズに分割して送信します。一定の大きさ(PAKET_DATA_SIZE)に分割して送信します。size;}returnlen;}■連載2-6:画像転送システムその6(2009.2.25)今回からはクライアント側の話になります。基本的にはサーバー側と似ていますので要点以外はチャチャッと行きます。ネットワーク処理からですが特別な処理は必要ないので省略して、コーデックの処理から始めます。構造体やグローバル変数はサーバー側と同じモノを使い、最初にサーバー側で使ったコーデックの情報をネットワークから受信します。受信は2回に分けて行い、一回目はコーデックのFourCCとサーバー側glpbiOutのバイト数を取得。次にバイト数分のバッファをglpbiInに確保してから二回目の通信でサーバー側のglpbiOutの内容をglpbiInへ受信します。コレによりサーバー側のglpbiOutとクライアント側のglpbiInが同じ内容となりクライアントの入力フォーマットとします。次に出力フォーマットglpbiOutです。基本的にはサーバー側のglpbiInと同じ設定になりますが完全に一致させる必要は無いのでネットワークは使わずに独自に設定しましょう。このとき画像サイズの設定だけ注意が必要です。画像サイズはコーデックにより変更される場合がありまして、例えばAMV3ビデオコーデックのハーフサイズオプションにより画像サイズが変更されます。言ったケースに対応するためにglpbiOutの画像サイズはキャプチャー時の画像サイズではなくglpbiInに設定されている画像サイズを使います。入力/出力フォーマットが用意できたら、コーデックのデコンプレッサーのハンドルを取得します。このときコーデックのFourCCが必要になりますがこのFourCCはサーバー側から受信したFourCCを使います。次にデコンプレッサーを初期化してデコンプレッサーの準備は完了です。最後に圧縮されたデータを格納するためのバッファglpCompressBuffを確保して終わり。glpbiIn->biCompressionを使ってはダメ。//必ず、圧縮の時に使ったFourCCを使いましょう。//ICMODE_DECOMPRESSとなります。圧縮時とは異なるので注意。圧縮された画像データを格納するバッファを確保する。未圧縮よりもサイズが大きくなるコーデックも有りです。最大データサイズを取得しましょう。FALSE;returnTRUE;}サーバー側のコンプレッサーの準備に比べると、クライアント側のデコンプレッサーの準備はその殆どをサーバ側から受信して済ませるので楽ですね。レアなケースですが画像サイズが変更される可能性があることだけ注意しておけば大丈夫だと思います。次回は実際にデコンプレッサーを使って圧縮された画像の復元処理を紹介します。■連載2-7:画像転送システムその7(2009.2.28)今回は圧縮された画像データを元の画像に復元する処理を紹介します。利用するAPIはICDecompress()一つなので簡単です。注意すべきは入力フォーマットのメンバーに圧縮された画像データのバイト数を事前にセットしておく事だけです。後は、入力フォーマットと、入力データが格納されたバッファ、出力フォーマットと復元後の画像を納めるバッファを指定してICDecompress()を呼び出します。サンプルプログラムではの第二引数を0としていますが、ここにはキーフレームである事を示すフラグ(キーフレームでない事を示すフラグ)や、NULLフレームである事を示すフラグをセットしてコーデックにコレラの情報を伝えます。今回はNULLフレームは出てこない事とAMVコーデックはキーフレームの管理をコーデック側で行うので0となっています。圧縮された画像データのバイト数を圧縮フォーマットのメンバーにセットします。FALSE;returnTRUE;}次回はクライアント側のGDI処理です。■連載2-8:画像転送システムその8(2009.3.3)今回は最後に残ったクライアント側のGDI処理を紹介します。サーバー側と殆ど同じですが、作成するDIBセクションの画像サイズはキャプチャしたときの画像サイズではなくコーデックの入力フォーマットを参照しています。コーデックのところでも書きましたが、これはコーデックのリサイズ処理に対応するためです。後は、実際にウインドウへ画像を表示する部分は画像サイズが合わない場合が殆どなのでStretchBlt()を使って拡大または縮小表示しています。・圧縮された画像データはこのDIB(glpOff)に復元します。・ココではRGB32のフォーマットで復元するためのDIBを作成します。・DIBの画像サイズはコーデックの画像サイズに合わせます。※復元された画像はデバイスコンテキスト(ghOffDC)を使って自在に加工できます。);とすると画質がよくなります。メチャクチャ処理に時間がかかるので通常は使えません。画像サイズが小さい場合などに使いましょう。今は廃止になりましたが、アマレココのリサイズにあった「API早い」がCOLORONCOLORで、「API綺麗」がHALFTONEでした。サンプルプログラムでは処理を簡略化するために一番最後に画像サイズを変更していますが、ファンタジーリモートでは画像サイズを縮小する場合はキャプチャー直後にサーバー側で行っています。キャプチャー直後に縮小する事でコーデックの圧縮・復元処理やネットワークによる通信量を減らす事が出来て全体的なパフォーマンスが向上します。逆に画像を拡大しなければならないときはサンプルと同じように表示する時にストレッチしています。次回はサンプルには登場しなかったコーデックの選択方法や設定値の管理などを紹介したいと思います。■連載2-9:画像転送システムその9(2009.3.7)今回はコーデックの選択画面を表示するAPIのICCompressorChoose()を紹介します。初期状態で特定のコーデックを選択する場合はCOMPVARS構造体のfccHandlerメンバーに選択したいコーデックのFourCCをセットしてICCompressorChoose()を実行します。特定のフォーマットに対応したコーデックのみを表示する為にBITMAPINFOHEADER構造体を使い、主にbiCompressionとbiBitCountで利用可能なコーデックを絞ります。画像サイズは大きくすると表示されなくなるコーデックがありますので、実際に使う画像サイズを正直に設定するのではなく小さ目の適当なサイズにしておきましょう。ICCompressorChoose()はコーデックを選択してOKボタンが押されると戻り値としてTRUEを返します。エラーまたはキャンセルボタンが押されるとFALSEが返ります。TRUEが返った場合はCOMPVARS構造体にコーデックの情報が詰まっています。必要なのはFourCCを示すfccHandlerだけなので自前の変数へコピーしておきましょう。ICCompressorChoose()はコーデックを選択した後すぐに圧縮処理が開始できるように色々な準備(メモリの確保など)をしてくれますが、結局それらは使わないのでICCompressorChoose()のあとはすぐにICCompressorFree()でリソース(メモリー)を開放しておきましょう。TRUE){//dwFccに選択されたコーデックのFourCCが格納されています。//このFourCCを使ってAMV3以外の様々なコーデックで画像転送システムを実行できます。}else{//コーデックの選択はエラーまたはキャンセルされました。}}次のようにBITMAPINFOHEADERを使わずにNULLを指定するとインストールされている全てのコーデックを表示する事ができます。)この場合、実際には使えないコーデックや、圧縮処理を持たない(復元専用の)コーデックなども表示されてしまうので不便ですね。■連載2-10:画像転送システムその10(2009.3.10)今回はコーデックの設定画面を表示します。利用するAPIはICConfigure()となりますがこの関数は使わずにICSendMessage()を使って直接コーデックへメッセージを送る事で設定画面の表示を行ってみようと思います。ICSendMessage()ではメッセージ(ICM〜)と2つのパラメータを使ってコーデックを制御します。今回利用するメッセージはICM_CONFIGUREで第1パラメータのみ利用します。第1パラメータには設定画面の親となるウインドウハンドル(hWnd)を指定しますが、良く分からないときはNULLを指定すれば良いでしょう。この第1パラメータに-1を指定するとコーデックに設定画面を表示出来るかどうか問い合わせる事が出来ます。-1を指定して戻り値がICERR_OKであればコーデックは設定画面を表示できます。ICERR_OK以外の場合はコーデックは設定画面を持たないのでそこで処理を終了します。調べておいてコーデックの設定ボタンを無効にするなどしておくと親切ですね。ICERR_OK){//エラーreturnFALSE;}returnTRUE;}なを、ICM_CONFIGUREの部分をICM_ABOUTと置き換える事でコーデックのAbout画面を表示する事が出来ます。今回の様にICOpen()でコーデックのハンドルを取得し、そのハンドルへICSendMessage()でメッセージを送る事でコーデックに関する殆どの処理を行う事が出来ます。今まで紹介してきたIC〜と言う関数の殆どはこのメッセージ処理に置き換えるマクロとなっているので、最終的にはこのメッセージ処理を理解することでコーデックを自由自在に扱えるようになります。■連載2-11:画像転送システムその11(2009.3.13)今回はコーデックの固有ステータスを制御します。固有ステータスと言うのは各コーデックにより内容が異なる為一概に言えませんが多くの場合はコーデックの設定内容を含んでいます。AMVコーデックで言えばハーフサイズのON/OFFやマルチスレッドの設定など設定画面の内容が含まれています。これらのステータスをコーデックから取得したり、逆にアプリケーション側からコーデックへステータスを設定したりする事ができます。コーデックからステータスを取得するにはICM_GETSTATEメッセージを使います。第1パラメータにステータスを格納するバッファのアドレス、第2パラメータにバッファのサイズを設定しますが、第1パラメータをNULLにするとステータスを格納するのに必要なバッファサイズを返すので最初はバッファサイズの取得、次にバッファの確保、最後にステータスの取得と三段階で処理しましょう。次に、ステータスの設定です。利用するのはICM_SETSTATEメッセージです。こちらは第1パラメータにステータスが格納されたバッファのアドレス、第2パラメータにバッファサイズを指定するだけです。hic);returnTRUE;}上の例では取得したステータスをそのまま設定するので意味ないですが、アマレココの場合には設定Aから設定Dまで別々にコーデックのステータスを取得し管理する事で同じコーデックに対しても異なる設定で録画できるようになっています。ファンタジーリモートではクライアント側でコーデックの管理を一括しています。その為、接続時にクライアント側のコーデックのステータスを取得しそれをサーバーへ送信、サーバー側は受信したステータスをコーデックに設定という流れになっています。このようにステータスを上手く管理する事でアプリケーションの利便性が向上しますので是非とも活用したい機能です。全てのコーデックがステータスの取得、設定に対応しているわけではないのでその点は気を付けましょう。