媒體基礎和 COM

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

應用程式的最佳做法

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

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

如果您有 MTA 或自由線程物件的 STA Proxy,可以使用工作佇列回呼將物件封送處理至 MTA Proxy。 CoCreateInstance 函式可以傳回原始指標或 STA Proxy,視該 CLSID 登錄中定義的物件模型而定。 如果傳回 STA Proxy,您不得將指標傳遞至 Media Foundation 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事件的拓撲,是在媒體基礎使用同進程時直接指標,但在媒體基礎使用跨進程時為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 物件。 許多媒體基礎物件完全不需要是 COM 物件。 但是,如果他們是,他們不能在 STA 中執行。 所有媒體基礎元件都必須是安全線程。 某些媒體基礎對象也必須是自由線程或 Apartment 中性物件。 下表指定自訂介面實作的需求:

介面 類別 必要公寓
IMFActivate 跨進程 Proxy 自由線程或中性
IMFByteStreamHandler COM物件 MTA
IMFContentProtectionManager 跨進程 Proxy 自由線程或中性
IMFQualityManager COM物件 自由線程或中性
IMFMediaSource 跨進程 Proxy 自由線程或中性
IMFSchemeHandler COM物件 MTA
IMFTopoLoader COM物件 自由線程或中性
IMFTransform COM物件 MTA

 

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

實作自由線程或中性對象的建議方式是匯總自由線程封送處理器。 如需詳細資訊,請參閱 CoCreateFreeThreadedMarshaler 根據不將 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

媒體基礎架構