共用方式為


步驟 4:新增影片轉譯器

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

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

DirectShow 提供數個不同的篩選來轉譯視訊:

如需這些篩選準則之間差異的詳細資訊,請參閱 選擇正確的影片轉譯器

在本教學課程中,每個影片轉譯器篩選都會由類別包裝,以抽象化其中一些差異。 這些類別全都衍生自名為 CVideoRenderer 的抽象基類。 的宣告 CVideoRenderer 會顯示在 步驟 2:宣告 CVideoRenderer 和衍生類別中。

下列方法會嘗試接著建立每個視訊轉譯器,從 EVR 開始,然後從 VMR-9 開始,最後是 VMR-7。

HRESULT DShowPlayer::CreateVideoRenderer()
{
    HRESULT hr = E_FAIL;

    enum { Try_EVR, Try_VMR9, Try_VMR7 };

    for (DWORD i = Try_EVR; i <= Try_VMR7; i++)
    {
        switch (i)
        {
        case Try_EVR:
            m_pVideo = new (std::nothrow) CEVR();
            break;

        case Try_VMR9:
            m_pVideo = new (std::nothrow) CVMR9();
            break;

        case Try_VMR7:
            m_pVideo = new (std::nothrow) CVMR7();
            break;
        }

        if (m_pVideo == NULL)
        {
            hr = E_OUTOFMEMORY;
            break;
        }

        hr = m_pVideo->AddToGraph(m_pGraph, m_hwnd);
        if (SUCCEEDED(hr))
        {
            break;
        }

        delete m_pVideo;
        m_pVideo = NULL;
    }
    return hr;
}

EVR 篩選

下列程式碼會建立 EVR 篩選,並將其新增至篩選圖形。 此範例中使用的函 AddFilterByCLSID 式會顯示在 新增依 CLSID 篩選的主題中。

HRESULT CEVR::AddToGraph(IGraphBuilder *pGraph, HWND hwnd)
{
    IBaseFilter *pEVR = NULL;

    HRESULT hr = AddFilterByCLSID(pGraph, CLSID_EnhancedVideoRenderer, 
        &pEVR, L"EVR");

    if (FAILED(hr))
    {
        goto done;
    }

    hr = InitializeEVR(pEVR, hwnd, &m_pVideoDisplay);
    if (FAILED(hr))
    {
        goto done;
    }

    // Note: Because IMFVideoDisplayControl is a service interface,
    // you cannot QI the pointer to get back the IBaseFilter pointer.
    // Therefore, we need to cache the IBaseFilter pointer.

    m_pEVR = pEVR;
    m_pEVR->AddRef();

done:
    SafeRelease(&pEVR);
    return hr;
}

InitializeEVR 式會初始化 EVR 篩選。 此函式會執行下列步驟。

  1. 查詢 IMFGetService 介面的篩選準則。
  2. 呼叫 IMFGetService::GetService 以取得 IMFVideoDisplayControl 介面的 指標。
  3. 呼叫 IMFVideoDisplayControl::SetVideoWindow 來設定視訊視窗。
  4. 呼叫 IMFVideoDisplayControl::SetAspectRatioMode 來設定 EVR 以保留視訊外觀比例。

下列程式碼顯示 函 InitializeEVR 式。

HRESULT InitializeEVR( 
    IBaseFilter *pEVR,              // Pointer to the EVR
    HWND hwnd,                      // Clipping window
    IMFVideoDisplayControl** ppDisplayControl
    ) 
{ 
    IMFGetService *pGS = NULL;
    IMFVideoDisplayControl *pDisplay = NULL;

    HRESULT hr = pEVR->QueryInterface(IID_PPV_ARGS(&pGS)); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pGS->GetService(MR_VIDEO_RENDER_SERVICE, IID_PPV_ARGS(&pDisplay));
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the clipping window.
    hr = pDisplay->SetVideoWindow(hwnd);
    if (FAILED(hr))
    {
        goto done;
    }

    // Preserve aspect ratio by letter-boxing
    hr = pDisplay->SetAspectRatioMode(MFVideoARMode_PreservePicture);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the IMFVideoDisplayControl pointer to the caller.
    *ppDisplayControl = pDisplay;
    (*ppDisplayControl)->AddRef();

done:
    SafeRelease(&pGS);
    SafeRelease(&pDisplay);
    return hr; 
} 

建置圖形之後,會 DShowPlayer::RenderStreams 呼叫 CVideoRenderer::FinalizeGraph 。 這個方法會執行任何最終的初始化或清除。 下列程式碼顯示 CEVR 這個方法的實作。

HRESULT CEVR::FinalizeGraph(IGraphBuilder *pGraph)
{
    if (m_pEVR == NULL)
    {
        return S_OK;
    }

    BOOL bRemoved;
    HRESULT hr = RemoveUnconnectedRenderer(pGraph, m_pEVR, &bRemoved);
    if (bRemoved)
    {
        SafeRelease(&m_pEVR);
        SafeRelease(&m_pVideoDisplay);
    }
    return hr;
}

如果 EVR 未連線到任何其他篩選,這個方法會從圖表中移除 EVR。 如果媒體檔案不包含視訊串流,就會發生這種情況。

VMR-9 篩選準則

下列程式碼會建立 VMR-9 篩選,並將它新增至篩選圖形。

HRESULT CVMR9::AddToGraph(IGraphBuilder *pGraph, HWND hwnd)
{
    IBaseFilter *pVMR = NULL;

    HRESULT hr = AddFilterByCLSID(pGraph, CLSID_VideoMixingRenderer9, 
        &pVMR, L"VMR-9");
    if (SUCCEEDED(hr))
    {
        // Set windowless mode on the VMR. This must be done before the VMR 
        // is connected.
        hr = InitWindowlessVMR9(pVMR, hwnd, &m_pWindowless);
    }
    SafeRelease(&pVMR);
    return hr;
}

InitWindowlessVMR9 式會針對無視窗模式初始化 VMR-9。 (如需無視窗模式的詳細資訊,請參閱 VMR 無視窗模式。) 此函式會執行下列步驟。

  1. 查詢 IVMRFilterConfig9 介面的 VMR-9 篩選準則。
  2. 呼叫 IVMRFilterConfig9::SetRenderingMode 方法來設定無視窗模式。
  3. 查詢 IVMRWindowlessControl9 介面的 VMR-9 篩選準則。
  4. 呼叫 IVMRWindowlessControl9::SetVideoClippingWindow 方法來設定視訊視窗。
  5. 呼叫 IVMRWindowlessControl9::SetAspectRatioMode 方法來保留視訊外觀比例。

下列程式碼顯示 函 InitWindowlessVMR9 式。

HRESULT InitWindowlessVMR9( 
    IBaseFilter *pVMR,              // Pointer to the VMR
    HWND hwnd,                      // Clipping window
    IVMRWindowlessControl9** ppWC   // Receives a pointer to the VMR.
    ) 
{ 

    IVMRFilterConfig9 * pConfig = NULL; 
    IVMRWindowlessControl9 *pWC = NULL;

    // Set the rendering mode.  
    HRESULT hr = pVMR->QueryInterface(IID_PPV_ARGS(&pConfig)); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pConfig->SetRenderingMode(VMR9Mode_Windowless); 
    if (FAILED(hr))
    {
        goto done;
    }

    // Query for the windowless control interface.
    hr = pVMR->QueryInterface(IID_PPV_ARGS(&pWC));
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the clipping window.
    hr = pWC->SetVideoClippingWindow(hwnd);
    if (FAILED(hr))
    {
        goto done;
    }

    // Preserve aspect ratio by letter-boxing
    hr = pWC->SetAspectRatioMode(VMR9ARMode_LetterBox);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the IVMRWindowlessControl pointer to the caller.
    *ppWC = pWC;
    (*ppWC)->AddRef();

done:
    SafeRelease(&pConfig);
    SafeRelease(&pWC);
    return hr; 
} 

方法 CVMR9::FinalizeGraph 會檢查 VMR-9 篩選是否已連線,否則會從篩選圖表中移除它。

HRESULT CVMR9::FinalizeGraph(IGraphBuilder *pGraph)
{
    if (m_pWindowless == NULL)
    {
        return S_OK;
    }

    IBaseFilter *pFilter = NULL;

    HRESULT hr = m_pWindowless->QueryInterface(IID_PPV_ARGS(&pFilter));
    if (FAILED(hr))
    {
        goto done;
    }

    BOOL bRemoved;
    hr = RemoveUnconnectedRenderer(pGraph, pFilter, &bRemoved);

    // If we removed the VMR, then we also need to release our 
    // pointer to the VMR's windowless control interface.
    if (bRemoved)
    {
        SafeRelease(&m_pWindowless);
    }

done:
    SafeRelease(&pFilter);
    return hr;
}

VMR-7 篩選準則

VMR-7 篩選的步驟與 VMR-9 的步驟幾乎完全相同,但會改用 VMR-7 介面。 下列程式碼會建立 VMR-7 篩選,並將它新增至篩選圖形。

HRESULT CVMR7::AddToGraph(IGraphBuilder *pGraph, HWND hwnd)
{
    IBaseFilter *pVMR = NULL;

    HRESULT hr = AddFilterByCLSID(pGraph, CLSID_VideoMixingRenderer, 
        &pVMR, L"VMR-7");

    if (SUCCEEDED(hr))
    {
        // Set windowless mode on the VMR. This must be done before the VMR
        // is connected.
        hr = InitWindowlessVMR(pVMR, hwnd, &m_pWindowless);
    }
    SafeRelease(&pVMR);
    return hr;
}

InitWindowlessVMR 式會針對無視窗模式初始化 VMR-7。 此函式會執行下列步驟。

  1. 查詢 IVMRFilterConfig 介面的 VMR-7 篩選準則。
  2. 呼叫 IVMRFilterConfig::SetRenderingMode 方法來設定無視窗模式。
  3. 查詢 IVMRWindowlessControl 介面的 VMR-7 篩選準則。
  4. 呼叫 IVMRWindowlessControl::SetVideoClippingWindow 方法來設定視訊視窗。
  5. 呼叫 IVMRWindowlessControl::SetAspectRatioMode 方法來保留視訊外觀比例。

下列程式碼顯示 函 InitWindowlessVMR 式。

HRESULT InitWindowlessVMR( 
    IBaseFilter *pVMR,              // Pointer to the VMR
    HWND hwnd,                      // Clipping window
    IVMRWindowlessControl** ppWC    // Receives a pointer to the VMR.
    ) 
{ 

    IVMRFilterConfig* pConfig = NULL; 
    IVMRWindowlessControl *pWC = NULL;

    // Set the rendering mode.  
    HRESULT hr = pVMR->QueryInterface(IID_PPV_ARGS(&pConfig)); 
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pConfig->SetRenderingMode(VMRMode_Windowless); 
    if (FAILED(hr))
    {
        goto done;
    }

    // Query for the windowless control interface.
    hr = pVMR->QueryInterface(IID_PPV_ARGS(&pWC));
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the clipping window.
    hr = pWC->SetVideoClippingWindow(hwnd);
    if (FAILED(hr))
    {
        goto done;
    }

    // Preserve aspect ratio by letter-boxing
    hr = pWC->SetAspectRatioMode(VMR_ARMODE_LETTER_BOX);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the IVMRWindowlessControl pointer to the caller.
    *ppWC = pWC;
    (*ppWC)->AddRef();

done:
    SafeRelease(&pConfig);
    SafeRelease(&pWC);
    return hr; 
} 

方法 CVMR7::FinalizeGraph 會檢查 VMR-7 篩選是否已連線,如果不是,則會從篩選圖表中移除它。

HRESULT CVMR7::FinalizeGraph(IGraphBuilder *pGraph)
{
    if (m_pWindowless == NULL)
    {
        return S_OK;
    }

    IBaseFilter *pFilter = NULL;

    HRESULT hr = m_pWindowless->QueryInterface(IID_PPV_ARGS(&pFilter));
    if (FAILED(hr))
    {
        goto done;
    }

    BOOL bRemoved;
    hr = RemoveUnconnectedRenderer(pGraph, pFilter, &bRemoved);

    // If we removed the VMR, then we also need to release our 
    // pointer to the VMR's windowless control interface.
    if (bRemoved)
    {
        SafeRelease(&m_pWindowless);
    }

done:
    SafeRelease(&pFilter);
    return hr;
}

DirectShow 播放範例

使用 DirectShow EVR 篩選

使用影片混合轉譯器