何故かいきなりDirectShowのネタです。今日更新したERI DirectShow FilterとDirectShow Externd Fiter Libraryに絡むネタです。
というのも、実は自分が実装したいくつかのフィルタの中で気になっていた動作をしているものがありまして、それを持っているのがこれらだったわけです。はい。
バグではないのですが、フィルタをシステムに登録(regsvr32を使っての登録)して、Windows Media Playerで再生をさせてみると、どうもシークバーが全く動作しない。
GraphEditやGraphStudioといった直接グラフを扱うプログラムではシーク処理も正しく動作していたのですが、これの理由を気になって調べてみたときのことです。
答えとしては「それは気がつきにくいよね~」というたぐいのものでした。
今までのルーチンについて
フィルタと言っても、IMediaSeekingの実装が必要なのはParserフィルタ(というかSplitフィルタ)です。
通常、メディアを読み込んだ後にストリームラインを分け(Videoライン+Audioラインのような形)て、デコーダなどに渡す役割をしています。
このとき、対象のフィルタの出力ピンでIMediaSeekingを取得できなければ、そのグラフをシークさせることができなくなってしまいます。
そのため、その処理を行うのですが・・・。
問題となった部分の処理がこれです。
STDMETHODIMP CSplitOutputPin::NonDelegatingQueryInterface(REFIID refiid,void **lplpInterface) { //IMediaSeekingをフィルタから取得する if(IsEqualIID(refiid,IID_IMediaSeeking)){ if(static_cast<CBasePin *>(this) != GetSplitFilter()->GetPin(1)) return E_NOINTERFACE; return GetSplitFilter()->QueryMediaSeeking(lplpInterface); } return CBaseOutputPin::NonDelegatingQueryInterface(refiid,lplpInterface); }
何を意図したのかというと
- 各出力ピンのQueryInterfaceの実装部分であり、親フィルタからGetPinで各ピン(入力ピン:0、出力ピン:1以降)を判別する
- 先頭の出力ピンでないときはIMediaSeekingを取得できないようにする
- 先頭の出力ピンであるとき「だけ」IMediaSeekingを取得する
グラフから見れば、IMediaSeekingが構築されている一連のグラフ(出力ピンに一つもあまりがない状態のグラフのかたまり)からIMediaSeekingが一つでも取得できれば
そのグラフオブジェクトはシークできるわけですので、この処理でも普通に動作します。が・・・。
Windows Media Playerでグラフを構築するときは、メディアの出力ピンすべてでIMediaSeekingを取得できなければならないらしい
これが原因でした。おそらく、Windows Media PlayerがDirectShow経由でフィルタを構築するときに、いくつかのフィルタを出力までに割り込ませるのですが、
それらでIMediaSeekingが取得できないものが存在するときにWindows Media Playerは再生しているメディアのシーク動作そのものをあきらめてしまうようなのです。
意外とやっかいな仕様です。つまりParserフィルタの出力ピンはすべてIMediaSeekingを公開しなければWindows Media Playerでシークできない(ループ再生は例外)という意味です。
もちろん、IMediaSeekingがシーク動作をサポートしていない、とかは問題外ですよ。ストリーム再生のようなパターンですが…。
ちなみに、「何故、以前の実装で先頭ピンのみだけ公開にしているのか」の理由は、IFilterGraph(=IGraphBuilder)経由で、シーク処理を行うときの処理に問題があります。
IFilterGraphからQueryInterfaceでIMediaSeekingを取得したとき、直接Parserフィルタなどから取得しているわけではなく、内部的にいくつかの処理を中継しています。
これにより、たとえばVideoラインとAudioライン両方がIMediaSeekingを公開しているとき、IFilterGraphのIMediaSeekingを使ってシーク処理(=SetPositions)を行うと
両方のラインに対してシーク処理(=SetPositions)が発行してしまい、大本のParserフィルタのIMediaSeekingでは多重発行されてしまう、という問題があった為です。
これは、たとえばSetPositionsで直接デコーダに再生位置移動を発行しているときに不測の事態を招く可能性がある動作になります。そのためにしなかったのですが・・・。
いくつかの変数を追加することで多重発行を防止するようにして解決
放置しておいたつけを払うがごとく、この部分の動作を改良しました。
これによって、何とかWindows Media Player経由でも正しくシーク処理ができるようになったわけです。
この修正による恩恵はむしろERI DirectShow Filterの方が大きく、これでシステムに登録してしまえばWindows Media Playerで普通に再生できます。
DirectShow Extend Filter Libraryではこの修正の影響はほぼありません。違う部分の問題も一緒に発掘してしまったので修正を行っていますが。
IMediaSeekingの実装について
一応CSourceSeekingというこの用途で使えそうなクラスを提供はされていますが、実際には使わないと思います。
実装がかなりザルになっているので自分で組み直した方がいいです。私の場合はCSourceSeekingがあること自体に気がつかなかったのですが・・・。