手順 4: ビデオ レンダラーを追加する
[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayer、IMFMediaEngine、および Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayer、IMFMediaEngine、Audio/Video Capture を使用することを強くお勧めします。 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 ウィンドウレス モード」を参照してください)。この関数は、次の手順を実行します。
- VMR-9 フィルターに IVMRFilterConfig9 インターフェイスを照会します。
- 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-7 インターフェイスが代わりに使用される点を除き、VMR-9 の手順とほぼ同じです。 次のコードでは、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 を初期化します。 この関数は、次の手順を実行します。
- VMR-7 フィルターに IVMRFilterConfig インターフェイスのクエリを実行します。
- 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;
}
関連トピック