2005年07月10日
分離
もう少しで終わるだろうと思い続けていたけど、まだまだ続きそうな気がするので、カテゴリを新設。
ひとつのカテゴリにエントリーが増えると表示に時間がかかるので、これからは適度な数で分離していった方が良さそう。
投稿者 Takenori : 02:03 | コメント (0) | トラックバック
2005年07月11日
バージョン情報が出ない?
krkr.eXe "-about"
で出るバージョン・著作権・環境ダイアログに出るはずの情報が出ない。
と言うか、何も文字が出ていない。
クリップボードにはコピーされる。
前も何も出ないダイアログボックスがあって気のせいかと思ったけど、どうやらそうじゃなかった様子。
やっぱり、C++Builder6でビルドすると表示されなくなってしまうようだ。
そこで最新の物にしようとソース取得してメイクしたら…
WaveImpl.cppでエラー
あれ?
std::sortでsortがstdのメンバではないと言われる。
なぜ? と思いつつ、#include <algorithm>を追加してみる。
通った。
単にヘッダーがなかっただけのようだ。
で、もう一度krkr.eXe "-about"とするがやはり出ない。
どこに問題があるのだろう…
投稿者 Takenori : 17:49 | コメント (4) | トラックバック
IsEventPastと[wp]と右クリック
periodイベントが発生しない時、デバッガで見るとIsEventPastフラグが立っている。
このフラグが立っていると、periodEventFrameを通過してもperiodイベントを投げない。
これは、現在のフレームより前にperiodイベントが設定された場合、このフラグを立てることによって設定した瞬間にイベントが発生してしまうのを回避するように作ったもの。
このフラグは、Rewindでムービーの初めに戻すか、periodEventFrameより前にフレームを設定するか、再度periodイベントを設定する時に設定しようとしているフレームが現在のフレームより後の場合にクリアされる。
そうか!!と、これを書いていて気付いた。
右クリックに入った時にtempsaveで一時保存した後、抜ける時にtemploadで復元している。
ムービーの途中保存を可能にした時にperiodEventFrameも保存対象にしている。
復元時は保存したperiodEventFrameを再設定している。
つまり、ここでこのフレームよりも先に現在のフレームが進んでしまっていると、IsEventPastフラグが立ち、periodイベントが送られなくなってしまう。
これが原因か。
投稿者 Takenori : 18:45 | コメント (0) | トラックバック
2005年07月12日
復元順の変更
wpと右クリックの問題の解決策として、periodEventFrameの保存をやめようかと思ったけど、そうではなく単に順番を入れ替えれば済むことだと気付く。
今までは、open>frameの設定>periodEventFrameの設定と言う順番だったが、frameとperiodEventFrameの設定を入れ替えれば、IsEventPastフレームは立たない。
で、実際に動かしてみると、今まで発生していたようなケースでは発生しなくなった。
これでもうほとんど問題は解消されたかな。
結構いろいろとあった気がするけど。
もう少しテストしてソース整理したらコミットしよう。
投稿者 Takenori : 01:39 | コメント (0) | トラックバック
ピリオドイベントなどについて
[wp]と右クリックの不具合を修正した物をコミットした。
これで直っているはず。
ピリオドイベントなどは、扱う時にいくつか気を付けておくことがある。
特定のフレームで文字を出したいような場合は次のようにする必要があると思われる。###はフレーム番号。
[videoevent frame=###]
フレーム中にしたい処理
[if exp="kag.movies[0].frame < ###"][wp for="period"][endif]
このようにしてフレームが進んでしまっていた場合にピリオドイベントを待たないようにする。
右クリックメニューに入ったらムービーをポーズした方が良い。
しないと勝手に進んでしまう。
上のようにifでwpを囲んでいれば、戻った時現在の位置まですっとんで行くと思うけど、たぶんそれは困るだろう。
とりあえずは、これぐらいかな。
他にも何かありそうな気はするけど。
セグメントループやピリオドイベントを駆使して1つのムービーでフレームを移動させてムービーを構成した場合、思っていた以上にスムーズにつながってびっくりした。
ただ、無駄にスクリプトが長くなる。
もうちょっと簡潔に書けるようなのを作った方が良いかも。マクロで何とかできるかなぁ? まあ考えよう。
後、字幕も冗長になる。
これも専用の簡潔に書けるものがあったほうがいいかも。
投稿者 Takenori : 23:51 | コメント (0) | トラックバック
2005年07月13日
ダイアログを表示しているところ
W.Deeさんに聞いたので忘れないうちにメモ。
SysInitImpl.cppのTVPCheckAboutで"-about"オプションを付けた時に出るダイアログの処理をしているとのこと。
で、追うとVersionFormUnit.cppのTVPShowVersionFormでFormのMemoのLinesに文字列を入れている。
デバッガでform->Memo->Lines->Assignの後で値があるか確認しようとしたら・・・ 『エラー E2451 未定義のシンボル form』というダイアログボックスが出た。
なんだかよくわからない。
そこで、
list->Text = TVPGetAboutString().AsAnsiString();
の後に、
OutputDebugString(list->Text.c_str());
としてみた。
けど、何も出力されない。
あれ?
とりあえず、ここまで。
投稿者 Takenori : 00:43 | コメント (0) | トラックバック
2005年07月14日
より短く書けるスクリプトを考えてみる
かなり特定の用途に特化しているかもしれないけど、ムービーを制御するためのスクリプトを考える。
用途は、ムービー内の特定フレーム間をループさせたり、指定フレームから指定フレームへ移動したりするもの。
また、音声は別収録のパターンも考える。
まあ、ムービーをつなげてゴリゴリやろうとした時に必要になりそうなものです。
[deflabel name="label_name" frame=###]
[defcut name="section_name" start="label" end="label"]
[defsound name="sound_name" storage="sound_file.ogg"]
[startchapter]
[section name="section_name" cut="section_name" sound="sound_name" sound_delay=### waitfor="" endbehavior=""]
[endchapter]
waitforには、ループ数などの終了条件を書く。
endbehaviorには、終了条件発生時の振る舞いを書く。
これでいろいろな用途に対応できるかな。
字幕やレイヤーの制御などは考えてないけど。
投稿者 Takenori : 21:48 | コメント (0) | トラックバック
2005年07月15日
文字列の対策
-Aboutで出る、バージョン・著作権・環境ダイアログなどの対策が行われたようなのでソースを更新してメイク。
試してみると、出たけど一部文字化けが…
|
吉里吉里実行コアの使用/配布/改変は、 |
が
|
?????赳荒????/??/???? |
になり
|
環境情報 |
が
|
諂??? |
になってた。
とりあえず、問題となっている部分の文字列の前にLをつけたらキチンと表示されるようになった。
でも、そこだけって言うのは美しくないな。
まあ、とりあえずはそれで表示されることは表示されるけど。
投稿者 Takenori : 12:49 | コメント (5) | トラックバック
2005年07月16日
バージョン固有の問題?
特定のパターン(?)を含んだMPEGムービーで、フレーム移動などの操作をすると特定のフレーム付近で吉里吉里を道連れにして突然落ちることがあるようだ。(ただし、ごく稀な様子)
Win2000 + DirectX 8.1 (4.08.01.0901) と言う組み合わせで起こる模様。
Win2000 + DirectX 9.0cだと起こらない。
エラーログで1度だけハードウェア例外が記録されていたので、それを見るとquartz.dllがゼロ番地を読もうとしてアクセス違反が発生したようだ。
そのquartz.dllのバージョンは、6.3.1.885。
何かしら不具合が含まれているのかもしれない。
このような症状があった場合は、DirectXのバージョンをあげてみると良いかもしれない。
投稿者 Takenori : 01:08 | コメント (0) | トラックバック
ムービースクリプト
前回から仕様を少し変えて作りながら思う。
汎用的にはならなさそうだ。
と言うか、そうしない方が効率的かな。
汎用的なのは、現在のKAGを使ったもの。
少し冗長になるけど、いろいろと出来る。
投稿者 Takenori : 01:19 | コメント (0) | トラックバック
スクリプト作らなくてもいい?
ムービー用スクリプトを作らなくても、TJSで直に辞書配列などを使って書いていってしまってもいいんじゃないかと気付いた。
そのムービーの塊待ちタグだけ新たに作れば、いい感じに制御できそう。
ムービーの制御部分は初めVMみたいな感じにしようかと思っていたが、Compositeパターンで組む事にした。
まだ途中だがいい感じかも。
入れ子になった複雑なムービーも制御できそう。
投稿者 Takenori : 19:23 | コメント (0) | トラックバック
2005年07月20日
不用意に複雑かも
ムービーをコントロールスクリプトはとりあえず出来たが、妙に複雑。
単純に仕様が複雑で、判定が多くなってしまっているからのような気もするが、もう少しシンプルに出来ないだろうか?
Compositeパターンでenter、onPeriod、onSegmentLoopPeriod、onClickNextを処理しているが、各クラスでの処理が多すぎな気がする。
もう少し分散できるような構造にしないとメンテナンスが辛い。
検討しないと。
投稿者 Takenori : 14:54 | コメント (0) | トラックバック
2005年07月27日
データの分離
やっぱり、専用のスクリプトがあったほうが良さそうだ。
TJSで直に書いてもいいが、そうすると内部構造の変更がデータにまで及んで、データも変更しないといけなくなる。
データ用のスクリプトを読み込むようにした方が良さそうだ。
現在の構造だと、特定のタイミングで何かをしようとしたらその都度クラスを拡張しないといけない。
汎用性を持たせるためには、もうちょっとというか、かなり構造を変更する必要がありそう。
投稿者 Takenori : 13:35 | コメント (0) | トラックバック
2005年08月12日
ムービーをゴリゴリ動かす
現在のセグメントループやピリオドイベントは表示されているイメージと完全に同じフレーム番号で制御できているわけではない。
過去にいろいろと書いたと思うが、フィルタグラフ内部で描画しようとしているフレームの正確なフレーム番号を取得する方法がわからなかったので、再生が開始されたフレーム番号と再生が開始されてからのフレーム数によって現在のフレーム番号を得ている。
これには問題があって、再生レートなどを変更するとフレーム番号が狂ってしまう。(倍速時ピリオドイベントが機能しない 参照)
そして、ズレを考慮して、フィルタグラフ外の描画イベントで現在のフレームと指示されたフレームに2フレーム以上差がある場合は、現在のフレームを使うようにしている。
ただし、ダブルバッファリングしているので、現在のフレームと指示されたフレームは、1フレームずれているはず。
この関係で1フレーム早くセグメントループやピリオドイベントが処理されることがありうる。
また、内部的な時間はフレームではなく、doubleやlong longだったりするので、そこで誤差が生じている可能性もある。
処理落ちもある程度考えられる。
つまり、何が言いたいかと言うと、完全にフレームをつめてつなげたムービーをゴリゴリ動かすと、変なフレームが見えて、ちらついてしまうことがあるってこと。
また、ムービーの終わり付近にセグメントループの終端フレームを設定すると、セグメントループよりも、ムービー終端のイベントが処理されることもある。
だから、ムービーをつなげてセグメントループなどで制御する場合は、ムービーの区切りの間に2フレーム程度余裕を設けた方が良い。
ムービー終端付近でセグメントループする時も最後に5フレーム程度入れておくとよさそう。
入れるダミーフレームは、直前のフレームと同じ物にしておくと違和感は少ないように感じる。
とは言っても、ムービー終端付近のセグメントループ以外は、それほど問題は発生しない様子。
ピリオドイベントは、指定フレーム(eventFrame)が描画された瞬間に発生する。
セグメントループの終端イベントは、終端フレーム(goFrame)が描画されるべき時に発生する。(実際にはそのフレームは描画されない)
ピリオドイベントには、ムービー区切りの終端フレームを指定し、セグメントループの終端フレーム(goFrame)には、ムービー区切りの終端の次のフレームを指定するようにしておくと良さそうだ。
投稿者 Takenori : 13:38 | コメント (0) | トラックバック
ムービーの制御構造の再検討
現在のはコンポジットパターンで作っていて、下図のような構造で制御できる。

カットはムービーの特定フレーム間を表し、シーケンシャルはコンポーネントを順番に再生する。
シーケンシャルやカットなどの単位でループなどができる。
ただ、現在の構造ではちょっと無理のある部分がある。
現在、上図の単位で言うと、カットがサウンドを持つようになっているのだが、シーケンシャルに入った時に1度だけサウンドを鳴らすようにするってので問題が発生した。
フラグで判断ってのも厳しい。シーケンシャル2の時は1度だけ、シーケンシャル1のループでは、再度鳴らしたい場合などに困る。
とりあえず、シーケンシャルもサウンドを持てるようにしてみたが、はっきり言って美しくない。
で、再考してみた。
サウンドも別コンポーネントにすれば、うまくいくのでは?と思った。
入った瞬間に音鳴らして、すぐに次のコンポーネントを再生するようにすれば、うまくいきそうだ。
しかし、カットの途中、つまり数フレーム遅れで音を鳴らしたい場合にうまくいかない事に気付いた。
そこで、パラレルコンポジットを思い付いた。
ムービーとサウンドを並列に再生されるためのものを作ればうまくいきそうだ。
また、今まではそのコンポーネントに入った時に呼ばれるonEnterで初期化をしていたが、別にIntializeを設け、シーケンシャルのonEnterで呼ぶようにし、そこでフラグなどのクリアをするようにすれば、音を1度だけ鳴らしたいという要求にも堪えられそうだ。
以上のことから再考したクラス図

プロパティやメソッドなどの詳細はもう一度よく考える予定。
でも、やっぱりフレームの更新タイミングでイベントを発生させるようにした方が良さそう。
VideoOverlayクラスにonFrameUpdateイベントを追加しようかな。
一番助かるのは、常にフレームの更新タイミングでイベントが発生してくれることだけど、現状のものでは難しいな。
フィルタグラフの中にそれを行うフィルタとムービーのコントロールを行うフィルタを追加すれば出来そうだけど、かなりややこしいことになりそう。
ま、これは保留だな。
ああそうだ。
これの途中で保存すること考えてない。
投稿者 Takenori : 23:37 | コメント (0) | トラックバック
2005年08月19日
マルチビデオストリームを試してみる
マルチオーディオと同じようにマルチビデオを組み込んでみた。
切り替えにはやはり3~4秒程度かかっているようだ。
停止中に切り替えると、再生開始と同時にそのストリームが再生されるのもマルチオーディオと同じ。
レイヤー描画の場合、停止してもレイヤーに画像が残るので一時停止とあまり変わらない。
だから、一度停止してから、ビデオストリームを切り替えると瞬時に切り替わる。(当然、フレームの記憶と設定も行う)
ただし、音声が含まれているMPEGの時はどうしても一瞬音が途切れてしまう。
タイムラグがある関係で動的に使うにはやはり制限があるな。
音がなければ、それなりに使えると思うが。
投稿者 Takenori : 14:04 | コメント (0) | トラックバック
onFrameUpdateを追加
ムービーのフレームが更新される時に呼ばれるイベントを追加した。
function onFrameUpdate(frame) とした。
これで少し作りやすくなる。
KAGからだと関係なしだけど。
投稿者 Takenori : 14:29 | コメント (0) | トラックバック
2005年08月21日
あんまり用途ない? マルチビデオストリーム
マルチビデオストリームで現在有効なストリーム保存がまだなのでやらないとなーと思いつつ気付く、マルチビデオストリームって使えないんじゃ……
音の場合は、ビデオの方がはるかに容量がでかいので、ビデオは共通で音を何種類か用意しておくってことありそうだけど、逆はどうだろう?
一瞬でビデオが切り替わるのならまだしも、数秒のタイムラグがあるのが辛い。
すぐに切り替えようとしたら一度停止させないといけないので、それなら別ムービーにしてしまっても構わない気がする。
うーむぅ……
ま、保存は気が向いた時にでもやるか。
とりあえず必要としていないし。
投稿者 Takenori : 21:21 | コメント (0) | トラックバック
ASF/WMV/WMAについて調べる
VMR9のサンプルを眺めていてなんとなく、ASF/WMV/WMAが気になった。
PlayWndASFってサンプルをビルドしようとすると
Windows Media Format SDK 7.1.1をインストールして、WMStub.LIBをSamples\C++\DirectShow\Commonへコピーしろと出た。
でも、いまさら7.1.1って、と思いWindows Media SDK コンポーネントを見て、Windows Media Format 9 シリーズ SDKをダウンロード。
それ以上だとXP限定になってしまうので、Win98 SE以降となる9にした。
WMF9 SDKのサンプルのDSPlayをビルドしようとしたら次のようなエラーが出た。
error LNK2019:未解決の外部シンボル "long __stdcall ATL::AtlWinModuleInit( …
error LNK2001: 外部シンボル ""class ATL::CAtlBaseModule ATl:: …
検索するとこのページで同じことが書いてあった。
解決策は「atls.lib か atlsd.lib をリンクするといいそうだ.これは VS .NET 2003 限定みたい.」とのこと。
やってみるとうまくいって再生も出来た。
次にDirectX9 SDKのPlayWndASFをビルドしようとするが、WMStub.LIBはない。調べると、これはもう必要なくなったとか。ただし、DRMを使う時は何かあるよう。
ま、使わないからそこは無視。
インクルードファイルとライブラリファイルのパスを通してビルドするがリンクエラーが大量に出る。
LIBCMTDを無視するように設定する。
が、まだWMCreateCertificateがないというエラーが残っている。
どうもDRM用っぽいので、その部分のコードをコメントアウト。
そこを通ったら毎回失敗するようにした。
これでやっとビルドが通って、wmvファイルの再生も出来た。
が、サンプルではWM ASF Reader Filterを使用している。
これと同じような働きをするフィルターを作らないとしたら結構大変だなー
少し調べるとWM ASF Reader FilterからIWMReaderAdvanced2インターフェイスを取得し、IWMReaderAdvanced2::OpenStreamを使うことでIStream経由でデータを読めそう。
でも、OpenStreamにはIWMReaderCallbackインターフェイスを渡す必要がある。
ReadFromStreamサンプルでIWMReaderCallbackの実装のサンプルがあるけど、OnSampleの中身は空っぽ。あなたのコードをここに追加してと書いてあるだけ。
これで動くんだろうか?
ま、やってみるか。
投稿者 Takenori : 23:06 | コメント (0) | トラックバック
2005年08月22日
WMVは辛そう
CLSID_WMAsfReaderから取得したIWMReaderAdvanced2を使ってOpenStreamをコールするとE_NOTIMPLが返ってくる。
ReadFromStreamサンプルではWMCreateReaderでIWMReaderを作り、そこからIWMReaderAdvanced2を得て、そのIWMReaderAdvanced2のOpenStreamを使っている。
こちらはうまくいくようだ。
どうやらWMAsfReaderフィルタはOpenStreamをサポートしていない様子。
てことは、やっぱり……
これは、かなりしんどいな。
投稿者 Takenori : 15:50 | コメント (0) | トラックバック
2005年08月23日
DirectShowを使わないほうが簡単?
IWMReaderは、Startを呼んだ後、自動的に指定された再生速度でIWMReaderCallbackのOnSampleを呼びながらデコードしていってくれるよう。
IWMSyncReaderは、アプリ側で読み取りながら再生するようになっている。
DirectShowで使うならIWMSyncReaderかと思ったが、WM ASF リーダー フィルタはスプリッタ フィルタも兼ねているため、プッシュ モデルになりそう。
なら、IWMReaderを使ったほうが簡単そうだ。
それよりも、DirectShowを使わないほうが楽かも。
特殊なフィルターをつなぐならDirectShowの方が良いが、そうでないのなら別にDirectShowでなくてもいい。
ただ、VMR9を使うのならDirectShowを使う必要がありそう。
やっぱりフィルタとして実装する方向で行くかな。
投稿者 Takenori : 00:40 | コメント (0) | トラックバック
WMVを再生してみる
ReadFromStreamサンプルのOnSampleで、単純にパラメタとして入ってきたデータを標準出力へ書き出すように変えてみた。
が、OnSampleがコールされません。
デバッガで追ってみると、CReader::Start内で1回WaitForEventが足りない。
OnSampleの中に"Add your code here"って書いてるのに、そこに書いたコードは実行されないという……
で、WaitForEventを追加したらサンプルの情報がずらーっと表示された。
オーディオを再生するサンプルはWMF9 SDKについているのだが、ビデオはないので、これにビデオを表示する機能をつけることにした。
Open後、MediaTypeを取得し、ビデオストリームの番号とMVIDEOINFOHEADERを保持し、ビデオサイズのウィンドウを作って、OnSample内でビデオストリームが来た時にウィンドウへBitBlt。
比較的簡単に出来た。
きちんとした速度で再生されている様子。
フィルタにしなければもうほとんど出来たようなもの。
これをフィルタに持って行くと、ソースフィルタとスプリッタフィルタとデコーダーフィルタがくっついたフィルタになるのか。
つまり、レンダーフィルタだけがつながることになる。
なんとかなるかな。
投稿者 Takenori : 00:55 | コメント (0) | トラックバック
空のフィルタを作る
CBaseOutputPinを継承したビデオ用とオーディオ用のOutput Pinと、CBaseFilterを継承したフィルタを作った。
WMVの再生機能などはなく、単にビデオレンダーフィルタと接続できるようにするのが目的。
とりあえず接続できるようにしてから、中身を作っていく。
で、接続してみたがピンのGetMediaTypeで返すCMediaTypeの中身が完全ではないので、接続処理の途中のビデオレンダーフィルタのSetMediaTypeがコールされた時に落ちてしまう。
やっぱり、中の情報は全部詰めとかないとダメか。
という事で、WMVのIStreamを使ったオープン処理を作ることに。
投稿者 Takenori : 23:29 | コメント (0) | トラックバック
出力形式を変更できない?
WMVのリーダーからメディアタイプを取得するには、まずリーダーでIStreamを開き、IWMReader::GetOutputPropsでIWMOutputMediaPropsを取得して、そのIWMOutputMediaProps::GetMediaTypeでメディアタイプを取得する。
で、何もしない状態だと、ビデオはWMMEDIASUBTYPE_RGB24形式で出力されるようになっているようだ。(必ずそうなっているかどうかは知らない。Codecなどによって違いがありそう。)
だけど、バッファービデオレンダーが受け取る形式はRGB32。
そこで、上で取得したIWMOutputMediaPropsのSetMediaTypeをコールしてRGB32形式で出力されるようにしてみたが、うまくいかない。
SetMediaType自体はS_OKを返すし、その後GetMediaTypeで取得した情報はRGB32形式になっている。
でも、OnSampleでくるバッファの中身はRGB24形式。
IWMReader::SetOutputPropsで、独自のIWMOutputMediaPropsを渡さないとダメなのだろうか?
だとしたら、また面倒だな。
OnSample内では、ビデオレンダーが用意したバッファへコピーする作業が発生する。
だからそこでRGB24からRGB32へ変換しながらコピーしてしまってもいいかもしれない。
という事で、そうやってみたらRGB32で描画できた。
ま、当然か。
よく考えたら、ヘルプをあんまり読んでいない。
長い上に英語だからなぁ。
少し読んでみたが、出力形式を変更する方法は見当たらない。
DirectShowでも説明はなかった気がする。
インターフェイスの説明読みながらいろいろ試して何とかなったって感じだったはず。
書いていて気付いたが、WMF用のアロケーターをDirectShowのアロケーターをラップして作った方が良いかもしれない。
そうすれば、ダイレクトにビデオレンダーが用意したバッファへ書き込まれ、メモリコピー回数が減ってパフォーマンスがあがるはず。
でも、それはしばらく先だな。
出力形式の指定が出来ないと意味ないし。
とりあえずは、OnSample内で自分で変換しながらコピーする実装にしよう。
投稿者 Takenori : 23:36 | コメント (0) | トラックバック
2005年08月26日
E_NOTIMPLTが返ってくる
IWMReaderCallbackに必要なメソッドと、ストリームからオープンするメソッドなどを組み込んで、接続しようとするがつながらず。
失敗しているのは前回と同じ場所。
見てみると、WMVファイルを開いて設定しているはずのWM_MEDIA_TYPEがNULLになってる。
追うとIWMReaderAdvanced2::OpenStreamで失敗している。
E_NOTIMPLが返ってきている。
おかしい。
フィルタとして実装していない方は問題なく動作している。
違いはIStreamの実装か。
サンプルでIStreamのメソッドの内、実装しているのはRead, Seek, Statの3つ。
ブレークを張ると、Read, Statと順に呼ばれている。
Statがくさい。
吉里吉里側は、StorageImpl.h/.cppのtTVPIStreamAdapterが実態のよう。
で、tTVPIStreamAdapter::Statの実装を見ると……
……
return E_NOTIMPL;
これかーっ!
もしかして、WM ASF Reader Filterでも、これが実装されていれば動いたんじゃ……
サンプルの実装では、STATSTGの内typeとcbSizeのみ設定している。
吉里吉里の方ではcbSizeのみ設定している。
とりあえず、type設定して、S_OKを返すようにしてみよう。
投稿者 Takenori : 21:50 | コメント (0) | トラックバック
やはりWM ASF Reader Filterは無理
tTVPIStreamAdapter::Statで、typeを設定して、S_OKを返すようにし、ビルド。
DLL側でデバッグしてみると、問題の箇所は通過した。
やはり、ここが問題だったか。
WM ASF Reader Filterでも同じようにしてみるが、やっぱりE_NOTIMPLTが返ってきた。
やはり、WM ASF Reader FilterのIWMReaderAdvanced2::OpenStreamは使えないのか。
残念なような、無駄骨にならなくて良かったような。
次は、Run, Pause, Stopなどの実装と、IMediaSeekingの実装をしよう。
そうすれば、とりあえずは再生が出来る状態になるはず。
まだ、Audio側は何もしていないので、音は出せないが。
投稿者 Takenori : 22:38 | コメント (0) | トラックバック
2005年08月30日
Statはラッパーで
吉里吉里本体のStorageImpl.h/.cppを変更するのではなく、DLL側でIStraemのラッパークラスを作り、Statの呼び出しだけ異なる動作をするようにしたほうが良いと思い、そのようにした。
動作も問題ないよう。
まあ、ほとんどそのまま呼び出しているだけなので、当たり前といえば当たり前か。
投稿者 Takenori : 21:03 | コメント (2) | トラックバック
2005年09月20日
WMVのライセンス
あんまり詳しく調べていなかったけど、気になる記述を見かけたので調べてみた。
Windows Media をサポートするカスタムのデジタル メディア アプリケーションを開発するによれば、WMVを再生するアプリケーションの開発と配布は、SDKをDLする時に表示されるEULAに同意すればOKのような、ダメなような。。。
2.1 Windows Media フォーマットのファイルの読み書きと編集を行う、Windows ベース アプリケーション (プレーヤー、またはコンテンツの制作、編集、エンコーディングのアプリケーション) を作成して配布するには、どのようにすればよいですか。を見ると、SDKをDLする時に表示されるEULAに同意すればOKとある。
DRMを使用する場合は、別途契約が必要とある。
DRMを使わなければ、EULAに同意することで開発と配布が出来るっぽい。
EULAは、まだ詳しく見ていない。
見ないと。
投稿者 Takenori : 15:25 | コメント (0) | トラックバック
2005年09月25日
VMR9
VMRPlayer9サンプルをビルドして動かしてみた。
Pen4 3G HT + GeForce 6600 GT + WinXPで、640*480のMPEGムービーを2つブレンドしながら再生してもCPU負荷はほとんどない。
これはいいと思ったが、AthlonXP1600 + GeForce2 MX400 + Win2kでやると2つは全然無理。
1つでもかなり重い。
レイヤー描画で再生させた方が軽いぐらい。
HWアクセラレーションが効くかどうかとかあるのだろうか?
単純なオーバーレイ再生ならかなり軽くなるのだが……
ドライバか? と思って、最新のをDLしインストールしようとしたら途中で落ちた。。。
まだ詳しくはソースコードを見ていないが、期待していたほどの効果はないかも。
でも、もう少しいろんなビデオカードで動作させてみたいところだな。
他に今あるビデオカードはMGA Millenium G100とATI RAGE XLぐらい。
これはさすがに無理っぽいな。
投稿者 Takenori : 04:42 | コメント (0) | トラックバック
レイヤー描画の方が軽いかも
オーバーレイレンダーフィルタをVMR9に差し替えて吉里吉里で動作させてみた。
単純にCPU使用率を見た限りでは、オーバーレイぐらいの負荷からレイヤー描画以上の負荷とばらつきがある。
GeForce 6600 GTの方では、オーバーレイ < VMR9 < レイヤー描画となった。
VMR9では、ビットマップとのミキシングが可能なので、メッセージレイヤーをビデオとブレンドできる。
HW支援が効いた場合は、レイヤー描画より低負荷で動作すると思われるが、そうでない場合は同じかより高負荷になりそう。
3つ目のオーバーレイモードmixerとかで実装してみるかな。
投稿者 Takenori : 20:53 | コメント (0) | トラックバック
オーバーレイでもセグメントループを
VMR9を使えば複数の入力ストリームが扱えるので、ダミーストリームを作って、そこからデータが送られるタイミングでEC_UPDATEイベントを送ることによって、フレームの更新タイミングを取得すれば、セグメントループもピリオドイベントも使えるようになると考えていたが、そんなことをしなくてもインプレイスフィルタをレンダーの前に挿入して、そいつがEC_UPDATEイベントを送るようにすれば、更新タイミングを取得できることに気付いた。
描画周りの流れは……
フィルタのReceiveが呼び出されたらCTransInPlaceFilter::Receiveで、出力ピンにつながっている入力ピンのReceive を呼び出す。
入力ピンはレンダーのReceiveを呼び出し、
1. サンプルのレンダリングをスケジュールする (CBaseRenderer::PrepareReceive)。
2. スケジュールされた時間を待機する (CBaseRenderer::WaitForRenderTime)。
3. サンプルをレンダリングする (CBaseRenderer::Render)。
4. サンプルを解放する (CBaseRenderer::ClearPendingSample)。
という流れで処理する。
非同期で処理されている場合は、レンダリングされる前に返ってくる可能性があるが、その時は少しの誤差があるがあきらめることにする。
つまり、CTransInPlaceFilterを継承したクラスのReceiveで次のように書けばOKなはず。
HRESULT hr = CTransInPlaceFilter::Receive(pSample);
if( m_pSink )
m_pSink->Notify( EC_UPDATE, INT_MAX, NULL );
return hr;
後は、このフィルタをグラフ構築時にレンダーの前に突っ込んでやれば、レンダリング時にイベントが来るはず。
投稿者 Takenori : 22:55 | コメント (0) | トラックバック
単純なVMR9モード
単純にレンダーをVMR9にするだけのものを作った。
新たにモードを追加したので、
kag.movies[0].mode = vomMixer;
と言うようにすれば、オーバーレイやレイヤー描画の変わりにVMR9が使われるようになる。
後はミキシングするビットマップを設定するメソッドを追加し、UPDATEイベントを送るフィルタを追加すればOKかな。
どの程度使えるかは未知数だけど。
投稿者 Takenori : 23:53 | コメント (0) | トラックバック
2005年09月26日
インプレイスフィルタの接続
フレーム更新時にイベントを送るインプレイスフィルタの入力ピンと出力ピンはどんなメディアタイプでも受け付けるようにして作った。
で、接続時はまずVMR9をつなげてから、デコーダーとの接続を切り、その間に挿入するようにしたが、なぜか絵が出ない。
接続自体はエラーも出ずに完了している。
そこで、graphedt.exeでどのような接続になっているのか見てみたら、デコーダーとインプレイスフィルタがつながっていない。。。
エラーもなく完了しているのに……
NullInPlaceフィルタサンプルのソースコードを見て、入力ピンのメディアタイプのチェックで、出力ピンが接続されていたら、出力ピンが受け付けるタイプかどうかチェックするように書き換え、先にレンダーを接続するように順序を変えて動かすと、なぜかインプレイスフィルタとレンダーの接続のところでエラー。
エラーメッセージは"これらのピンに共通するメディアの種類はありません。"。
なんだろう?
メディアタイプは出力ピンが提案するような仕組みだった気がするけど、そのような部分はまったく作っていないので、そのせいで接続が失敗してしまっているのだろうか?
他にもいろいろと試行錯誤していて気付いた。
IGraphBuilder::ConnectDirectでは、接続に使うメディア タイプへのポインタを指定出来る。
なら、最初の接続されている状態の時に、接続に使われているメディアタイプを取得して、接続解除した後のインプレイスフィルタとの接続時にそのメディアタイプを指定してやればつながるのではないかと。
で、早速試したところうまくいった。
一瞬、レンダーをラップするようなフィルターを作らないと出来ないかもと思ったが、接続出来てよかった。
後で考えるとそれはそうかと思う。
ピンの接続時には、いくつかのメディアタイプを順番に提示して、入力ピンがつなげると返したメディアタイプが使われる。
つまり、何でも受け入れるインプレイスフィルタは、デコーダーが最初に提示したメディアタイプで接続する。
そして、その後レンダーと接続する時は、そのメディアタイプ以外では接続できない。
が、何でも受け付けるので接続は完了する。
で、いざ再生となるとメディアタイプが一致せずにつながらない状態に。
と言うことだろう。
インプレイスフィルタが、メディアタイプの変換機能を持っていればそうはならないんだろうけど。
投稿者 Takenori : 01:57 | コメント (0) | トラックバック
VMRでセグメントループ
インプレイスフィルタでは、UPDATEイベントをフレーム番号を指定せずに送り、アプリ側ではUPDATEイベントが着たらGetFrameし、そこで得られたフレーム番号を使うことにした。
で、modeがvomMixerの時にもセグメントループやピリオドイベントの処理を追加。
モードをvomMixerとして動作させてみたところ、指定した区間内でループしていることを確認。
ただし、厳密なフレームのつながりなどはまだ見ていない。
GetFrameで得たフレーム番号と実際に表示されているフレームでどの程度差があるのか確認しないと。
これでVMRやオーバーレイでのセグメントループとピリオドイベントの利用が現実的になった。
投稿者 Takenori : 02:44 | コメント (0) | トラックバック
2005年09月27日
VMRでのBMPコピー先矩形の指定
VMR9AlphaBitmapで使われる、コピー先矩形の指定がテクスチャなどのように0.0~1.0方式だ……
使い手としては、ピクセル値の方がいいだろうから、内部的に変換するか。
基本的には幅や高さで割れば出るはずだけど、そのままやったら誤差が出たような。
確か、Direct3Dのテクスチャはラスタ化ルールの影響を受けたはず。
今回はDirectShowの話だけど、同じように0.0~1.0で指定するから同じ問題が発生しそう。
ビデオカードによっては異なる場合があるけど、そこは諦める。
実際の計算については、(left + 0.5) / 幅 というようにやればOKのはず。
投稿者 Takenori : 00:42 | コメント (0) | トラックバック
メッセージレイヤーとのミキシング
VideoOverlay::setMixingLayer( Layer l )で指定されたレイヤーとミキシングするようにした。
ただし、setMixingLayerはレイヤー画像が更新されるたびにコールする必要がある。
ブレンド値は、Layer::opacityを使い
転送先矩形は、
dest.left = Layer::left + Layer::imageLeft;
dest.top = Layer::top + Layer::imageTop;
dest.right = dest.lerf + Layer::imageWidth;
dest.bottom = dest.top + Layer::imageHeight;
とした。
ミキシングするには、画像のHDCを指定する必要がある。
レイヤーが持っているBitmapのHDCを取得するためには次のようにすれば良いようだ。
tTJSNI_BaseLayer::GetMainImage()->GetBitmap()->GetBitmapDC()
でも、デフォルトではDIBSectionは使わないようになっており、実行時の引数に"-dibtype=dibsect"を指定してやればDIBSectionが使われるようになる。
"-dibtype=dibsect"を指定して実行し、kag.movies[0].setMixingLayer( kag.current );とすれば、メッセージレイヤーとブレンドして表示されるようになった。
負荷は、GeForce 6600 GTだとかなり少ない。
他はまだ試していない。
後は次の2つを組み込んでやれば、VMRモードを公開できるレベルになる。
1. DIBSectionでない時は、DCを作ってそこへコピーしてやる。
2. 対象レイヤーが更新される時、ムービーが再生中でかつvomMixingモードの時はsetMixingLayerをコールするようにする。
でも、1はいいとして、2はどうしよう?
KAGの方でLayer::onPaintをオーバーライドしてやればいいかな。
そしたら、自分が設定されているかどうかも知る必要があるか。
KAGのMovieでsetMixingLayerをオーバーライドして覚えるようにしておくか。
なんとかなりそうだな。
投稿者 Takenori : 02:54 | コメント (0) | トラックバック
VMRの他の機能
現時点では、単純にレイヤーとブレンドできるオーバーレイとしか実装していないが、VMR9では次のようなことが出来る。
・Direct3D サーフェイスとのブレンド、
・ウィンドウレスモードで、ウィンドウへの描画
・レンダリングレス モードで、Direct3D サーフェイスへの描画
・複数ストリームの描画
・各ストリームの描画位置の指定
・各ストリームのブレンド率の指定
・独自ブレンド方法の実装
他にもあるかもしれないけど、詳しくは見ていない。
比較的実用的だと思われるのは、複数ムービーのブレンドだろうか。
ただ、途中から再生を始めるとかなると、かなり実装が大変そう。
でも、途中から出来ないと意味なしのような……
考えるといろいろと出来そうだけど、きりがないのでとりあえずは今のでいいか。
投稿者 Takenori : 03:04 | コメント (0) | トラックバック
2005年09月28日
メディアタイプの色深度の確認コードメモ
AM_MEDIA_TYPE mt;
if( mt.formattype == FORMAT_VideoInfo ) {
VIDEOINFOHEADER *vih = reinterpret_cast<VIDEOINFOHEADER *>(mt.pbFormat);
char c[64];
sprintf( c, "%d",vih->bmiHeader.biBitCount);
OutputDebugString(c);
} else if( mt.formattype == FORMAT_VideoInfo2 ) {
VIDEOINFOHEADER2 *vih = reinterpret_cast<VIDEOINFOHEADER2 *>(mt.pbFormat);
char c[64];
sprintf( c, "%d",vih->bmiHeader.biBitCount);
OutputDebugString(c);
}
投稿者 Takenori : 18:10 | コメント (0) | トラックバック
自前でBMP
tTJSNI_BaseLayer::GetMainImage()->GetBitmap()->GetBitmapDC()でHDCが取得できない時は、次のようにして作ることにした。
tTVPBitmap *bmp = Layer::GetMainImage()->GetBitmap();
HDC ref = GetDC(0);
HBITMAP myDIB = CreateDIBitmap( ref, bmp->GetBITMAPINFOHEADER(), CBM_INIT, bmp->GetBits(), bmp->GetBITMAPINFO(), bmp->Is8bit() ? DIB_PAL_COLORS : DIB_RGB_COLORS );
HDC hdc = CreateCompatibleDC( NULL );
HGDIOBJ hOldBmp = SelectObject( hdc, myDIB );
VideoOverlay->SetMixingBitmap( hdc, &dest, alpha );
SelectObject( hdc, hOldBmp );
DeleteObject( myDIB );
デバイス依存ビットマップ(DDB)を作成していので、色深度は画面モードと同じになってしまう。
CreateDIBSectionで作ることも考えたが、わざわざ画像をコピーするのが面倒なので、CreateDIBitmapにした。
最終的に描画される色深度は画面モードと同じになってしまうわけだが、VMRで拡縮された時、32bppで処理されてから16bppになるのと、初めから16bppで処理された場合とでは、初めから16bppの方が少し画質が悪くなりそうだ。
誤差などを考慮すると丸め処理などは出来るだけ後でやった方がいい。
ただ、この場合に認識できるぐらい画質が劣化するのかどうかは知らない。
そこで、VMRが接続されている時に使われているメディアタイプの色深度を確認してみた。
まず、画面モードを32bitにして実行し、確認。
16bitカラー……って、えっ? 16bitカラーで接続されてるの?
デコーダーの優先メディアタイプは16bitカラーなのだろうか?
なら、別に画面モードと同じになってもいいや。
投稿者 Takenori : 18:29 | コメント (0) | トラックバック
2005年09月29日
更新タイミングはいつ?
KAGLayer::onPaintにVideoOverlay::setMixingLayerを呼ぶ処理を追加したが、文字を表示させてもonPaintには来ない様だ。
スクリプトからupdateが呼ばれたら、callOnPaintがtrueになり、onPaintがコールされるようだが、MessageLayer::processChはdrawTextやfillRectを呼んでおり、updateは呼んでいない。
Layer.imageModifiedがtrueなら、VideoOverlay::setMixingLayerを呼ぶようにしてみたが、falseにしないとずっとtrueのようだ。
そこでVideoOverlay::setMixingLayerの中でLayer.imageModified = falseにするようにしてみたのだが、なぜか画像がクリアされない。
文字を表示すると上書きされる。
ソースコードを追っても、imageModifiedで判断しているところは見当たらないと思ったら、MessageLayer.tjsのclearLayerでimageModifiedを参照して、imageModifiedがtrueなら消去している。
でも、その後 imageModified = falseにしているようだが……
その後の処理で何かがtrueにしているのだろう。
とりあえず、imageModifiedで判断している処理を常に行うようにし、EC_UPDATE内でレイヤーの設定を行った後にimageModifiedをfalseにしたら、更新された時のみ再設定されるようになった。
これで目的は達成されたが、imageModifiedの意味が変わってしまうのが問題。
そこで、Movie::onFrameUpdate内で処理するようにした。
でも、imageModifiedを変更してしまってるのは変わらない。
どうするかなぁ。
投稿者 Takenori : 02:30 | コメント (0) | トラックバック
VMRで指定できるプロパティ
VMRでは、ビデオストリームのアルファ値、バックグラウンドカラー、出力矩形、Zオーダーが指定できるので、それぐらいは指定できたほうがいいかな。
他にコントラスト、輝度、色相、彩度の指定も可能だが、ハードウェアに依存するようなので、実装しないことにする。
mixingStreamAlpha
mixingStreamBGColor
mixingStreamLeft
mixingStreamTop
mixingStreamRight
mixingStreamBottom
mixingStreamZ
と言った感じかな。
でも、多いなぁ。
やっぱり、mixingStreamAlphaとmixingStreamBGColorぐらいでいいかな。
投稿者 Takenori : 02:50 | コメント (0) | トラックバック
2005年10月05日
VMR用のプロパティ追加
プロパティにmixingMovieAlphaとmixingMovieBGColorを追加した。
mixingMovieAlphaは、0.0 (完全に透明) ~ 1.0 (完全に不透明)の値をとる。
mixingMovieBGColorは0xRRGGBB形式となる。
これで若干VMRの使い道が出来たかも。
レイヤーでも出来ることだけど、ハードウェア支援があったら軽くなる。
投稿者 Takenori : 19:53 | コメント (0) | トラックバック
2005年10月06日
WMF SDK 9 EULA
Windows Media Format SDK 9のEULAを読んだ感じでは、今回のような用途であれば使用に差し支えなさそう。
ライセンス文は日本語でもわかりづらいのに、英語なのでさらにわかりづらく解釈が間違っているかもしれないが。
関連する部分を抜粋すると・・・
・再生のみを行う。
・再配布可能コンポーネントとサンプルソースを同梱しない。
・DRMで保護されたコンテンツを再生しない。
・サンプルソースから派生して作る場合は、オブジェクトコードでのみ配布可なので、サンプルソースから派生しないようにする。
・ACELP.net Codecを使用しない。
・リバースエンジニアリングしない。
・再配布可能コンポーネントを改変しない。
・ドキュメントに記述されたインターフェイスを通じてのみアクセスする。
以上の条件下で使用するのであれば、特に気にすることはなさそう。
まあ、すでにある程度制約があるともいえるが。
再配布可能コンポーネントを同梱する場合は、いろいろと面倒なので、別途Windows Media Player 9 以降をインストールしてくださいとした方が賢明。(同梱するアプリケーションのライセンスが適切かどうか確認するためにMSに連絡を取らなくてはならないなど)
この文章を元に判断した結果発生した問題について私 井元 武則は一切責任を負いません。
判断は自分の責任において行ってください。
投稿者 Takenori : 19:48 | コメント (0) | トラックバック
Ogg Theoraを見てみる
途中、WMV面倒だなぁと思って、Ogg Theoraを見てみたが、まだアルファ版だった。
Directshow Filters for Ogg Vorbis, Speex, Theora and FLAC がインストールしてある環境でのみTheoraの再生をサポートすると言うのであれば、比較的楽に実装できるかも。
ソースが公開されているので、インストールなしにフィルタ部分を抜き出して内部でフィルタを生成してもいいが、少し面倒だな。
ま、もう少し見てみよう。
投稿者 Takenori : 20:06 | コメント (0) | トラックバック
2005年10月07日
WMでストリームの例外?
ストリームオープン時に"tvpwin32.exe の 0x086c99cc で初回の例外が発生しました : 0xC0000005: 場所 0x00000000 に書き込み中にアクセス違反が発生しました。 。"と言う例外が出るようになった。
しかも、複数。
だけど、何事もなかったように進む。
前はこんなの出ていなかったような…… と思っていろいろと見てみる。
IStream::Read, Seek, Statが呼ばれた後からIWMStatusCallback::OnStatusが呼ばれるまでに起きている。
で、IStreamの実装を見てみたらIStreamのStatが実装されていたので、ラッパーを使わないようにしてみたが、やはり例外は発生する。
そこで、BCBの方でデバッグしてみると、例外が報告されない。
なんだろう?
例外文を良く見ると"ストリームオープン時に"と書いてある。
ファイル自体に何か問題があるのだろうか?
例外が発生するアドレスにブレークを張って呼び出し履歴を見てみると、wmvcore.dll内で例外が発生している様子。
前から発生していたのかなぁ? 記憶にないけど。
wmvcore.dllのバージョンが変わるようなのをインストールした記憶もないし……
ReadFromStreamサンプルの方で確認してみたら、同じ例外が出た。
なんだろう?
もともと発生していたのかな?
よくわからないので、とりあえずは様子見と言うことにしよう。
投稿者 Takenori : 18:05 | コメント (0) | トラックバック
必要なインターフェイスは?
Run, Pause, Stopを実装して、OnSampleでIMediaSampleを得てから、その中へ画像データなどをコピーし出力ピンのDeliverをコールするようにしたが、再生が開始されない。
IMediaControl::Runの返り値を確認してみると、S_FALSEが返ってきている。
どうやら、CBaseFilterを継承しただけのフィルタではダメなようだ。
そこで、IMediaSeekingインターフェイスを実装するべく、CSourceSeekingを継承するも、CUnknownへのキャスト時にあいまいと出てコンパイルが通らない。
仕方ないので、CSourceSeekingを継承したクラスを作り、そのクラスをフィルタクラスのメンバーに保持し、QueryInterfaceでIID_IMediaSeekingへの問い合わせがあった時はそのメンバーへのポインタを返すようにする。
これでコンパイルが通るようになったが、再生は失敗する。
他にどのインターフェイスが必要なんだろう?
もしくは、いずれかのメソッドの呼び出しの返り値などがまずいのだろうか?
もう少し追ってみる必要がありそうだ。
投稿者 Takenori : 20:05 | コメント (0) | トラックバック
Vorbis + TheoraのOgg動画ファイルを作る
昨日Theoraのことを書いたが、kikyou.infoさんのところの日記でも、Theoraを標準にとしてTheoraのことが書かれていた。
で、なんとなくトラックバック。
この辺りについてどのように考えていたかなどについては、また別エントリーにする予定。
最近、いくつか動画をWMVファイルにエンコードをしていたが、エンコードが異様に重い。
もうちょっと速いのがいいなぁとMPEG4やTheoraを検討。
MPEG4も、結構重かった記憶があるので、Theoraについて調べてみようと思ったが、何を使ってエンコードすればいいのか良くわからないってことで、GraphEditを使い、手動でフィルタグラフを構築してエンコードすることにした。
で、Directshow Filters for Ogg Vorbis, Speex, Theora and FLACを入れようとしたら、すでにインストールされてた。
1個古いバージョンだったが、とりあえずそのまま進めることにした。
GraphEditで作ったフィルタグラフは、次のようなもの。
WMVファイルを読んで、Ogg(Vorbis+Theora)ファイルを作っている。
で、再生してしばらく待つと、GraphEditが落ちた。(笑
でも、oggファイルは出来ていて、最後までエンコードも出来ているようだ。
まあ、DX9 SDKに付いているGraphEditはなぜかよく落ちるので、落ちたのはまあ気にしないことにする。(DX8に付いているGraphEditの方がなぜか安定している)
このことから、DirectShowのフィルタで読めるデータは、Ogg動画ファイルに出来そうだ。
また、エディタなどでもプラグインなどで出力フォーマットを増やせるのであれば、DirectShowを利用してダイレクトにOgg動画ファイルに出来そう。
現状Theoraがalpha版なので、サポートしているツールがどの程度あるかは謎だが。
エンコード時間を計るために、SSE2版へコンポーネントをインストールしなおして、フィルタグラフを作りエンコードしてみたら…… 途中で落ちた。
やっぱり、途中で落ちることもあるのか。
でも、コンポーネントのリリースされた時期を見てみると、まだlibtheoraはalpha5ではないよう。
この辺りを差し替えるともう少し安定するかもしれないな。
後、Theoraのクオリティなどをまったく設定できないのも難点。
Vorbis側はプロパティページを実装していて、ある程度設定できるようではあるが。
で、肝心の時間は、70%ぐらいまでしかエンコードできていないものの、15分程度で終わった。
640*480 約24分の動画をTMPGEnc 3.0 XPressで1パスVBRでWMVへエンコードした時は4時間弱もかかったのに。
換算すると約12倍ぐらい速い。
これは期待が持てるなぁ。
と思ったが、途中で終了した動画の時間を見ると16分ぐらいだった。
つまり、リアルタイムでエンコードされてた?
CPU負荷は、Pen4 3GHz 60%ぐらいだった。
もしかして、もっと速くエンコード出来る?
まあ、TMPGEnc 3.0の方ではインターレース除去などもしているので、まったく同じ状況だとは言えないが、WMVよりかなり速そうではある。ドロップフレームがあったのかもしれないけど。
このエンコード速度は結構期待できるかも。
エンコーダー作ろうかな。
投稿者 Takenori : 23:19 | コメント (0) | トラックバック
2005年10月08日
インターフェイスを調べるも良くわからず
何が必要なのかさっぱりわからないので、QueryInterfaceにブレークを張って、どのようなインターフェイスが要求されるか見てみた。
フィルタ関係
IAMOpenProgress ネットワークを介してファイルを開く場合に使われるよう
IAMDeviceRemoval ?ドキュメントには書かれていない
IKsPropertySet 特に関係なさそう
IReferenceClock 基準クロックを提供するフィルタには必要
IMediaSeeking 実装済み
IBasicVideo 関係ない
IVideoWindow 関係ない
IBasicAudio 関係ない
IAMFilterMiscFlags
フィルタがソース フィルタかレンダラかを示す。ソースおよびレンダラ フィルタはこのインターフェイスを実装できる。
とある。
実装したら、問い合わせられるようになった。
IFileSourceFilter 一応実装したが、まったくクエリーで呼ばれない。
自動的にフィルタグラフを作る時以外は関係なさそう。
出力ピン関係
IAMPushSource ライブ ソースをレンダリングする時に使われるよう。関係なさそう。
IKsPropertySet 特に関係なさそう
IMediaSeeking CPosPassThruによって、ダウンストリームから要求されているようなので、実装した。
ただし、中身はフィルタのIMediaSeeking をコールしているのみ。
以上のようにいくつか実装してみたが、まだ再生できない。
いったい何が足りないのだろう?
もう少しヘルプをよく読んだ方がいいかも。
それでもわからなさそうな気はするが。
投稿者 Takenori : 01:30 | コメント (0) | トラックバック
ログの出力
IGraphBuilder::SetLogFile メソッドは、ログ ファイルを設定する。このファイルには操作を実行しようとしたときに行われた各アクションが出力される。
このメソッドはデバッグに使い、フィルタ グラフの自動作成に失敗したときに、その原因を判断することを目的としている。
と言うのがあるので、使ってみることにした。
実行して終了してログを見てみると……
---
フィルタはグラフに追加されました。
フィルタはグラフに追加されました。
[EOF]
えーーーっ!
これじゃ何もわからないって。
自動作成時にどこでグラフが削除されたかとかその言うのを追えるんだろうか?
ま、なんにしても今回は使えなさそうだ。
投稿者 Takenori : 01:57 | コメント (0) | トラックバック
WMVの画像が出るようになった
比較的似ているフィルタであるプッシュ ソース フィルタ サンプルのソースコードを追うイマイチよくわからない。
と言うより、CSourceStreamによってほとんどの機能が実装されているようだ。
と言うことで、CSourceStreamのソースコードを追うと、CBaseFilter::Pause内でCBaseOutputPin::Activeが呼ばれ、その中で再生を開始しているようだ。
つまり、CBaseFilter::Runが最初に呼ばれると言うわけではないのか。
さらに追うと、CBaseRenderer::Receiveが、ポーズ状態の時にコールされたら、イベントを投げて状態の移行を完了し、CBaseRenderer::GetStateからリターンするようだ。
で、そこからリターンした後に、各フィルタのCBaseFilter::Runが呼ばれているみたい。
で、そのように実装するとIMediaControl::Run()がコールされたしばらく後に、CBaseFilter::Runがコールされるようになった。
そういう風に実装するのか。
これでうまくいくかと思いきや、数フレーム再生された後、アクセス違反が発生する。
あれ? なんだろう?
wmvcore.dll内で発生しているようなので、そちらの問題かと思ったが、ReadFromStreamサンプルを実行してみても発生しないので、やっぱり何かまずいことをやっているようだ。
いろいろと調べるとBufferRendererの不具合だったようだ。
BufferRendererでは、最初に出力の対象となるバッファが設定されていない場合、自分で作る。(吉里吉里の初期化順序では、どうしても最初に設定されている状態にはならない)
そして、アロケーターでメディアサンプルが生成される時、バックバッファに設定されているポインタをメディアサンプルのバッファに設定する。
以降、メディアサンプルのバッファは、SwapBufferによって入れ替えるが、これはレンダリングされた直後に行われる。
問題は、アロケーターのメディアサンプル生成後、出力したいバッファを設定する時に、もともとあったバッファが自分で生成したものであった場合に開放するだけで、新たに設定されたバッファをメディアサンプルへ設定しないことにある。
そのせいで、メディアサンプルのバッファとして設定されるのは、最初にレンダリングされた後になってしまう。
つまり、最初のレンダリング時は開放されたメモリ領域に書き込みを行ってしまっていた。
そこで、出力したいバッファを設定する時に、それがバックバッファであったなら、メディアサンプルのバッファとして設定するようにした。
その後確認したら、問題なく再生できるようになった。
いやー、長かった。
今までBufferRendererの不具合がなぜ顕在化しなかったのかはなぞ。
デコーダーの作りが何か違うのだろうか? と言うか、それ以外考えづらいのだが。
出力したいバッファの設定は、レンダーフィルタの状態が停止か一時停止状態の時のみ行えるようにしておいた方が、より安全になりそうだ。
現在は再生中に出力したいバッファ変更するような使い方はしていないが、念のためにそのように変更した。
次にサウンド部分を作れば、一応WMVの再生が行えるようになる。
投稿者 Takenori : 05:23 | コメント (0) | トラックバック
ソース フィルタの振る舞い
フィルタの状態に、フィルタ グラフ マネージャがどのように指示を出すかや、各フィルタの動作について説明があった。
前も読んだ気がするけど、見落としていた。
前はレンダーフィルタだったから、ソースフィルタの振る舞いの部分は意識していなかったのだろう。
投稿者 Takenori : 20:47 | コメント (0) | トラックバック
2005年10月09日
使用するインターフェイスの変更
何とか画像が出るようになったけど、IWMReaderを使ったフィルタではいろいろと不都合が出そうだ。
ソースフィルタはひたすらデータを処理してレンダーへ送る。
Receive または GetBuffer 内でブロックされたら、そこで待つ必要がある。
と言うような動作を行うが、IWMReaderは内部でスレッドを持ち、それ自身で同期を取りながら再生を行う。
だけど、GetBuffer 内でブロックされたら停止しなければならない。
現在、OnSampleで次のフィルタへデータを送っているわけだが、この中で勝手にブロックするのはまずいだろう。
かといって、ブロックされる時にPauseしてしまうと、OnSampleが呼ばれなくなり再生がとまってしまう。
CSourceStreamの実装を見ると、GetDeliveryBufferをポーリングするような作りになっている。
つまり、止まってしまうとまずい。
なら、ワーカースレッドを作ってそいつに……と出来なくはないが、ここはおとなしくIWMSyncReaderを使うように書き換えたほうが良さそうだ。
IWMSyncReaderであれば、非同期にデータの取得が可能なので、Pauseなどの心配がない。
ってことで、WMSyncReaderサンプルを見て、IWMSyncReaderの使い方を調べないと。
投稿者 Takenori : 00:29 | コメント (0) | トラックバック
2005年10月10日
動画拡張の予定と吉里吉里3について
VMR9モードの追加とWMVファイルへの対応が終わったら、とりあえず機能追加の予定はない。
でも、必要になってさらに機能追加する可能性はあるけど。
としたら…… 本家にマージしてもらう時どうしよう?
吉里吉里3が出来るまで、2系のマイナーバージョンが上がっていくこともありうる?
公開しないと言う手もあるにはあるけど、マージしてもらおうと思った時にW.Deeさんが対応してくれるのなら、追加した機能を公開すると言うスタンスになるかな。
それならずっと、吉里吉里2で行くのかと言うと、やはり改良したい(して欲しい)点と言うのはある。
1番大きいのは、BCBじゃなくてVCでデバッグしたいと言うもの。
BCBのデバッガはやっぱり使い辛いっす。
後、現状の構造だと、VideoOverlayクラスにひたすらプロパティとメソッドが追加されて、モードによっては使えたり使えなかったりするのが増えていくのは何とかしたい。出来れば、クラスを分けたい。
内部的には、DSMovie - Layer、DSMovie - Overlay - VMRと言うような構造になっているので、それに近いクラス構造がいいが、OverlayやVMRを使い続けるのかどうかと言うのがある。
吉里吉里3でマルチプラットホームとなった場合、それらが使えるのかどうかと言うのがまず問題になる。
ただ、SDL_Overlayと言うのがあるようなので、Overlayは他のプラットホームでも使えそうではある。(詳しくは知らない)
VMRは無理そうだが、そもそもそれほど必要性がない気がする。
今回追加してみて、思ったほどの効果が得られなかったので、意味なさげだ。
Direct3Dでテクスチャへのムービーレンダリングなどの用途で効果を発揮するもののような気がする。(ムービー再生中に他にいろいろとやることがあるから)
個人的な意見としては、レイヤー描画モードでムービーが再生できれば十分。
ただ、CPU負荷と言うことを考えるのなら、Overlayなどでの再生も必要になるかもしれない。
ま、その差がどの程度かによるが。
DirectShowでMPEGをオーバーレイで再生した時、ビデオカードのムービー再生支援機能などが使われていたとしたら、TheoraをCPUのみでデコードした後、オーバーレイ領域に転送するだけではそれほど恩恵がなさそうだ。
でも、拡大縮小をするのなら効果があるか。
一番の悩みどころは、吉里吉里3への対応をどうするかだな。
思い付くところとしては……
1. 吉里吉里3が出来た後、欲しい機能をポーティングする。
2. 吉里吉里3のムービー部分の開発に参加させて欲しいとお願いする。
3. ムービー再生用のライブラリを作って吉里吉里3に使って欲しいなぁと願う。
4. 独自のゲームエンジンを作る。
さてどれ?
しばらく、考えよっと。
『5. 欲しい機能だけ伝えて、寝て待つ』と言うのもあるにはあるか。
IRC以外に、吉里吉里3開発室ってな感じで、掲示板とメーリングリストみたいのがあるといいなぁ。
XOOPSで作れるようなコミュニティーサイトとか。
今度聞いてみよう。
投稿者 Takenori : 04:27 | コメント (4) | トラックバック
2005年10月12日
IWMSyncReaderでの出力フォーマットの指定
IWMReader、もしくはIWMSyncReaderで、出力させたいフォーマットの指定方法がわかった。
まずは、対象となる出力の番号を得る。
これはストリーム番号とは異なる。
ストリーム番号がわかっている場合は、IWMSyncReader::GetOutputNumberForStreamで出力番号が得られる。
IWMSyncReader::GetOutputFormatCountで出力できるフォーマットの種類の数を得る。
IWMSyncReader::GetOutputFormatでフォーマット番号を指定して、そのフォーマット番号のIWMOutputMediaPropsを得る。(フォーマット番号は、単なる0からフォーマットの種類の数までの値。forとかでIWMOutputMediaPropsを得ていく。)
IWMOutputMediaProps::GetMediaTypeで、メディアタイプを取得し、取得したメディアタイプが出力したいものであれば、IWMSyncReader::SetOutputPropsへIWMOutputMediaPropsを渡す。
このようにすれば、欲しい形式で出力させられる。
投稿者 Takenori : 00:43 | コメント (0) | トラックバック
2005年10月16日
WMVからの読み出し
DirectShowには、フィルタを開発を行いやすくするためにいくつか基底クラスが用意されている。
ソースフィルタの開発用にはCSourceと言うフィルタ用の基底クラスとCSourceStreamと言う出力ピン用の基底クラスがある。
CSourceStreamのFillBufferをオーバーライドし、この中でダウンストリームに渡すIMediaSampleへ各フレームの画像などをコピーするようにすれば、楽にソースフィルタを作ることが出来る。(実際にはこれ以外にアロケーターの設定用と受け入れるメディアタイプの取得用のメソッドもオーバーライドする必要がある。後、CSourceの実装も)
また、CSourceStreamはワーカースレッドを持っており、内部で適切にFillBufferを呼び出すような実装になっている。
WMVファイルから各フレームの画像などを同期的に読み出すIWMSyncReaderインターフェイスには、GetNextSampleと言うメソッドがあり、これでサンプルを得ることができる。
このメソッドは指定したストリームのサンプルを得ることも出来るし、単純にファイルの中の並びでサンプルを取得することも出来る。
ムービーファイルの中にはビデオとオーディオがインターリーブされている(と思う)ので、ストリームを指定した呼び出しの場合、ファイルへのアクセスがシーケンシャルにならない場合も出てくる。(ビデオストリームかオーディオストリームの取得順がファイル中の並びと異なる場合)
これでは…… と書きかけて気付いた。(ちなみに、ここまでが説明のための長い前置き)
別にシークが発生してもそんなに大した負荷増にならないんじゃないかと。
内部でキャッシュするような構造になっていれば、関係ない話だし。
うーん……
フィルタにワーカースレッドを持たせて、各出力ピンへサンプルをプッシュするような構造にしようかと思っていたけど、CSource/CSourceStreamのように各出力ピンがワーカースレッド持ちプルするような構造の方がいい気がしてきた。
別にシーケンシャルじゃなくても問題なさそうと気付いた後、liboggとVFAPIの仕様について確認してみた。
ビデオとオーディオがどういう順番で並んでいるかを得るのは、VFAPIでは無理っぽい。
また、liboggでも面倒くさそうだ。まあ、oggファイルフォーマットは仕様が公開されているので、自分で実装すれば何とでもなるが。
これを書く前にクラス構造などをある程度詰めていたが再考した方が良さそうだ。
追加説明
ソースフィルタは、画像を表示したり音を出したりするタイミングを考慮せずにひたすらサンプルをダウンストリームに送るような作りになっている。
では、どうやって同期を取るのかというと、それはレンダーフィルタに任されている。
レンダーフィルタは、適切なタイミングでサンプルをレンダリングする。
もし、レンダリング時間より早くサンプルが到達した場合は、それの受け取りを拒否する。(デコードの遅延などを考慮して、いくつかのサンプルをキャッシュしている場合もある)
ソースフィルタは、サンプルの受け取りが拒否されたらそこでレンダリングフィルタが受け取れるようになるまで待つ。(CSourceStreamではポーリングによる実装になっている)
このような作りになっているので、ビデオとオーディオの並びがどうなっているかわからなくても問題ない。
オーディオレンダラとビデオレンダラが暗にタイミングを調整してくれている。
つまり、各出力ピンがワーカースレッド持ちフィルタからサンプルをプルするような構造の方が望ましい。
と言うか、そうしないと並びがわからない場合、実装が難しそう。
投稿者 Takenori : 11:13 | コメント (0) | トラックバック
2005年10月17日
DirectShowのデバッグ支援
DirectShowには、DbgSetModuleLevelでタイプとレベルを指定することで、デバッグ用の出力を表示することが出来ると今頃気付いた。
出力のタイプには次のようなものがある。
LOG_ERROR エラー通知。
LOG_LOCKING クリティカル セクションのロックとアンロック。
LOG_MEMORY メモリ割り当てと、オブジェクトの作成および破棄。
LOG_TIMING タイミングとパフォーマンスの測定。
LOG_TRACE 一般的な呼び出しトレース。
CUSTOM1 ~ CUSTOM5 カスタム デバッグ メッセージ用に使用可能。
レベルは大きくすればするほどいろいろと出力されるようだ。
ざっとソースを見たところ、5が使われている最大値だった。
これらの機能を使用するには、使用前にDbgInitialiseをコールし、使い終わったらDbgTerminateをコールする必要があるが、CBaseObjectを継承しているオブジェクトを使っていたら、それらは自動的にコールされるようになっている。
フィルタやピンなど多くの基底クラスはこのクラスを継承しているので、独自のフィルタなどを作った場合は、DbgInitialiseのコールについて気にする必要はなさそう。
デバッグ文字列の出力先はレジストリで指定されており、コンソールかデバッガウィンドウか指定ファイルになる。
CBaseObjectを継承したクラスを使っている場合、自動的にレジストリの"\HKEY_LOCAL_MACHINE\SOFTWARE\Debug\[Module Name]\LogToFile"へ登録されるようだ。(気付いたらkrkr.eXeなどが登録されていた)
ただし、これらはデバッグビルド時のみ有効なので、リリース版では出力や登録は行われないよう。
今後はこれを使っていく予定。
現行のソースは気が向いたら書き換えていくことにする。(OutputDebugStringを使っているので特に問題はない。統一感がないのが問題と言えば問題だけど)
投稿者 Takenori : 19:15 | コメント (0) | トラックバック
DirectX Video Acceleration
ムービーのハードウェアデコード支援機能を使用するための機能としてDirectX Video Acceleration(DirectX VA or DXVAと略記されている)と言うものがある。
これ用のインターフェイスであるIAMVideoAcceleratorはVideo Mixing Renderer フィルタの入力ピン、オーバーレイ ミキサのピン 0でサポートされている。
これらはデコーダーを作る人以外は特に関係なさそうだが、WMF SDKを使ってWMV用のフィルタでハードウェアデコード支援機能使う場合は関係してくる。
とは言っても、細部は特に気にする必要はなく、単にWMF SDKとDirectX VAのネゴシエーションの手順だけわかれば良さそうだ。
でも、少し気になったので見てみた。
IAMVideoAcceleratorを使えば、動き補償や逆離散コサイン変換などをハードウェアに任せられるようになるようだ。
つまり、これを使えばTheoraのデコードが軽くなるかもしれない。(デコードプロセスによっては使えないかもしれないが)
動き補償や逆離散コサイン変換などのデコード処理をしなくていいし、圧縮データをビデオカードに送ることになるので転送量も減る。
問題はハードウェアがサポートしていないと使えないことだが、動き補償(Motion compensation)や逆離散コサイン変換(IDCT)程度であれば、数年前のビデオカードでもサポートしていたはず。(GeForce4 MXは確認した。もっと前のものでもサポートしていたはず)
GeForce 6600を見ると、デコード部がプログラミング可能とか。
プログラマブルシェーダーみたいなものだろうけど、いつの間にかそういう機能が積まれていたようだ。
にしても、DirectShowのフィルタを作るのでも情報が少なくマニアックなのにDirectX VAとか使い出したら余計に濃くなりそうだ。
後、Windows以外ではどうなのかと言うのも問題。
ハードウェア的には機能があるのでドライバがあれば何とかなる話だと思うけど、統一されたアクセス手段があるのかどうか。。。
まあ、Windows以外は考えない。CPUのみでやる。と言うのでも良いと思うが。
投稿者 Takenori : 19:45 | コメント (0) | トラックバック
2005年10月19日
WMリーダーの構造
CSource/CSourceStreamを派生したクラスを使い、CSourceを継承したクラスにサンプルを読む機能を持たせたデマルチプレクサリーダークラスを持たせるような構造にすることにした。
このような構造にすれば、WMVのソースフィルタもOggTheoraもVFAPIも、フィルタとピンを使いまわせるので、他のフォーマットに対応させるのが楽になる。
ただ、DirectX VAをサポートしようとしたらこのままでは出来ないので、その場合はまた別の構造にする。
と言っても、そのような構造が必要になるのはWMVだけ。
VFAPIはDirectX VAを使えないし、Theoraの場合はDemuxとDecoderを別フィルタとする構造になるはず。
この3つ以外のソースフィルタ以外には、とりあえず欲しいものはないのでこれでいけそう。
ただ、ツールを作ることを考えると、QuickTime(MPEG4)とRealVideoのサポートがあったほうがいいけど、今はいらない。
投稿者 Takenori : 06:13 | コメント (0) | トラックバック
2005年10月21日
WMソースフィルタの構造
初め出力ピンにはVideoとAudioが必要だろうと言うことで、次のような構造にした。

ただし、一番初めはCDemuxOutputPinはなく、Video等の出力ピンはCSourceStreamを直接継承していた。
が、コーディングしていくうちに、ほとんどのメソッドが共通になると思い、CDemuxOutputPinを追加。
さらに書いていくと、VideoとAudioにほとんど違いがないことに気付く。
IDemuxReaderにはGetVideoMediaTypeやGetAudioMediaTypeなど、単にAudioやVideoの文字だけが違うメソッドを用意していたのだが、CDemuxVideoOutputPinとCDemuxAudioOutputPinの違いは、その呼ぶメソッドだけが違うだけだ。(各メソッドの引数にはCMediaTypeなどがあり、これによってAudioやVideoなどのメディアタイプは抽象化され、ソースフィルタとしてはその違いを意識する必要はなくなっている)
呼ぶメソッドが違うだけなら、CDemuxOutputPinだけ作り、CDemuxVideoOutputPinなどは必要なく出来る。
で、どのような実装にするか考える。
まず、GetMediaTypeなどの引数を増やし、ストリーム番号などをとるようにすることを思い付いたが、面倒な上にイマイチ。
もう少し考えて気付く、ストリーム番号ではなく出力ピンがインターフェイスを持つようにすればいいんじゃないかと。
つまり、次のようなクラス図になる。

このような構造であればソースフィルタ側はVideoやAudioの違いを意識することはなくなるし、VideoやAudio以外にも楽に対応できる。
また、メディアタイプを知っているのはリーダーになるので、本来あるべき姿のようにも思う。
ってことで、このような設計で行こう。
投稿者 Takenori : 11:08 | コメント (0) | トラックバック
2005年10月22日
WMVの再生が出来るように
WMソースフィルタの構造のように実装していたが、CVideoOutputとCAudioOutputが同じになるので、まとめて1つにした。
CWMReaderはAudioとVideoの違いを知っている必要があるが、IOutputStreamの方は単にストリーム番号さえわかっていれば良い。
で、すべて実装してビルドが通るようにし、再生も出来るようにした。
絵が出るようになったので、今度はDirectShound Filterをつなぐ。
フィルタはつながって音も出るのだが、途切れ途切れに再生される。
サンプルのプレゼンテーション時間がおかしいのかと思っていろいろとやるがうまくいかず。
で、ふとIMediaSample::SetActualDataLengthじゃないかと思って、コピーしたバイト数をセットしてやったらきれいに音がなるようになった。
これで、WMVの再生が出来るようになった。
再生が出来るようになったので、640*480のムービーでCPU負荷を見てみた。
50%…… んっ? このマシンでこの数字は……
AthlonXP 1600の方で見てみると、80~100%!
重っ!
現在、メモリの単純コピーしている部分は、カスタムアロケータを作ることによってなくせるはずなので、バッファレンダーの時を考えると、あと10%ぐらいは減りそうだが、それでも70%~90%。
これは…… レイヤー描画モードの場合の必要スペック1.5GHz、推奨2.0GHzとかになってしまうのだろうか。
もし、ハードウェア支援が受けられた場合はどうなるのだろうか?
単純にGraphEditへWMVファイルをドロップして再生してみたら、CPU負荷35%~50%。
低っ!
Media Playerで再生してみても同程度だった。
どうやら、ハードウェア支援があるかどうかで全然違うようだ。
ここにきてVMRの効果が発揮されそうな予感。
MPEG Iだと大して効果がなかったが、WMVでは明らかに違いが出そうだ。
ただ、VMRにつなげるにはDirectX VAのネゴシエーションとやらをしなければならない。
WMVの再生には、あとカスタムアロケータとDirectX VAか。
投稿者 Takenori : 15:27 | コメント (0) | トラックバック
2005年10月23日
DirectX VAネゴシエーション ステップ訳
Windows Media Format SDKのヘルプのDirecX VAネゴシエーション ステップを訳してみた。
以下の訳は、ほとんど直訳の上に私の英語力のなさが加わって意味不明な文章になってます。
でも、大体何をすればいいかはわかります。(私だけかも)
現状単なるメモなので、コーディングして動くようになって理解したら、文章を修正します。
1. プレーヤーは、ソースフィルタとリーダーオブジェクトをインスタンス化します。リーダーは、ビデオデコーダーDMOの生成と(圧縮された)入力タイプの設定を行います。
これらはプレーヤーがフィルタグラフの構築を行うより前に行わなければなりません。
なぜなら、SDKとデコーダーDMOは、グラフのネゴシエーションプロセスにかかわらなければならないからです。
また、DMOはステップ9の間、入力フォーマットを知っていなければなりません。
2. プレーヤーは、ビデオソースフィルタの出力ピンを引数にしてIGraphBuilder::Renderをコールします。
DirectShowフィルタグラフマネージャはVMRをプレーヤーのビデオソースフィルタのこの出力ピンへ接続しようとします。
3. フィルタグラフマネージャは、プレーヤーのビデオソースフィルタの出力ピンのIPin::Connectをコールします。
ステップ 4 ~ 10は、IPin::Connect内で起こります。
4. ソースフィルタは、リーダーのIWMReaderAccelerator::GetCodecInterfaceでIWMCodecAMVideoAcceleratorを取得します。
CodecがDirectX VAをサポートしない場合、GetCodecInterfaceの呼び出しはたぶん失敗します。
この場合、ネゴシエーションプロセスはいつも通りの(DirectX VAをサポートしない)物になります。
5. ソースフィルタはIWMCodecAMVideoAccelerator::SetAcceleratorInterfaceを通して内部に接続されたピンからデコーダDMOへIAMVideoAcceleratorのポインタを渡します。
6. 次に、ソースフィルタはIPin::Connect オペレーションの残りをCBaseOutputPin::Connectメソッドへ任せます。
SDKとのフォーマットの列挙は今日するように続きます。???
Codecが接続されているコンテンツのためのDirectX VAをサポートしていたら、CodecのDMOはそれらのDirectX VAサブタイプをサポートするYUVやRGBサブタイプの前に最初に提供します。
DirectX VAサポートが利用できるのなら、ステップ7~11はDirectX VAサブタイプのコンテキスト中で試みられます。
以下のソースコードはDirectX VAメディアサブタイプを識別するためのものです。
ヘルプにソースコードがあります。
7. CBaseOutputPin::Connectの実装は、ステップ3の間にIPin::CompleteConnectを呼びます。
もし、DirectX VAサブタイプに確定していたなら、DirectX VAネゴシエーションが試みられます。
出力ピンは、現在の出力メディアタイプを渡してIWMCodecAMVideoAccelerator::NegotiateConnectionをコールします。
8. デコーダDMOはIAMVideoAcceleratorインタフェースを通してVMRとの必要なネゴシエーションを実行して、2が同意したビデオsubtype GUIDを返します。
出力ピンがこのプロセスの間にIAMVideoAcceleratorNotifyインタフェースで受信したすべての呼び出しは、デコーダDMOのIAMVideoAcceleratorNotifyインタフェースが代理します。(また、IWMReaderAccelerator::GetCodecInterface メソッドでそれを得ることができます)
9. IWMCodecAMVideoAccelerator::NegotiateConnectionが成功するなら、出力ピンはIWMPlayerTimestampHookインターフェイスを渡してIWMCodecAMVideoAccelerator::SetPlayerNotifyをコールする。
このHookで、それらがレンダラーに手渡される前にソースフィルタはサンプルの上のタイムスタンプをアップデートします。
10. ソースフィルタはIWMReaderAccelerator::Notifyにネゴシエートされたメディアタイプを渡してコールする。
これでリーダーは内部変数をアップデートし、DirectX VAを受け入れる。
これはCodecかリーダーが失敗できる最後の場所です。
上のステップのどれかが失敗するなら、ソースフィルタは、ステップ3に戻って、リーダーが列挙した次のタイプを試すべきです。
11. 再生は始まります。 リーダーは接続の出力タイプがDirectX VAであるならデコーダDMOからの出力バッファを無視します。
12. IPin::Disconnectが発生したら、ソースフィルタはNULLを引数にしてIWMCodecAMVideoAccelerator::SetAcceleratorInterfaceをコールします。
これはコーデックとレンダラーとのDirectX VA接続を解除します。
投稿者 Takenori : 22:44 | コメント (0) | トラックバック
2005年10月24日
Async Readerでないと無理
パトラッシュ、僕はもう疲れたよ。
IWMSyncReaderとCSource/CSourceStreamの組み合わせで、ソースフィルタを作った。
再生も出来た。
DirectX VAのネゴシエーションも組んだ。
が、IWMReaderAcceleratorインターフェイスがもらえません。
IWMSyncReaderインターフェイスは、IWMReaderAcceleratorを実装していないと返ってくる。
ヘルプをよく読むと、IWMReaderを使わないとDirectX VAが使えないっぽい。
なんてことだ orz
OnSampleが非同期でコールされるスタイルでは、ソースフィルタを組みづらいためIWMSyncReaderにして作ったのに、それらが水泡に帰した。
ま、TheoraやVFAPIを実装する場合、現在の構造でやることになるので、まったくの無駄ってことはないのだが。
IWMReaderでOnSampleがコールバックされるスタイルで組むのなら、ソースフィルタにスレッドと、バッファのキューを持たせて、OnSample時にレンダーが受け取れなかったらキューにデータを入れて、イベントをスレッドに投げて起こして、起こされたスレッドはレンダーが受け取れるまでポーリングする。
もし、キューにデータが多くなりすぎたら、IWMReaderの再生を一時停止するか、データを捨てるかする。
と言った実装になるだろうか。
よし、1時間ぐらい現実逃避してからリトライだ。
投稿者 Takenori : 20:11 | コメント (0) | トラックバック
インプレイスフィルタ with VMR
DirectX VAを使用する場合は、レンダーの入力ピンからIAMVideoAcceleratorを取得する必要がある。
だが、インプレイスフィルタをデコーダーとVMRの間に挿入した場合、デコーダーに渡される入力ピンはインプレイスフィルタのものになる。
つまり、インプレイスフィルタの入力ピンに対してQueryInterfaceしてIAMVideoAcceleratorを得ようとするわけだが、インプレイスフィルタの入力ピンはそんなもの持っていない。
VMRでパフォーマンスが下がってしまう原因はこれだろうか?
本来、インプレイスフィルタはデータの変換を行うので、DirectX VAの利用はできなくなってしまうが、現在使用しているインプレイスフィルタは単にイベントを投げるだけなので、DirectX VAの使用は問題ないはず。
現在の接続プロセスでは、一度デコーダーとVMRをつないで、その接続に使われているメディアタイプを取得し、取得したメディアタイプを使ってインプレイスフィルタをつなぐ。
もし、最初の接続でDirectX VAが使われていた場合、このメディアタイプはDirectX VAの物となる。
だけど、WMF SDKのヘルプには、IPin::Disconnectが発生したらソースフィルタはNULLを引数にしてIWMCodecAMVideoAccelerator::SetAcceleratorInterfaceをコールし、CodecとレンダラーとのDirectX VA接続を解除するように実装しろとある。
つまり、現在の実装では一度VMRの入力ピンのデコーダーとVMRをDisconnectしてしまっているので、DirectX VAの接続が切れてしまっている可能性がある。
ただ、Disconnectなどの実装はいい加減なことが多いようなので、DirectX VAの接続を解除していない可能性もある。
この場合、DirectX VAがたまたま使えているかもしれない。
でも、そのような手抜きのたまたまの処理に依存するのは好ましくない。
単にイベントを投げるだけのインプレイスフィルタでは、レンダーの入力ピンへのポインタを保持しておき、QueryInterfaceでIID_IAMVideoAcceleratorが指定された時に、保持しているレンダーの入力ピンへ処理を委譲した方が良さそうだ。
だけど、DirectX VAが使えないとしたら、以前の接続に使われていたメディアタイプで再接続できるのは変だな。。。
DirectX VAの接続の解除を行っていないのか?
後、良くわからないのはインプレイスフィルタのReceiveがコールされていること。
WMF SDKのヘルプとDirectXのヘルプによると、DirectX VAが使われている時は、IAMVideoAcceleratorを使ってダイレクトに表示の更新を行う。
もしインプレイスフィルタを使うなら、アップストリームからダウンストリームへ順次Receiveを呼び出していくような処理が必要だし、現にReceiveがコールされているので、そのような処理が組まれていると思われる。
これはどうやっているのか?
単純にWMF SDKのヘルプのDirectX VAのネゴシエーション方法を参照しただけではこの辺りがうまく実装できなさそうだ。
デコーダフィルタでIAMVideoAcceleratorを使う方法なども良く読んでおいたほうが良いな。
投稿者 Takenori : 22:36 | コメント (0) | トラックバック
2005年10月25日
DirectShowとWMF SDKのメディアタイプ
メモるのを忘れていたのでメモ。
WM_MEDIA_TYPEとAM_MEDIA_TYPEは別に定義されているが、実体はまったく同じ。
ついでにDMO_MEDIA_TYPEも同じ。
WM_MEDIA_TYPEの定義を見るとコメントに、『名前の競合を避けるために別名で定義しているが、これはAM_MEDIA_TYPEとぴったり一致する。』と書かれている。
ヘルプにも同じことが書かれている。
また、MEDIATYPE_VideoとWMMEDIATYPE_VideoなどのメジャータイプやサブタイプのGUID値も同じ。
これはヘッダーを見て、実際の値を見ればわかる。
WM_MEDIA_TYPEからAM_MEDIA_TYPEへ変換する関数や、メジャータイプの変換を行うテーブルを作ったりしたけど、それはまったくの無用。
キャストすれば難なく使える。
投稿者 Takenori : 12:11 | コメント (0) | トラックバック
2005年10月26日
IWMReaderを使うように組み直し
IWMReaderを使うように組み直した。
DirectX VAを使う前にまずは正常に再生されることを確認しようと、レイヤー描画で動作させてみる。
が、CBaseOutputPin::Deliverでブロックされる。
どうやら、レンダーでイベント待ちが発生しているようだ。
IWMSyncReaderを使った方で確認すると、イベントが発生している。
見ると別スレッドでCBaseRenderer::Runがコールされ、そこでイベントがセットされているようだ。
OnSampleでブロックされたスレッドとCBaseRenderer::Runをコールするスレッドは同じなのだろうか?
でも、最初にIWMReaderを使って実装した時は再生できたのだが……
もう少し追う必要がありそうだ。
投稿者 Takenori : 22:38 | コメント (0) | トラックバック
IWMCodecAMVideoAcceleratorの取得
IWMReaderAdvanced2::OpenStreamでストリームを開いた後、いろいろとインターフェイスを取得していくようにしているのだが、IWMReaderAcceleratorは取得できたが、IWMReaderAccelerator::GetCodecInterfaceでIWMCodecAMVideoAcceleratorが取得出来ない。
E_UNEXPECTED : The WM Reader has no pointer to the codec.が返ってくる。
なぜ?
せっかくIWMReaderにしたのに、DirectX VA使えないのか?
他のデバッグをやっていると、OpenStreamよりも後に"wmvdmod.dllを読み込まれました。"のメッセージが表示されていることに気付いた。
もしかしてこれが原因か?
オープン直後ではなく、出力フォーマットが確定した後にGetCodecInterfaceを呼ばないとダメかもと思っていたが、どうやらその考えは近かったようだ。
実際に、wmvdmod.dllが読み込まれていたのは、IWMReader::GetOutputFormatCountがコールされた時だった。
GetOutputFormatCountは、サポートしているフォーマットを取得する時に、まずその数を取得するのに使っている。
これ以外でもwmvdmod.dllがロードされるメソッドはありそうだが、たぶんこれを最初にコールすることになると思う。
それはともかく、GetOutputFormatCountをコールした後にGetCodecInterfaceでIWMCodecAMVideoAcceleratorが取得できるかどうか確認したところS_OKが返ってきた。
どうやら読み通りだったようだ。
でも、こんなこと書かれていなかったと思うんだけど……
