Come scrivere un filtro di origine per DirectShow

[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 filtro di origine personalizzato per DirectShow.

Nota

Questo argomento descrive solo le origini push; non descrive le origini pull, ad esempio il filtro lettore asincrono o i filtri splitter che si connettono alle origini pull. Per la distinzione tra origini push e pull, vedere Flusso di dati per sviluppatori di filtri.

Modello di streaming DirectShow

Quando si scrive un filtro di origine, è importante comprendere che un'origine push non è la stessa di un'origine live. Un'origine live ottiene dati da un'origine esterna, ad esempio una fotocamera o un flusso di rete. In genere, un'origine dinamica non può controllare la frequenza in ingresso dei dati. Se i filtri downstream non usano i dati abbastanza velocemente, l'origine dovrà eliminare esempi.

Ma un'origine push non deve essere un'origine live. Ad esempio, un'origine push può leggere i dati da un file locale. In tal caso, i filtri del renderer downstream determinano la velocità con cui utilizzano i dati dall'origine, in base all'orologio di riferimento e ai timestamp di esempio. Il filtro di origine fornisce esempi il più rapidamente possibile, ma il flusso di dati effettivo è limitato dai renderer. I meccanismi per la creazione di un flusso di dati sono descritti in Flusso di dati per gli sviluppatori di filtri.

Ogni pin di output nel filtro di origine crea un thread denominato thread di streaming. Il pin fornisce esempi nel thread di streaming. In genere, tutto il decodificamento, l'elaborazione e il rendering si verifica in questo thread, anche se alcuni filtri downstream potrebbero creare thread aggiuntivi per accodare gli esempi di output.

Il thread di streaming esegue un ciclo con la struttura seguente:

until (stopped)
  1. Get a media sample from the allocator.
  2. Fill the sample with data.
  3. Time stamp the sample. 
  4. Deliver the sample downstream.

Se non sono disponibili esempi, il passaggio 1 blocca fino a quando non diventa disponibile un esempio. Il passaggio 4 può anche bloccare; ad esempio, può bloccare mentre il grafico viene sospeso.

Il ciclo viene eseguito il più rapidamente possibile, ma è limitato dalla velocità con cui il filtro del renderer esegue il rendering di ogni esempio. Supponendo che il grafico del filtro abbia un orologio di riferimento, la frequenza viene determinata dai tempi di presentazione degli esempi. Se non è presente un orologio di riferimento, il renderer usa i campioni il più rapidamente possibile.

Uso di CSource e CSourceStream

Le classi di base DirectShow includono due classi che supportano origini push: CSource e CSourceStream.

Pin di output

Un filtro di origine può avere più di un pin di output. Nel metodo del costruttore del filtro creare uno o più pin derivati da CSourceStream (un pin per flusso di output). Non è necessario archiviare puntatori ai pin; i pin si aggiungono automaticamente al filtro quando vengono creati.

Formati di output

Il pin di output gestisce la negoziazione del formato con i metodi CSourceStream seguenti:

Metodo Descrizione
GetMediaType Ottiene un tipo di supporto dal pin di output.
Il pin deve proporre almeno un tipo di supporto, perché il filtro downstream potrebbe non proporre alcun tipo. Nella maggior parte dei casi, il filtro downstream sarà un decodificatore o un renderer, a seconda che il filtro di origine fornisca dati compressi o non compressi. Un filtro renderer richiede in genere un tipo di supporto completo, contenente tutte le informazioni di formato necessarie per eseguire il rendering del flusso. Per un decodificatore, la quantità di informazioni necessarie nel tipo di supporto dipende molto dal formato di codifica.
CheckMediaType Verifica se il pin di output accetta un determinato tipo di supporto. L'override di questo metodo è facoltativa, a seconda della modalità di implementazione di GetMediaType.

Il metodo GetMediaType è in overload:

Se il pin di output del filtro di origine supporta esattamente un formato multimediale, è necessario eseguire l'override (1) per inizializzare l'oggetto CMediaType con tale formato. Lasciare l'implementazione predefinita di (2) e lasciare anche l'implementazione predefinita di CheckMediaType.

Se il pin supporta più formati, eseguire l'override (2). Inizializzare l'oggetto CMediaType in base al valore della variabile di indice. Il pin deve restituire i formati come elenco ordinato. In questo caso, è anche necessario eseguire l'override di CheckMediaType per controllare il tipo di supporto rispetto all'elenco di formati.

Per i formati video non compressi, tenere presente che il filtro downstream può proporre formati con vari valori di stride. Il filtro deve accettare qualsiasi valore di stride valido. Per altre informazioni, vedere BITMAPINFOHEADER.

È inoltre necessario eseguire l'override del metodo CBaseOutputPin::D ecideBufferSize . Usare questo metodo per impostare le dimensioni dei buffer di esempio.

Streaming

La classe CSourceStream crea il thread di streaming per il pin. La procedura del thread viene implementata nel metodo CSourceStream::D oBufferProcessingLoop . Questo metodo chiama il metodo CSourceStream::FillBuffer pure, che la classe derivata deve eseguire l'override. Questo metodo è il luogo in cui il pin riempie il buffer con dati. Ad esempio, se il filtro recapita video non compresso, si tratta della posizione in cui si disegnano i fotogrammi video.

La classe di base avvia automaticamente e arresta automaticamente il ciclo del thread al momento giusto, quando il filtro viene sospeso o arrestato. In questo caso, la classe CSourceStream chiama alcuni metodi per notificare alla classe derivata:

È possibile eseguire l'override di questi metodi se è necessario aggiungere una gestione speciale. In caso contrario, le implementazioni predefinite restituiscono semplicemente S_OK.

Riercare

Se si dispone di un filtro di origine con un pin di output, è possibile usare la classe CSourceSeeking come punto di partenza per implementare la ricerca. Ereditare la classe pin sia da CSourceStream che da CSourceSeeking.

Nota

CSourceSeeking non è consigliato per un filtro con più pin di output. Il problema principale è che un solo pin deve rispondere alle richieste di ricerca. In genere, questa operazione richiede la comunicazione tra i pin e il filtro.

La classe CSourceSeeking gestisce la frequenza di riproduzione, l'ora di inizio, l'ora di arresto e la durata. La classe derivata deve impostare il tempo di arresto iniziale e la durata. Ogni volta che uno di questi valori cambia, viene chiamato il metodo CSourceSeeking::ChangeRate, CSourceSeeking::ChangeStart o CSourceSeeking::ChangeStop. I metodi sono tutti metodi virtuali pure. La classe pin derivata esegue l'override di questi metodi per eseguire le operazioni seguenti:

  1. Chiamare IPin::BeginFlush sul pin downstream. In questo modo, i filtri downstream rilasciano esempi che contengono e rifiutano nuovi esempi.
  2. Chiamare CSourceStream::Stop per arrestare il thread di streaming. Il filtro di origine sospende la produzione di nuovi dati.
  3. Chiamare IPin::EndFlush sul pin downstream. Questo segnala i filtri downstream per accettare nuovi dati.
  4. Chiamare IPin::NewSegment con il nuovo orario di inizio e arresto e frequenza.
  5. Impostare la proprietà di discontinuità nell'esempio successivo.

Per altre informazioni, vedere Supporto della ricerca in un filtro di origine.

Se il filtro supporta la ricerca, la posizione del flusso è ora indipendente dall'ora di presentazione. Dopo una ricerca, i timestamp vengono reimpostati su zero. La formula generale per i timestamp è:

  • ora di inizio di esempio = tempo trascorso/frequenza di riproduzione
  • sample end time = sample start time + (time per frame/playback rate)

dove è trascorso il tempo trascorso è il tempo trascorso dall'avvio del filtro o dall'ultimo comando seek.

Formati di tempo per la ricerca

Per impostazione predefinita, i comandi seek sono in unità di 100 nanosecondi. Il filtro di origine può supportare formati di tempo aggiuntivi, ad esempio la ricerca in base al numero di fotogramma. Ogni volta che il formato viene identificato da un GUID; vedere GUID formato ora.

Per supportare formati di tempo aggiuntivi, è necessario implementare i metodi seguenti nel pin di output:

Se l'applicazione imposta un nuovo formato temporale, tutti i parametri di posizione nei metodi IMediaSeeking vengono interpretati in termini di nuovo formato ora. Ad esempio, se il formato temporale è frame, il metodo IMediaSeeking::GetDuration deve restituire la durata in fotogrammi.

In pratica, alcuni filtri DirectShow supportano formati di tempo aggiuntivi e, di conseguenza, poche applicazioni DirectShow usano questa funzionalità.

Scrittura di filtri di origine