2016-10-05 28 views
1

私はDirectShow.NETを使用してウェブカメラコントロールを持っています。私はビデオを表示し、ウェブカメラから画像をキャプチャするカスタムコントロールを作成しました。私は別のWPFウィンドウでそのカスタムコントロールを使用しています。私はちょうど少しDirectShowのプログラミングを抽象化し、単にBitmapを返すカスタムコントロールのpublic Bitmap CaptureImage()関数があります。イメージは比較的大きいので(1920x1080)、IVMRWindowlessControl9GetCurrentImage()機能は処理にかなりの時間がかかります(2〜3秒)。私は自分のコードを踏んで、この呼び出しが処理に時間がかかる唯一の呼び出しであることを確認できます。バックグラウンドでIVMRWindowlessControl9.GetCurrentImage()を実行します。

メインWPFウィンドウのGUIスレッドがハングし、数秒間応答しなくなるため、イメージがキャプチャされている間に進行スピナーを表示したい場合は、そのまま固定されます。ここで

CaptureImage()ためのコードです:

public Bitmap CaptureImage() 
{ 
    if (!IsCapturing) 
    return null; 

    this.mediaControl.Stop(); 
    IntPtr currentImage = IntPtr.Zero; 
    Bitmap bmp = null; 

    try 
    { 
    int hr = this.windowlessControl.GetCurrentImage(out currentImage); 
    DsError.ThrowExceptionForHR(hr); 

    if (currentImage != IntPtr.Zero) 
    { 
     BitmapInfoHeader bih = new BitmapInfoHeader(); 
     Marshal.PtrToStructure(currentImage, bih); 

     ... 
     // Irrelevant code removed 
     ... 

     bmp = new Bitmap(bih.Width, bih.Height, stride, pixelFormat, new IntPtr(currentImage.ToInt64() + Marshal.SizeOf(bih))); 
     bmp.RotateFlip(RotateFlipType.RotateNoneFlipY); 
    } 
    } 
    catch (Exception ex) 
    { 
    MessageBox.Show("Failed to capture image:" + ex.Message); 
    } 
    finally 
    { 
    Marshal.FreeCoTaskMem(currentImage); 
    } 

    return bmp; 
} 

は、この問題を解決するために、私は次のようにバックグラウンドタスクとしてこれを実行しようとしました:

public async void CaptureImageAsync() 
{ 
    try 
    { 
    await Task.Run(() => 
    { 
     CaptureImage(); 
    }); 
    } 
    catch(Exception ex) 
    { 
    MessageBox.Show(ex.Message); 
    } 
} 

私は複数を試してみましたこれを行う方法はBackgroundWorkerを使用していますが、この呼び出しを非同期的に行うたびにこのエラーが発生します。

Unable to cast COM object of type 'DirectShowLib.VideoMixingRenderer9' to interface type 'DirectShowLib.IVMRWindowlessControl9'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{8F537D09-F85E-4414-B23B-502E54C79927}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

ときに、このライン上の

エラーが常に起こる:

CaptureImage()を呼び出す
int hr = this.windowlessControl.GetCurrentImage(out currentImage); 

は同期の正常な結果が得られます。イメージがキャプチャされ、すべてが意図どおりに機能します。しかし、あらゆる種類の非同期機能を使用するように切り替えると、そのエラーが発生します。

答えて

2

ここには2つの問題があります。まず第一に、APIの本来の低速性は設計上の振る舞いです。 MSDN mentions thisとして:

However, frequent calls to this method will degrade video playback performance.

ビデオメモリは、リードバックのためにかなりの時間がかかる可能性があります - これは2-3秒の処理の問題ではなく、それ自体が画像の解像度です。悪い知らせは、バックグラウンドスレッドからのスナップショットをポーリングしてもビジュアルストリームに影響を及ぼす可能性が高いということです。

この方法は、散発的なスナップショットをとっていました。対話的にユーザによって開始され、自動化されないもの。より集中的かつ自動化されたアプリケーションやビジュアルフィードスナップショットに影響を与えないアプリケーションは、ビデオメモリに送られる前にフィードを傍受することになっています(これにはオプションがありますが、最も一般的ですがサンプルグラッバーを使用します)。

第2に、上記の例外を引き起こす.NETスレッドの問題described in this questionが発生する可能性があります。ネイティブコード開発では、COMスレッディング規則に違反したり、アパートメント間にインターフェイスポインタを渡したりすることによって、同じインターフェイスポインタを使用するのは簡単です。 CLRはコードとCOMオブジェクトの間に中間層を追加しているため、COMスレッディング規則が適用されるため、バックグラウンドスレッドからCOMオブジェクト/インタフェースを使用して操作することはできません。

ダイレクトAPIコールに関連する長いフリーズに苦しんでいるか、フリーズを回避するためのネイティブコード開発を追加する必要があると思います(特に、ビデオメモリに送信する前にフレームをキャプチャするヘルパーフィルタ、バックグラウンドスレッドコールをサポートするための.NET呼び出し側のヘルパー機能を実装しています)。おそらく、あなたはバックグラウンドスレッドの呼び出し元の問題を解決するヘルパーMTAスレッドのプールで、すべてのDirectShow関連の処理を行うこともできますが、この場合、STAのUIスレッドからこのコードを移動する必要があります。それが何であれ、何かをしばしば考えると思う。

+0

大きな説明 - 「最も人気がありますが不器用なのはサンプルグラバーを使用しています」という記述を少し拡張できますか?サンプルグラバーの仕組みがぎこちなく、何が良い方法でしょうか? –

+0

@Roman R. DirectShow関連のコード用にMTAスレッドのプールを実装する方法についてもう少し詳細を教えてください。スレッドをSTAにする必要があるため、MTAスレッドでWPFウィンドウを開くことができません。私はあなたが言及したメソッドを実装しようとしてきましたが、実際の解決策を考え出すことはできません。 –

+0

@MikeDinescu:これはこの質問の範囲を超えていると思いますが、後ほどこれを書き留めるノートを作成しました。 –

関連する問題