共用方式為


使用無視窗模式

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

視訊混合轉譯器篩選器 7 (VMR-7) 和視訊混合轉譯器篩選器 9 (VMR-9) 支援無視窗模式,這代表IVideoWindow介面的重大改善。 本主題描述無視窗模式與視窗模式之間的差異,以及如何使用無視窗模式。

為了保持與現有應用程式的回溯相容,VMR 預設為視窗模式。 在視窗模式中,轉譯器會建立自己的視窗以顯示視訊。 應用程式通常會將視訊視窗設定為應用程式視窗的子系。 不過,個別視訊視窗的存在會造成一些問題:

  • 最重要的是,如果視窗訊息線上程之間傳送,可能會發生死結。
  • 篩選圖形管理員必須將某些視窗訊息轉送至影片轉譯器,例如WM_PAINT。 應用程式必須使用 Filter Graph Manager 的 IVideoWindow (實作,而不是影片轉譯器的) ,讓 Filter Graph 管理員維持正確的內部狀態。
  • 若要從視訊視窗接收滑鼠或鍵盤事件,應用程式必須設定 清空訊息,導致視訊視窗將這些訊息轉送至應用程式。
  • 若要避免裁剪問題,視訊視窗必須有正確的視窗樣式。

無視窗模式可避免這些問題,方法是使用 DirectDraw 直接在應用程式視窗的工作區上繪製 VMR 來裁剪視訊矩形。 無視窗模式可大幅減少死結的機會。 此外,應用程式不需要設定擁有者視窗或視窗樣式。 事實上,當 VMR 處於無視窗模式時,甚至不會公開不再需要的 IVideoWindow 介面。

若要使用無視窗模式,您必須明確設定 VMR。 不過,您會發現比視窗模式更有彈性且更容易使用。

VMR-7 篩選器和 VMR-9 篩選器會公開不同的介面,但步驟相當於每個介面。

設定無視窗模式的 VMR

若要覆寫 VMR 的預設行為,請先設定 VMR,再建置篩選圖形:

VMR-7

  1. 建立 Filter Graph Manager。
  2. 建立 VMR-7,並將其新增至篩選圖形。
  3. 使用VMRMode_Windowless旗標在 VMR-7 上呼叫IVMRFilterConfig::SetRenderingMode
  4. 查詢 VMR-7 以取得 IVMRWindowlessControl 介面。
  5. 在 VMR-7 上呼叫 IVMRWindowlessControl::SetVideoClippingWindow 。 指定應顯示視訊之視窗的控制碼。

VMR-9

  1. 建立 Filter Graph Manager。
  2. 建立 VMR-9,並將其新增至篩選圖形。
  3. 使用VMR9Mode_Windowless旗標在 VMR-9 上呼叫IVMRFilterConfig9::SetRenderingMode
  4. 查詢 VMR-9 以取得 IVMRWindowlessControl9 介面。
  5. 在 VMR-9 上呼叫 IVMRWindowlessControl9::SetVideoClippingWindow 。 指定應顯示視訊之視窗的控制碼。

現在呼叫 IGraphBuilder::RenderFile 或其他圖形建置方法,以建置其餘的篩選圖形。 Filter Graph 管理員會自動使用您新增至圖形的 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; 
} 

此函式假設只顯示一個視訊資料流程,而且不會將靜態點陣圖混用到影片上。 如需詳細資訊,請參閱 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 之後,下一個步驟是設定視訊的位置。 有兩個要考慮的矩形: 來源 矩形和 目的地 矩形。 來源矩形會定義要顯示的視訊部分。 目的矩形會指定視窗工作區中將包含視訊的區域。 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_PAINT。 呼叫 IVMRWindowlessControl::RepaintVideo。 此方法會導致 VMR-7 重新繪製最新的視訊畫面。
  2. WM_DISPLAYCHANGE:呼叫 IVMRWindowlessControl::D isplayModeChanged。 這個方法會通知 VMR-7,視訊必須以新的解析度或色彩深度顯示。
  3. WM_SIZEWM_WINDOWPOSCHANGED:重新計算視訊的位置,並視需要呼叫 IVMRWindowlessControl::SetVideoPosition 來更新位置。

VMR-9

  1. WM_PAINT。 呼叫 IVMRWindowlessControl9::RepaintVideo。 這個方法會使 VMR-9 重新繪製最新的視訊畫面。
  2. WM_DISPLAYCHANGE:呼叫 IVMRWindowlessControl9::D isplayModeChanged。 這個方法會通知 VMR-9,視訊必須以新的解析度或色彩深度顯示。
  3. WM_SIZEWM_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 訊息之間不需要執行任何動作,即可更新影片。 如本範例所示,無視窗模式可讓您將視訊影像視為視窗上的自我繪製區域。

使用視訊混合轉譯器

影片轉譯