Esempi e allocatori

[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.

Quando un pin distribuisce i dati multimediali a un altro pin, non passa un puntatore diretto al buffer di memoria. Distribuisce invece un puntatore a un oggetto COM che gestisce la memoria. Questo oggetto, denominato esempio multimediale, espone l'interfaccia IMediaSample . Il pin ricevente accede al buffer di memoria chiamando i metodi IMediaSample , ad esempio IMediaSample::GetPointer, IMediaSample::GetSize e IMediaSample::GetActualDataLength.

I campioni viaggiano sempre a valle, dal pin di output al pin di input. Nel modello push il pin di output recapita un esempio chiamando IMemInputPin::Receive sul pin di input. Il pin di input elabora i dati in modo sincrono (ovvero completamente all'interno del metodo Receive ) o lo elabora in modo asincrono in un thread di lavoro. Il pin di input può bloccarsi all'interno del metodo Receive , se deve attendere le risorse.

Un altro oggetto COM, denominato allocatore, è responsabile della creazione e della gestione degli esempi multimediali. Gli allocatori espongono l'interfaccia IMemAllocator . Ogni volta che un filtro richiede un campione multimediale con un buffer vuoto, chiama il metodo IMemAllocator::GetBuffer , che restituisce un puntatore all'esempio. Ogni connessione pin condivide un allocatore. Quando due pin si connettono, decidono quale filtro fornirà l'allocatore. I pin impostano anche le proprietà sull'allocatore, ad esempio il numero di buffer e le dimensioni di ogni buffer. Per informazioni dettagliate, vedere Come i filtri si connettono e negoziano gli allocatori.

La figura seguente mostra le relazioni tra l'allocatore, gli esempi multimediali e il filtro.

campioni multimediali e allocatori

Conteggi dei riferimenti di esempio multimediali

Un allocatore crea un pool finito di campioni. In qualsiasi momento, alcuni esempi possono essere in uso, mentre altri sono disponibili per le chiamate GetBuffer . L'allocatore usa il conteggio dei riferimenti per tenere traccia dei campioni. Il metodo GetBuffer restituisce un esempio con un conteggio dei riferimenti pari a 1. Se il conteggio dei riferimenti va a zero, l'esempio torna nel pool dell'allocatore, dove può essere usato nella chiamata GetBuffer successiva. Se il conteggio dei riferimenti rimane superiore a zero, l'esempio non è disponibile per GetBuffer. Se ogni campione appartenente all'allocatore è in uso, il metodo GetBuffer si blocca fino a quando non diventa disponibile un campione.

Si supponga, ad esempio, che un pin di input riceva un esempio. Se elabora il campione in modo sincrono, all'interno del metodo Receive , non incrementa il conteggio dei riferimenti. Al termine di Receive , il pin di output rilascia l'esempio, il conteggio dei riferimenti passa a zero e l'esempio torna al pool dell'allocatore. D'altra parte, se il pin di input elabora l'esempio in un thread di lavoro, incrementa il conteggio dei riferimenti prima di uscire dal metodo Receive . Il conteggio dei riferimenti è ora 2. Quando il pin di output rilascia l'esempio, il conteggio passa a 1; l'esempio non torna ancora al pool. Al termine del thread di lavoro con l'esempio, chiama Release per liberare l'esempio. Ora l'esempio torna al pool.

Quando un pin riceve un esempio, può copiare i dati in un altro esempio oppure modificare l'esempio originale e recapitarlo al filtro successivo. Potenzialmente, un campione può percorrere l'intera lunghezza del grafico, ogni filtro che chiama AddRef e Release a sua volta. Pertanto, il pin di output non deve mai riutilizzare un esempio dopo la chiamata a Receive, perché un filtro downstream può usare l'esempio. Il pin di output deve sempre chiamare GetBuffer per ottenere un nuovo esempio.

Questo meccanismo riduce la quantità di allocazione di memoria, perché i filtri usano nuovamente gli stessi buffer. Impedisce inoltre ai filtri di scrivere accidentalmente sui dati che non sono stati elaborati, perché l'allocatore mantiene un elenco di esempi disponibili.

Un filtro può usare allocatori separati per l'input e l'output. Questa operazione può essere eseguita se espande i dati di input, ad esempio decompresse. Se l'output non è maggiore dell'input, un filtro potrebbe elaborare i dati sul posto, senza copiarlo in un nuovo esempio. In tal caso, due o più connessioni pin possono condividere un allocatore.

Commit e decommitazione di allocatori

Quando un filtro crea per la prima volta un allocatore, l'allocatore non ha riservato alcun buffer di memoria. A questo punto, tutte le chiamate al metodo GetBuffer avranno esito negativo. All'avvio del flusso, il pin di output chiama IMemAllocator::Commit, che esegue il commit dell'allocatore, causando l'allocazione della memoria. I pin possono ora chiamare GetBuffer.

Quando lo streaming si arresta, il pin chiama IMemAllocator::D ecommit, che decommette l'allocatore. Tutte le chiamate successive a GetBuffer hanno esito negativo fino a quando non viene nuovamente eseguito il commit dell'allocatore. Inoltre, se le chiamate a GetBuffer sono attualmente bloccate in attesa di un esempio, restituiscono immediatamente un codice di errore. Il metodo Decommit può o non liberare la memoria, a seconda dell'implementazione. Ad esempio, la classe CMemAllocator attende fino a quando il relativo metodo distruttore non libera la memoria.

Flusso di dati nel grafico dei filtri