Uso della modalità senza finestra

[La funzionalità associata a questa pagina, DirectShow, è una funzionalità legacy. È stata sostituita da MediaPlayer, IMFMediaEngine e Audio/Video Capture in Media Foundation. Queste funzionalità sono state ottimizzate per Windows 10 e Windows 11. Microsoft consiglia vivamente che il nuovo codice usi MediaPlayer, IMFMediaEngine e Audio/Video Capture in Media Foundation invece di DirectShow, quando possibile. Microsoft suggerisce che il codice esistente che usa le API legacy venga riscritto per usare le nuove API, se possibile.

Sia il filtro del renderer di combinazione video 7 (VMR-7) che il filtro renderer di combinazione video 9 (VMR-9) supportano la modalità senza finestra, che rappresenta un miglioramento importante rispetto all'interfaccia IVideoWindow . In questo argomento vengono descritte le differenze tra la modalità senza finestra e la modalità finestra e come usare la modalità senza finestra.

Per mantenere la compatibilità con le versioni precedenti con le applicazioni esistenti, per impostazione predefinita VMR è la modalità finestrata. In modalità finestra, il renderer crea la propria finestra per visualizzare il video. In genere l'applicazione imposta la finestra video come figlio della finestra dell'applicazione. L'esistenza di una finestra video separata causa alcuni problemi, tuttavia:

  • Soprattutto, esiste un potenziale di deadlock se i messaggi di finestra vengono inviati tra thread.
  • Filter Graph Manager deve inoltrare determinati messaggi di finestra, ad esempio WM_PAINT, al renderer video. L'applicazione deve usare l'implementazione di Filter Graph Manager di IVideoWindow (e non di Video Renderer), in modo che Filter Graph Manager mantenga lo stato interno corretto.
  • Per ricevere eventi del mouse o della tastiera dalla finestra video, l'applicazione deve impostare uno svuotamento dei messaggi, causando l'inoltro dei messaggi alla finestra video all'applicazione.
  • Per evitare problemi di ritaglio, la finestra video deve avere gli stili di finestra corretti.

La modalità senza finestra consente di evitare questi problemi con il disegno vmR direttamente nell'area client della finestra dell'applicazione, usando DirectDraw per ritagliare il rettangolo video. La modalità senza finestra riduce significativamente la probabilità di deadlock. Inoltre, l'applicazione non deve impostare la finestra del proprietario o gli stili della finestra. Infatti, quando vmR è in modalità senza finestra, non espone nemmeno l'interfaccia IVideoWindow , che non è più necessaria.

Per usare la modalità senza finestra, è necessario configurare in modo esplicito la macchina virtuale. Tuttavia, troverai che è più flessibile e più facile da usare rispetto alla modalità finestra.

Il filtro VMR-7 e il filtro VMR-9 espongono interfacce diverse, ma i passaggi sono equivalenti per ognuno di essi.

Configurare VMR per la modalità senza finestra

Per eseguire l'override del comportamento predefinito di VMR, configurare la macchina virtuale prima di compilare il grafo del filtro:

VMR-7

  1. Creare Filter Graph Manager.
  2. Creare vmR-7 e aggiungerlo al grafico dei filtri.
  3. Chiamare IVMRFilterConfig::SetRenderingMode in VMR-7 con il flag VMRMode_Windowless .
  4. Eseguire una query su VMR-7 per l'interfaccia IVMRWindowlessControl .
  5. Chiamare IVMRWindowlessControl::SetVideoClippingWindow in VMR-7. Specificare un handle per la finestra in cui dovrebbe essere visualizzato il video.

VMR-9

  1. Creare Filter Graph Manager.
  2. Creare vmR-9 e aggiungerlo al grafico dei filtri.
  3. Chiamare IVMRFilterConfig9::SetRenderingMode in VMR-9 con il flag VMR9Mode_Windowless .
  4. Eseguire una query su VMR-9 per l'interfaccia IVMRWindowlessControl9 .
  5. Chiamare IVMRWindowlessControl9::SetVideoClippingWindow in VMR-9. Specificare un handle per la finestra in cui dovrebbe essere visualizzato il video.

Compilare ora il resto del grafico del filtro chiamando IGraphBuilder::RenderFile o altri metodi di compilazione del grafo. Filter Graph Manager usa automaticamente l'istanza di VMR aggiunta al grafo. Per informazioni dettagliate sul motivo per cui si verifica questo problema, vedere Intelligent Connect.

Il codice seguente mostra una funzione helper che crea VMR-7, la aggiunge al grafo e configura la modalità senza finestra.

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

Questa funzione presuppone che vengano visualizzati un solo flusso video e che non si combinano una bitmap statica nel video. Per informazioni dettagliate, vedere Modalità senza finestra vmr. Questa funzione viene chiamata come segue:

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

Posizionare il video

Dopo aver configurato vmr, il passaggio successivo consiste nell'impostare la posizione del video. Esistono due rettangoli da considerare, il rettangolo di origine e il rettangolo di destinazione . Il rettangolo di origine definisce la parte del video da visualizzare. Il rettangolo di destinazione specifica l'area nell'area client della finestra che conterrà il video. VmR ritaglia l'immagine video nel rettangolo di origine e estende l'immagine ritagliata per adattarla al rettangolo di destinazione.

VMR-7

  1. Chiamare il metodo IVMRWindowlessControl::SetVideoPosition per specificare entrambi i rettangoli.
  2. Il rettangolo di origine deve essere uguale o inferiore alla dimensione del video nativo; è possibile usare il metodo IVMRWindowlessControl::GetNativeVideoSize per ottenere le dimensioni del video nativo.

VMR-9

  1. Chiamare il metodo IVMRWindowlessControl9::SetVideoPosition per specificare entrambi i rettangoli.
  2. Il rettangolo di origine deve essere uguale o inferiore alla dimensione del video nativo; è possibile usare il metodo IVMRWindowlessControl9::GetNativeVideoSize per ottenere le dimensioni native del video.

Ad esempio, il codice seguente imposta i rettangoli di origine e di destinazione per VMR-7. Imposta il rettangolo di origine uguale all'intera immagine video e il rettangolo di destinazione uguale all'intera area client della finestra:

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

Se si vuole video occupare una parte più piccola dell'area client, modificare il parametro rcDest . Se si vuole ritagliare l'immagine video, modificare il parametro rcSrc .

Gestire i messaggi della finestra

Poiché vmr non dispone di una propria finestra, deve ricevere una notifica se è necessario aggiornare o ridimensionare il video. Rispondere ai messaggi della finestra seguenti chiamando i metodi VMR elencati.

VMR-7

  1. WM_PAINT. Chiama IVMRWindowlessControl::RepaintVideo. Questo metodo determina l'aggiornamento del fotogramma video più recente da parte di VMR-7.
  2. WM_DISPLAYCHANGE: chiamare IVMRWindowlessControl::D isplayModeChanged. Questo metodo notifica a VMR-7 che il video deve essere visualizzato in una nuova risoluzione o profondità di colore.
  3. WM_SIZE o WM_WINDOWPOSCHANGED: ricalcolare la posizione del video e chiamare IVMRWindowlessControl::SetVideoPosition per aggiornare la posizione, se necessario.

VMR-9

  1. WM_PAINT. Chiama IVMRWindowlessControl9::RepaintVideo. Questo metodo determina l'aggiornamento del fotogramma video più recente da parte di VMR-9.
  2. WM_DISPLAYCHANGE: chiama IVMRWindowlessControl9::D isplayModeChanged. Questo metodo notifica a VMR-9 che il video deve essere visualizzato in una nuova risoluzione o profondità di colore.
  3. WM_SIZE o WM_WINDOWPOSCHANGED: ricalcolare la posizione del video e chiamare IVMRWindowlessControl9::SetVideoPosition per aggiornare la posizione, se necessario.

Nota

Il gestore predefinito per il messaggio WM_WINDOWPOSCHANGED invia un messaggio di WM_SIZE . Tuttavia, se l'applicazione intercetta WM_WINDOWPOSCHANGED e non la passa a DefWindowProc, devi chiamare SetVideoPosition nel gestore WM_WINDOWPOSCHANGED , oltre al gestore WM_SIZE .

 

Nell'esempio seguente viene illustrato un gestore di messaggi WM_PAINT . Disegna un'area definita dal rettangolo client meno il rettangolo video. Non disegnare sul rettangolo video, perché VMR lo dipingerà, causando sfarfallio. Per lo stesso motivo, non impostare un pennello di sfondo nella classe della finestra.

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

Anche se è necessario rispondere a WM_PAINT messaggi, non è necessario eseguire alcuna operazione tra WM_PAINT messaggi per aggiornare il video. Come illustrato in questo esempio, la modalità senza finestra consente di considerare l'immagine video semplicemente come un'area di disegno automatica nella finestra.

Uso del renderer di missaggio video

Video Rendering