読者です 読者をやめる 読者になる 読者になる

Paradigm Shift Design

ISHITOYA Kentaro's blog.

DirectShow.NETを使って音声キャプチャ

C#

えー。
DirectShow.NETを使った静止画キャプチャは、サンプルがたくさんありますが、音声キャプチャはあまりないので載せておきます。


DShowNETから「Download Source Files」を選択してdirectshownet.zipをダウンロードします。codeproject.comのアカウントが必要なので面倒くさいですが。


DirectShow.NETを用いたサンプルは
USBカメラをC#で使おうのサンプルが一番わかりやすいです。
これを切った貼ったすれば、すぐにUSBカメラを使ったアプリケーションが作れます。


正直GraphがどうのとかFilterがどうのとか、覚えるつもりが全くないのでわかりませんが、プログラムはかけました。


で、本題の音声キャプチャですが、動画から静止画キャプチャするのとあまり変わりませんので、サンプルのGraphを作っているところ、DSCapture::SetupGraphにある数か所を変更すれば音声キャプチャができるようになります。

まず247:258行目のSampleGrabberの設定

//キャプチャするビデオデータのフォーマットを設定.
AMMediaType amMediaType = new AMMediaType();
amMediaType.majorType = MediaType.Video;
amMediaType.subType = MediaSubType.RGB24;
amMediaType.formatType = FormatType.VideoInfo; 

result = sampleGrabber.SetMediaType(amMediaType);
if(result < 0) Marshal.ThrowExceptionForHR(result);

//grabFilter(変換フィルタ)をgraphBuilder(フィルタグラフマネージャ)に追加.
result = graphBuilder.AddFilter(grabFilter, "Frame Grab Filter");
if(result < 0) Marshal.ThrowExceptionForHR(result);

と、263:268行目、RenderStreamの設定

//キャプチャフィルタをサンプルグラバーフィルタに接続する.
pinCategory = PinCategory.Capture;
mediaType = MediaType.Video;
result = captureGraphBuilder.RenderStream(ref pinCategory, ref mediaType, 
	captureFilter, null, grabFilter);
if(result < 0) Marshal.ThrowExceptionForHR(result);

及び291:298行目のSampleGrabberコールバックの設定

//フィルタ内を通るサンプルをバッファにコピーしないように指定する.
result = sampleGrabber.SetBufferSamples(false);
//サンプルを一つ(1フレーム)受け取ったらフィルタを停止するように指定する.
if(result == 0) result = sampleGrabber.SetOneShot(false);
//コールバック関数の利用を停止する.
if(result == 0) result = sampleGrabber.SetCallback(null, 0);

if(result < 0) Marshal.ThrowExceptionForHR(result);

です。


これを次のように

  • SampleGrabberをAudio向けに作成
  • audioFilterにgrabFilterを接続
  • ISampleGrabberCBインターフェースを実装したコールバックを作成してgrabFileterにセット

という流れで、実装することができます。


まずAudio向けSampleGrabberの作成ですが、

//キャプチャするオーディオのフォーマットを設定.
AMMediaType amMediaType = new AMMediaType();
amMediaType.majorType = MediaType.Audio;
amMediaType.subType = MediaSubType.PCM;

result = sampleGrabber.SetMediaType(amMediaType);
if (result < 0) Marshal.ThrowExceptionForHR(result);

//grabFilter(変換フィルタ)をgraphBuilder(フィルタグラフマネージャ)に追加.
result = graphBuilder.AddFilter(grabFilter, "Audio Grab Filter");
if (result < 0) Marshal.ThrowExceptionForHR(result);

で作れます。Filterの初期化なんかは最初に紹介したサンプルと同じです。
ここで問題が。MediaSubType.PCMはDirectShow.NETには存在しません。
ので、GUIDを最初にダウンロードしたソースコードの中にある、DShowNETプロジェクトのDsUUid.csの106行目あたりに

// 00000001-0000-0010-8000-00AA00389B71            MEDIASUBTYPE_PCM
public static readonly Guid PCM = new Guid(0x00000001, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71);

// e436eb8b-524f-11ce-9f53-0020af0ba770            MEDIASUBTYPE_WAVE
public static readonly Guid Wave = new Guid(0xe436eb8b, 0x524f, 0x11ce, 0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70);

を追加して、ビルドしてできたDLLを使うか、上記guidを直接使うかしてください。その他のUUIDを使いたい場合は、

Media Types


Koders Code Search: uuids.h - C++
を参考に、新たに定義してください。


これができたら、

pinCategory = PinCategory.Capture;
result = captureGraphBuilder.RenderStream(ref pinCategory, ref audioMediaType,
                                    audioFilter, null, grabFilter);
if (result < 0) Marshal.ThrowExceptionForHR(result);

のようにgrabFilterをaudioFilterに接続します。
あとはISampleGrabberCBを実装した

class DSAudioSampler : ISampleGrabberCB
{
    public int BufferCB(double SampleTime, IntPtr pBuffer, int BufferLen)
    {
        int[] frameArray = new int[BufferLen / 10];
        Marshal.Copy(pBuffer, frameArray, 0, BufferLen / 10);

        //do something here
        return 0; 
    }

    public int SampleCB(double SampleTime, IMediaSample pSample)
    {
        return 0;
    }
}

のようなクラスを作成し、

DSAudioSampler sampler = new DSAudioSampler();
result = this.sampleGrabber.SetCallback(sampler, 1);
if (result < 0) Marshal.ThrowExceptionForHR(result);
result = this.sampleGrabber.SetBufferSamples(true);
if (result < 0) Marshal.ThrowExceptionForHR(result);

のように、CallBackを登録してやると、BufferCBに、データがやってくるようになります。
SampleTimeを短くしたりする方法はわかりません。
ので、分かる方は教えてください…


音声レベルのインジケーターを作りたかったので、ProgressBarを使って、ProgressBar.Valueに、CallBackで取得したaverageを設定するようにしています。

というわけで、DirectShowNETの音声キャプチャでした。


せんでん

1タップで写真共有tottepost
カメラのついたiPad/iPhoneで撮った写真を、その場でFacebook/Mixi/DropboxなどのサービスにアップロードできるtottepostというiOSアプリを開発しています!詳しくは、iTunes App Storeをご覧ください。


ご購入はこちら!
1タップで写真共有 - tottepost - ISHITOYA Kentaro