SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

DirectShowフィルタの作成

オーディオチャンネルを分離する
DirectShowフィルタの作成 - 後編

複数の出力ピンを持つDirectShowフィルタの作成方法

  • X ポスト
  • このエントリーをはてなブックマークに追加

サンプルの転送

サンプルの受信

 いよいよサンプルの転送を実装します。アップストリームフィルタはサンプルを送信するとき、入力ピンの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(&copy_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 DestAudioRecorder WAV Destに渡して、WAVファイルとして書き出すことができます(図3)。

図3.動作結果
図3.動作結果
Wav DestとAudioRecorder WAV Dest

 Wav DestはWindows SDKにサンプルプログラムとして付属しています。事前にコンパイルとフィルタの登録を行ってください。AudioRecorder WAV Dest は Vista にあります。XPにはありません。

おわりに

 本稿では、出力ピンから優先メディアタイプを提示し、ダウンストリームフィルタの入力ピンストリームフィルタと接続できるようにしました。そして各種イベントの通知やシーク要求の受け渡しを実装しました。最後にサンプルの転送処理を実装しました。

 これまで見てきたようにCBaseFilterから派生してフィルタを作成すると手間がかかります。しかし、その分柔軟な設計・動作が実現できます。

 駆け足で進めたので説明が足りないところもあったかと思いますが、本稿がDirectShowに興味を持つ方の参考になれば幸いです。

参考文献

  • MSDN 『DirectShow
  • Windows SDK のサンプルコード

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
DirectShowフィルタの作成連載記事一覧

もっと読む

この記事の著者

syu5()

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/3090 2008/12/13 13:49

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング