高速キャプチャ

ここでは入力されたすべてのイメージを取得するFrameQueueSinkクラスライブラリリファレンス>クラス>FrameQueueSinkの使い方について説明しています。タスク処理がフレーム間隔時間よりも長い場合においても連続 したフレームを確保できます。
Visual Studio プロジェクトとフレームフィルタモジュールは IC Imaging Controlインストールディレクトリのsamples\VC\HighSpeedCaptureにあります。

概要

このサンプルでは、最初にビデオキャプチャデバイスの選択と設定を要求します。デバイスが選択されると、デバイスからのライブ映像を表示します。

ウィンドウの下部では、取得する画像データを保存する保存先ディレクトリと、使用する一時メモリサイズを設定することができます。 [Maximize]ボタンを押すことで、使用しているPCシステムで使用できる最大のシステムメモリを割り当てます。

[Start]ボタンを押すと、キャプチャされた全てのフレームを指定先フォルダに保存しようと動作します。保存先ストレージへの転送帯域がキャプチャデバイスから入力されるデータ帯域より遅い場合、まだストレージに送られていないフレームはシステムに確保された一時メモリバッファに確保されます。このメモリバッファが一杯になるとアプリケーションはフレームドロップを発生することになります。

[Stop]ボタンを押すことで、ビデオキャプチャデバイスからのバッファの入力を停止します。一時メモリに残っているフレームデータはすべてストレージに保存されます。

Sinkのセットアップ

ユーザーによりビデオフォーマットが選択されると、SinkはOnBnClickedDeviceに生成されます。

GUID sinkFormat = selectMatchingSinkFormat( m_Grabber.getVideoFormat() .getSubtype() );
m_pSink = DShowLib::FrameQueueSink::create( *this, sinkFormat );
m_pSink->setSinkMode( DShowLib::GrabberSinkType::ePAUSE );
m_Grabber.setSinkType( m_pSink );
m_Grabber.startLive();

ビデオフォーマットに依存したsinkバッファフォーマットを選択するために、ヘルパー関数のSelectMatchingSinkFormatを使います。
FrameQueueSink::createクラスライブラリリファレンス>クラス>FrameQueueSink>FrameQueueSink::create MethodをコールしてSinkが作成されると、その参照情報がリスナーに引き渡されます。これにより、ダイアログクラスの中で直接情報を受け取ることができます。
このSinkは初期状態としてGrabberSinkType::ePAUSEとなっているので、ユーザーによりStartされるまで入力画像は処理されません。

バッファの管理

TryAllocateBuffers関数は、 FrameQueueSink::allocAndQueueBuffersクラスライブラリリファレンス>クラス>FrameQueueSink>FrameQueueSink::allocAndQueueBuffers Methodを使用して、ユーザーが指定したバッファメモリのサイズから計算されたバッファ数を確認します。

コールバック関数

FrameQueueSinkListener::framesQueuedクラスライブラリリファレンス>クラス>FrameQueueSinkListener>FrameQueueSinkListener::framesQueued Methodは、CHighSpeedCaptureDlgに実装します。

void CHighSpeedCaptureDlg::framesQueued( DShowLib::FrameQueueSink& sink )
{
    std::vector<smart_ptr<DShowLib::FrameQueueBuffer>> buffers = sink.popAllOutputQueueBuffers();
    for( size_t i =0; i < buffers.size(); ++i )
    {
        CString fileName;
        fileName.Format( TEXT("%s\\image_%d.bmp"), m_destinationFolder.GetString(),m_frameNumber++ );
        DShowLib::saveToFileBMP( *buffers[i], fileName );
        UpdateStats( sink, m_sinkStats, buffers.size() -i, 1 );
        sink.queueBuffer( buffers[i] );
    }
    UpdateStats( sink, m_sinkStats,0, 0 );
}

コールバック関数によって、Sinkの出力キューにあるすべてのイメージデータがストレージに保存されます。

統計情報

ドライバとSinkそれぞれのフレームカウント情報を表示します。

DShowLib::DriverFrameDropInformation streamStats = {};
m_Grabber.getDriverFrameDropInformation( streamStats );
uint64_t driver_delivered = streamStats.FramesDelivered;
uint64_t driver_dropped = streamStats.FrameDroppedDueToPacketLoss + streamStats.FramesDroppedDueToTransforms
              + streamStats.FramesDroppedDueToApplicationQueue + streamStats.FramesDroppedDueUnspecified;
DShowLib::FrameQueueSink::FrameCountInformation sinkStats = m_pSink->getFrameCountInfo();
int64_t sink_copied = sinkStats.framesCopied;
int64_t sink_dropped = sinkStats.framesDropped;
txt.Format( TEXT( "Driver: delivered %d, dropped %d, - Sink: copied %d, dropped %d - Processed: %d" ),
              driver_delivered, driver_dropped, sink_copied, sink_dropped, stats.numProcessed );
m_lblCaptureInfo.SetWindowText( txt );

ドライバのフレームカウンタにアクセスするのに、Grabber::getDriverFrameDropInformationクラスライブラリリファレンス>クラス>Grabber>Grabber::getDriverFrameDropInformation Methodを使用します。デバイスからのデータ送信中に問題が発生した場合やドライバが要求する十分なCPUリソースを持っていなかった場合に発生します。すべてのデバイスドライバがこれをサポートしているわけではありません。
Sinkのフレームカウンタ情報はFrameQueueSink::getFrameCountInfoクラスライブラリリファレンス>クラス>FrameQueueSink>FrameQueueSink::getFrameCountInfo Methodをコールして取得します。Sinkの バッファが一杯になると、フレームドロップが発生します。