Renderer video alternativi
[La funzionalità associata a questa pagina, DirectShow, è una funzionalità legacy. È stata sostituita da MediaPlayer, IMFMediaEnginee Acquisizione audio/video in Media Foundation. Queste funzionalità sono state ottimizzate per Windows 10 e Windows 11. Microsoft consiglia vivamente di usare un nuovo codice MediaPlayer, IMFMediaEngine e Acquisizione audio/video 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 relatore di plug-in per il renderer di mixaggio video (VMR) o Enhanced Video Renderer (EVR). Questo approccio offre tutti i vantaggi offerti da VMR/EVR, incluso il supporto per DirectX Video Acceleration (DXVA), la denterlacing hardware e l'esecuzione dei fotogrammi ed è probabilmente più affidabile di un renderer video personalizzato. Per altre informazioni, vedere gli argomenti seguenti:
Scrittura di un renderer alternativo
Microsoft DirectShow fornisce 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 classiCBaseRenderer e CBaseVideoRenderer per seguire queste linee guida quando si implementa un rendering video alternativo. A causa dello sviluppo continuo 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 DirectShow può essere utile per impostare la fase. Esistono essenzialmente tre tipi di notifiche che si verificano in DirectShow:
- notifiche di Stream, che sono eventi che si verificano nel flusso multimediale e vengono passati da un filtro all'altro. Queste notifiche possono essere di avvio, scaricamento finale o 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 grafo, che sono eventi inviati da un filtro a Filter Graph Manager, ad esempio EC_COMPLETE. A tale scopo, chiamare 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 attraverso gli eventi ricevuti all'applicazione.
In questo argomento viene descritta la responsabilità del filtro del renderer nella gestione delle notifiche di flusso ricevute e nell'invio di notifiche del grafo di filtro appropriate.
Gestione delle notifiche di fine flusso e scaricamento
Una notifica di fine flusso inizia in corrispondenza di un filtro upstream (ad esempio il filtro di origine) quando tale filtro rileva che non può inviare altri dati. Viene passato attraverso ogni filtro nel grafico e termina infine al renderer, che è responsabile dell'invio successivo 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 di fine flusso quando il relativo pin di input metodo IPin::EndOfStream viene chiamato dal filtro upstream. Un renderer deve prendere nota di questa notifica e continuare a eseguire il rendering dei 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, EC_COMPLETE le notifiche 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 di fine flusso, EC_COMPLETE non deve essere inviato finché il grafico del filtro non viene infine eseguito.
Qualsiasi chiamata ai metodi IMemInputPin::Receiveo IMemInputPin::ReceiveMultiple dopo che viene segnalata una notifica di fine flusso deve essere rifiutata. E_UNEXPECTED è il messaggio di errore più appropriato da restituire in questo caso.
Quando un grafico di filtro viene arrestato, qualsiasi notifica di fine flusso memorizzata nella cache deve essere cancellata e non deve essere reinviata all'avvio successivo. Questo avviene perché Filter Graph Manager sospende sempre tutti i filtri appena prima di eseguirli in modo che si verifichi un corretto scaricamento. Ad esempio, se il grafico del filtro viene sospeso e viene ricevuta una notifica di fine flusso e quindi il grafico del filtro viene arrestato, il renderer non deve inviare una notifica EC_COMPLETE quando viene eseguita successivamente. Se non è stata eseguita alcuna ricerca, il filtro di origine invierà automaticamente un'altra notifica di fine flusso durante lo stato di sospensione che precede uno stato di esecuzione. Se, d'altra parte, si è verificata una ricerca mentre il grafico del filtro è arrestato, il filtro di origine potrebbe avere dati da inviare, quindi non invierà una notifica di fine flusso.
I renderer video spesso dipendono dalle notifiche end-of-stream per più dell'invio di notifiche EC_COMPLETE. Ad esempio, se un flusso ha terminato la riproduzione (ovvero, viene inviata una notifica di fine flusso) e un'altra finestra viene trascinata su una finestra del renderer video, verranno generati diversi messaggi della finestra di WM_PAINT. La procedura tipica per l'esecuzione di renderer video consiste nell'evitare di ridisegnare il fotogramma corrente al ricevimento di WM_PAINT messaggi (in base al presupposto che venga ricevuto un altro fotogramma da disegnare). Tuttavia, quando è stata inviata la notifica di fine del flusso, il renderer è 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 di IPin denominati BeginFlush e EndFlush. Lo scaricamento è essenzialmente uno stato aggiuntivo che il renderer deve gestire. È illegale che un filtro di origine chiami BeginFlush senza chiamare EndFlush, quindi speriamo che lo stato sia breve e discreto; Tuttavia, il renderer deve gestire correttamente i dati o le notifiche ricevute durante la transizione di scaricamento.
Tutti i dati ricevuti dopo la chiamata BeginFlush devono essere rifiutati immediatamente restituendo S_FALSE. Inoltre, qualsiasi notifica di fine flusso 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 campioni aggiornati. In genere, la riproduzione di due sezioni di un flusso, una dopo l'altra, viene gestita meglio tramite comandi posticipati anziché attendere il completamento di una sezione e quindi l'esecuzione di un comando seek.
Gestione delle modifiche dello stato e sospensione del completamento
Un filtro 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 in coda, pronti per essere sottoposti a rendering quando successivamente vengono eseguiti. Quando il renderer video viene arrestato, mantiene i dati in coda. Si tratta di un'eccezione alla regola DirectShow che non deve essere mantenuta dai filtri mentre il grafico del filtro viene arrestato.
Il motivo di questa eccezione è che mantenendo le risorse, il renderer avrà sempre un'immagine con cui ridipingere la 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 memorizzazione delle risorse è che il mantenimento dell'immagine impedisce che l'allocatore venga decommesso, che a sua volta rende la modifica dello stato successiva molto più veloce perché i buffer di immagine sono già allocati.
Un renderer video deve eseguire il rendering e rilasciare esempi solo durante l'esecuzione. Mentre è in pausa, il filtro potrebbe eseguirne il rendering (ad esempio, quando si disegna un'immagine poster statica in una finestra), ma non deve rilasciarli. I renderer audio non eseguiranno alcun rendering mentre sono sospesi (anche se possono eseguire altre attività, ad esempio la preparazione del dispositivo wave). L'ora in cui deve essere eseguito il rendering degli esempi viene ottenuta combinando l'ora del flusso nell'esempio con il tempo di riferimento passato come parametro al metodo IMediaControl::Run. I renderer devono rifiutare campioni con orari di inizio inferiori o uguali all'ora di fine.
Quando un'applicazione sospende un grafico di filtro, il grafico del filtro non restituisce il relativo metodo IMediaControl::P ause fino a quando non sono presenti dati in coda nei renderer. Per garantire questo problema, quando un renderer viene sospeso, deve restituire S_FALSE se non sono presenti dati in attesa di essere sottoposti a rendering. Se contiene dati in coda, 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 dati in coda. 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) in attesa dell'arrivo dei dati prima di completare la modifica dello stato restituisce VFW_S_STATE_INTERMEDIATE se il metodo GetState scade. Quando i dati arrivano al renderer, il GetState deve essere restituito immediatamente con S_OK.
Sia nello stato intermedio che in quello completato, lo stato del filtro segnalato verrà State_Paused. Solo il valore restituito indica se il filtro è effettivamente pronto o meno. Se, mentre un renderer è in attesa dell'arrivo dei dati, il filtro di origine invia una notifica di fine flusso, che dovrebbe anche completare la modifica dello stato.
Dopo che tutti i filtri hanno effettivamente dati in attesa di essere sottoposti a rendering, 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 la corretta nascondere 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 definitivamente (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 campioni aggiuntivi recapitati.
Il flag arrestato dal grafico deve essere lasciato dal renderer fino a quando non viene arrestato successivamente, 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 lo desidera. Se ALT+F4 viene premuto mentre il video è in esecuzione, la finestra verrà nascosta e tutti gli altri campioni recapitati verranno rifiutati. Se la finestra viene visualizzata successivamente (ad esempio tramite IVideoWindow::p ut_Visible), non devono essere generate notifiche EC_REPAINT.
Il renderer video deve anche inviare la notifica EC_WINDOW_DESTROYED al grafico del filtro quando il renderer video termina. In effetti, è preferibile gestirlo quando il renderer IBaseFilter::JoinFilterGraph metodo viene chiamato con un parametro Null (a indicare che il renderer sta per essere rimosso dal grafico del filtro), anziché attendere che la finestra video effettiva venga eliminata definitivamente. L'invio di questa notifica consente al server di distribuzione plug-in di Filter Graph Manager di passare le risorse che dipendono dallo stato attivo della finestra ad altri filtri, ad esempio i 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 decompressore 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 sul pin di input del renderer. Un renderer video ha un po' di tempo per quanto riguarda i tipi di formato dinamico che deve supportare. 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 campione distribuito nel nuovo formato. Se il renderer contiene campioni 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 negativo. Quando il renderer viene sospeso, deve chiamare QueryAccept sul pin upstream per vedere quali formati possono essere forniti dal decodificatore. Il decodificatore potrebbe non enumerare tutti i tipi che può accettare, pertanto il renderer deve 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 IMemInputPin::NotifyAllocator in modo che non riesca se il filtro upstream non usa l'allocatore del renderer.
Con alcuni decodificatori, impostando biHeight su un numero positivo sui tipi YUV, il decodificatore di disegnare l'immagine capovolto. Questo 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 valida e deve inviare questa notifica. Il primo parametro deve essere il pin che deve riconnettersi. Filter Graph Manager consente di arrestare il grafico del filtro e di riconnettersi. 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 di EC_PALETTE_CHANGED a Filter Graph Manager. I renderer video DirectShow rilevano se una tavolozza è effettivamente cambiata in formato dinamico o meno. I renderer video eseguono 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 riquadro necessarie.
Infine, il renderer video potrebbe anche rilevare che le dimensioni del video sono state modificate, nel qual caso dovrebbe inviare la notifica 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. Di conseguenza, la disconnessione e la riconnessione di un renderer non dovrebbero 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 sui valori predefiniti. Le posizioni di origine e di destinazione vengono impostate tramite l'interfaccia IBasicVideo.
Sia IBasicVideo che IVideoWindow forniscono accesso sufficiente alle proprietà per consentire a un'applicazione di salvare e ripristinare tutti i dati nell'interfaccia in un formato permanente. 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 EC_REPAINT
La notifica 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, attende che tutti i filtri ricevano i dati (chiamando GetState) e quindi arrestarlo di nuovo. In caso di arresto, un renderer video deve tenere premuto l'immagine in modo che i successivi WM_PAINT messaggi 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 mentre è in pausa, 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 dei filtri e causano l'invio di nuovi dati tramite il grafico del filtro.
Un renderer deve inviare una sola di queste notifiche alla volta. Pertanto, dopo che il renderer invia una notifica, deve assicurarsi che non vengano più inviati fino a quando non vengono recapitati alcuni esempi. Il modo convenzionale per eseguire questa operazione consiste nell'avere un flag per indicare che è possibile inviare un repository, che viene disattivato dopo l'invio di una notifica di EC_REPAINT. Questo flag deve essere reimpostato una volta recapitati i dati o quando il pin di input viene scaricato, ma non se la fine del flusso viene segnalata sul pin di input.
Se il renderer non monitora le relative notifiche EC_REPAINT, inonda Il gestore di filtri graph 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 WM_PAINT. Solo il primo di questi deve generare una notifica degli eventi EC_REPAINT dal renderer a Filter Graph Manager.
Un renderer deve inviare il pin di input come primo parametro alla notifica di EC_REPAINT. In questo modo, il pin di output associato verrà sottoposto a query per IMediaEventSinke, se supportato, la notifica di EC_REPAINT verrà inviata per prima. In questo modo, i pin di output possono gestire i riquadri prima che il grafico del filtro venga toccato. 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 è in grado di 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 aggiornamento. Il pin di output verrà chiamato nel thread di lavoro di Filter Graph Manager, evitando così che il renderer chiami direttamente il pin di output e quindi esegue il laterale di eventuali problemi di deadlock. Se il grafico del filtro viene arrestato o sospeso e l'output non gestisce la richiesta, l'elaborazione predefinita viene eseguita.
Gestione delle notifiche in modalità Full-Screen
Il IVideoWindow distributore di plug-in (PID) nel grafico dei filtri gestisce la riproduzione a schermo intero. Scambierà un renderer video per un renderer a schermo intero specializzato, estenderà una finestra di un renderer a schermo intero o avrà il renderer implementare direttamente la riproduzione a schermo intero. Per interagire con 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 un renderer viene usato in modalità schermo intero, queste notifiche gestiscono il passaggio alla modalità schermo intero e viceversa. 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 quando si disattiva la modalità 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 | Inviati dai renderer video in modalità di rendering a schermo intero per ogni WM_ACTIVATEAPP messaggio ricevuto. |
EC_COMPLETE | Inviato dai renderer dopo il rendering di tutti i dati. |
EC_DISPLAY_CHANGED | Inviato dai renderer video quando viene modificato un formato di visualizzazione. |
EC_PALETTE_CHANGED | Inviato ogni volta che un renderer video rileva una modifica della tavolozza nel flusso. |
EC_REPAINT | Inviato 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 per la visualizzazione. |
EC_USERABORT | Inviati 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 | Inviato dai renderer video ogni volta che viene rilevata una modifica delle dimensioni del video nativo. |
EC_WINDOW_DESTROYED | Inviati dai renderer video quando il filtro viene rimosso o eliminato definitivamente in modo che le risorse che dipendono dallo stato attivo della finestra possano essere passate ad altri filtri. |
Argomenti correlati