步驟 4:新增影片轉譯器
[與此頁面相關的功能 DirectShow是舊版功能。 它已被 MediaPlayer、 IMFMediaEngine和 Media Foundation 中的音訊/視訊擷取取代。 這些功能已針對Windows 10和Windows 11進行優化。 Microsoft 強烈建議新程式碼盡可能使用 MediaPlayer、 IMFMediaEngine 和 音訊/視訊擷取 ,而不是 DirectShow。 Microsoft 建議使用舊版 API 的現有程式碼盡可能重寫為使用新的 API。
本主題是 DirectShow 中音訊/視訊播放教學課程的步驟 4。 完整的程式碼會顯示在 DirectShow 播放範例主題中。
DirectShow 提供數個不同的篩選來轉譯視訊:
- 增強的視訊轉譯器篩選 (EVR)
- 影片混合轉譯器篩選 9 (VMR-9)
- 影片混合轉譯器篩選 7 (VMR-7)
如需這些篩選準則之間差異的詳細資訊,請參閱 選擇正確的影片轉譯器。
在本教學課程中,每個影片轉譯器篩選都會由類別包裝,以抽象化其中一些差異。 這些類別全都衍生自名為 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 篩選。 此函式會執行下列步驟。
- 查詢 IMFGetService 介面的篩選準則。
- 呼叫 IMFGetService::GetService 以取得 IMFVideoDisplayControl 介面的 指標。
- 呼叫 IMFVideoDisplayControl::SetVideoWindow 來設定視訊視窗。
- 呼叫 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 無視窗模式。) 此函式會執行下列步驟。
- 查詢 IVMRFilterConfig9 介面的 VMR-9 篩選準則。
- 呼叫 IVMRFilterConfig9::SetRenderingMode 方法來設定無視窗模式。
- 查詢 IVMRWindowlessControl9 介面的 VMR-9 篩選準則。
- 呼叫 IVMRWindowlessControl9::SetVideoClippingWindow 方法來設定視訊視窗。
- 呼叫 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。 此函式會執行下列步驟。
- 查詢 IVMRFilterConfig 介面的 VMR-7 篩選準則。
- 呼叫 IVMRFilterConfig::SetRenderingMode 方法來設定無視窗模式。
- 查詢 IVMRWindowlessControl 介面的 VMR-7 篩選準則。
- 呼叫 IVMRWindowlessControl::SetVideoClippingWindow 方法來設定視訊視窗。
- 呼叫 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;
}
相關主題