2005年10月08日
フィルタを内包する
Directshow Filters for Ogg Vorbis, Speex, Theora and FLACから、DirectShowフィルタのソースをDLし、Xiph.orgからliboggとlibtheora、libvorbisの最新版をDL。
lib…は、スタティックライブラリを作った。
で、TheoraエンコーダーフィルタとVorbisエンコーダーフィルタとOggマルチプレクサフィルタを独立したDLLとしてビルド。
いろいろなDLLやライブラリに依存していてかなりてこづった。
でも、なんとか3つのDLLにすることが出来た。
最終的にはexeファイル1個にする予定。
でも、DLLの合計サイズが1405KBもある。
Vorbisのエンコーダーフィルタが1201KBもあるのがその原因。
vorbisenc_statc.libが1112KBもあるので、そのせいだろうけど…… Vorbisってそんなに大きいのだろうか?
次は、各フィルタDLLにあるコンポーネント登録用のコードを削って一つにまとめて、フィルタグラフを構築する部分を作れば、ほとんど出来たようなもの。
投稿者 Takenori : 21:39 | トラックバック
2005年10月10日
作成環境
ゲームでTheora動画を使う場合に必要となる動画作成環境とはどんなものだろう?
昔、3Dのデザイナーさんにレンダリングの時は、連番静止画で出力すると言うのを聞いたことがある。
理由は単純明快。
途中でレンダラが落ちた時に、続きからはじめられるからだそうだ。
ものによってはレンダリングに恐ろしく時間がかかるから、また一からとなるとしんどいので、なるほどと思った。
ああそういえばその時、そのためにツール対応した記憶が……
それはともかくとして、入力として連番静止画像を受け取れたなら、何とでもなるだろう。
動画編集ツールなら、大体は連番静止画像で出力できるはずだ。
後、可能かどうかはまだわからないが、指定した位置にキーフレームを入れる機能は必要になると思う。
セグメントループでループさせる場合、ループ始点にキーフレームがないと困る。
と言うことで、とりあえず必要な機能は、連番静止画像を受け取れることと指定位置にキーフレームを設定できることかな。
クオリティなどの設定が出来るのは当然として。
とは言っても、まずはゲーム用途ではないので、この2つの機能はなくてもいいのだが。
投稿者 Takenori : 04:33 | トラックバック
libtheoraのサンプル
アルファ5のソースを見ると、エンコードとデコードのサンプルプログラムがついていたので、ソースを追ってみた。
見た感じ、比較的簡単に使えそう。
しかも、なんか面白そうだ。
いろいろといじってみたい気もするが、WMVの方を優先しよう。
投稿者 Takenori : 17:44 | トラックバック
2005年10月12日
1つのEXEへ
DLLのエントリーポイントやフィルタとして登録するための関数やテーブルを削除し、一つのプロジェクトの中にすべてのファイルを入れる。
フィルターのGUIDがかち合わないように、念のため別のGUIDに変更し、ビルド。
プリコンパイル済みヘッダーに頼っているものと、そうでないものが混ざっていてインクルードぐちゃぐちゃでコンパイルエラー出まくり。
問題が発生していた部分を調整しビルド。
今度はリンクエラー。
フィルタを作る時に必要なクラスファクトリを持ったグローバル変数がないと言われているが、そもそもフィルタを登録しないのでそんなものは消してしまった。
また、必要もないはずなのだが……
そのため、初めはなぜそのエラーが出るのか良くからなかった。
が、エラーメッセージを見ると、strmbasd.lib(dllentry.obj)で参照されていると出ている。
dllentry.objだけ、リンクしないように出来ないかと思ったが、良くわからず。
少し悩んで、ピンときた。
strmbasd.libは、フィルタを作る時の基底クラスやユーティリティークラスを集めたライブラリで、これは自分でビルドする。
つまり、dllentry.objを含まないライブラリを作ればいいんだ。
で、dllentry.objだけでなく、も一つ必要なさそうなdllsetup.objも含まないライブラリを作った。
作ったライブラリをリンクするように変更してビルドするが、今度はg_hInstやg_amPlatformがないと言われる。
どうやら、さっき省いた物の中に入っていたらしい。
そこで、WinMainが入っているソースと同じ場所に定義した。
ただ、これだけだとまずそうなので、dllentry.cppのソースを見て、同じようにg_hInstなどの値を設定するようにした。
これで、一応ビルドが通るようになった。
投稿者 Takenori : 06:55 | トラックバック
とりあえずエンコードが出来るように
Directshow Filters for Ogg Vorbis, Speex, Theora and FLACのOgg Muxフィルタはフィルタ同士の接続シーケンス中にコールされるSetMediaTypeによって、各種パラメタを設定しているのだが、TheoraEncoderに何も接続されていない状態で接続すると、SetMediaTypeで渡されるメディアタイプには必要なパラメタが入っておらず、不定な値を使ってパラメタを設定してしまう。
で、その後フィルタグラフを構築して開始すると、0除算で落ちる。
フィルタグラフは、入力側から順番に作っていかないといけないようだ。(不定な順序で構築されても、最初のサンプルを受け取った時点でパラメタを設定するような作りになっていれば大丈夫だが、そのような作りにはしなさそうだな。。。)
そこで、初めは単にTheoraEncoderとVorbisEncoder、OggMuxを入れるだけにし、後はIGraphBuilder::RenderFileでフィルタグラフを作るようにした。
こうしたらうまく接続され、0除算は発生しなくなった。
これで、入力ファイルと出力ファイルを選択するとエンコードが出来るようになるところまで出来た。
ただし、進み具合などはまったく不明。
後は、サンプルグラバフィルタを入れて途中の画像を得て表示したり、エンコードの設定をGUIで出来るようにすれば、とりあえずはそれっぽくなる。
投稿者 Takenori : 19:58 | トラックバック
2005年10月13日
エンコードしてみる
msec単位での現在時とパーセントで進み具合を表示するようにしてからいくつかエンコードしてみた。
libtheoraをalpha5にしたおかげか、前に試したムービーでも落ちなくなった。
WMVやAVI、MPEG Iではうまくエンコード出来た。
MPEG IIの場合、標準のものはないので他のソフトのフィルタを使うことになるが、レンダーに普通のビデオレンダーをつなげようとしたり、Overlay Mixerをつなげようとしたりでうまくいかない。
また、AVIもCodecによってはうまくつながらない。
ま、その辺りは仕方ないだろう。
ユーザー環境依存だし。
でも、MPEG系の場合は自力でフィルタグラフを構築するようにした方が良いかも。
投稿者 Takenori : 01:01 | トラックバック
2005年10月14日
MPEG2をつなげようとする
Graph Edit上で試したところ、PowerDVD XPのフィルターは無理だった。
なぜか常にOverlayMixerを間に入れようとする。
で、WinDVD 2000のフィルターで試すとつながらなかったが、単にメディアタイプが一致しないのが問題のようだ。
ソースを見ると、様々なフォーマットに対応している。
そこで、つなげるコードを組んでみることにした。
が、なぜかWinDVD 2000のフィルターをインスタンス化しようとすると落ちる。CoCreateInstanseのパラメタを変更してみるも効果なし。
デバッガでステップ実行していていたら、そこで突然落ちる。
なんだろう?
Graph Editならインスタンス化出来るのに。
Graph Editでは、フィルタを選択出来る。
つまり、フィルタを列挙してから、ユーザーが選択したものをインスタンス化している。
なので、それにならってフィルタを列挙してから、BindObjectでインスタンス化する方法で書いてみたら、BindObjectで落ちた。
さっぱりわからない。
で、なんとなくReleaseビルドしてみて、実行するとインスタンス化の後の、フィルタが接続できませんってエラーが投げられてきた。
なぜだかわからないけど、Releaseビルドだとインスタンス化出来るようだ。
でも、やはりつなげられないのは変わらない。
接続部分のソースコードを追ってみると、かなりの形式に対応しているのだが……
Major TypeやSub Typeの問題ではなさそうな気がする。
Format Typeを見ると、FORMAT_VideoInfoのみに対応している。
確か、MPEG2はFORMAT_VideoInfo2を使うと言う記述がどこかにあったようななかったような。
と言うことで、次はFORMAT_VideoInfo2をサポートするようにしてみることにした。
投稿者 Takenori : 01:16 | トラックバック
MPEG2のライセンス
MPEG LAのMPEG2ライセンスページを見ると、デコーダーを内包していようといまいとMPEG2を使うプログラムにはライセンス料が発生するようだ。
TMPGEncのページを見ると、有料無料に関わらず、MPEG2を使うとライセンス料が発生するとある。
つまり、MPEG2をサポートすると配布できないな。
まあ、ライセンス料を払えば可能だけど。
MPEG1はどうなのだろう?
TMPGEncはサポートしているし、Windowsも標準で再生できる。
よくわからないけど、大丈夫なのかな。
ググってもMPEG1のライセンスについては見付からなかった。
投稿者 Takenori : 01:33 | トラックバック
MPEG1とMPEG2の見分け方
MPEG1とMPEG2で使われている拡張子は両方mpgが多いので、拡張子だけではこの二つを識別するのは難しい。
DirectShowでは、ファイルの特定位置の値で見分けている。
MPEG2の場合、次のようになっている。
offset : 0
length : 5
mask : FFFFFFFFC0
value : 000001BA40
offsetはファイル先頭からの位置で、lengthはバイト長、maskはマスク、valueはマスク適用後の値になる。
つまり、ソースとしては次のような感じ。
int offset = 0;
int length = 5;
unsigned char mask[5] = {0xFF,0xFF,0xFF,0xFF,0xC0};
unsigned char value[5] = {0x00,0x00,0x01,0xBA,0x40};
unsigned char checkByteBuff[5]; // ここにファイルから読み込む
bool isMPEG2 = true;
for( int i = 0; i < 5; i++ )
{
if( (checkByteBuff[i] & mask[i]) != value[i] )
{
isMPEG2 = false;
break;
}
}
これで、isMPEG2がtrueなら、そのファイルはMPEG2と言うこと。
MPEG1 Systemの場合は、次の通り。
offset : 0
length : 16
mask : FFFFFFFFF100010001800001FFFFFFFF
value : 000001BA2100010001800001000001BB
DirectShowでは、まず拡張子で判断し、なければ上述のチェックバイトによる方法で識別しているようだ。(詳しくはカスタム ファイル タイプの登録に書かれている)
それはともかく、チェックバイトを使えばMPEG2かどうか判別できるので、MPEG2用のグラフを構築したり、サポートしていませんって出して終了できる。
投稿者 Takenori : 09:25 | トラックバック
2005年10月15日
目的のMPEG2フィルタつながらず
Theora Encodeフィルタの入力ピンでFORMAT_VideoInfo2に対応した。
ただし、インターレース形式は非サポートとして、プログレッシブ形式の時のみ受け入れるようにした。
で、実際に動作させて見るとフィルタはつながるが、実行中にIllegal Instruction例外で落ちる。
デバッガで見るとpavgusbと言う命令がコールされた時に例外が出ているようだ。
調べてみると、pavgusbは3D Now!命令だとか……って、なんでPen4で3D Now!命令が。
でも、それならAthlonマシンで!っと、違うだろうなぁと思いつつ動作させると、ランタイムエラーで落ちた。
で、ふとWinDVDで見たらどうなるだろうと思って試したら、同じように落ちた。。。
このWinDVDはAthlon用なのか。
確か何かのバンドル版だったと思ったけど、Athlonマザーだったかな?
でも、AthlonマシンならWinDVDは動くので、ランタイムエラーは別の原因だろう。
なんかffmpeg2theoraでいい気がしてきた。
ただ、キーフレームを入れたりするには、作らないと厳しそうだが、libtheoraのAPIを見た感じでは、どうも指定位置にキーフレームを入れるものはないようだ。
そのうち追加されるかもしれないけど、されなかったら自分で何とかする必要がありそうだ。
エンコーダではなく、先にデコードフィルタを作るかな。。。
ま、すでにMPEG1やAVI、WMVからなら普通にエンコードは出来るようになってるとけど。
投稿者 Takenori : 06:36 | トラックバック
エンコーダはフィルタでなくてもいい?
もとにしたソースがエンコーダとマルチプレクサをDirectShowのフィルタとして実装していたので、何の気なしにそのままフィルタとして使っていたが、良く考えたらフィルタである必要はない。
と言うか、フィルタでない方が使いやすそうだ。
DirectShowにはメディア ディテクタ (MediaDet)という、ヘルパー オブジェクトが存在する。
これを使うと指定した動画ファイルの指定したフレームの画像を楽に取得できる。
このメディア ディテクタは内部にフィルタグラフを持ち、間接的にそれらを呼び出すことによって、そのような機能を実装している。
吉里吉里のVideoOverlyオブジェクトも似たようなもので、内部にフィルタグラフを持って、楽にビデオの再生が出来るようになっている。
今のエンコーダーのソースでも、エンコーダーと言うオブジェクトがフィルタグラフを持っていて、そいつに対して入力ファイルと出力ファイルを指定し、Startを呼ぶとエンコードが出来るような構成になっている。
この部分はこのような形でいいと思うが、入力データを受け取る部分も、このような形にした方が良さそうだ。
エンコーダーに渡す画像データや音声データは、動画データの初めから順番にデータを受け取って順次エンコードしていくので、キューのような構造である方が使いやすい。
つまり、フィルタグラフを内部に持ってて、画像データなどを順次取得できるヘルパー オブジェクトを作った方が良さそうだ。(独自のレンダーフィルタを作る必要がありそうたが)
このようにすれば、連番静止画などへの対応もしやすい。
インターフェイスが共通の連番静止画用のクラスを作れば簡単に対応できそうだ。
DirectShowのフィルタの問題点は作るのが面倒だってこと。
仕様が良くわからないことが多いし、実装しなければならないメソッドも多いので大変。
それを解消するためにDirectX Media Object(DMO)と言うものがあるようだが、詳しくはまだ見ていない。
投稿者 Takenori : 07:14 | トラックバック
2005年10月31日
キーフレームを入れる
ThisIsKeyFrameを設定してtheora_encode_YUVinへtheora_stateを渡すことで強制的にキーフレームを挿入できそう。
つまり、theora_state *tをreinterpret_cast<CP_INSTANCE *>(t->internal_encode)->ThisIsKeyFrame = 1;
として、
int theora_encode_YUVin( theora_state *t, yuv_buffer *yuv );
に渡せば出来そう。
実際にはまだ試していない。
そもそも、キーフレームが判別できるて任意位置にシークできる再生環境がないので、任意位置にキーフレームを入れたムービーを作っても確認できない。
どうやらまずデコーダーフィルタを作った方が良さそう。
投稿者 Takenori : 17:59 | トラックバック
2007年07月25日
WMV のキーフレーム埋め込み
TMPGEnc でキーフレームを指定して、WMV ヘ変換してもキーフレームは入らないようだ。
「キーフレームをマーカーに変換する」と指定してやるとマーカーにはなるが、キーフレームにはならない。
マーカーがあればそこへジャンプすることは可能だが、その場合は WMV の同期リーダーを使う必要がある。
でも、吉里吉里の方では非同期リーダーを使っている ( 最初は同期リーダーを使っていたがいろいろあって非同期リーダーにした )。
だから、マーカーではなくキーフレームを入れたい。
AviUtl で WMV 出力プラグインを使って出力してもキーフレームは入らないようだ。
WMV 出力プラグインのソースを見ると、キーフレームの時 IWMWriter::WriteSample のフラグに WM_SF_CLEANPOINT を渡している様子。
でも、Windows Media Format SDK の "To Force Key-Frame Insertion"の項を見るとその方法ではなく、INSSBuffer3::SetProperty を使って、WM_SampleExtensionGUID_OutputCleanPoint でキーフレームを指定するようになっている。
WMV 出力プラグインを改造して、キーフレームを正しく出力できるようにしようとしたが、make 方法がイマイチよくわからず止めた。( make は出来るが、エンコードすると最後のほうで落ちた )
そこで、Windows Media Format SDK についているサンプルの WMVRecompress を少し改良して、キーフレームを直に指定してみると、ちゃんとキーフレームが入った。
ソースは以下のような感じ。
一応、IWMWriter::WriteSample のフラグも設定した。
INSSBuffer3* pSample3;
if( S_OK == pSample->QueryInterface( IID_INSSBuffer3, (void **)&pSample3 ) ) {
BOOL boolean = TRUE;
hr = pSample3->SetProperty( WM_SampleExtensionGUID_OutputCleanPoint, &boolean, sizeof(boolean) );
pSample3->Release();
}
WMV 出力プラグインの正しい make 方法を探る手もあるが、今後もエンコーダーには手を入れることもあるだろうから独立したプログラムで作ることにした。
とりあえずは、AVI から WMV のエンコーダーで、キーフレーム指定が可能なもの。
最終フレームにキーフレームを入れるとか、フォルダ内を一気にエンコードなど用途に適したものにする予定。
その後は、入力フォーマット増やしたり、出力で Theora に対応できたらなぁと夢想中。
後、アルファ付き MotionJPEG とか。
投稿者 Takenori : 00:21 | トラックバック
2007年07月26日
WMV にキーフレームを入れる
取り立てて必要なのは無圧縮AVI から WMV へのエンコーダーで、キーフレームを入れられるもの。
と言うことで、 サンプルの UncompAVIToWMV を改良してキーフレームを入れられるようにすることにした。
コマンドライン引数を追加して、「 WMV のキーフレーム埋め込み 」の要領でキーフレームを入れるようにして、エンコード。
再生してシークして見るが、正しくシークしない。
キーフレーム入ってない? と思ったが、キーフレームは入っているようだ。
キーフレームは WMVRecompress に追加した方法と同じ方法でやっている。
同じムービーをもう一度 WMVRecompress でやってもうまくいかない。
なぜ?
うまくいったムービーをもう一度 WMVRecompress でやるとうまくいく。
UncompAVIToWMV と WMVRecompress のプロファイル設定周りを比べるが、プロファイルは同じになるようだ。
マルチパスだろうか?
WMVRecompress は 1pass エンコードしているが、元は WMV 、2pass にしたらうまくいくかもと思い、プロファイルを変更して、2pass エンコード。
うまくいった。
理由は良くわからないが、2pass エンコードにすると入れたキーフレームにシークできるようになった。
だがもう一つなぞがある。
キーフレームは最終フレームに入れることが多いので、それ用のオプションを追加している ( ムービーをスキップしたら最終フレームの絵で続くように最終フレームへシークするため )。
その時、最終フレームにだけキーフレームを指定するとうまくいかない。
理由はわからないが、最後の2フレームにキーフレームを入れるとうまくいく。
しかも、不思議なことに最後の2フレームにキーフレームを入れた方がファイルサイズが小さい。
さっぱりわからない。
まあ、目的は果たせているのだが、少し気持ち悪い。
WMVRecompress の方では最終フレームにだけキーフレームを入れればうまく行く。
エンコーダーのソースを追っていて気付いたが、同期リーダーでもユーザー定義のクロックを与えてやることで、再生速度に関係なく読み出せるようだ。
ただ、ムービーソースがユーザー提供のクロックに対応していない場合は、失敗するとか。
また、微妙な・・・
WMVRecompress で使っているから、WMV の場合はうまく行きそうだが。