ウィンドウレス モードの使用

[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayer、IMFMediaEngine、Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayerIMFMediaEngineAudio/Video Capture を使用することを強くお勧めします。 Microsoft は、レガシ API を使用する既存のコードを、可能であれば新しい API を使用するように書き換えるよう提案しています。]

ビデオ ミキシング レンダラー フィルター 7 (VMR-7) とビデオ ミキシング レンダラー フィルター 9 (VMR-9) の両方でウィンドウレス モードがサポートされています。これは、IVideoWindow インターフェイスに対する大幅な改善を表します。 このトピックでは、ウィンドウレス モードとウィンドウ モードの違いと、ウィンドウレス モードの使用方法について説明します。

既存のアプリケーションとの下位互換性を維持するために、VMR は既定でウィンドウ モードになります。 ウィンドウ モードでは、レンダラーはビデオを表示する独自のウィンドウを作成します。 通常、アプリケーションはビデオ ウィンドウをアプリケーション ウィンドウの子に設定します。 ただし、別のビデオ ウィンドウが存在すると、いくつかの問題が発生します。

  • 最も重要なのは、スレッド間でウィンドウ メッセージが送信された場合にデッドロックが発生する可能性があることです。
  • フィルター グラフ マネージャーは、WM_PAINTなどの特定のウィンドウ メッセージをビデオ レンダラーに転送する必要があります。 フィルター グラフ マネージャーが正しい内部状態を維持するには、アプリケーションでフィルター グラフ マネージャーの IVideoWindow の実装 (ビデオ レンダラーではなく) を使用する必要があります。
  • ビデオ ウィンドウからマウスまたはキーボード イベントを受信するには、アプリケーションで メッセージ ドレインを設定する必要があります。これにより、ビデオ ウィンドウでこれらのメッセージがアプリケーションに転送されます。
  • クリッピングの問題を防ぐには、ビデオ ウィンドウに適切なウィンドウ スタイルが必要です。

ウィンドウレス モードでは、VMR をアプリケーション ウィンドウのクライアント領域に直接描画し、DirectDraw を使用してビデオの四角形をクリップすることで、これらの問題を回避できます。 ウィンドウレス モードでは、デッドロックの可能性が大幅に低下します。 また、アプリケーションで所有者ウィンドウまたはウィンドウ スタイルを設定する必要はありません。 実際、VMR がウィンドウレス モードの場合、 不要になった IVideoWindow インターフェイスも公開されません。

ウィンドウレス モードを使用するには、VMR を明示的に構成する必要があります。 ただし、ウィンドウ モードよりも柔軟性が高く、使いやすいことがわかります。

VMR-7 フィルターと VMR-9 フィルターは異なるインターフェイスを公開しますが、手順はそれぞれ同じです。

ウィンドウレス モード用に VMR を構成する

VMR の既定の動作をオーバーライドするには、フィルター グラフを作成する前に VMR を構成します。

VMR-7

  1. フィルター グラフ マネージャーを作成します。
  2. VMR-7 を作成し、フィルター グラフに追加します。
  3. VMRMODE_WINDOWLESS フラグを使用して、VMR-7 で IVMRFilterConfig::SetRenderingMode を呼び出します。
  4. IVMRWindowlessControl インターフェイスの VMR-7 に対してクエリを実行します。
  5. VMR-7 で IVMRWindowlessControl::SetVideoClippingWindow を呼び出します。 ビデオが表示されるウィンドウへのハンドルを指定します。

VMR-9

  1. フィルター グラフ マネージャーを作成します。
  2. VMR-9 を作成し、フィルター グラフに追加します。
  3. VMR9MODE_WINDOWLESS フラグを使用して、VMR-9 で IVMRFilterConfig9::SetRenderingMode を呼び出します。
  4. IVMRWindowlessControl9 インターフェイスの VMR-9 に対してクエリを実行します。
  5. VMR-9 で IVMRWindowlessControl9::SetVideoClippingWindow を呼び出します。 ビデオが表示されるウィンドウへのハンドルを指定します。

次に、 IGraphBuilder::RenderFile またはその他のグラフ構築メソッドを呼び出して、フィルター グラフの残りの部分をビルドします。 フィルター グラフ マネージャーでは、グラフに追加した VMR のインスタンスが自動的に使用されます。 (これが発生する理由の詳細については、「 Intelligent Connect」を参照してください)。

次のコードは、VMR-7 を作成してグラフに追加し、ウィンドウレス モードを設定するヘルパー関数を示しています。

HRESULT InitWindowlessVMR( 
    HWND hwndApp,                  // Window to hold the video. 
    IGraphBuilder* pGraph,         // Pointer to the Filter Graph Manager. 
    IVMRWindowlessControl** ppWc   // Receives a pointer to the VMR.
    ) 
{ 
    if (!pGraph || !ppWc) 
    {
        return E_POINTER;
    }
    IBaseFilter* pVmr = NULL; 
    IVMRWindowlessControl* pWc = NULL; 
    // Create the VMR. 
    HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL, 
        CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr); 
    if (FAILED(hr))
    {
        return hr;
    }
    
    // Add the VMR to the filter graph.
    hr = pGraph->AddFilter(pVmr, L"Video Mixing Renderer"); 
    if (FAILED(hr)) 
    {
        pVmr->Release();
        return hr;
    }
    // Set the rendering mode.  
    IVMRFilterConfig* pConfig; 
    hr = pVmr->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig); 
    if (SUCCEEDED(hr)) 
    { 
        hr = pConfig->SetRenderingMode(VMRMode_Windowless); 
        pConfig->Release(); 
    }
    if (SUCCEEDED(hr))
    {
        // Set the window. 
        hr = pVmr->QueryInterface(IID_IVMRWindowlessControl, (void**)&pWc);
        if( SUCCEEDED(hr)) 
        { 
            hr = pWc->SetVideoClippingWindow(hwndApp); 
            if (SUCCEEDED(hr))
            {
                *ppWc = pWc; // Return this as an AddRef'd pointer. 
            }
            else
            {
                // An error occurred, so release the interface.
                pWc->Release();
            }
        } 
    } 
    pVmr->Release(); 
    return hr; 
} 

この関数は、1 つのビデオ ストリームのみを表示し、ビデオに静的ビットマップを混在させないことを前提としています。 詳細については、「 VMR ウィンドウレス モード」を参照してください。 この関数は次のように呼び出します。

IVMRWindowlessControl *pWc = NULL;
hr = InitWindowlessVMR(hwnd, pGraph, &g_pWc);
if (SUCCEEDED(hr))
{
    // Build the graph. For example:
    pGraph->RenderFile(wszMyFileName, 0);
    // Release the VMR interface when you are done.
    pWc->Release();
}

ビデオの配置

VMR を構成した後、次の手順はビデオの位置を設定することです。 考慮すべき四角形は、 ソース 四角形と 変換先 の四角形の 2 つあります。 ソース四角形は、表示するビデオの部分を定義します。 変換先の四角形は、ビデオを含むウィンドウのクライアント領域の領域を指定します。 VMR は、ビデオ イメージをソース四角形にトリミングし、トリミングされたイメージをコピー先の四角形に合わせて拡大します。

VMR-7

  1. 両方の四角形を指定するには、 IVMRWindowlessControl::SetVideoPosition メソッドを呼び出します。
  2. ソースの四角形は、ネイティブ ビデオ サイズと同じか小さい必要があります。 IVMRWindowlessControl::GetNativeVideoSize メソッドを使用して、ネイティブ ビデオ サイズを取得できます。

VMR-9

  1. IVMRWindowlessControl9::SetVideoPosition メソッドを呼び出して、両方の四角形を指定します。
  2. ソースの四角形は、ネイティブ ビデオ サイズと同じか小さい必要があります。 IVMRWindowlessControl9::GetNativeVideoSize メソッドを使用して、ネイティブ ビデオ サイズを取得できます。

たとえば、次のコードでは、VMR-7 のソース四角形と変換先の四角形を設定します。 ソースの四角形をビデオ イメージ全体に設定し、変換先の四角形をウィンドウ クライアント領域全体に設定します。

// Find the native video size.
long lWidth, lHeight; 
HRESULT hr = g_pWc->GetNativeVideoSize(&lWidth, &lHeight, NULL, NULL); 
if (SUCCEEDED(hr))
{
    RECT rcSrc, rcDest; 
    // Set the source rectangle.
    SetRect(&rcSrc, 0, 0, lWidth, lHeight); 
    
    // Get the window client area.
    GetClientRect(hwnd, &rcDest); 
    // Set the destination rectangle.
    SetRect(&rcDest, 0, 0, rcDest.right, rcDest.bottom); 
    
    // Set the video position.
    hr = g_pWc->SetVideoPosition(&rcSrc, &rcDest); 
}

クライアント領域の小さな部分を占めるビデオを作成する場合は、 rcDest パラメーターを変更します。 ビデオ イメージをトリミングする場合は、 rcSrc パラメーターを変更します。

ウィンドウ メッセージの処理

VMR には独自のウィンドウがないため、ビデオを再描画またはサイズ変更する必要がある場合は、通知を受け取る必要があります。 一覧表示されている VMR メソッドを呼び出して、次のウィンドウ メッセージに応答します。

VMR-7

  1. WM_PAINTIVMRWindowlessControl::RepaintVideo を呼び出します。 この方法により、VMR-7 は最新のビデオ フレームを再描画します。
  2. WM_DISPLAYCHANGE: IVMRWindowlessControl::D isplayModeChanged を呼び出します。 このメソッドは、ビデオを新しい解像度または色深度で表示する必要があることを VMR-7 に通知します。
  3. WM_SIZE または WM_WINDOWPOSCHANGED: ビデオの位置を再計算し、 IVMRWindowlessControl::SetVideoPosition を呼び出して、必要に応じて位置を更新します。

VMR-9

  1. WM_PAINTIVMRWindowlessControl9::RepaintVideo を呼び出します。 この方法により、VMR-9 は最新のビデオ フレームを再描画します。
  2. WM_DISPLAYCHANGE: IVMRWindowlessControl9::D isplayModeChanged を呼び出します。 このメソッドは、ビデオを新しい解像度または色深度で表示する必要があることを VMR-9 に通知します。
  3. WM_SIZE または WM_WINDOWPOSCHANGED: ビデオの位置を再計算し、 IVMRWindowlessControl9::SetVideoPosition を呼び出して、必要に応じて位置を更新します。

注意

WM_WINDOWPOSCHANGED メッセージの既定のハンドラーは、WM_SIZE メッセージを送信します。 ただし、アプリケーションがWM_WINDOWPOSCHANGEDをインターセプトし、DefWindowProc に渡さない場合は、WM_SIZE ハンドラーに加えて、WM_WINDOWPOSCHANGED ハンドラーで SetVideoPosition を呼び出す必要があります。

 

次の例は、 WM_PAINT メッセージ ハンドラーを示しています。 クライアントの四角形からビデオ四角形を引いた領域を描画します。 VMR がビデオの四角形の上に描画され、ちらつきが発生するため、ビデオの四角形には描画しないでください。 同じ理由で、ウィンドウ クラスに背景ブラシを設定しないでください。

void OnPaint(HWND hwnd) 
{ 
    PAINTSTRUCT ps; 
    HDC         hdc; 
    RECT        rcClient; 
    GetClientRect(hwnd, &rcClient); 
    hdc = BeginPaint(hwnd, &ps); 
    if (g_pWc != NULL) 
    { 
        // Find the region where the application can paint by subtracting 
        // the video destination rectangle from the client area.
        // (Assume that g_rcDest was calculated previously.)
        HRGN rgnClient = CreateRectRgnIndirect(&rcClient); 
        HRGN rgnVideo  = CreateRectRgnIndirect(&g_rcDest);  
        CombineRgn(rgnClient, rgnClient, rgnVideo, RGN_DIFF);  
        
        // Paint on window.
        HBRUSH hbr = GetSysColorBrush(COLOR_BTNFACE); 
        FillRgn(hdc, rgnClient, hbr); 

        // Clean up.
        DeleteObject(hbr); 
        DeleteObject(rgnClient); 
        DeleteObject(rgnVideo); 

        // Request the VMR to paint the video.
        HRESULT hr = g_pWc->RepaintVideo(hwnd, hdc);  
    } 
    else  // There is no video, so paint the whole client area. 
    { 
        FillRect(hdc, &rc2, (HBRUSH)(COLOR_BTNFACE + 1)); 
    } 
    EndPaint(hwnd, &ps); 
} 

WM_PAINTメッセージに応答する必要がありますが、ビデオを更新するためにメッセージWM_PAINT間で行う必要はありません。 この例に示すように、ウィンドウレス モードでは、ビデオ イメージをウィンドウ上の自己描画領域として扱うことができます。

ビデオ ミキシング レンダラーの使用

ビデオのレンダリング