メディア ファンデーションと COM
Microsoft Media Foundation は COM コンストラクトを組み合わせて使用しますが、完全な COM ベースの API ではありません。 このトピックでは、COM と Media Foundation の間の相互作用について説明します。 また、Media Foundation プラグイン コンポーネントを開発するためのベスト プラクティスについても説明します。 これらのプラクティスに従うと、一般的且つ微妙なプログラミング エラーを回避するのに役立ちます。
アプリケーションのベスト プラクティス
Media Foundation では、非同期処理とコールバックは、ワーク キューによって処理されます。 ワーク キューにはマルチスレッド アパートメント (MTA) スレッドが常に存在するため、アプリケーションは MTA スレッドでも実行すれば、実装が簡単になります。 そのため、COINIT_MULTITHREADED フラグを使用して CoInitializeEx を呼び出することをお勧めします。
Media Foundation では、シングル スレッド アパートメント (STA) オブジェクトを処理キュー スレッドにマーシャリングしません。 また、STA インバリアントが維持されることを保証しません。 そのため、STA アプリケーションは、Media Foundation API に STA オブジェクトまたはプロキシを渡さないように注意する必要があります。 STA 専用のオブジェクトは、Media Foundation ではサポートされていません。
MTA またはフリー スレッド オブジェクトへの STA プロキシがある場合は、ワーク キュー コールバックを使用して、オブジェクトを MTA プロキシにマーシャリングできます。 CoCreateInstance 関数は、その CLSID のレジストリで定義されているオブジェクト モデルに応じて、生ポインターまたは STA プロキシを返すことができます。 STA プロキシが返された場合は、Media Foundation API へのポインターを渡してはなりません。
たとえば、IPropertyStore ポインターを IMFSourceResolver::BeginCreateObjectFromURL メソッドに渡すとします。 PSCreateMemoryPropertyStore を呼び出して、IPropertyStore ポインターを作成できます。 STA から呼び出す場合は、ポインターを BeginCreateObjectFromURL に渡す前にマーシャリングする必要があります。
次のコードは、Media Foundation API に STA プロキシをマーシャリングする方法を示しています。
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 クロスプロセスを使用する最も一般的なシナリオは、 Protected Media Path (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 オブジェクトは、フリースレッドまたはアパートメントニュートラルである必要があります。 次の表は、カスタム インターフェイス実装の要件を示しています。
Interface | カテゴリ | 必須アパートメント |
---|---|---|
IMFActivate | クロスプロセス プロキシ | フリースレッドまたはニュートラル |
IMFByteStreamHandler | COM オブジェクト | MTA |
IMFContentProtectionManager | クロスプロセス プロキシ | フリースレッドまたはニュートラル |
IMFQualityManager | COM オブジェクト | フリースレッドまたはニュートラル |
IMFMediaSource | クロスプロセス プロキシ | フリースレッドまたはニュートラル |
IMFSchemeHandler | COM オブジェクト | MTA |
IMFTopoLoader | COM オブジェクト | フリースレッドまたはニュートラル |
IMFTransform | COM オブジェクト | MTA |
実装によっては、追加の要件が存在する場合があります。 たとえば、アプリケーションがシンクに直接関数呼び出しを行えるようにする別のインターフェイスがメディア シンクに実装されている場合、シンクは、直接のクロスプロセス呼び出しを処理できるように、フリースレッドまたはニュートラルである必要があります。 任意のオブジェクトをフリー スレッドにすることができます。この表では、最小要件を指定します。
フリー スレッドオブジェクトまたはニュートラル オブジェクトを実装する上で推奨される方法は、フリースレッド マーシャラーを集計することです。 詳細については、「CoCreateFreeThreadedMarshaler」を参照してください。 Media Foundation API に STA オブジェクトまたはプロキシを渡さないという要件に従って、フリー スレッド オブジェクトは、フリー スレッド コンポーネントでの STA 入力ポインターのマーシャリングについて心配する必要はありません。
長い関数のワークキュー (MFASYNC_CALLBACK_QUEUE_LONG_FUNCTION) を使用するコンポーネントは、さらに注意を払う必要があります。 長い関数ワーク キュー内のスレッドは、独自の STA を作成します。 コールバックに長い関数のワーク キューを使用するコンポーネントは、これらのスレッドで COM オブジェクトを作成しないようにし、必要に応じてプロキシを STA にマーシャリングするように注意する必要があります。
まとめ
アプリケーションは MTA スレッドから Media Foundation と対話する方が簡単ですが、STA スレッドから Media Foundation を使用する場合は注意が必要です。 Media Foundation は STA コンポーネントを処理しないため、アプリケーションは MEDIA Foundation API に STA オブジェクトを渡さないように注意する必要があります。 一部のオブジェクトには、特にプロセス間の状況で実行されるオブジェクトなど、追加の要件があります。 これらのガイドラインに従うと、COM エラー、デッドロック、およびメディア処理の予期しない遅延を回避できます。
関連トピック