サンプルの転送
サンプルの受信
いよいよサンプルの転送を実装します。アップストリームフィルタはサンプルを送信するとき、入力ピンの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 のサンプルコード

