サンプル グラバ フィルタ (ISampleGrabber)

サンプル グラバ フィルタとは、ビデオをサンプリングして静止画として抜き出すフィルタです。

ISampleGrabberのメソッド
分類 メソッド 説明
動作設定 SetBufferSamples フィルタ内を通るサンプルをバッファにコピーするかどうかを指定する。(サンプリングの開始と停止の指示)
SetOneShot サンプルを1つ受け取ったときに、フィルタ グラフを停止させるかどうかを指定する。
メディア タイプ SetMediaType サンプリング形式のメディア タイプを設定する。
GetConnectedMediaType サンプリング形式のメディア タイプを取得する。
  GetCurrentBuffer バッファの内容を取得する。
  GetCurrentSample [現在、使用されていない]
コールバック SetCallback サンプリング終了時に呼び出すコールバックを指定する。

サンプル グラバ フィルタの作成

ISampleGrabber sampleGrabber = null;

sampleGrabber = ( ISampleGrabber )new SampleGrabber();

メディア タイプの設定 (SetMediaType)

どのような形式で画像をサンプリングするのかを設定します。

ISampleGrabber.SetMediaType()メソッドは、フィルタが受け入れるメディア タイプの範囲を制限します。それはAMMediaTypeクラスによって定義され、majorType、subType、formatTypeメンバの順で適用されます。

AMMediaType mediaType = new AMMediaType();

mediaType.majorType = MediaType.Video;        // メジャータイプ
mediaType.subType = MediaSubType.RGB24;       // サブタイプ
mediaType.formatType = FormatType.VideoInfo;  // フォーマットタイプ


// メディア タイプを設定する
int hr = sampleGrabber.SetMediaType( mediaType );
DsError.ThrowExceptionForHR( hr );

DsUtils.FreeAMMediaType( mediaType );
ISampleGrabber::SetMediaType | MSDN

AMMediaTypeクラス

AMMediaTypeのインスタンスを作成したときには、メモリリークを防ぐためDsUtils.FreeAMMediaTypeで解放する必要があります。

AMMediaTypeのフィールド
分類 フィールド 説明
タイプ majorType Guid メディアサンプルのメジャータイプ。MediaTypeクラスから指定。
subType Guid メディアサンプルのサブタイプ。
フォーマット formatType Guid フォーマット タイプ。
formatSize int フォーマット ブロックのバイト数。
formatPtr IntPtr フォーマット ブロックへのポインタ。
種類 fixedSizeSamples bool サンプルのサイズが固定であるかどうか。
temporalCompression bool サンプルが時系列圧縮方式で圧縮されているかどうか。
  sampleSize int サンプルのバイト数。
  unkPtr IntPtr [未使用]
AM_MEDIA_TYPE 構造体 | MSDN

formatPtrが格納するフォーマットの内容は、下記のようにVideoInfoHeaderに変換して取得します。

// アンマネージ ポインタをマネージ オブジェクトにマーシャリングする
object formatObj = Marshal.PtrToStructure(
    mediaType.formatPtr,
    typeof( VideoInfoHeader ) );

// VideoInfoHeaderにキャストする
VideoInfoHeader videoInfoHeader = ( VideoInfoHeader )formatObj;

MediaTypeクラス

MediaTypeのフィールド
フィールド 説明
Audio オーディオ
Video ビデオ
AnalogAudio アナログ オーディオ
AnalogVideo アナログ ビデオ
ScriptCommand スクリプト コマンド (クローズド キャプション用)
AuxLine21Data Line21データ (クローズド キャプション用)
AUXTeletextPage  
Interleaved インターリーブされたオーディオとビデオ (デジタル ビデオ用)
Mpeg2Sections MPEG-2 PES パケット
MSTVCaption  
Texts テキスト
Midi MIDIフォーマット
Timecode タイムコード データ
File ファイル [廃止]
Stream タイム スタンプのないバイト ストリーム
VBI  
CC_Container  
DTVCCData  
LMRT [現在、使用されていない]
URLStream [現在、使用されていない]
メジャー タイプ | MSDN

サンプリング バッファの確保

サンプリング結果を受け取るための、バッファを確保します。

バッファに必要なメモリサイズの取得

1.サンプル グラバのメディア タイプから算出

AMMediaType mediaType = new AMMediaType();

// サンプル グラバからメディア タイプを取得する
int hr = sampGrabber.GetConnectedMediaType( mediaType );
DsError.ThrowExceptionForHR( hr );

// サイズ情報を取得する
VideoInfoHeader videoInfoHeader
    = ( VideoInfoHeader )Marshal.PtrToStructure( mediaType.formatPtr, typeof( VideoInfoHeader ) );

int width = videoInfoHeader.BmiHeader.Width;
int height = videoInfoHeader.BmiHeader.Height;
int stride = videoInfoHeader.BmiHeader.Width * ( videoInfoHeader.BmiHeader.BitCount / 8 );

DsUtils.FreeAMMediaType( mediaType );

// バッファのサイズを算出する
int bufferSize = Math.Abs( stride ) * height;

2.サンプリング バッファから取得

SampleGrabber.GetCurrentBuffer()メソッドの呼び出し時に、第2引数にIntPtr.Zeroを渡すことでバッファのサイズを取得できます

int bufferSize = 0;
IntPtr buffer = IntPtr.Zero;

// バッファのサイズを取得する
sampleGrabber.GetCurrentBuffer( ref bufferSize, buffer );

3.サンプリング終了時のコールバックで取得

ISampleGrabberCB.BufferCBメソッドの引数BufferLenには、バッファのサイズが格納されています。

メモリの確保

Marshal.AllocCoTaskMemでメモリを確保できます。なお確保したメモリは、Marshal.FreeCoTaskMemで解放する必要があります。

サンプリングの実行

サンプル グラバは1つのフレームしか保持できません。サンプリングは動画のフレームごとにくり返し処理されるため、古いフレームのデータは上書きして処理されます。よってサンプリングは、必要なときにのみ処理すべきです。

サンプリングの開始 (SetBufferSamples)

int SetBufferSamples(
    bool BufferThem  // trueでサンプリングの開始、falseで停止
    );

サンプリングの実行と停止を指示します。

サンプリング終了の検知 (SetCallback)

サンプリングの終了を検知するために、コールバックを登録します。

int SetCallback(
    ISampleGrabberCB pCallback,  // コールバック メソッドが格納されたISampleGrabberCBインターフェイスへのポインタ
    int WhichMethodToCallback    // 呼び出すコールバックメソッドの指定
    );

WhichMethodToCallbackには呼び出したいメソッドによって、次のいずれかの値を指定します。

呼び出されるメソッド
0 ISampleGrabberCB.SampleCB
1 ISampleGrabberCB.BufferCB
ISampleGrabber::SetCallback | MSDN

SampleCB

int SampleCB(
    double SampleTime,      // サンプルの開始時間 [sec]
    IMediaSample pSample    // サンプルのIMediaSampleインターフェイス
    );

BufferCB

int BufferCB(
    double SampleTime,  // サンプルの開始時間 [sec]
    IntPtr pBuffer,     // サンプル データを含むバッファへのポインタ
    int BufferLen       // サンプル データを含むバッファのバイト数
    );

サンプリング バッファからのデータの取得

バッファからデータを取得するには、事前にSetBufferSamplesでサンプリングを開始している必要があります。

int GetCurrentBuffer(
    ref int pBufferSize,    // バッファのサイズ
    IntPtr pBuffer          // バッファ
    );
int bufferSize = 0;
IntPtr buffer = IntPtr.Zero;

// バッファのサイズを取得する
sampleGrabber.GetCurrentBuffer( ref bufferSize, buffer );

// バッファにメモリを割り当てる
buffer = Marshal.AllocCoTaskMem( bufferSize );

// バッファのコピーを取得する
sampleGrabber.GetCurrentBuffer( ref bufferSize, buffer );

コールバックからの取得

[DllImport( "Kernel32.dll", EntryPoint = "RtlMoveMemory" )]
private static extern void MoveMemory( IntPtr Destination, IntPtr Source, [MarshalAs( UnmanagedType.U4 )] int Length );

/// <summary>
/// サンプリング終了時に呼び出されるコールバック
/// </summary>
/// <param name="sampleTime">サンプルの開始時間</param>
/// <param name="pBuffer">サンプル データを含むバッファへのポインタ</param>
/// <param name="bufferLength">サンプル データを含むバッファのバイト数</param>
/// <returns></returns>
int ISampleGrabberCB.BufferCB( double sampleTime, IntPtr pBuffer, int bufferLength )
{
    // バッファを保存する
    MoveMemory(
        this.buffer,   // 移動先のメモリへのポインタ
        pBuffer,       // 移動元のメモリへのポインタ
        bufferLength   // 移動するメモリのバイト数
        );
}

画像への変換

サンプリング バッファのデータはIntPtr型で返されるため、これをSystem.Drawing.Bitmapのインスタンスに変換して、画像としての処理を容易にします。

IntPtr buffer;  // サンプリング バッファのデータ

AMMediaType mediaType = new AMMediaType();

// サンプル グラバからメディア タイプを取得する
int hr = sampGrabber.GetConnectedMediaType( mediaType );
DsError.ThrowExceptionForHR( hr );

// サイズ情報を取得する
VideoInfoHeader videoInfoHeader
    = ( VideoInfoHeader )Marshal.PtrToStructure( mediaType.formatPtr, typeof( VideoInfoHeader ) );

int width = videoInfoHeader.BmiHeader.Width;
int height = videoInfoHeader.BmiHeader.Height;
int stride = videoInfoHeader.BmiHeader.Width * ( videoInfoHeader.BmiHeader.BitCount / 8 );

DsUtils.FreeAMMediaType( mediaType );

// ビットマップの作成
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(
    width,                      // Bitmapの幅
    height,                     // Bitmapの高さ
    stride,                     // スキャン ラインの間のバイト オフセット数
    PixelFormat.Format24bppRgb, // カラーデータの形式
    buffer                      // ピクセル データを格納しているバッファ
    );

// 上下を反転する
bitmap.RotateFlip( System.Drawing.RotateFlipType.RotateNoneFlipY );