« 独自エンジンとその有用性 | メイン | Theora のデコードをもっと速く その2 »

2007年11月07日

動画再生エンジン開発日誌:: Theora のデコードをもっと速く その1

    

Theora と MPEG I で Theora の再生が軽くなっていると書いたが、 mplayer や VLC media player はもっと軽い。
この CPU 負荷の差はなんだ? ということで高速化に取り組むことにした。

まず、前回公開したものは MMX が有効になっていなかったようなので有効にした。
そうすると 30% ぐらい高速化される。
でも、これと比較しても mplayer はかなり軽い。
ついでに theora-exp にも対応したけど、負荷は大差なし。
ソースを見ていて気付いたのだが、最新のソースは theora-exp を旧インターフェイスでラップしているようだ。
なので、負荷がほとんど変わらないのは当然か。
後、当然のことながら theora-exp のインターフェイスでも使えるようだ。

負荷に差が出るとしたら、読み込みの部分と最終的な描画部分が考えられる。
最終的に オーバーレイ を使用して描画しているが、この辺りの扱いの違いで負荷が異なるのかもしれないと試してみる。
オーバーレイ 周りは大体ビデオカード側の話なので CPU には影響しないかと思いきや、オンボードのGPUの場合 ( と言うか自分のマシンで ) 、やり方によって負荷が大きく変わる。
VRAM だし、余裕があるからそんなに切り詰めなくてもいいやと大きい領域でコピーとかすると CPU 負荷に大きく響く。
で、初め オーバーレイ 周りは、プラマリサーフェイスを作って、オーバーレイサーフェイスを作って、デコード後の画像をメインメモリにコピーしてから、オーバーレイサーフェイスへコピーしていた。
これが初期の状態。
いろいろ見ていると オーバーレイサーフェイス が複数作れる環境があることを知った。
ならばと、メインメモリ の代わりに オーバーレイサーフェイス を複数作って、そこにコピーし、UpdateOverlay でプラマリサーフェイスへ反映するのが速いのではないかと思い、そうするとオンボードのマシンでは軽くなった。
オンボードでないマシンでは、そもそも複数の オーバーレイサーフェイス が作れなかったので、オフスクリーンサーフェイス を使い、BltFast でオーバーレイサーフェイスに反映するようにした。
でも、mplayer や VLC の CPU 負荷には及ばない。
そこで、VLC のソースを見てみた。
オーバーレイ周りは、サーフェイスオーバーレイのバックバッファを作って、そこにコピーした後、Flip しているようだ。
なるほど。
そんな手があるのかとやってみるが、オンボードのマシンでは重くなった。
なぜ?
よくわからないけど、そういうことのようなので、オーバーレイサーフェイス が複数作れる場合は、複数作って 、UpdateOverlay 、そうでない場合はオフスクリーンサーフェイスを使うことにした。
まあ、オーバーレイサーフェイス が複数作れない場合は、バックバファを複数持つのも手かもしれないが、いくつまで作れるのかが問題になるし、オンボードでない場合は大して気にすることもないかとそうした。

次は読み込み周りを疑う。
SHCreateStreamOnFile で IStream を得て、それを用いてファイルからの読み込みを行っていたが、シーケンシャルに読み込みを行う場合は、CreateFile で FILE_FLAG_SEQUENTIAL_SCAN を使えば、場合によってはかなりパフォーマンスが向上するらしいので、そうしたが効果なし。負荷に影響は見られない。もっと読み込み量が多い場合に影響するのだろうか。
で、再び VLC のソースを追う。
どうも 低レベルの open や read で読み込んでいる様子で、待ちが発生する場合はウェイトが入っているよう。
と言うことは、非同期にすればいいのかな? と ReadFileEx を使い、イベント待ちするようにしたが、遅くなったので止めた。
VLC のソースとは同じにしていないが、あんまり読み込みまわりは関係ない気がしてきたのでこれ以上は止めた。

結局、読み込みと描画部分では mplayer や VLC に追いつけなかった。
まあ、最終的にソースは全然違う形なのだが。

何かないかと theora の ヘッダーを追うと、コールバック関数を登録できるようだ。
登録した関数は、デコードが終わった直後に、デコードができたブロックを引数に入れて呼び出されるようだ。
それで何がうれしいかと言うと、デコード直後なのでキャッシュにのっている可能性が高く、高速化されるかもしれない。
実際、利用すると 2.5% ほど高速化された。
ただ、それでも追いつかない。
※ このコールバック関数には開始と終了行がブロック幅単位で渡されるので、8倍する必要があるのだが、ムービーの画像サイズが16の倍数でない場合は、はみ出た部分も関係なく渡されたりするので、メモリのアクセス違反が出ないように画像サイズを超えていないか確認する必要がある ( 800x600を再生した時これでエラーが出て気付いた ) 。

もしかしたら、MinGW の最適化がすごいのか? ということで、 mplayer のところにある MinGW の環境を落としてメイクすることにした。
落としてきた環境には既に libtheora.a などが入っているので、それを使うことにした。
とりあえず、libtheora.a を使って VC でビルドするが、高速化されない ( __allocaがないとか言われたので、libgcc.a をくっつけて無理やりビルドした ) 。
全てを MinGW でコンパイルしないとだめなのかと、MinGW でコンパイルを通そうとする。
基本的に Win API 直叩きで書いているので、それほど労せずコンパイルは通った ( ATL の CComPtr 部分のみ使わないように修正した ) 。
が、起動してすぐに落ちる。
よくわからない。MinGW がよくわからない。
よくわからないので、libtheora についてる dump_video を -f オプションつけてデコード時間を計ってみることにした。
VC と大差なし。
何でだろうか?
いろいろ調べて、MinGW でコンパイルしたものが起動するようになった。
でも、やはり速くはならない。
とりあえず、起動するようにするのに以下のようなことをした。
リソースは windres でコンパイルできる ( 引数に --input-format=rc --output-format=coff をつける )
リンクは gcc に"-mno-cygwin -mwindows"を付ける ( -mno-cygwin は要らないかも )。
ライブラリとして -lmingw32 -lstdc++ -lgcc -lmoldname -lmingwex -lmsvcrt -lkernel32 -lwinmm -luser32 -lshell32 -lmingwthrd を付ける ( 本当は要らないのがいっぱいあるかも ) 。
VC でビルドしたのが 380 KB なのに MinGW のは 2MB とでかいのは気になるが ( 後に strip ○○.exeってやれば400KBになることがわかった。でも、速くはならない ) 。
しかし、MinGW でも普通にウィンドウズアプリ作れるんだなぁ。
ちょっと感動。

libtheora.a と libogg.a を使った時に気付いたのだが、libogg.a にあるはずの bitwise.c の中の関数がないとリンク時に怒られる。
その時は、なんでないんだろう? と思ったけど、bitwise.c をくっつけてビルドして通した。
でも、もしかして bitwise.c の中の関数を高速化しているのか? と思い、調べることにした。
bitwise.c の中にはビット単位での読み出し用の関数が入っているので、この部分の高速化はかなり効くはずだ。
呼び出し回数をカウントしてみると、1フレーム中に数千~数万回呼び出される ( ちなみに、libtheora の中からは oggpackB_readinit, oggpackB_adv, oggpackB_read, oggpackB_look, oggpackB_bits, oggpackB_read1 の6つの関数が使われている ) 。
他のと同じように bitwise.c の中には、参照するだけ、参照してポインタを進める、ポインタを進めるだけの3種の関数がある。
てことで、単に関数を間接的に呼び出してたりするだけだったりするのとかあるので、インライン化することにした。
でも、C なのでマクロか直埋め。
が、わずかに高速化するもののたいした効果なし。
関数内を根本的に変えないとだめなのか、それともここはあんまり関係ないのか。
とりあえず、大変なので放置。
( 後で気付くが、mplayer では tremor と言うライブラリが使われており、その中に同じ関数が入っている。でも、それに差し替えても速度は大差なし )

libogg の中で、page や packet の読み込みを行っているが、見るとメモリのコピーなどをよく使っているように見える。
と言うことで、これ ( demux ) を自分で作ることにした。
初め、packet さえあればいいと思っていたけど、segment の境界もきちんと認識する必要があると気付き、少し非効率な作りになったが、それでもわずかばかり高速化した。
ただ、ヘッダー付近でうまく読み込めてないらしく、最初に少し画像が乱れるので直す必要がある。
と言うか、segment を考えて作り変えた方が良さそう。
でも、そんなに高速化しないのがなぁ。
と言うことで、ここは今元通りに libogg を使ってる。

しかし、mplayer などに全然追いつかない。
かなり長くなったので、ここでエントリーを分割。
つづく。



投稿者 Takenori : 2007年11月07日 02:31




comments powered by Disqus
Total : Today : Yesterday : なかのひと