次の方法で共有


手順 4: ビデオ レンダラーを追加する

[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayerIMFMediaEngine、および Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayerIMFMediaEngineAudio/Video Capture を使用することを強くお勧めします。 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. VMR-9 フィルターに IVMRFilterConfig9 インターフェイスを照会します。
  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-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 を初期化します。 この関数は、次の手順を実行します。

  1. VMR-7 フィルターに IVMRFilterConfig インターフェイスのクエリを実行します。
  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 フィルターの使用

ビデオ 混合レンダラーの使用