媒體基礎和 COM

Microsoft Media Foundation 會混合使用 COM 建構,但不是完全以 COM 為基礎的 API。 本主題描述 COM 與媒體基礎之間的互動。 它也會定義一些開發 Media Foundation 外掛程式元件的最佳做法。 遵循這些做法可協助您避免一些常見的但細微的程式設計錯誤。

應用程式的最佳做法

在媒體基礎中,非同步處理和回呼是由 工作佇列處理。 工作佇列一律會有多執行緒 Apartment (MTA) 執行緒,因此,如果應用程式在 MTA 執行緒上執行,則會有更簡單的實作。 因此,建議使用COINIT_MULTITHREADED旗標呼叫CoInitializeEx

媒體基礎不會封送處理單線程 Apartment (STA) 物件到工作佇列執行緒。 也不會確保會維護 STA 非變異值。 因此,STA 應用程式必須小心不要將 STA 物件或 Proxy 傳遞至 Media Foundation API。 Media Foundation 不支援僅限 STA 的物件。

如果您有 MTA 或自由執行緒物件的 STA Proxy,可以使用工作佇列回呼,將物件封送處理至 MTA Proxy。 CoCreateInstance函式可以根據該 CLSID 登錄中定義的物件模型,傳回原始指標或 STA Proxy。 如果傳回 STA Proxy,您不得將指標傳遞至媒體基礎 API。

例如,假設您想要將 IPropertyStore 指標傳遞至 IMFSourceResolver::BeginCreateObjectFromURL 方法。 您可以呼叫 PSCreateMemoryPropertyStore 來建立 IPropertyStore 指標。 如果您是從 STA 呼叫,您必須先封送處理指標,再將指標傳遞至 BeginCreateObjectFromURL

下列程式碼示範如何將 STA Proxy 封送處理至媒體基礎 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 方法和函式傳回的物件是物件的直接指標。 針對跨進程媒體基礎,這些物件可能是 MTA Proxy,而且應該視需要封送處理至 STA 執行緒。 同樣地,在回呼內取得的物件,例如 MESessionTopologyStatus 事件的拓撲,是在媒體基礎使用同進程時直接指標,但在使用 Media Foundation 跨進程時為 MTA Proxy。

注意

使用媒體基礎跨進程最常見的案例是使用 受保護的媒體路徑 (PMP) 。 不過,這些備註適用于透過 RPC 使用媒體基礎 API 時的任何情況。

 

IMFAsyncCallback的所有實作都應該與 MTA 相容。 這些物件完全不需要是 COM 物件。 但如果是,就無法在 STA 中執行。 IMFAsyncCallback::Invoke函式會在 MTA 工作佇列執行緒上叫用,而提供的IMFAsyncResult物件將會是直接物件指標或 MTA Proxy。

媒體基礎元件的最佳做法

媒體基礎物件有兩種類別需要擔心 COM。 某些元件,例如轉換或位元組資料流程處理常式,都是 CLSID 所建立的完整 COM 物件。 這些物件必須遵循 COM Apartment 的規則,才能進行跨進程和跨進程媒體基礎。 其他媒體基礎元件不是完整的 COM 物件,但確實需要 COM Proxy 來進行跨進程播放。 此類別中的物件包括媒體來源和啟用物件。 如果這些物件只用于處理中的媒體基礎,這些物件可以忽略 Apartment 問題。

雖然並非所有 Media Foundation 物件都是 COM 物件,但所有 Media Foundation 介面都衍生自 IUnknown。 因此,所有 Media Foundation 物件都必須根據 COM 規格實作 IUnknown ,包括參考計數和 QueryInterface的規則。 所有參考計數物件也應該確保 DllCanUnloadNow 不允許在物件仍然保存時卸載模組。

媒體基礎元件不能是 STA 物件。 許多 Media Foundation 物件完全不需要是 COM 物件。 但如果是,就無法在 STA 中執行。 所有媒體基礎元件都必須是安全線程。 某些媒體基礎物件也必須是自由執行緒或 Apartment 中性物件。 下表指定自訂介面實作的需求:

介面 類別 必要 Apartment
IMFActivate 跨進程 Proxy 自由執行緒或中性
IMFByteStreamHandler COM 物件 MTA
IMFContentProtectionManager 跨進程 Proxy 自由執行緒或中性
IMFQualityManager COM 物件 自由執行緒或中性
IMFMediaSource 跨進程 Proxy 自由執行緒或中性
IMFSchemeHandler COM 物件 MTA
IMFTopoLoader COM 物件 自由執行緒或中性
IMFTransform COM 物件 MTA

 

視實作而定,可能會有其他需求。 例如,如果媒體接收實作另一個介面,讓應用程式能夠對接收進行直接函數呼叫,接收就必須是自由執行緒或中性,以便處理直接跨進程呼叫。 任何物件都可以自由執行緒;下表指定最低需求。

實作自由執行緒或中性物件的建議方式是匯總自由執行緒封送處理器。 如需詳細資訊,請參閱 CoCreateFreeThreadedMarshaler上的 MSDN 檔。 根據不傳遞 STA 物件或 Proxy 至 Media Foundation API 的需求,自由執行緒物件不需要擔心封送處理自由執行緒元件中的 STA 輸入指標。

使用長函式工作佇列 (MFASYNC_CALLBACK_QUEUE_LONG_FUNCTION) 的元件必須更小心。 long 函式工作佇列中的執行緒會建立自己的 STA。 針對回呼使用 long 函式工作佇列的元件應該避免在這些執行緒上建立 COM 物件,而且必須小心視需要將 Proxy 封送處理至 STA。

摘要

如果應用程式從 MTA 執行緒與 Media Foundation 互動,可能會比較容易,但可能小心使用 STA 執行緒中的 Media Foundation。 媒體基礎不會處理 STA 元件,而且應用程式應該小心不要將 STA 物件傳遞至 Media Foundation API。 某些物件有額外的需求,特別是跨進程狀況中執行的物件。 遵循這些指導方針有助於避免媒體處理中的 COM 錯誤、死結和非預期的延遲。

媒體基礎平臺 API

媒體基礎架構