Media Foundation と COM

Microsoft Media Foundation では、COM コンストラクトの組み合わせを使用しますが、完全な COM ベースの API ではありません。 このトピックでは、COM と Media Foundation の相互作用について説明します。 また、Media Foundation プラグイン コンポーネントを開発するためのいくつかのベスト プラクティスも定義されています。 これらのプラクティスに従うと、一般的で微妙なプログラミング エラーを回避するのに役立ちます。

アプリケーションのベスト プラクティス

Media Foundation では、非同期処理とコールバックは 作業キューによって処理されます。 作業キューには常にマルチスレッド アパートメント (MTA) スレッドがあるため、MTA スレッドでも実行する場合は、アプリケーションの実装が簡単になります。 したがって、COINIT_MULTITHREADED フラグを使用して CoInitializeEx を呼び出することをお勧めします。

Media Foundation では、シングル スレッド アパートメント (STA) オブジェクトをマーシャリングしてキュー スレッドを処理することはありません。 また、STA 不変性が維持されることを保証しません。 したがって、STA アプリケーションは、STA オブジェクトまたはプロキシを Media Foundation API に渡さないように注意する必要があります。 STA 専用のオブジェクトは Media Foundation ではサポートされていません。

MTA またはフリー スレッド オブジェクトへの STA プロキシがある場合は、ワーク キュー コールバックを使用して、オブジェクトを MTA プロキシにマーシャリングできます。 CoCreateInstance 関数は、その CLSID のレジストリで定義されているオブジェクト モデルに応じて、生ポインターまたは STA プロキシを返すことができます。 STA プロキシが返される場合は、Media Foundation API へのポインターを渡してはなりません。

たとえば、 IPropertyStore ポインターを IMFSourceResolver::BeginCreateObjectFromURL メソッドに渡すとします。 PSCreateMemoryPropertyStore を呼び出して、IPropertyStore ポインターを作成できます。 STA から呼び出す場合は、 BeginCreateObjectFromURL に渡す前にポインターをマーシャリングする必要があります。

次のコードは、STA プロキシを Media Foundation API にマーシャリングする方法を示しています。

class CCreateSourceMarshalCallback
    : public IMFAsyncCallback
{
public:
    CCreateSourceMarshalCallback(
        LPCWSTR szURL, 
        IMFSourceResolver* pResolver, 
        IPropertyStore* pSourceProps, 
        IMFAsyncCallback* pCompletionCallback, 
        HRESULT& hr
        )
        : m_szURL(szURL), 
          m_pResolver(pResolver), 
          m_pCompletionCallback(pCompletionCallback),
          m_pGIT(NULL),
          m_cRef(1)
    {
        hr = CoCreateInstance(CLSID_StdGlobalInterfaceTable, NULL, 
            CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pGIT));

        if(SUCCEEDED(hr))
        {
            hr = m_pGIT->RegisterInterfaceInGlobal(
                pSourceProps, IID_IPropertyStore, &m_dwInterfaceCookie);
        }
    }
    ~CCreateSourceMarshalCallback()
    {
        SafeRelease(&m_pResolver);
        SafeRelease(&m_pCompletionCallback);
        SafeRelease(&m_pGIT);
    }


    STDMETHOD_(ULONG, AddRef)()
    {
        return InterlockedIncrement(&m_cRef);
    }

    STDMETHOD_(ULONG, Release)()
    {
        LONG cRef = InterlockedDecrement(&m_cRef);
        if (0 == cRef)
        {
            delete this;
        }
        return cRef;
    }

    STDMETHOD(QueryInterface)(REFIID riid, LPVOID* ppvObject)
    {
        static const QITAB qit[] = 
        {
            QITABENT(CCreateSourceMarshalCallback, IMFAsyncCallback),
            { 0 }
        };
        return QISearch(this, qit, riid, ppvObject);

    }

    STDMETHOD(GetParameters)(DWORD* pdwFlags, DWORD* pdwQueue)
    {
        return E_NOTIMPL;
    }

    STDMETHOD(Invoke)(IMFAsyncResult* pResult)
    {
        IPropertyStore *pSourceProps = NULL;

        HRESULT hr = m_pGIT->GetInterfaceFromGlobal(
            m_dwInterfaceCookie, 
            IID_PPV_ARGS(&pSourceProps)
            );

        if(SUCCEEDED(hr))
        {
            hr = m_pResolver->BeginCreateObjectFromURL(
                m_szURL, MF_RESOLUTION_MEDIASOURCE, pSourceProps, NULL, 
                m_pCompletionCallback, NULL);
        }

        SafeRelease(&pSourceProps);
        return hr;
    }

private:
    LPCWSTR m_szURL;
    IMFSourceResolver *m_pResolver;
    IMFAsyncCallback *m_pCompletionCallback;
    IGlobalInterfaceTable *m_pGIT;
    DWORD m_dwInterfaceCookie;
    LONG m_cRef;
};

グローバル インターフェイス テーブルの詳細については、「 IGlobalInterfaceTable」を参照してください

Media Foundation をインプロセスで使用している場合、Media Foundation メソッドと関数から返されるオブジェクトは、オブジェクトへの直接ポインターです。 クロスプロセス Media Foundation の場合、これらのオブジェクトは MTA プロキシである可能性があり、必要に応じて STA スレッドにマーシャリングする必要があります。 同様に、コールバック内で取得されたオブジェクト ( MESessionTopologyStatus イベントからのトポロジなど) は、Media Foundation がインプロセスで使用される場合は直接ポインターですが、Media Foundation がクロスプロセスで使用される場合は MTA プロキシです。

Note

Media Foundation クロスプロセスを使用する最も一般的なシナリオは、 保護されたメディア パス (PMP) です。 ただし、これらの備考は、Media Foundation API が RPC を介して使用されるあらゆる状況に適用されます。

 

IMFAsyncCallback のすべての実装は、MTA と互換性がある必要があります。 これらのオブジェクトは、COM オブジェクトである必要はありません。 ただし、それらがある場合は、STA で実行することはできません。 IMFAsyncCallback::Invoke 関数は MTA ワークキュー スレッドで呼び出され、指定された IMFAsyncResult オブジェクトはダイレクト オブジェクト ポインターまたは MTA プロキシのいずれかになります。

Media Foundation コンポーネントのベスト プラクティス

COM について心配する必要がある Media Foundation オブジェクトには、2 つのカテゴリがあります。 変換やバイト ストリーム ハンドラーなどの一部のコンポーネントは、CLSID によって作成された完全な COM オブジェクトです。 これらのオブジェクトは、インプロセス Media Foundation とクロスプロセス Media Foundation の両方について、COM アパートメントの規則に従う必要があります。 他の Media Foundation コンポーネントは完全な COM オブジェクトではありませんが、クロスプロセス再生には COM プロキシが必要です。 このカテゴリのオブジェクトには、メディア ソースとアクティブ化オブジェクトが含まれます。 これらのオブジェクトは、インプロセス Media Foundation でのみ使用される場合、アパートメントの問題を無視できます。

すべての Media Foundation オブジェクトが COM オブジェクトであるわけではありませんが、すべての Media Foundation インターフェイスは IUnknown から派生します。 したがって、すべての Media Foundation オブジェクトは、参照カウントと QueryInterface の規則を含め、COM 仕様に従って IUnknown を実装する必要があります。 また、参照カウントされたオブジェクトはすべて、 DllCanUnloadNow で、オブジェクトがまだ保持されている間にモジュールをアンロードできないようにする必要もあります。

Media Foundation コンポーネントを STA オブジェクトにすることはできません。 多くの Media Foundation オブジェクトは、COM オブジェクトである必要はありません。 ただし、それらがある場合は、STA で実行することはできません。 すべての Media Foundation コンポーネントはスレッド セーフである必要があります。 一部の Media Foundation オブジェクトは、フリースレッドまたはアパートメントニュートラルである必要があります。 次の表に、カスタム インターフェイス実装の要件を示します。

インターフェイス カテゴリ 必須アパートメント
IMFActivate クロスプロセス プロキシ フリースレッドまたはニュートラル
IMFByteStreamHandler COM オブジェクト MTA
IMFContentProtectionManager クロスプロセス プロキシ フリースレッドまたはニュートラル
IMFQualityManager COM オブジェクト フリースレッドまたはニュートラル
IMFMediaSource クロスプロセス プロキシ フリースレッドまたはニュートラル
IMFSchemeHandler COM オブジェクト MTA
IMFTopoLoader COM オブジェクト フリースレッドまたはニュートラル
IMFTransform COM オブジェクト MTA

 

実装によっては、追加の要件が存在する場合があります。 たとえば、メディア シンクが、アプリケーションがシンクに直接関数呼び出しを行えるようにする別のインターフェイスを実装する場合、シンクは、直接のクロスプロセス呼び出しを処理できるように、フリー スレッドまたはニュートラルである必要があります。 任意のオブジェクトをフリー スレッドにすることができます。この表では、最小要件を指定します。

フリースレッドまたはニュートラル オブジェクトを実装する推奨される方法は、フリースレッド マーシャラーを集計することです。 詳細については、 CoCreateFreeThreadedMarshaler の MSDN ドキュメントを参照してください。 STA オブジェクトまたはプロキシを Media Foundation API に渡さないという要件に従って、フリースレッド オブジェクトは、フリースレッド コンポーネントでの STA 入力ポインターのマーシャリングについて心配する必要はありません。

長い関数の作業キュー (MFASYNC_CALLBACK_QUEUE_LONG_FUNCTION) を使用するコンポーネントは、より注意を払う必要があります。 長い関数ワークキュー内のスレッドは、独自の STA を作成します。 コールバックに長い関数ワークキューを使用するコンポーネントは、これらのスレッドで COM オブジェクトを作成しないようにし、必要に応じてプロキシを STA にマーシャリングするように注意する必要があります。

まとめ

アプリケーションは MTA スレッドから Media Foundation と対話する方が簡単ですが、STA スレッドから Media Foundation を使用する場合は注意が必要です。 Media Foundation は STA コンポーネントを処理しないため、アプリケーションは MEDIA Foundation API に STA オブジェクトを渡さないように注意する必要があります。 一部のオブジェクトには、特にプロセス間の状況で実行されるオブジェクトなど、追加の要件があります。 これらのガイドラインに従うと、COM エラー、デッドロック、およびメディア処理の予期しない遅延を回避できます。

Media Foundation プラットフォーム API

メディア ファンデーションのアーキテクチャ