サンプルの転送
サンプルの受信
いよいよサンプルの転送を実装します。アップストリームフィルタはサンプルを送信するとき、入力ピンのIMemInputPin::Receive
を呼び出しますので、これをオーバーライドします。
まず、基底クラスのReceive
を呼び出しエラーチェックを行います。戻り値がS_OK
であれば処理を続けます。
STDMETHODIMP AudioInPin::Receive(IMediaSample *pSample) { NOTE1("%S", __FUNCTION__); HRESULT hr=__super::Receive(pSample); if(hr!=S_OK) { return hr; } // (以下、サンプルの処理を実装する)
次に、受信したサンプルのストリームタイム、メディアタイム、実際のデータ長、データへのポインタを取得します。ストリームタイムとメディアタイムについてはアップストリームフィルタによっては設定しない場合があるので注意してください。また、受け取るオーディオサンプルの実データ長は受信するたびに変化することがあるのでGetActualDataLength
を呼び出して確認する必要があります。
LPBYTE copy_src, copy_dest; REFERENCE_TIME start_time_in, end_time_in, dur; LONGLONG start_media_time, end_media_time; HRESULT get_time_hr= pSample->GetTime(&start_time_in, &end_time_in); dur=end_time_in-start_time_in; HRESULT get_media_time_hr= pSample->GetMediaTime(&start_media_time, &end_media_time); pSample->GetPointer(©_src); long input_sample_length=pSample->GetActualDataLength();
入力サンプルの確認が終わったら、各出力ピンごとにGetDeliveryBuffer
を呼び、出力用サンプル(空のバッファ)を取得します。サンプルはIMediaSample
というCOMインターフェイスなのでATLのCComPtr
を使うと便利です。
AudioChSplitterFilter *filter= (AudioChSplitterFilter*)m_pFilter; CComPtr<IMediaSample> out_sample[2]; for(int i=0;i<2;++i) { if(filter->GetOutPin(i)->IsConnected()==FALSE) continue; hr=filter->GetOutPin(i)->GetDeliveryBuffer( &out_sample[i], NULL, NULL, 0);
チャンネル分離処理(コードは割愛)を行い出力サンプルに書き込んだら、ストリームタイム、メディアタイム、実データ長を設定します。ただし、ストリームタイムとメディアタイムは受信サンプルから取得できなかったら設定しないこととします。
最後に出力ピンのDeliver
を呼んで、処理を委譲します。
WAVEFORMATEXTENSIBLE *wfxe=(WAVEFORMATEXTENSIBLE *)m_mt.pbFormat; // サンプルの実データ長 hr=out_sample[i]->SetActualDataLength(write_size); // メディアタイム if(SUCCEEDED(get_media_time_hr)) { end_media_time=start_media_time+sample_count; out_sample[i]->SetMediaTime( &start_media_time, &end_media_time); start_media_time=end_media_time; }else { out_sample[i]->SetMediaTime(NULL, NULL); } // ストリームタイム if(SUCCEEDED(get_time_hr)) { int sample_sum= write_count/wfxe->Format.nBlockAlign; REFERENCE_TIME start_time= start_time_in+ sample_sum*UNITS/wfxe->Format.nSamplesPerSec; REFERENCE_TIME end_time = start_time+write_size/(wfxe->Format.nBlockAlign/2) *UNITS/wfxe->Format.nSamplesPerSec; out_sample[i]->SetTime(&start_time, &end_time); }else { out_sample[i]->SetTime(NULL, NULL); } // その他 out_sample[i]->SetPreroll(FALSE); out_sample[i]->SetDiscontinuity(FALSE); out_sample[i]->SetSyncPoint(TRUE); //ダウンストリームフィルタへ送信 hr=filter->GetOutPin(i)->Deliver(out_sample[i]); // (以下略...)
サンプルの送信
出力ピンクラスにサンプルを送信するDeliver
を実装します。ここではダウンストリームフィルタの入力ピン上のIPin::Receive
を呼び出しますが、今回はCOutputQueue
を使っているので、それのReceive
を呼び出します。
このときIMediaSample
の参照カウンタに注意してください。COutputQueue
が作成したストリーミングスレッドは、サンプルを送信した後にRelease
します。一方、CComPtr
はデストラクタでRelease
を呼び出します。したがってサンプルを送信する前に解放されてしまう可能性があるので、手動でAddRef
しておく必要があります。
HRESULT AudioOutPin::Deliver(IMediaSample *pSample) { pSample->AddRef(); return m_OutQ->Receive(pSample); }
動作確認
ようやく実装が終わりました。GraphEditを使って動作確認してみましょう。分離したオーディオはWAV Dest
かAudioRecorder WAV Dest
に渡して、WAVファイルとして書き出すことができます(図3)。
Wav DestはWindows SDKにサンプルプログラムとして付属しています。事前にコンパイルとフィルタの登録を行ってください。AudioRecorder WAV Dest は Vista にあります。XPにはありません。
おわりに
本稿では、出力ピンから優先メディアタイプを提示し、ダウンストリームフィルタの入力ピンストリームフィルタと接続できるようにしました。そして各種イベントの通知やシーク要求の受け渡しを実装しました。最後にサンプルの転送処理を実装しました。
これまで見てきたようにCBaseFilter
から派生してフィルタを作成すると手間がかかります。しかし、その分柔軟な設計・動作が実現できます。
駆け足で進めたので説明が足りないところもあったかと思いますが、本稿がDirectShowに興味を持つ方の参考になれば幸いです。
参考文献
- MSDN 『DirectShow』
- Windows SDK のサンプルコード