Использование режима без окон

[Функция, связанная с этой страницей DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngine, и аудио/ видео захвата в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует использовать в новом коде MediaPlayer, IMFMediaEngine и аудио/видеозахват в Media Foundation вместо DirectShow, когда это возможно. Корпорация Майкрософт предлагает переписать существующий код, в котором используются устаревшие 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. Вызовите метод IVMRFilterConfig::SetRenderingMode для VMR-7 с флагом VMRMode_Windowless .
  4. Запросите VMR-7 для интерфейса IVMRWindowlessControl .
  5. Вызовите метод IVMRWindowlessControl::SetVideoClippingWindow на VMR-7. Укажите дескриптор для окна, в котором должно отображаться видео.

VMR-9

  1. Создайте диспетчер фильтров графов.
  2. Создайте VMR-9 и добавьте его в граф фильтра.
  3. Вызовите метод IVMRFilterConfig9::SetRenderingMode для VMR-9 с флагом VMR9Mode_Windowless .
  4. Запросите VMR-9 для интерфейса IVMRWindowlessControl9 .
  5. Вызовите метод IVMRWindowlessControl9::SetVideoClippingWindow на VMR-9. Укажите дескриптор для окна, в котором должно отображаться видео.

Теперь создайте остальную часть графа фильтра, вызвав метод 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; 
} 

Эта функция предполагает, что отображают только один видеопоток и не смешивают статическое растровое изображение по видео. Дополнительные сведения см. в разделе VmR Windowless Mode. Эту функцию можно вызвать следующим образом:

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_SIZE или WM_WINDOWPOSCHANGED: пересчитайте положение видео и вызовите метод IVMRWindowlessControl::SetVideoPosition , чтобы при необходимости обновить позицию.

VMR-9

  1. WM_PAINT. Вызовите МЕТОД IVMRWindowlessControl9::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, следует вызвать SetVideoPosition в обработчике WM_WINDOWPOSCHANGED в дополнение к обработчику WM_SIZE .

 

В следующем примере показан обработчик сообщений 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 сообщениями, чтобы обновить видео. Как показано в этом примере, режим без окон позволяет рассматривать видеоимощение просто как саморисовываемую область окна.

Использование отрисовщика для смешанного видео

Рендеринг видео