Udostępnij przez


Korzystanie z trybu bez okna

[Funkcja skojarzona z tą stroną, DirectShow, jest starszą funkcją. Został zastąpiony przez MediaPlayer, IMFMediaEnginei Audio/Video Capture w Media Foundation. Te funkcje zostały zoptymalizowane pod kątem systemów Windows 10 i Windows 11. Firma Microsoft zdecydowanie zaleca, aby nowy kod używał MediaPlayer, IMFMediaEngine i Audio/Video Capture w programie Media Foundation zamiast DirectShow, jeśli to możliwe. Firma Microsoft sugeruje, że istniejący kod, który używa starszych interfejsów API, należy przepisać go do korzystania z nowych interfejsów API, jeśli to możliwe.]

Zarówno filtr renderowania mieszania wideo 7 (VMR-7), jak i filtr renderowania mieszania wideo 9 (VMR-9) obsługują tryb bezokienkowy, co stanowi znaczące udoskonalenie w stosunku do interfejsu IVideoWindow. W tym temacie opisano różnice między trybem bez okna i trybem okien oraz sposób korzystania z trybu bez okien.

Aby zachować zgodność z istniejącymi aplikacjami, VMR domyślnie działa w trybie okienkowym. W trybie okna renderator tworzy własne okno, aby wyświetlić wideo. Zazwyczaj aplikacja ustawia okno wideo jako element podrzędny okna aplikacji. Istnienie oddzielnego okna wideo powoduje jednak pewne problemy:

  • Najważniejsze, istnieje możliwość zakleszczenia, jeśli komunikaty okienkowe są wysyłane między wątkami.
  • Menedżer Grafu Filtrów musi przekazywać niektóre komunikaty okna, takie jak WM_PAINT, do renderera wideo. Aplikacja musi używać implementacji programu Filter Graph Manager IVideoWindow (a nie renderera wideo), aby Filter Graph Manager utrzymywał prawidłowy stan wewnętrzny.
  • Aby odbierać zdarzenia myszy lub klawiatury z okna wideo, aplikacja musi ustawić komunikat, powodując, że okno wideo przekazuje te komunikaty do aplikacji.
  • Aby zapobiec problemom z wycinaniem, okno wideo musi mieć odpowiednie style okna.

Tryb bezokienny pozwala uniknąć tych problemów, ponieważ VMR rysuje bezpośrednio w obszarze klienta okna aplikacji, używając DirectDraw do przycinania prostokąta wideo. Tryb bez okienek znacznie zmniejsza prawdopodobieństwo wystąpienia zawieszeń. Ponadto aplikacja nie musi ustawiać okna właściciela ani stylów okna. W rzeczywistości gdy VMR jest w trybie bezokienkowym, nie udostępnia nawet interfejsu IVideoWindow, który nie jest już potrzebny.

Aby korzystać z trybu bez okna, należy jawnie skonfigurować VMR. Jednak okaże się, że jest bardziej elastyczny i łatwiejszy w użyciu niż tryb okienny.

Filtr VMR-7 i filtr VMR-9 uwidaczniają różne interfejsy, ale kroki są równoważne dla każdego z nich.

Konfiguracja VMR dla trybu bezokienkowego

Aby zastąpić domyślne zachowanie VMR, skonfiguruj VMR przed utworzeniem grafu filtrów.

VMR-7

  1. Utwórz menedżera programu Graph Filter.
  2. Utwórz VMR-7 i dodaj go do grafu filtru.
  3. Wywołaj IVMRFilterConfig::SetRenderingMode na VMR-7 z flagą VMRMode_Windowless.
  4. Wykonaj zapytanie do VMR-7 o interfejs IVMRWindowlessControl .
  5. Wywołaj IVMRWindowlessControl::SetVideoClippingWindow na VMR-7. Określ dojście do okna, w którym ma być wyświetlane wideo.

VMR-9

  1. Utwórz menedżera programu Graph Filter.
  2. Utwórz VMR-9 i dodaj go do grafu filtru.
  3. Wywołaj IVMRFilterConfig9::SetRenderingMode na VMR-9 z flagą VMR9Mode_Windowless.
  4. Zapytaj VMR-9 o interfejs IVMRWindowlessControl9.
  5. Wywołaj IVMRWindowlessControl9::SetVideoClippingWindow na VMR-9. Określ dojście do okna, w którym ma być wyświetlane wideo.

Teraz skompiluj resztę wykresu filtru, wywołując metodę IGraphBuilder::RenderFile lub inne metody tworzenia grafu. Menedżer filtrów Graph automatycznie używa wystąpienia VMR, które dodałeś do grafu. (Aby uzyskać szczegółowe informacje na temat tego, dlaczego tak się dzieje, zobacz Intelligent Connect.)

Poniższy kod przedstawia funkcję pomocnika, która tworzy maszynę wirtualną VMR-7, dodaje ją do grafu i konfiguruje tryb bez okien.

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; 
} 

Ta funkcja zakłada, że wyświetla tylko jeden strumień wideo i nie miesza statycznej mapy bitowej na wideo. Aby uzyskać szczegółowe informacje, zobacz tryb bezokienkowy VMR. Ta funkcja jest wywoływana w następujący sposób:

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();
}

Ustaw wideo

Po skonfigurowaniu VMR, kolejnym krokiem jest ustawienie pozycji wideo. Należy rozważyć dwa prostokąty: prostokąt źródła i prostokąt docelowy . Prostokąt źródłowy definiuje, która część wideo ma być wyświetlana. Prostokąt docelowy określa region w obszarze roboczym okna, który będzie zawierał wideo. VMR przycina obraz wideo do prostokąta źródłowego i rozciąga przycięty obraz, aby dopasować się do prostokąta docelowego.

VMR-7

  1. Wywołaj metodę IVMRWindowlessControl::SetVideoPosition, aby określić oba prostokąty.
  2. Prostokąt źródłowy musi być równy lub mniejszy niż natywny rozmiar wideo; Możesz użyć metody IVMRWindowlessControl::GetNativeVideoSize, aby uzyskać natywny rozmiar wideo.

VMR-9

  1. Wywołaj metodę IVMRWindowlessControl9::SetVideoPosition, aby określić oba prostokąty.
  2. Prostokąt źródłowy musi być równy lub mniejszy niż natywny rozmiar wideo; Możesz użyć metody IVMRWindowlessControl9::GetNativeVideoSize, aby uzyskać natywny rozmiar wideo.

Na przykład poniższy kod ustawia prostokąty źródłowe i docelowe dla VMR-7. Ustawia prostokąt źródłowy równy całemu obrazowi wideo, a docelowy prostokąt jest równy całemu obszarowi klienta okna:

// 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); 
}

Jeśli chcesz, aby wideo zajmowało mniejszą część obszaru klienta, zmodyfikuj parametr rcDest. Jeśli chcesz przycinać obraz wideo, zmodyfikuj parametr rcSrc.

Obsługa komunikatów okna

Ze względu na to, że VMR nie ma własnego okna, musi zostać powiadomiony, jeśli zajdzie potrzeba przemalowania lub zmiany rozmiaru wideo. Zareaguj na następujące komunikaty okna, wywołując wymienione metody VMR.

VMR-7

  1. WM_PAINT. Wywołaj IVMRWindowlessControl::RepaintVideo. Ta metoda powoduje, że VMR-7 odświeża najnowszą klatkę wideo.
  2. WM_DISPLAYCHANGE: Wywołaj IVMRWindowlessControl::DisplayModeChanged. Ta metoda powiadamia maszynę wirtualną VMR-7 o tym, że wideo musi być wyświetlane w nowej rozdzielczości lub głębokości koloru.
  3. WM_SIZE lub WM_WINDOWPOSCHANGED: Oblicz ponownie położenie wideo i wywołaj IVMRWindowlessControl::SetVideoPosition, aby zaktualizować położenie, jeśli to konieczne.

VMR-9

  1. WM_PAINT. Wywołaj IVMRWindowlessControl9::RepaintVideo. Ta metoda powoduje, że VMR-9 odświeża najnowszą ramkę wideo.
  2. WM_DISPLAYCHANGE: Wywołaj IVMRWindowlessControl9::DisplayModeChanged. Ta metoda powiadamia maszynę wirtualną VMR-9 o tym, że wideo musi być wyświetlane w nowej rozdzielczości lub głębokości koloru.
  3. WM_SIZE lub WM_WINDOWPOSCHANGED: Oblicz ponownie położenie wideo i wywołaj IVMRWindowlessControl9::SetVideoPosition, aby zaktualizować położenie, jeśli to konieczne.

Notatka

Domyślna procedura obsługi komunikatu WM_WINDOWPOSCHANGED wysyła komunikat WM_SIZE. Jeśli jednak aplikacja przechwytuje WM_WINDOWPOSCHANGED i nie przekazuje jej do DefWindowProc, należy wywołać SetVideoPosition w procedurze obsługi WM_WINDOWPOSCHANGED, oprócz procedury obsługi WM_SIZE.

 

W poniższym przykładzie przedstawiono procedurę obsługi komunikatu WM_PAINT. Maluje region zdefiniowany przez prostokąt klienta pomniejszonego o prostokąt wideo. Nie rysuj na prostokąt wideo, ponieważ VMR przykryje go, co spowoduje migotanie. Z tego samego powodu nie ustawiaj pędzla tła w klasie okna.

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); 
} 

Mimo że musisz odpowiedzieć na komunikaty WM_PAINT, nie musisz nic robić między komunikatami WM_PAINT, aby zaktualizować wideo. Jak pokazano w tym przykładzie, tryb bezokienny pozwala traktować obraz wideo po prostu jako obszar samodzielnego rysowania na oknie.

Użycie Video Mixing Renderer

Renderowanie wideo