8

Stack Overflow メンバーの皆様、こんにちは。

私はC#プログラミングの初心者です。基本的なカメラ ストリーミングとキャプチャ アプリケーションを開発しています。ユーザーが撮影したら、VMR9 のビットマップ ミキシング コンセプトを使用してオーバーレイに表示します。

私がしたこと?

  • ここからC#ダイレクトショーライブラリを利用しています
  • まず、必要なフィルター インターフェイスをすべて取得します。接続されているキャプチャ デバイスを見つけます。PREVIEW ピンのソース フィルターと vmr9 でレンダー ストリームと呼ばれます。STILL PIN のソース フィルター、サンプル グラバー、ヌル レンダラー。
  • 私は3つのメニューボタンを持っています->静止画を撮り、オーバーレイを表示し、オーバーレイを非表示にします。
  • そのライブラリで提供されているビットマップ ミキサー サンプルを利用しています。
  • ユーザーが [Take Still] メニューを押すたびに、画像がデスクトップに保存され、小さな解像度にサイズ変更されてビデオ オーバーレイに表示されます。
  • オーバーレイの表示とオーバーレイの非表示は、vmr9 フィルターから VMR9BitmapMixer インターフェイスを照会する操作を実行する ShowHideBitmap() を呼び出し、VMR9AlphaBitmap 構造を埋めてから IVMRMixerBitmap9.SetAlphaBitmap 関数を呼び出します。

私が直面している問題は何ですか?

  • 静止画を撮影した後、メニュー オプションから ShowHideBitmap() を呼び出すと、撮影した静止画がオーバーレイ上で完全に更新されます。
  • これは、静止画像が保存されるとすぐにオーバーレイの自動更新を実行する別のオプションです。イベントベースのスレッドを作成し、EventWaitHandle を使用して作成された更新イベントを待機するようにしました。samplegrabber BufferCB 関数から戻る前に、この更新イベントを設定します。次に、待機中のスレッドに進みます。スレッド内で ShowHideBitmap 関数を呼び出します。このシナリオでは、次のようなエラー メッセージが表示されます。

Unable to case COM object of type 'DirectShowLib.VideoMixingRenderer9' to interface type 'DirectShowLib.IVMRMixerBitmap9'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{ced175e5-1935-4820-81bd-ff6ad00c9108}' failed due to the following error: No such interface supported (Exception from HRESULT: 0X80040002 (E_NOINTERFACE)

ShowHideBitmap 関数のコード ブロックは次のとおりです。

//Declarations
private static IBaseFilter vmr9 = null;
private static IVMRMixerBitmap9 vmr9mixerBitmap = null;
private IVMRWindowlessControl9 vmr9windowlessCtrl = null;

private static void ShowHideBitmap(Boolean bEnable)
{
int hr = 0;
VMR9AlphaBitmap alphaBmp;

if (!bEnable)
{
    if (vmr9mixerBitmap != null)
    {
        // Get current Alpha Bitmap Parameters
        hr = vmr9mixerBitmap.GetAlphaBitmapParameters(out alphaBmp);
        DsError.ThrowExceptionForHR(hr);

        // Disable them
        alphaBmp.dwFlags = VMR9AlphaBitmapFlags.Disable;

        // Update the Alpha Bitmap Parameters
        hr = vmr9mixerBitmap.UpdateAlphaBitmapParameters(ref alphaBmp);
        DsError.ThrowExceptionForHR(hr);

        // Create a surface from our alpha bitmap
        surface.Dispose();

        vmr9mixerBitmap = null;

        //Release this alpha bitmap source.
        if (alphaBitmap != null)
        {
            alphaBitmap.Dispose();
        }
    }
    return;
}
else
{
    try
    {
        alphaBitmap = BitmapGenerator.GenerateAlphaBitmap();

        // Create a surface from our alpha bitmap
        if(surface == null)
            surface = new Surface(device, alphaBitmap, Pool.SystemMemory);

        // Get the unmanaged pointer
        unmanagedSurface = surface.GetObjectByValue(DxMagicNumber);

        if (vmr9mixerBitmap == null)
            vmr9mixerBitmap = (IVMRMixerBitmap9)vmr9;


        // Set Alpha Bitmap Parameters for using a Direct3D surface
        alphaBmp = new VMR9AlphaBitmap();
        alphaBmp.dwFlags = VMR9AlphaBitmapFlags.EntireDDS;
        alphaBmp.pDDS = unmanagedSurface;
        alphaBmp.rDest = GetDestRectangle();
        alphaBmp.fAlpha = 1.0f;

        // Set Alpha Bitmap Parameters
        hr = vmr9mixerBitmap.SetAlphaBitmap(ref alphaBmp);
        DsError.ThrowExceptionForHR(hr);
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
}

そして、これが更新イベントを待つスレッドです。

    Thread overlayupdatethreadhandle = new Thread(new ThreadStart(overlayupdatethread));
            overlayupdatethreadhandle.Start();

    private void overlayupdatethread()
    {
        do
        {
            overlayupdateeventhandle.WaitOne();
            ShowHideBitmap(GlobalVar.m_ShowOverlay);
        } while (true);
    }

バックグラウンドで100ミリ秒間隔で実行されていたタイマーを使用して、このオーバーレイを更新しようとしました。タイマーの使用はうまく機能していましたが、この操作ではタイマーの使用は不適切です。だから私はスレッドの概念で動いた。

スレッドからの呼び出し中にインターフェイスの取得に失敗し、メニュー オプションから呼び出したときにうまく機能するのはなぜですか? 特別な世話をする必要がありますか?パラメータ化されたスレッドも試しましたが、うまくいきません。

よろしくお願いします。

編集: ShowHideBitmap がメイン スレッドから呼び出された場合、すべて正常に動作します。ワーカー スレッドから ShowHideBitmap が呼び出されると、COM オブジェクトは Exception を作成します。このクロススレッド操作を処理する方法は?

4

2 に答える 2

4

例外はラティーであり、COM では珍しくありません。それが実際に意味することは、「ワーカー スレッドから使​​用できるインターフェイス参照を提供する方法がわかりません」ということです。これはよくある事故ですが、この種の COM コンポーネントはまったくスレッドセーフではありません。そして、ワーカー スレッドからオーナー スレッドへの呼び出しを自動的にマーシャリングすることによって、それを強制します。または、マーシャリングが無意味になり、非常に遅くなるため、別のスレッドからそれらをまったく使用できないようにすることによって。VMR は後者のカテゴリに分類されます。

これは .NET とは大きく異なり、完全にスレッドセーフでないクラスも多数あります。基本的なものも、たとえばコレクションクラスはありません。ただし、これらのクラスをスレッド内で使用できるため、スレッドセーフにするかどうかはユーザー次第です。もちろん、これは非常に頻繁に失敗します。適切なロックを使用することはスキルです。

COM は、設計上、常にスレッドに対応しています。スレッド化を正しく行うのは非常に難しいという哲学があるため、賢い人が面倒を見る必要があります。これは、95% の確率で素晴らしく機能します。そして、残りの時間は大きな片頭痛を引き起こします。COM がスレッド処理を処理するときに、パフォーマンスの低下を診断するのが難しいために引き起こされる片頭痛のようなものです。そして、そうでない場合のくだらないエラー報告。

VMR インスタンスを作成したのと同じスレッドからそのインターフェイスを使用する必要があります。それを回避する方法はありません。

于 2013-03-27T13:51:07.220 に答える
2

Delphi ライブラリのリスナー/イベント ハンドラ オブジェクトを使用しようとすると、エラー E_NOINTERFACE が発生しました。マーシャリングとさまざまなスレッドの問題を克服するために、リスナーを割り当てるスレッドのディスパッチャーを保存し、それを使用してイベントを発生させました。

インターフェース:

[ComVisible(true)]
[Guid("2FFC2C20-A27B-4D67-AEA3-350223D3655F")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDataSystemInterfaceEventListener
{
    void OnIntializeCompleted(int status);
    void OnTerminateCompleted(int status);
    void OnRunCompleted(int status);
}

[ComVisible(true)]
[Guid("B9953413-A8C9-4CE2-9263-B488CA02E7EC")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDataSystemInterface
{
    void Intialize(string config);
    void StartRun(string conditions);
    void StopRun();
    void Terminate();        

    IDataSystemInterfaceEventListener Listener { get; set; }
}

次に実装(Dispatcher.CurrentDispatcherが格納されていることに注意してください)

[ComVisible(true)]
[Guid("0818F830-DA37-4167-BF31-3A2C55A9BF2B")]        
public class DataSystemModule : IDataSystemInterface
{
    private Dispatcher m_dispatcherListener = null;
    private IDataSystemInterfaceEventListener m_listener = null;
    public IDataSystemInterfaceEventListener Listener
    {
        get
        {
            return m_listener;
        }
        set
        {
            m_dispatcherListener = Dispatcher.CurrentDispatcher;
            m_listener = value;
        }
    } 
}

次にコードで:

if (Listener != null)
{
    m_dispatcherListener.Invoke((Action)delegate()
    {
        Listener.OnTerminateCompleted((int)TerminateStatus.Completed);
    });
}

リスナーが別のスレッドで呼び出された場合、dispacther がないとエラーが発生します

于 2014-01-08T13:03:22.977 に答える