特殊なコーデックをDirectShowで対処する

世の中の大抵の汎用コーデックはffmpegがあれば大抵のことは出来てしまう

ところが独自コーデックを使う稀有なシーンもある訳で、これを扱おうとした時恐ろしいまでにできることが減り、不便になる。動画をコーデックが提供する逆コンバート無しでC#のビットマップオブジェクトとして読みたい場合どうするのか

まあ悩みつつ色々調べた結果、DirectShowならWMPで再生でき、かつ古めかしくもそここそこ一般的なフォーマット、例えば独自コーデックのAVIならある程度再現可能ということが分かった。検証例としてはアマレココのAMV4コーデックで、こういうものなら抽出できそうだと分かった

DirectShowはXP時代の遺物だが、未だにそこそこ使われているWindowsのメディア用低レイヤAPIで、本来はバリバリのC++になる。ググるとぶっちゃけ静的な動画なんかより、リアルタイムのカメラデバイスの話がわんさか出てくる。基本的に特殊コーデックを扱いたいなんてニーズは少ないから、そりゃそうだとは言えるが

なお、後継としてMedia Foundationが存在するが、対応コーデックが少なかったり、従来のmp4ファイルが上手く情報を取れなかったり、肝心な部分がイマイチピンとこなかった。ただし、どちらもDirectXを支えるAPIセットではあるので、コンシューマ系ゲームエンジンとかにはそこそこ使われているようだ

Visual StudioのC++パックでWindows SDKを入れておくとProgram Files (x86)\Windows Kits\10\bin\10.0.xxxx\x64に入ってるgraphedt.exeにて、その挙動を確認することができる

つまるところ、この仕組みは「フィルター」というものの組み合わせで出来ており、例えばギターミュージシャンが音を出すのに色々なエフェクターを重ねるように、インストールされたスプリッタやデコーダをつなげていって、最終的にレンダラに繋げる。例えばAMV4ならAVIスプリッタを通してAVIデコーダに繋げ、Enhanced Rendererに繋げればPCで再生できるようになる。こういうものを、本来プログラムで実現できるようである。なるほどわからん

ただし、とりあえず色々あれこれ利用しなくても動画でサムネイルを取る目的なら、できるようだ。利用するのはIMediaDet インターフェイスで、COM参照のDexterWindows\System32\qedit.dllを参照に追加することで、映像ファイルを読み取って.WriteBitmapBits関数によりビットマップデータを吐き出せるようになる

…と思うやろ?ところがどっこいこれが異常に大変なのである(笑)

.SaveBitmapFile関数が提供されているが、これは恐ろしく簡単でファイルパスを引数に与えれば良い。しかし、データで取ろうとなると、実際には必要なのはマーシャルしまくりの行為であり、結局そこそこのコード量になりうる。実のところまだまだ理解していない。あと、時間単位なのでフレーム単位とかで取るのはできない。フレームレートから端的にタイムスパンを割り出すのは1つの策だと思うが、実際のところ1秒毎にデータを取ったりしてなにかするみたいなケースのほうがやりやすいと思われる

画像数値の評価

さて、本来何が何がしたかっというと、個人的にユースケースだと思ってるのがSSIM

画像データというのは、統計的にノイズ比較を行い、劣化具合を客観的に計測することは工学的に研究されている。このうち著名な指標がPSNRとSSIMであり、最近ではNetflixなどが品質調整アルゴリズムに使うVMAFという手法もある(結構重い)

SSIMは画像構造に着目した指標で、端的には小領域に分けて輝度コントラストなどを分けて標準偏差などを割り出し、それらを掛け合わせたものになる。とても複雑な数式なので、詳細は省く

SSIMが良いと思ったのは個人的な主観でいくつかのモデルケースを検証した結果、画像劣化しやすいと判別できるポイントにブレや下降線が顕著だったからになる

こういうものを計測しておくことによって、最終的に人間が分析することになるが、単なる主観によらない根拠のある予測や、思いもよらない劣化点を見出すことが可能になる

この客観評価は原信号との比較になるので、特殊なコーデックを扱うときはどうしたものかと思った。まあ、これできるかどうか、とてもやることが多いはず…

なお、ffmpegでやると途方もなく簡単だったりする

下記のコマンドを叩けばレポートが弾き出されるので、(それはそれで作るの大変だが)ログを処理してjsonなどのフォーマットでレポート化し、グラフ画像化するプログラムを作成すれば分かりやすくなるだろう

ffmpeg -i target.mp4 -i origin.mp4 -filter_complex ssim=log.txt -an -f null -

まあ検証することは多そうだ