Bagikan melalui


Menggunakan Mode Tanpa Jendela

[Fitur yang terkait dengan halaman ini, DirectShow, adalah fitur warisan. Ini telah digantikan oleh MediaPlayer, IMFMediaEngine, dan Tangkapan Audio/Video di Media Foundation. Fitur-fitur tersebut telah dioptimalkan untuk Windows 10 dan Windows 11. Microsoft sangat menyarankan agar kode baru menggunakan MediaPlayer, IMFMediaEngine dan Audio/Video Capture di Media Foundation alih-alih DirectShow, jika memungkinkan. Microsoft menyarankan agar kode yang ada yang menggunakan API warisan ditulis ulang untuk menggunakan API baru jika memungkinkan.]

Baik Video Mixing Renderer Filter 7 (VMR-7) dan Video Mixing Renderer Filter 9 (VMR-9) mendukung mode tanpa jendela, yang mewakili peningkatan besar atas antarmuka IVideoWindow . Topik ini menjelaskan perbedaan antara mode tanpa jendela dan mode berjendela, dan cara menggunakan mode tanpa jendela.

Untuk tetap kompatibel mundur dengan aplikasi yang ada, VMR default ke mode berjendela. Dalam mode berjendela, perender membuat jendelanya sendiri untuk menampilkan video. Biasanya aplikasi mengatur jendela video menjadi anak dari jendela aplikasi. Keberadaan jendela video terpisah menyebabkan beberapa masalah, namun:

  • Yang paling penting, ada potensi kebuntuan jika pesan jendela dikirim di antara utas.
  • Filter Graph Manager harus meneruskan pesan jendela tertentu, seperti WM_PAINT, ke Video Renderer. Aplikasi harus menggunakan implementasi Filter Graph Manager dari IVideoWindow (dan bukan Video Renderer), sehingga Filter Graph Manager mempertahankan status internal yang benar.
  • Untuk menerima peristiwa mouse atau keyboard dari jendela video, aplikasi harus mengatur pengurasan pesan, menyebabkan jendela video meneruskan pesan ini ke aplikasi.
  • Untuk mencegah masalah kliping, jendela video harus memiliki gaya jendela yang tepat.

Mode tanpa jendela menghindari masalah ini dengan membuat VMR menarik langsung di area klien jendela aplikasi, menggunakan DirectDraw untuk mengklip persegi video. Mode tanpa jendela secara signifikan mengurangi kemungkinan kebuntuan. Selain itu, aplikasi tidak perlu mengatur jendela pemilik atau gaya jendela. Bahkan, ketika VMR dalam mode tanpa jendela, VMR bahkan tidak mengekspos antarmuka IVideoWindow , yang tidak lagi diperlukan.

Untuk menggunakan mode tanpa jendela, Anda harus secara eksplisit mengonfigurasi VMR. Namun, Anda akan menemukan bahwa lebih fleksibel dan lebih mudah digunakan daripada mode berjendela.

Filter VMR-7 dan filter VMR-9 mengekspos antarmuka yang berbeda, tetapi langkah-langkahnya setara untuk masing-masing antarmuka.

Mengonfigurasi VMR untuk Mode Tanpa Jendela

Untuk mengambil alih perilaku default VMR, konfigurasikan VMR sebelum membangun grafik filter:

VMR-7

  1. Buat Filter Graph Manager.
  2. Buat VMR-7 dan tambahkan ke grafik filter.
  3. Panggil IVMRFilterConfig::SetRenderingMode pada VMR-7 dengan bendera VMRMode_Windowless .
  4. Kueri VMR-7 untuk antarmuka IVMRWindowlessControl .
  5. Panggil IVMRWindowlessControl::SetVideoClippingWindow pada VMR-7. Tentukan handel ke jendela tempat video akan muncul.

VMR-9

  1. Buat Filter Graph Manager.
  2. Buat VMR-9 dan tambahkan ke grafik filter.
  3. Panggil IVMRFilterConfig9::SetRenderingMode pada VMR-9 dengan bendera VMR9Mode_Windowless .
  4. Kueri VMR-9 untuk antarmuka IVMRWindowlessControl9 .
  5. Panggil IVMRWindowlessControl9::SetVideoClippingWindow pada VMR-9. Tentukan handel ke jendela tempat video akan muncul.

Sekarang buat grafik filter lainnya dengan memanggil IGraphBuilder::RenderFile atau metode pembuatan grafik lainnya. Filter Graph Manager secara otomatis menggunakan instans VMR yang Anda tambahkan ke grafik. (Untuk detail tentang mengapa hal ini terjadi, lihat Intelligent Connect.)

Kode berikut menunjukkan fungsi pembantu yang membuat VMR-7, menambahkannya ke grafik, dan menyiapkan mode tanpa jendela.

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

Fungsi ini mengasumsikan bahwa hanya menampilkan satu aliran video dan tidak mencampur bitmap statis di atas video. Untuk detailnya, lihat Mode Tanpa Jendela VMR. Anda akan memanggil fungsi ini sebagai berikut:

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

Posisikan Video

Setelah mengonfigurasi VMR, langkah selanjutnya adalah mengatur posisi video. Ada dua persegi panjang yang perlu dipertimbangkan, persegi panjang sumber dan persegi panjang tujuan . Persegi panjang sumber menentukan bagian video mana yang akan ditampilkan. Persegi panjang tujuan menentukan wilayah di area klien jendela yang akan berisi video. VMR memangkas gambar video ke persegi panjang sumber dan membentangkan gambar yang dipangkas agar sesuai dengan persegi tujuan.

VMR-7

  1. Panggil metode IVMRWindowlessControl::SetVideoPosition untuk menentukan kedua persegi panjang.
  2. Persegi panjang sumber harus sama dengan atau lebih kecil dari ukuran video asli; Anda dapat menggunakan metode IVMRWindowlessControl::GetNativeVideoSize untuk mendapatkan ukuran video asli.

VMR-9

  1. Panggil metode IVMRWindowlessControl9::SetVideoPosition untuk menentukan kedua persegi panjang.
  2. Persegi panjang sumber harus sama dengan atau lebih kecil dari ukuran video asli; Anda dapat menggunakan metode IVMRWindowlessControl9::GetNativeVideoSize untuk mendapatkan ukuran video asli.

Misalnya, kode berikut mengatur persegi panjang sumber dan tujuan untuk VMR-7. Ini mengatur persegi panjang sumber sama dengan seluruh gambar video, dan persegi panjang tujuan sama dengan seluruh area klien jendela:

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

Jika Anda ingin video menempati bagian area klien yang lebih kecil, ubah parameter rcDest . Jika Anda ingin memangkas gambar video, ubah parameter rcSrc .

Menangani Pesan Jendela

Karena VMR tidak memiliki jendelanya sendiri, VMR harus diberi tahu jika perlu dicat ulang atau mengubah ukuran video. Tanggapi pesan jendela berikut dengan memanggil metode VMR yang tercantum.

VMR-7

  1. WM_PAINT. Panggil IVMRWindowlessControl::RepaintVideo. Metode ini menyebabkan VMR-7 mengecat ulang bingkai video terbaru.
  2. WM_DISPLAYCHANGE: Panggil IVMRWindowlessControl::D isplayModeChanged. Metode ini memberi tahu VMR-7 bahwa video harus ditampilkan pada resolusi atau kedalaman warna baru.
  3. WM_SIZE atau WM_WINDOWPOSCHANGED: Hitung ulang posisi video dan panggil IVMRWindowlessControl::SetVideoPosition untuk memperbarui posisi, jika diperlukan.

VMR-9

  1. WM_PAINT. Panggil IVMRWindowlessControl9::RepaintVideo. Metode ini menyebabkan VMR-9 mengecat ulang bingkai video terbaru.
  2. WM_DISPLAYCHANGE: Panggil IVMRWindowlessControl9::D isplayModeChanged. Metode ini memberi tahu VMR-9 bahwa video harus ditampilkan pada resolusi atau kedalaman warna baru.
  3. WM_SIZE atau WM_WINDOWPOSCHANGED: Hitung ulang posisi video dan panggil IVMRWindowlessControl9::SetVideoPosition untuk memperbarui posisi, jika diperlukan.

Catatan

Handler default untuk pesan WM_WINDOWPOSCHANGED mengirim pesan WM_SIZE . Tetapi jika aplikasi Anda mencegat WM_WINDOWPOSCHANGED dan tidak meneruskannya ke DefWindowProc, Anda harus memanggil SetVideoPosition di handler WM_WINDOWPOSCHANGED Anda, selain handler WM_SIZE Anda.

 

Contoh berikut menunjukkan penanganan pesan WM_PAINT . Ini melukis wilayah yang ditentukan oleh persegi panjang klien dikurangi persegi panjang video. Jangan menggambar ke persegi panjang video, karena VMR akan melukis di atasnya, menyebabkan kedlipan. Untuk alasan yang sama, jangan atur kuas latar belakang di kelas jendela Anda.

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

Meskipun Anda harus menanggapi pesan WM_PAINT , tidak ada yang perlu Anda lakukan di antara pesan WM_PAINT untuk memperbarui video. Seperti yang ditunjukkan contoh ini, mode tanpa jendela memungkinkan Anda memperlakukan gambar video hanya sebagai wilayah gambar mandiri di jendela.

Menggunakan Video Mixing Renderer

Penyajian Video