步驟 3:建置篩選圖形

[與此頁面相關的功能 DirectShow是舊版功能。 它已被 MediaPlayerIMFMediaEngineMedia Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayerIMFMediaEngine音訊/視訊擷取 ,而不是 DirectShow。 Microsoft 建議使用舊版 API 的現有程式碼盡可能重寫為使用新的 API。

本主題是 DirectShow 中音訊/視訊播放教學課程的步驟 3。 完整的程式碼會顯示在 DirectShow 播放範例主題中。

下一個步驟是建置篩選圖形以播放媒體檔案。

開啟媒體檔案

方法會 DShowPlayer::OpenFile 開啟媒體檔案以供播放。 此方法會執行下列動作:

  1. 建立新的 (空白) 篩選圖形。
  2. 呼叫 IGraphBuilder::AddSourceFilter 以新增指定檔案的來源篩選。
  3. 轉譯來源篩選上的資料流程。
HRESULT DShowPlayer::OpenFile(PCWSTR pszFileName)
{
    IBaseFilter *pSource = NULL;

    // Create a new filter graph. (This also closes the old one, if any.)
    HRESULT hr = InitializeGraph();
    if (FAILED(hr))
    {
        goto done;
    }
    
    // Add the source filter to the graph.
    hr = m_pGraph->AddSourceFilter(pszFileName, NULL, &pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    // Try to render the streams.
    hr = RenderStreams(pSource);

done:
    if (FAILED(hr))
    {
        TearDownGraph();
    }
    SafeRelease(&pSource);
    return hr;
}

建立篩選圖形管理員

方法 DShowPlayer::InitializeGraph 會建立新的篩選圖形。 此方法會執行下列動作:

  1. 呼叫 CoCreateInstance 以建立 篩選圖形管理員的新實例。
  2. 查詢 IMediaControlIMediaEventEx 介面的 Filter Graph 管理員。
  3. 呼叫 IMediaEventEx::SetNotifyWindow 來設定事件通知。 如需詳細資訊,請參閱 DirectShow 中的事件通知
HRESULT DShowPlayer::InitializeGraph()
{
    TearDownGraph();

    // Create the Filter Graph Manager.
    HRESULT hr = CoCreateInstance(CLSID_FilterGraph, NULL, 
        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pGraph));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pControl));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&m_pEvent));
    if (FAILED(hr))
    {
        goto done;
    }

    // Set up event notification.
    hr = m_pEvent->SetNotifyWindow((OAHWND)m_hwnd, WM_GRAPH_EVENT, NULL);
    if (FAILED(hr))
    {
        goto done;
    }

    m_state = STATE_STOPPED;

done:
    return hr;
}

轉譯資料流程

下一個步驟是將來源篩選連接到一或多個轉譯器篩選。

方法 DShowPlayer::RenderStreams 會執行下列步驟。

  1. 查詢 IFilterGraph2 介面的 Filter Graph 管理員。
  2. 將視訊轉譯器篩選新增至篩選圖表。
  3. DirectSound 轉譯器篩選 新增至篩選圖形,以支援音訊播放。 如需將篩選新增至篩選圖表的詳細資訊,請參閱 新增依 CLSID 篩選
  4. 列舉來源篩選上的輸出針腳。 如需列舉針腳的詳細資訊,請參閱 列舉針腳
  5. 針對每個針腳,呼叫 IFilterGraph2::RenderEx 方法。 此方法會將輸出釘選連接到轉譯器篩選,並視需要新增中繼篩選 (,例如解碼器) 。
  6. 呼叫 CVideoRenderer::FinalizeGraph 以完成影片轉譯器初始化。
  7. 如果 DirectSound 轉譯器 篩選準則未連線到另一個篩選,則會移除該篩選準則。 如果來源檔案不包含音訊資料流程,就會發生這種情況。
// Render the streams from a source filter. 

HRESULT DShowPlayer::RenderStreams(IBaseFilter *pSource)
{
    BOOL bRenderedAnyPin = FALSE;

    IFilterGraph2 *pGraph2 = NULL;
    IEnumPins *pEnum = NULL;
    IBaseFilter *pAudioRenderer = NULL;
    HRESULT hr = m_pGraph->QueryInterface(IID_PPV_ARGS(&pGraph2));
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the video renderer to the graph
    hr = CreateVideoRenderer();
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the DSound Renderer to the graph.
    hr = AddFilterByCLSID(m_pGraph, CLSID_DSoundRender, 
        &pAudioRenderer, L"Audio Renderer");
    if (FAILED(hr))
    {
        goto done;
    }

    // Enumerate the pins on the source filter.
    hr = pSource->EnumPins(&pEnum);
    if (FAILED(hr))
    {
        goto done;
    }

    // Loop through all the pins
    IPin *pPin;
    while (S_OK == pEnum->Next(1, &pPin, NULL))
    {           
        // Try to render this pin. 
        // It's OK if we fail some pins, if at least one pin renders.
        HRESULT hr2 = pGraph2->RenderEx(pPin, AM_RENDEREX_RENDERTOEXISTINGRENDERERS, NULL);

        pPin->Release();
        if (SUCCEEDED(hr2))
        {
            bRenderedAnyPin = TRUE;
        }
    }

    hr = m_pVideo->FinalizeGraph(m_pGraph);
    if (FAILED(hr))
    {
        goto done;
    }

    // Remove the audio renderer, if not used.
    BOOL bRemoved;
    hr = RemoveUnconnectedRenderer(m_pGraph, pAudioRenderer, &bRemoved);

done:
    SafeRelease(&pEnum);
    SafeRelease(&pAudioRenderer);
    SafeRelease(&pGraph2);

    // If we succeeded to this point, make sure we rendered at least one 
    // stream.
    if (SUCCEEDED(hr))
    {
        if (!bRenderedAnyPin)
        {
            hr = VFW_E_CANNOT_RENDER;
        }
    }
    return hr;
}

以下是函式的程式 RemoveUnconnectedRenderer 代碼,此程式碼用於上一個範例中。

HRESULT RemoveUnconnectedRenderer(IGraphBuilder *pGraph, IBaseFilter *pRenderer, BOOL *pbRemoved)
{
    IPin *pPin = NULL;

    *pbRemoved = FALSE;

    // Look for a connected input pin on the renderer.

    HRESULT hr = FindConnectedPin(pRenderer, PINDIR_INPUT, &pPin);
    SafeRelease(&pPin);

    // If this function succeeds, the renderer is connected, so we don't remove it.
    // If it fails, it means the renderer is not connected to anything, so
    // we remove it.

    if (FAILED(hr))
    {
        hr = pGraph->RemoveFilter(pRenderer);
        *pbRemoved = TRUE;
    }

    return hr;
}

釋放篩選圖形

應用程式結束時,它必須釋放篩選圖形,如下列程式碼所示。

void DShowPlayer::TearDownGraph()
{
    // Stop sending event messages
    if (m_pEvent)
    {
        m_pEvent->SetNotifyWindow((OAHWND)NULL, NULL, NULL);
    }

    SafeRelease(&m_pGraph);
    SafeRelease(&m_pControl);
    SafeRelease(&m_pEvent);

    delete m_pVideo;
    m_pVideo = NULL;

    m_state = STATE_NO_GRAPH;
}

下一 步:步驟 4:新增影片轉譯器

DirectShow 中的音訊/視訊播放

DirectShow 播放範例

建置篩選圖形

一般Graph-Building技術