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 プロキシです。

注意

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

まとめ

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

Media Foundation Platform API

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