Condividi tramite


Renderer video alternativi

[La funzionalità associata a questa pagina, DirectShow, è una funzionalità legacy. È stata sostituita da MediaPlayer, FMMediaEngine 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, FMMediaEngine e Audio/Video Capture in Media Foundation anziché DirectShow, quando possibile. Microsoft suggerisce che il codice esistente che usa le API legacy venga riscritto per usare le nuove API, se possibile.

Questo argomento descrive come scrivere un renderer video personalizzato per DirectShow.

Nota

Invece di scrivere un renderer video personalizzato, è consigliabile scrivere un presentatore di plug-in per il renderer di mix video (VMR) o Il renderer video avanzato (EVR). Questo approccio offre tutti i vantaggi offerti da VMR/EVR, incluso il supporto per l'accelerazione video DirectX (DXVA), l'esecuzione deinterlacing hardware e l'esecuzione di fotogrammi e probabilmente sarà più affidabile di un renderer video personalizzato. Per altre informazioni, vedere gli argomenti seguenti:

 

Scrittura di un renderer alternativo

Microsoft DirectShow offre un renderer video basato su finestre; fornisce anche un renderer a schermo intero nell'installazione in fase di esecuzione. È possibile usare le classi di base DirectShow per scrivere renderer video alternativi. Per consentire ai renderer alternativi di interagire correttamente con le applicazioni basate su DirectShow, i renderer devono rispettare le linee guida descritte in questo articolo. È possibile usare le classi CBaseRenderer e CBaseVideoRenderer per seguire queste linee guida quando si implementa un rendering video alternativo. A causa dello sviluppo in corso di DirectShow, esaminare periodicamente l'implementazione per assicurarsi che i renderer siano compatibili con la versione più recente di DirectShow.

Questo argomento illustra molte notifiche che un renderer è responsabile della gestione. Una breve revisione delle notifiche di DirectShow può aiutare a impostare la fase. Esistono essenzialmente tre tipi di notifiche che si verificano in DirectShow:

  • Notifiche di flusso, che sono eventi che si verificano nel flusso multimediale e vengono passati da un filtro al successivo. Queste possono essere di tipo begin-flushing, end-flushing o notifiche end-of-stream e vengono inviate chiamando il metodo appropriato sul pin di input del filtro downstream (ad esempio IPin::BeginFlush).
  • Filtrare le notifiche del grafico, che sono eventi inviati da un filtro a Filter Graph Manager, ad esempio EC_COMPLETE. Questa operazione viene eseguita chiamando il metodo IMediaEventSink::Notify in Filter Graph Manager.
  • Notifiche dell'applicazione recuperate da Filter Graph Manager dall'applicazione di controllo. Un'applicazione chiama il metodo IMediaEvent::GetEvent in Filter Graph Manager per recuperare questi eventi. Spesso, Filter Graph Manager passa gli eventi ricevuti all'applicazione.

Questo argomento illustra la responsabilità del filtro del renderer nella gestione delle notifiche di flusso ricevute e nell'invio di notifiche di grafo di filtro appropriate.

Gestione delle notifiche end-of-stream e scaricamento

Una notifica end-of-stream inizia a un filtro upstream (ad esempio il filtro di origine) quando tale filtro rileva che non può inviare più dati. Viene passato attraverso ogni filtro nel grafico e termina infine nel renderer, responsabile dell'invio successiva di una notifica di EC_COMPLETE a Filter Graph Manager. I renderer hanno responsabilità speciali quando si tratta di gestire queste notifiche.

Un renderer riceve una notifica end-of-stream quando il relativo metodo IPin::EndOfStream viene chiamato dal filtro upstream. Un renderer deve notare questa notifica e continuare a eseguire il rendering di tutti i dati già ricevuti. Una volta ricevuti tutti i dati rimanenti, il renderer deve inviare una notifica EC_COMPLETE a Filter Graph Manager. La notifica EC_COMPLETE deve essere inviata una sola volta da un renderer ogni volta che raggiunge la fine di un flusso. Inoltre, le notifiche EC_COMPLETE non devono mai essere inviate tranne quando il grafico del filtro è in esecuzione. Pertanto, se il grafico del filtro viene sospeso quando un filtro di origine invia una notifica end-of-stream, EC_COMPLETE non deve essere inviato finché il grafico del filtro non viene infine eseguito.

Tutte le chiamate ai metodi IMemInputPin::Receive o IMemInputPin::ReceiveMultiple dopo che deve essere rifiutata una notifica end-of-stream. E_UNEXPECTED è il messaggio di errore più appropriato da restituire in questo caso.

Quando viene arrestato un grafico di filtro, è necessario cancellare qualsiasi notifica end-of-stream memorizzata nella cache e non riscrivire al successivo avvio. Ciò avviene perché Filter Graph Manager sospende sempre tutti i filtri prima di eseguirli in modo che si verifichino lo scaricamento corretto. Ad esempio, se il grafico del filtro viene sospeso e viene ricevuta una notifica end-of-stream e quindi il grafico del filtro viene arrestato, il renderer non deve inviare una notifica di EC_COMPLETE quando viene eseguita successivamente. Se non si è verificato alcun tentativo, il filtro di origine invierà automaticamente un'altra notifica end-of-stream durante lo stato di sospensione che precede uno stato di esecuzione. Se, d'altra parte, si è verificato un tentativo durante l'arresto del grafico del filtro, il filtro di origine potrebbe avere dati da inviare, quindi non invierà una notifica end-of-stream.

I renderer video dipendono spesso dalle notifiche end-of-stream per più dell'invio di notifiche di EC_COMPLETE . Ad esempio, se un flusso ha completato la riproduzione ,ovvero, viene inviata una notifica end-of-stream e un'altra finestra viene trascinata su una finestra del renderer video, verrà generato un numero di messaggi di finestra WM_PAINT . La procedura tipica per l'esecuzione di renderer video consiste nell'evitare di riassegnare il frame corrente al momento della ricezione di messaggi di WM_PAINT (in base al presupposto che venga ricevuto un altro frame da disegnare). Tuttavia, quando è stata inviata la notifica end-of-stream, il renderer si trova in uno stato di attesa; è ancora in esecuzione, ma è consapevole che non riceverà dati aggiuntivi. In queste circostanze, il renderer disegna in modo personalizzato l'area di riproduzione nera.

La gestione dello scaricamento è una complicazione aggiuntiva per i renderer. Lo scaricamento viene eseguito tramite una coppia di metodi IPin denominati BeginFlush e EndFlush. Il download è essenzialmente uno stato aggiuntivo che il renderer deve gestire. È illegale che un filtro di origine chiami BeginFlush senza chiamare EndFlush, quindi lo stato è breve e discreto; tuttavia, il renderer deve gestire correttamente i dati o le notifiche ricevuti durante la transizione di scaricamento.

Tutti i dati ricevuti dopo aver chiamato BeginFlush devono essere rifiutati immediatamente restituendo S_FALSE. Inoltre, qualsiasi notifica end-of-stream memorizzata nella cache deve essere cancellata anche quando viene scaricato un renderer. Un renderer verrà in genere scaricato in risposta a una ricerca. Lo scaricamento garantisce che i dati precedenti vengano cancellati dal grafico del filtro prima che vengano inviati esempi nuovi. In genere, la riproduzione di due sezioni di un flusso, una dopo l'altra, viene gestita in modo ottimale tramite comandi posticipati anziché attendere il completamento di una sezione e quindi l'emissione di un comando di ricerca.

Gestione delle modifiche dello stato e sospensione del completamento

Un filtro del renderer si comporta come qualsiasi altro filtro nel grafico del filtro quando lo stato viene modificato, con l'eccezione seguente. Dopo essere stato sospeso, il renderer avrà alcuni dati accodati, pronto per essere eseguito successivamente. Quando il renderer video viene arrestato, contiene i dati in coda. Si tratta di un'eccezione alla regola DirectShow che non deve essere mantenuta da filtri mentre il grafico del filtro viene arrestato.

Il motivo di questa eccezione è che tenendo le risorse, il renderer avrà sempre un'immagine con cui eseguire il repository della finestra se riceve un messaggio di WM_PAINT . Include anche un'immagine per soddisfare i metodi, ad esempio CBaseControlVideo::GetStaticImage, che richiedono una copia dell'immagine corrente. Un altro effetto della conservazione delle risorse consiste nel fatto che l'esecuzione dell'immagine impedisce il decommesso dell'allocatore, che a sua volta rende più veloce la modifica dello stato successivo perché i buffer di immagine sono già allocati.

Un renderer video deve eseguire il rendering e rilasciare esempi solo durante l'esecuzione. Durante la pausa, il filtro potrebbe eseguirne il rendering(ad esempio, quando si disegna un'immagine del poster statico in una finestra), ma non deve rilasciarli. I renderer audio non eseguiranno alcun rendering durante la pausa (anche se possono eseguire altre attività, ad esempio la preparazione del dispositivo wave). Il tempo in cui è necessario eseguire il rendering degli esempi viene ottenuto combinando il tempo di flusso nell'esempio con il tempo di riferimento passato come parametro al metodo IMediaControl::Run . I renderer devono rifiutare esempi con orari di inizio inferiori o uguali agli orari di fine.

Quando un'applicazione sospende un grafico di filtro, il grafico del filtro non restituisce dal relativo metodo IMediaControl::P ause fino a quando non è presente una coda di dati nei renderer. Per garantire questo problema, quando viene sospeso un renderer, deve restituire S_FALSE se non sono presenti dati in attesa del rendering. Se ha accodato dati, può restituire S_OK.

Filter Graph Manager controlla tutti i valori restituiti durante la sospensione di un grafico di filtro per assicurarsi che i renderer abbiano accodato i dati. Se uno o più filtri non sono pronti, Filter Graph Manager esegue il polling dei filtri nel grafico chiamando IMediaFilter::GetState. Il metodo GetState accetta un parametro di timeout. Un filtro (in genere un renderer) che è ancora in attesa dell'arrivo dei dati prima di completare la modifica dello stato restituisce VFW_S_STATE_INTERMEDIATE se il metodo GetState scade. Una volta che i dati arrivano al renderer, GetState deve essere restituito immediatamente con S_OK.

Nello stato intermedio e completato, lo stato del filtro segnalato sarà State_Paused. Solo il valore restituito indica se il filtro è davvero pronto o meno. Se, mentre un renderer è in attesa dell'arrivo dei dati, il relativo filtro di origine invia una notifica end-of-stream, che dovrebbe anche completare la modifica dello stato.

Dopo aver eseguito il rendering di tutti i filtri, il grafico del filtro completerà la modifica dello stato di sospensione.

Gestione della terminazione

I renderer video devono gestire correttamente gli eventi di terminazione dall'utente. Ciò implica nascondere correttamente la finestra e sapere cosa fare se una finestra viene successivamente forzata a essere visualizzata. Inoltre, i renderer video devono notificare a Filter Graph Manager quando la finestra viene eliminata (o più accuratamente, quando il renderer viene rimosso dal grafico del filtro) per liberare risorse.

Se l'utente chiude la finestra video (ad esempio premendo ALT+F4), la convenzione consiste nel nascondere immediatamente la finestra e inviare una notifica EC_USERABORT a Filter Graph Manager. Questa notifica viene passata all'applicazione, che interromperà la riproduzione del grafico. Dopo l'invio di EC_USERABORT, un renderer video deve rifiutare eventuali esempi aggiuntivi recapitati.

Il flag arrestato dal grafico deve essere lasciato dal renderer fino a quando non viene successivamente arrestato, a quel punto deve essere reimpostato in modo che un'applicazione possa eseguire l'override dell'azione dell'utente e continuare a riprodurre il grafico se desidera. Se ALT+F4 viene premuto mentre il video è in esecuzione, la finestra verrà nascosta e tutti gli altri esempi recapitati verranno rifiutati. Se la finestra viene visualizzata successivamente (forse tramite IVideoWindow::p ut_Visible), non deve essere generata alcuna notifica EC_REPAINT .

Il renderer video deve anche inviare la notifica di EC_WINDOW_DESTROYED al grafico del filtro quando il renderer video termina. In effetti, è consigliabile gestire questo problema quando il metodo IBaseFilter::JoinFilterGraph del renderer viene chiamato con un parametro Null (che indica che il renderer sta per essere rimosso dal grafico del filtro), anziché attendere fino a quando non viene eliminata la finestra video effettiva. L'invio di questa notifica consente al server di distribuzione plug-in in Filter Graph Manager di passare le risorse che dipendono dallo stato attivo della finestra ad altri filtri, ad esempio dispositivi audio.

Gestione delle modifiche al formato dinamico

In alcuni casi, il filtro upstream del renderer potrebbe provare a modificare il formato video durante la riproduzione del video. È più spesso il decompressor video che avvia una modifica dinamica del formato.

Un filtro upstream che tenta di modificare i formati in modo dinamico deve sempre chiamare il metodo IPin::QueryAccept nel pin di input del renderer. Un renderer video ha un'ampia gamma di informazioni su quali tipi di formato dinamico cambiano. Almeno, dovrebbe consentire al filtro upstream di modificare le tavolozze. Quando un filtro upstream modifica i tipi di supporti, collega il tipo di supporto al primo esempio recapitato nel nuovo formato. Se il renderer contiene esempi in una coda per il rendering, non deve modificare il formato finché non esegue il rendering dell'esempio con la modifica del tipo.

Un renderer video può anche richiedere una modifica del formato dal decodificatore. Ad esempio, potrebbe chiedere al decodificatore di fornire un formato compatibile con DirectDraw con un biHeight negativo. Quando il renderer viene sospeso, deve chiamare QueryAccept sul pin upstream per vedere quali formati possono fornire il decodificatore. Il decodificatore potrebbe non enumerare tutti i tipi che può accettare, tuttavia, in modo che il renderer debba offrire alcuni tipi anche se il decodificatore non li annuncia.

Se il decodificatore può passare al formato richiesto, restituisce S_OK da QueryAccept. Il renderer associa quindi il nuovo tipo di supporto all'esempio multimediale successivo nell'allocatore upstream. Per il funzionamento, il renderer deve fornire un allocatore personalizzato che implementa un metodo privato per collegare il tipo di supporto all'esempio successivo. In questo metodo privato chiamare IMediaSample::SetMediaType per impostare il tipo.

Il pin di input del renderer deve restituire l'allocatore personalizzato del renderer nel metodo IMemInputPin::GetAllocator . Eseguire l'override di IMemInputPin::NotifyAllocator in modo che il filtro upstream non usi l'allocatore del renderer.

Con alcuni decodificatori, impostando biHeight su un numero positivo sui tipi YUV, il decodificatore determina il disegno dell'immagine sottosopra. Questo errore non è corretto e deve essere considerato un bug nel decodificatore.

Ogni volta che viene rilevata una modifica del formato dal renderer video, deve inviare una notifica di EC_DISPLAY_CHANGED . La maggior parte dei renderer video seleziona un formato durante la connessione in modo che il formato possa essere disegnato in modo efficiente tramite GDI. Se l'utente modifica la modalità di visualizzazione corrente senza riavviare il computer, un renderer potrebbe trovarsi con una connessione di formato immagine non valido e deve inviare questa notifica. Il primo parametro deve essere il pin che deve riconnettersi. Filter Graph Manager prevede che il grafico del filtro venga arrestato e che il pin venga riconnesso. Durante la riconnessione successiva, il renderer può accettare un formato più appropriato.

Ogni volta che un renderer video rileva una modifica della tavolozza nel flusso, deve inviare la notifica EC_PALETTE_CHANGED a Filter Graph Manager. I renderer video DirectShow rilevano se una tavolozza è effettivamente cambiata in formato dinamico o meno. Il renderer video esegue questa operazione non solo per filtrare il numero di notifiche EC_PALETTE_CHANGED inviate, ma anche per ridurre la quantità di creazione, installazione ed eliminazione del tavolozza necessaria.

Infine, il renderer video potrebbe anche rilevare che le dimensioni del video sono state modificate, in tal caso, deve inviare la notifica di EC_VIDEO_SIZE_CHANGED . Un'applicazione potrebbe usare questa notifica per negoziare lo spazio in un documento composto. Le dimensioni video effettive sono disponibili tramite l'interfaccia di controllo IBasicVideo . I renderer DirectShow rilevano se il video ha effettivamente modificato le dimensioni o meno prima di inviare questi eventi.

Gestione delle proprietà persistenti

Tutte le proprietà impostate tramite le interfacce IBasicVideo e IVideoWindow devono essere persistenti tra le connessioni. Pertanto, la disconnessione e la riconnessione di un renderer non devono mostrare effetti sulle dimensioni della finestra, sulla posizione o sugli stili. Tuttavia, se le dimensioni video cambiano tra le connessioni, il renderer deve reimpostare i rettangoli di origine e di destinazione ai valori predefiniti. Le posizioni di origine e destinazione vengono impostate tramite l'interfaccia IBasicVideo .

Sia IBasicVideo che IVideoWindow forniscono un accesso sufficiente alle proprietà per consentire a un'applicazione di salvare e ripristinare tutti i dati nell'interfaccia in un formato persistente. Ciò sarà utile per le applicazioni che devono salvare la configurazione esatta e le proprietà dei grafici di filtro durante una sessione di modifica e ripristinarli in un secondo momento.

Gestione delle notifiche di EC_REPAINT

La notifica di EC_REPAINT viene inviata solo quando il renderer viene sospeso o arrestato. Questa notifica segnala a Filter Graph Manager che il renderer necessita di dati. Se il grafico del filtro viene arrestato quando riceve una di queste notifiche, sospende il grafico del filtro, attendere che tutti i filtri ricevano i dati (chiamando GetState) e quindi arrestarlo di nuovo. Quando si arresta, un renderer video deve essere premuto sull'immagine in modo che i messaggi successivi WM_PAINT possano essere gestiti.

Pertanto, se un renderer video riceve un messaggio di WM_PAINT quando viene arrestato o sospeso e non ha nulla con cui disegnare la finestra, deve inviare EC_REPAINT a Filter Graph Manager. Se viene ricevuta una notifica di EC_REPAINT durante la sospensione, Filter Graph Manager chiama IMediaPosition::p ut_CurrentPosition con la posizione corrente, ovvero cerca la posizione corrente. In questo modo, i filtri di origine scaricano il grafico del filtro e causano l'invio di nuovi dati tramite il grafico del filtro.

Un renderer deve inviare una sola di queste notifiche alla volta. Pertanto, una volta che il renderer invia una notifica, deve assicurarsi che non vengano inviati più fino a quando non vengono recapitati alcuni esempi. Il modo convenzionale per eseguire questa operazione consiste nell'avere un flag per firmare che un repository può essere inviato, che viene disattivato dopo l'invio di una notifica di EC_REPAINT . Questo flag deve essere reimpostato dopo che i dati vengono recapitati o quando il pin di input viene scaricato, ma non se il pin di input viene segnalato sul pin di input.

Se il renderer non monitora le notifiche di EC_REPAINT , eseguirà l'inondazione di Filter Graph Manager con richieste di EC_REPAINT (che sono relativamente costose da elaborare). Ad esempio, se un renderer non dispone di un'immagine da disegnare e un'altra finestra viene trascinata nella finestra del renderer in un'operazione di trascinamento completo, il renderer riceve più messaggi di WM_PAINT . Solo il primo di questi deve generare una notifica di evento EC_REPAINT dal renderer a Filter Graph Manager.

Un renderer deve inviare il pin di input come primo parametro alla notifica di EC_REPAINT . A tale scopo, il pin di output collegato verrà sottoposto a query per IMediaEventSink e, se supportato, verrà inviata prima la notifica di EC_REPAINT . Ciò consente ai pin di output di gestire le repaint prima che sia necessario toccare il grafico del filtro. Questa operazione non verrà eseguita se il grafico del filtro viene arrestato, perché nessun buffer sarebbe disponibile dall'allocatore del renderer decommesso.

Se il pin di output non riesce a gestire la richiesta e il grafico del filtro è in esecuzione, la notifica di EC_REPAINT viene ignorata. Un pin di output deve restituire S_OK da IMediaEventSink::Notify per segnalare che ha elaborato correttamente la richiesta di ripristino. Il pin di output verrà chiamato nel thread di lavoro Filter Graph Manager, che evita di avere il renderer chiamare direttamente il pin di output e quindi esegue il sidesteps di eventuali problemi di deadlock. Se il grafico del filtro viene arrestato o sospeso e l'output non gestisce la richiesta, viene eseguita l'elaborazione predefinita.

Gestione delle notifiche in modalità Full-Screen

Il plug-in IVideoWindow (PID) nel grafico del filtro gestisce la riproduzione a schermo intero. Scambia un renderer video per un renderer a schermo intero specializzato, estende una finestra di un renderer a schermo intero o implementa direttamente la riproduzione a schermo intero. Per interagire in protocolli a schermo intero, un renderer video deve inviare una notifica EC_ACTIVATE ogni volta che la finestra viene attivata o disattivata. In altre parole, deve essere inviata una notifica di EC_ACTIVATE per ogni WM_ACTIVATEAPP messaggio ricevuto da un renderer.

Quando viene usato un renderer in modalità schermo intero, queste notifiche gestiscono l'opzione e fuori dalla modalità schermo intero. La disattivazione della finestra si verifica in genere quando un utente preme ALT+TAB per passare a un'altra finestra, che il renderer a schermo intero DirectShow usa come segnale per tornare alla modalità di rendering tipica.

Quando la notifica EC_ACTIVATE viene inviata a Filter Graph Manager al passaggio dalla modalità a schermo intero, Filter Graph Manager invia una notifica EC_FULLSCREEN_LOST all'applicazione di controllo. L'applicazione potrebbe usare questa notifica per ripristinare lo stato di un pulsante a schermo intero, ad esempio. Le notifiche di EC_ACTIVATE vengono usate internamente da DirectShow per gestire l'attivazione a schermo intero dei segnali dai renderer video.

Riepilogo delle notifiche

In questa sezione sono elencate le notifiche del grafico di filtro che un renderer può inviare.

Notifica degli eventi Descrizione
EC_ACTIVATE Inviato dai renderer video in modalità di rendering a schermo intero per ogni messaggio WM_ACTIVATEAPP ricevuto.
EC_COMPLETE Inviato dai renderer dopo il rendering di tutti i dati.
EC_DISPLAY_CHANGED Inviato dai renderer video quando un formato di visualizzazione cambia.
EC_PALETTE_CHANGED Inviato ogni volta che un renderer video rileva una modifica della tavolozza nel flusso.
EC_REPAINT Inviati da renderer video arrestati o sospesi quando viene ricevuto un messaggio di WM_PAINT e non sono presenti dati da visualizzare. In questo modo, Filter Graph Manager genera una cornice da disegnare al display.
EC_USERABORT Inviato dai renderer video per segnalare una chiusura che l'utente ha richiesto (ad esempio, un utente che chiude la finestra video).
EC_VIDEO_SIZE_CHANGED Inviati dai renderer video ogni volta che viene rilevata una modifica delle dimensioni del video native.
EC_WINDOW_DESTROYED Inviati dai renderer video quando il filtro viene rimosso o eliminato in modo che le risorse che dipendono dallo stato attivo della finestra possano essere passate ad altri filtri.

 

Scrittura di renderer video