Un'introduzione a Media Foundation: comprendere l'API attraverso una lente COM

· Aggiornato il: · · Media Foundation, COM, C++, Sviluppo Windows

Quando inizi a lavorare con Media Foundation, è facile pensare: “Pensavo di usare il video e l’audio di Windows API, e all’improvviso tutto riguarda COM.” CoInitializeEx, MFStartup, IMFSourceReader, IMFMediaType, IMFTransform, IMFActivate, HRESULT, GUIDs - arrivano tutti insieme, l’atmosfera cambia improvvisamente Win32 / COM, e cosa sia effettivamente Media Foundation diventa difficile da vedere.

Piuttosto che coprire tutto Media Foundation come un dizionario, questo articolo si concentra su tre cose.

  • Perché COM appare naturalmente quando usi Media Foundation?
  • Dove si intensifica esattamente il sapore del COM?
  • Da dove dovresti iniziare: Source Reader / Sink Writer / Media Session / MFT?

Gli esempi di codice sono basati su C++, ma il modo di pensare è essenzialmente lo stesso quando si toccano i wrapper da .NET e simili.

Sommario

  1. Prima la conclusione (in una riga)
  2. Le tabelle di orientamento da considerare per prime
    • 2.1. Cosa toccare per quello che vuoi fare
    • 2.2. Dove si mette la faccia COM
    • 2.3. Termini per comprendere il significato del primo
  3. Il quadro generale di Media Foundation (diagramma)
  4. Dove Media Foundation indossa la sua faccia COM
    • 4.1. CoInitializeEx e MFStartup Affiancati durante l’inizializzazione
    • 4.2. I trasferimenti di oggetti sono incentrati sull’interfaccia
    • 4.3. Vengono visualizzati gli oggetti di attivazione
    • 4.4. Impostazioni e tipo Centro informazioni su IMFAttributes e GUIDs
    • 4.5. Anche l’asincronia, i callback e il threading vengono gestiti nel modo COM
    • 4.6. Ma Media Foundation ≠ COM
  5. Una guida approssimativa alla scelta
    • 5.1. A partire dal Source Reader
    • 5.2. Scrittura su file con Sink Writer
    • 5.3. Gestire la riproduzione e la sincronizzazione con Media Session
    • 5.4. Inserimento di componenti personalizzati con MFTs
  6. Una lista di controllo pratica
  7. Estratti di codice
    • 7.1 Inizializzazione
    • 7.2 Creazione di un Source Reader in modalità sincrona
    • 7.3 Creazione di un Source Reader in modalità asincrona
    • 7.4 Enumerazione e istanziazione di MFTs con MFTEnumEx
  8. Conclusione
  9. Riferimenti

1. Prima la conclusione (in una riga)

  • Media Foundation è una piattaforma per la gestione di video e audio - l’API nel suo complesso non è, di per sé, pura COM
  • Tuttavia, i confini tra origine / trasformazione / sink / attivazione / attributi / callback sono espressi come interfacce COM, quindi quando lo usi, IUnknown, HRESULT, GUIDs e gli argomenti dell’appartamento emergono naturalmente
  • È più semplice iniziare con Source Reader / Sink Writer, passare a Media Session quando hai bisogno del controllo della riproduzione e a MFTs quando hai bisogno di trasformazioni personalizzate

In breve, Media Foundation è una piattaforma di elaborazione multimediale le cui superfici di confine sono profondamente intrise di COM.

Se lo interiorizzi in anticipo, la domanda “perché improvvisamente assume una faccia da COM?” diventa molto più chiaro.

2. Le tabelle di orientamento da considerare per prime

2.1. Cosa toccare per quello che vuoi fare

Osservando prima questa tabella è più facile scegliere il punto di ingresso.

Cosa vuoi fare Toccare prima COM intensità Note
Ottieni fotogrammi / campioni da un file o da una fotocamera Source Reader Medio Si prende cura dei decoder per te quando necessario
Scrivi l’audio / video generato in un file Sink Writer Medio Può raggruppare il codificatore e il dissipatore multimediale per te
Gestisci riproduzione, arresto, ricerca, sincronizzazione A / V, controllo qualità Media Session Alto Richiede la comprensione di topologie e sessioni
Collega trasformazioni personalizzate o componenti simili a codec MFT Alto Pensaci su IMFTransform
Ispeziona i candidati elencati, quindi crea un’istanza solo di ciò che ti serve IMFActivate Alto Ciò che ottieni potrebbe essere un oggetto di attivazione, non la cosa reale

2.2. Dove si mette la faccia COM

Punto Cosa appare Cosa capire prima
Inizializzazione CoInitializeEx, MFStartup L’inizializzazione COM e l’inizializzazione Media Foundation sono separate
Creazione e trasferimento di oggetti IMFSourceReader, IMFMediaType, IMFTransform La maggior parte sono puntatori all’interfaccia + HRESULT
Impostazioni IMFAttributes, GUIDs Impostazioni e informazioni sul tipo espresse come chiave / valore + GUIDs
Enumerazione / creazione differita IMFActivate, ActivateObject I risultati dell’enumerazione potrebbero non essere gli oggetti reali
Asincronia IMFSourceReaderCallback, code di lavoro Devi essere consapevole dei richiami e degli appartamenti
Controllo della riproduzione Topologie, Media Session Il flusso complessivo della pipeline è un concetto specifico di Media Foundation

2.3. Termini per comprendere il significato del primo

Termine Significato qui
Fonte multimediale Il punto di ingresso che inserisce i dati multimediali nella pipeline: file, reti, dispositivi di acquisizione, ecc.
MFT Media Foundation Trasforma. Il modello comune per decodificatori, codificatori, convertitori video e così via
Lavandino multimediale Dove vanno i dati multimediali: visualizzazione dello schermo, uscita audio, uscita file, ecc.
Media Session Il meccanismo che gestisce il flusso dell’intera pipeline; gestisce la riproduzione e la sincronizzazione
Topologia Lo schema di collegamento che descrive come sono cablati source / transform / sink
Oggetto di attivazione Un oggetto helper per creare successivamente l’oggetto reale; rappresentato da IMFActivate
Attributi Un archivio chiave / valore con chiave GUIDs; utilizzato pesantemente in tutto il Media Foundation

Avere questi come vocabolario in anticipo riduce drasticamente l’attrito della lettura della documentazione.

3. Il quadro generale di Media Foundation (diagramma)

Visto in generale, Media Foundation è la storia di una pipeline mediatica. L’aspetto COM è importante, ma è più facile organizzarsi guardando prima il quadro generale.

Modello in cui l'app gestisce direttamente i datiSource Reader (+ decodificatore)Fonte multimedialeAppSink Writer (+ codificatore)Dissipatore multimedialeModello utilizzando l'intera pipelineMFTFonte multimedialeDissipatore multimedialeMedia Session

In generale, Media Foundation ha due modalità di utilizzo.

  • Il modello che utilizza l’intera pipeline
    • Collega sorgente / trasformazione / sink e Media Session gestisce il flusso di dati e la sincronizzazione A / V
  • Il modello in cui l’app gestisce direttamente i dati
    • Estrai i dati da una fonte con Source Reader e li inserisci in un sink con Sink Writer

Quest’ultimo è il punto di ingresso più semplice quando si desidera elaborare da soli fotogrammi e campioni. D’altra parte, se vuoi che la riproduzione e la sincronizzazione siano gestite dalla piattaforma, la prima è la strada giusta.

La cosa da tenere a mente è che Media Foundation è, in sostanza, una piattaforma di elaborazione multimediale: è un po’ diversa dalla sensazione di frugare direttamente in una pila di oggetti COM.

Ma una volta che inizi a guardare i confini tra i suoi componenti, la faccia di COM improvvisamente si intensifica. Il capitolo successivo esamina questi punti in ordine.

4. Dove Media Foundation mette la sua faccia da COM

4.1. CoInitializeEx e MFStartup affiancati durante l’inizializzazione

È qui che la maggior parte delle persone avverte per la prima volta la stranezza. Prima di parlare di apertura di un file o di acquisizione da una fotocamera, vengono visualizzati CoInitializeEx e MFStartup.

  • CoInitializeEx inizializza la libreria COM
  • MFStartup inizializza la piattaforma Media Foundation

In altre parole, l’inizializzazione COM da sola non è sufficiente: è necessaria anche l’inizializzazione lato Media Foundation. Qui ti viene in mente: “questa non è solo un’API video; sotto c’è un sostanziale contratto basato su COM.”

In pratica decidere a questo punto quanto segue rende tutto più semplice in seguito.

  • Quale thread utilizza Media Foundation?
  • Il thread sarà STA o MTA?
  • A chi spettano le responsabilità di MFStartup / MFShutdown e CoInitializeEx / CoUninitialize?

Se si va avanti con questo design lasciato vago, le cose diventano confuse in seguito riguardo ai callback e all’integrazione dell’UI.

4.2. I trasferimenti di oggetti sono incentrati sull’interfaccia

Mentre leggi Media Foundation API, la maggior parte dei valori restituiti e dei parametri out sono interfacce COM.

  • IMFSourceReader
  • IMFMediaType
  • IMFTransform
  • IMFActivate
  • IMFSample
  • IMFMediaBuffer

Ciò che lo differenzia è che non solo i dati stessi, ma anche le informazioni sul tipo e gli oggetti delle impostazioni sono espressi come interfacce.

Ad esempio:

  • IMFTransform è l’interfaccia che rappresenta un MFT
  • IMFAttributes è un archivio chiave / valore
  • IMFMediaType eredita IMFAttributes ed è “una descrizione di un formato multimediale”

Anche qualcosa di simile ai dati di impostazione come un media type viene considerato un’interfaccia COM. È qui che il contesto di IUnknown, QueryInterface, AddRef / Release e HRESULT confluisce in modo naturale.

IUnknownIMFAttributesIMFMediaTypeIMFActivateIMFSourceReaderIMFTransform

A questo punto puoi vedere: “Media Foundation è un’API multimediale, ma il modo in cui esprime i suoi confini è molto COM.”

4.3. Vengono visualizzati gli oggetti di attivazione

La COM-ness di Media Foundation si mostra più fortemente negli oggetti di attivazione.

IMFActivate è un oggetto di supporto per creare successivamente l’oggetto reale. Intuitivamente, è più semplice vederlo come qualcosa di simile alla class factory di COM.

Quando viene visualizzato, il valore restituito di un’API di enumerazione potrebbe non essere “un oggetto immediatamente utilizzabile” ma, innanzitutto, un array di IMFActivate*. Quindi istanzia solo ciò di cui hai bisogno con ActivateObject.

IMFTransform / sink, etc.IMFActivateEnumeration APIAppIMFTransform / sink, etc.IMFActivateEnumeration APIAppCall the enumerationArray of IMFActivate*Inspect the attributesActivateObject(...)The real COM object

Questa forma si adatta bene al progetto di Media Foundation di scoprire i componenti scambiabili in un secondo momento e comporli.

Inoltre, poiché un oggetto di attivazione può esso stesso portare attributi, il flusso tende ad essere: “ispezionare prima gli attributi dei candidati”, “configurare se necessario”, “istanziare in seguito”. Anche questo è molto simile a COM.

4.4. Impostazioni e tipo Centro informazioni su IMFAttributes e GUIDs

Lavorando con Media Foundation, c’è un punto in cui le impostazioni improvvisamente sembrano nient’altro che GUIDs. Al centro c’è IMFAttributes, un archivio chiave / valore con chiave GUIDs. È utilizzato in modo estremamente intenso in Media Foundation.

Particolarmente importante è IMFMediaType, che eredita IMFAttributes e trasporta le informazioni sul formato multimediale come attributi.

Ad esempio:

  • Tipo principale (audio o video)
  • Sottotipo (H.264, AAC, RGB32, PCM, ecc.)
  • Dimensioni del telaio
  • Frequenza dei fotogrammi
  • Frequenza di campionamento
  • Conteggio canali
IMFMediaTypeMF_MT_MAJOR_TYPEMF_MT_SUBTYPEDimensioni / FPS / frequenza di campionamento, ecc.

È facile percepirlo come “una foresta di GUIDs”, ma ciò che sta realmente accadendo è abbastanza chiaro.

  • Conserva le impostazioni in un archivio di attributi
  • Esprimi anche i tipi di media come negozi di attributi
  • Negoziare i formati tra sorgente / trasformazione / sink controllando tali attributi

È semplicemente che le interfacce in stile COM e GUIDs vengono utilizzate per esprimere impostazioni e digitare informazioni.

4.5. Anche l’asincronia, i callback e il threading vengono gestiti nel modo COM

Ciò che è facile non notare nel lavoro Media Foundation del mondo reale è l’elaborazione asincrona e il modello di threading.

Ad esempio, Source Reader utilizza per impostazione predefinita la modalità sincrona. In modalità sincrona, ReadSample si blocca. A seconda dello stato del file, della rete o del dispositivo, l’attesa può diventare visibilmente lunga.

Per utilizzare la modalità asincrona, si passa un callback durante la creazione del lettore di origine. Si prepara un oggetto che implementa IMFSourceReaderCallback, lo si imposta sull’attributo MF_SOURCE_READER_ASYNC_CALLBACK e quindi si crea il lettore.

Un po’ più importante è ancora la questione dell’appartamento. L’elaborazione asincrona di Media Foundation utilizza code di lavoro e i thread delle code di lavoro sono MTA. Pertanto l’implementazione diventa più semplice se il lato applicativo funziona anche nel MTA.

IMFSourceReaderCallbackMF work queue (MTA)Source ReaderApp threadIMFSourceReaderCallbackMF work queue (MTA)Source ReaderApp threadReadSample(...)Returns immediatelyProcesses internallyOnReadSample(...)

Per quanto riguarda i callback, queste sono le cose da guardare.

  • Non toccare gli oggetti STA del thread dell’UI direttamente dal lato callback
  • Rendere l’implementazione del callback thread-safe
  • Se sono necessari aggiornamenti dell’UI, eseguire il marshalling solo dei risultati nel thread dell’UI
  • Fissa nella tua mente, fin dall’inizio, “su quale thread arrivano i callback Media Foundation?”

Media Foundation non assorbe le circostanze degli oggetti STA per te. Quindi è più semplice mantenere le cose organizzate inclinando il lavoratore Media Foundation verso MTA e costruendo un ponte esplicito verso l’UI.

4.6. Ma Media Foundation ≠ COM

Dopo aver letto fin qui, è facile pensare “quindi Media Foundation è solo COM dopo tutto.” Ma questo non è del tutto corretto.

Media Foundation contiene concetti specifici della piattaforma che le generalità di COM non coprono.

  • MFStartup / MFShutdown
  • Media Session
  • Topologie
  • Il caricatore di topologia
  • L’orologio della presentazione
  • Source Reader / Sink Writer

Questi sono i ruoli di Media Foundation: come eseguire la pipeline multimediale.

Ad esempio, in Media Session, l’applicazione trasmette una topologia parziale e il caricatore di topologia la risolve in una topologia completa compilando le trasformazioni necessarie. Questo non è COM generico: è la funzionalità che Media Foundation ha come piattaforma di elaborazione multimediale.

Topologia parzialeSorgente -> OutputCaricatore di topologiaTopologia completaSorgente -> Decodificatore MFT -> Uscita

Media Foundation utilizza COM per esprimere i contratti tra i suoi componenti e, inoltre, funziona come una piattaforma di elaborazione multimediale. Mantenere questa visualizzazione a due livelli ti impedisce di perderti.

5. Una guida approssimativa alla scelta

Quando si decide il primo punto di ingresso, questo diagramma è solitamente sufficiente.

Leggere fotogrammi / campioniScrivere su un fileÈ necessario il controllo della riproduzione o la sincronizzazione A / VInserisci una trasformazione personalizzataCosa vuoi fareDi cosa hai bisogno prima?Source ReaderSink WriterMedia SessionMFT

5.1. A partire da Source Reader

Source Reader è un punto di ingresso molto accessibile quando si desidera estrarre dati da file o dispositivi.

Si adatta a casi come questi.

  • Ottenere fotogrammi da un file video
  • Decodifica di un file audio per ottenere campioni
  • Ottenere fotogrammi da una fotocamera
  • Collegamento di una sorgente Media Foundation alla propria pipeline di elaborazione

Il Source Reader carica i decodificatori secondo necessità e trasmette i dati all’applicazione. D’altro canto, non si occupa della gestione dell’orologio di presentazione, della sincronizzazione A / V o del rendering sullo schermo stesso.

Diventa chiaro se lo si considera un punto di ingresso per “ottenere dati”, non per “riprodurli”.

5.2. Scrittura su file con Sink Writer

Sink Writer è il punto di ingresso quando desideri scrivere audio o video su file.

Gli usi tipici includono:

  • Salvataggio dei fotogrammi generati in un file video
  • Codifica e scrittura di campioni audio
  • Conversione dei dati letti in un altro formato e salvataggio

Sink Writer trova e carica gli encoder secondo necessità e gestisce il flusso di dati nel dissipatore multimediale. Viene spesso combinato con il Source Reader, ma i due sono componenti indipendenti: non è necessario utilizzarli in coppia.

5.3. Gestire la riproduzione e la sincronizzazione con Media Session

Se il tuo obiettivo non è “estrarre dati da un file” ma riprodurli correttamente, è più naturale pensare alla sessione multimediale.

Il turno del Media Session arriva quando hai requisiti come questi.

  • Gestione del gioco / stop / ricerca
  • Lasciare la sincronizzazione audio-video alla piattaforma
  • Gestione della pipeline compreso controllo qualità e cambi formato
  • Composizione del flusso source / transform / sink utilizzando topologie

L’accesso a questo livello ti avvicina a “Media Foundation corretto” rispetto a Source Reader / Sink Writer. Di conseguenza, si moltiplicano anche i concetti specifici della Media Foundation - topologie, eventi di sessione.

5.4. Inserimento di componenti personalizzati con MFTs

MFT è il modello comune di Media Foundation per le trasformazioni.

Si entra in questo mondo in situazioni come:

  • Costruisci il tuo decoder o codificatore
  • Collegamento di componenti di elaborazione video o audio nella pipeline
  • Enumerare codec e convertitori e sceglierli tu stesso
  • Controllo delle cose più approfondito rispetto alla risoluzione automatica predefinita

Nel mondo MFT prevalgono i contratti stile COM: negoziazione IMFTransform, IMFActivate, media type, gestione campioni / buffer. Pertanto, piuttosto che immergersi direttamente nel MFTs come primo punto di ingresso, è più chiaro determinare prima se ciò di cui hai effettivamente bisogno è il Source Reader / Sink Writer / Media Session.

6. Una lista di controllo pratica

Infine, ecco un riepilogo di una pagina di cosa controllare prima nel lavoro reale.

Articolo Cosa controllare Cosa tende ad accadere se mancato
Responsabilità di inizializzazione Decidi dove vengono chiamati CoInitializeEx e MFStartup e chi possiede lo smontaggio Inizializzazione mancata, ordine di smontaggio confuso
Appartamento Decidi in anticipo se il thread che tocca MF è STA o MTA Confusione sui callback, collisioni con l’UI
Source Reader modalità Decidi la sincronizzazione o l’asincrono al momento della creazione ReadSample si blocca inaspettatamente; impossibile cambiare più tardi
Media type trattativa Enumerare i formati di output e indicare quello effettivamente utilizzato esplicitamente MF_E_INVALIDMEDIATYPE, formati diversi da quelli previsti
Durata degli oggetti Esplicitare le responsabilità per Release, Unlock, ShutdownObject Perdite di memoria, buffer conservati, incoerenza allo spegnimento
Oggetti di attivazione Distinguere se i risultati dell’enumerazione sono oggetti reali o IMFActivate Fallendo perché pensavi di poterlo fare QueryInterface
Topologia Scopri se stai gestendo una topologia parziale o completa Rimanere bloccato supponendo che “dovrebbe connettersi automaticamente”
Errore durante il controllo Controlla HRESULT, flag di streaming ed eventi ogni volta Guasti parziali mancanti
Integrazione dell’UI Non toccare mai l’UI direttamente dai callback; marshal risultati solo nel thread dell’UI Blocchi, gare, bug difficili da diagnosticare

I tre elementi con la priorità più alta sono:

  1. Non scegliere la voce API sbagliata
    • Determina innanzitutto quale tra Source Reader / Sink Writer / Media Session ti serve effettivamente
  2. Decidi l’appartamento in anticipo
    • Se le code di lavoro dell’UI di STA e di Media Foundation si mescolano, decidere prima l’approccio bridge
  3. Non essere negligente con la negoziazione media type
    • Procedere con “probabilmente è questo formato” diventa molto confuso in seguito

6.3. Smart pointer COM

In questo articolo il codice usa SafeRelease scritto a mano e puntatori non gestiti alle interfacce. Questo perché gli esempi nella documentazione Microsoft sono scritti in questa forma, così si vede dove agiscono AddRef / Release. Tuttavia, questo non è un motivo per non usare smart pointer nel codice reale. Se scrivi nuovo codice C++, è più sicuro orientarsi su uno dei due seguenti.

Scelta Implementazione Note
Microsoft::WRL::ComPtr<T> <wrl/client.h> Incluso nel Windows SDK, nessuna dipendenza aggiuntiva. Get() restituisce il puntatore non gestito, GetAddressOf() / & per gli argomenti out, As<U>() per scrivere QueryInterface.
wil::com_ptr<T> wil/com.h delle WIL (Windows Implementation Libraries) Da installare separatamente, ad esempio tramite NuGet. Include helper che convertono HRESULT in eccezioni.

Se scrivi con ComPtr, combinazioni come goto done; e SafeRelease non servono più: Release viene chiamato automaticamente all’uscita dallo scope. In questo articolo lasciamo i puntatori non gestiti per rendere visibili le convenzioni COM, ma per codice nuovo consigliamo di iniziare con ComPtr.

7. Estratti di codice

Invece di esempi completi, qui ci sono solo estratti sufficienti per mostrare dove appare la faccia COM.

7.1. Inizializzazione

template <class T>
void SafeRelease(T** pp)
{
    if (pp != nullptr && *pp != nullptr)
    {
        (*pp)->Release();
        *pp = nullptr;
    }
}

HRESULT InitializeMediaFoundationForCurrentThread()
{
    HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if (FAILED(hr))
    {
        return hr;
    }

    hr = MFStartup(MF_VERSION);
    if (FAILED(hr))
    {
        CoUninitialize();
        return hr;
    }

    return S_OK;
}

void UninitializeMediaFoundationForCurrentThread()
{
    MFShutdown();
    CoUninitialize();
}

Qui, CoInitializeEx e MFStartup siedono fianco a fianco. Questo è il primo punto in cui l’atmosfera del COM si addensa improvvisamente mentre si lavora con Media Foundation.

Nelle implementazioni, un altro livello potrebbe già essere responsabile dell’inizializzazione COM. Anche in questo caso, è più sicuro stabilire in anticipo chi ha la responsabilità.

7.2. Creazione di un Source Reader in modalità sincrona

HRESULT ReadOneVideoSample(PCWSTR path)
{
    IMFSourceReader* pReader = nullptr;
    IMFMediaType* pType = nullptr;
    IMFSample* pSample = nullptr;

    HRESULT hr = MFCreateSourceReaderFromURL(path, nullptr, &pReader);
    if (FAILED(hr)) goto done;

    hr = MFCreateMediaType(&pType);
    if (FAILED(hr)) goto done;

    hr = pType->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Video);
    if (FAILED(hr)) goto done;

    hr = pType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
    if (FAILED(hr)) goto done;

    hr = pReader->SetCurrentMediaType(
        MF_SOURCE_READER_FIRST_VIDEO_STREAM,
        nullptr,
        pType);
    if (FAILED(hr)) goto done;

    DWORD streamFlags = 0;
    LONGLONG timestamp = 0;

    hr = pReader->ReadSample(
        MF_SOURCE_READER_FIRST_VIDEO_STREAM,
        0,
        nullptr,
        &streamFlags,
        &timestamp,
        &pSample);
    if (FAILED(hr)) goto done;

    // Extract the IMFMediaBuffer from pSample and process it

done:
    SafeRelease(&pSample);
    SafeRelease(&pType);
    SafeRelease(&pReader);
    return hr;
}

Cosa diventa visibile qui:

  • Sia il lettore che il media type sono interfacce COM
  • Le impostazioni sono basate su GUID
  • I valori restituiti sono HRESULT
  • In modalità sincrona, ReadSample si blocca

Anche “Voglio solo leggere un fotogramma” assume una faccia pesantemente COM al confine di Media Foundation.

Procedura di negoziazione del media type (è uno degli elementi elencati nella checklist “3 cose da vedere per prime” del capitolo 6)

Il codice sopra dichiara solo “lo voglio in RGB32”, quindi nella pratica servono passaggi prima e dopo. Il flusso indicato dalla documentazione Microsoft per Source Reader è composto da questi 4 passaggi.

  1. Enumerare i tipi nativi — Chiamare IMFSourceReader::GetNativeMediaType(streamIndex, typeIndex, &pType) aumentando typeIndex a partire da 0. Quando si supera il range, viene restituito MF_E_NO_MORE_TYPES: questo è il segnale di fine enumerazione (se streamIndex è fuori range, si ottiene MF_E_INVALIDSTREAMNUMBER). Per i file di solito c’è un solo tipo per stream, ma una webcam può averne diversi
  2. Verificare il major type — Dal media type enumerato, leggere MF_MT_MAJOR_TYPE per capire se è audio o video. Se si procede a tentoni senza questo controllo, si rischia di impostare un formato video su uno stream audio
  3. Compilare e impostare il formato di output desiderato — Creare un nuovo media type con MFCreateMediaType, impostare MF_MT_MAJOR_TYPE e MF_MT_SUBTYPE, quindi chiamare SetCurrentMediaType. Se si vuole ricevere i dati compressi, passare il tipo ottenuto al passo 1; se si vuole decodificarli, specificare un formato non compresso come MFVideoFormat_RGB32 o MFAudioFormat_PCM. Il Source Reader carica automaticamente il decoder
  4. Rileggere il formato effettivamente confermato — Dopo SetCurrentMediaType, chiamare GetCurrentMediaType per leggere i dettagli del formato effettivo (dimensioni del frame, stride, sample rate, ecc.). Poiché quanto passato al passo 3 è una specifica parziale, i valori finali vanno letti qui

Se si saltano questi 4 passaggi e si procede con “probabilmente è questo formato”, si ottiene MF_E_INVALIDMEDIATYPE o, peggio, si leggono buffer in un formato diverso da quello atteso.

7.3. Creazione di un Source Reader in modalità asincrona

HRESULT CreateSourceReaderAsync(
    PCWSTR path,
    IMFSourceReaderCallback* pCallback,
    IMFSourceReader** ppReader)
{
    IMFAttributes* pAttributes = nullptr;

    HRESULT hr = MFCreateAttributes(&pAttributes, 1);
    if (FAILED(hr))
    {
        return hr;
    }

    hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, pCallback);
    if (SUCCEEDED(hr))
    {
        hr = MFCreateSourceReaderFromURL(path, pAttributes, ppReader);
    }

    SafeRelease(&pAttributes);
    return hr;
}

In questo caso, per abilitare la modalità asincrona, il callback viene inserito negli attributi prima che venga creato il lettore.

Cioè:

  • La richiamata stessa è un’interfaccia COM
  • La configurazione asincrona passa attraverso IMFAttributes
  • La modalità è fissa al momento della creazione

In pratica, ciò che conta è rendere l’implementazione IMFSourceReaderCallback thread-safe e non trasportare oggetti dell’UI direttamente al suo interno.

7.4. Enumerazione e istanziazione di MFTs con MFTEnumEx

HRESULT FindH264Decoder(IMFTransform** ppTransform)
{
    *ppTransform = nullptr;

    IMFActivate** ppActivate = nullptr;
    UINT32 count = 0;

    MFT_REGISTER_TYPE_INFO inputType = {};
    inputType.guidMajorType = MFMediaType_Video;
    inputType.guidSubtype = MFVideoFormat_H264;

    HRESULT hr = MFTEnumEx(
        MFT_CATEGORY_VIDEO_DECODER,
        MFT_ENUM_FLAG_SYNCMFT | MFT_ENUM_FLAG_LOCALMFT,
        &inputType,
        nullptr,
        &ppActivate,
        &count);
    if (FAILED(hr))
    {
        return hr;
    }

    if (count == 0)
    {
        CoTaskMemFree(ppActivate);
        return MF_E_TOPO_CODEC_NOT_FOUND;
    }

    hr = ppActivate[0]->ActivateObject(
        __uuidof(IMFTransform),
        reinterpret_cast<void**>(ppTransform));

    for (UINT32 i = 0; i < count; ++i)
    {
        ppActivate[i]->Release();
    }
    CoTaskMemFree(ppActivate);

    return hr;
}

Qui il risultato dell’enumerazione non ritorna come IMFTransform* dall’inizio ma come IMFActivate**. Solo chiamando ActivateObject ottieni finalmente il vero IMFTransform.

Questo flusso cattura il modo in cui Media Foundation “ha improvvisamente messo su la sua faccia da COM” sentendosi straordinariamente bene.

8. Conclusione

Non è un caso che gli argomenti COM si moltiplichino quando lavori con Media Foundation.

  • Media Foundation è una piattaforma di elaborazione multimediale
  • I suoi confini - sorgente / trasformazione / sink / attivazione / callback - sono espressi come interfacce COM
  • Di conseguenza, IUnknown, HRESULT, GUIDs, appartamenti e richiamate vengono visualizzati naturalmente
  • Tuttavia, il cuore di Media Foundation è una pipeline multimediale con Media Session e topologie: non è un semplice rimaneggiamento di COM

In pratica, pensare in questo ordine mantiene le cose ben organizzate.

  1. Innanzitutto, determina quale tra Source Reader / Sink Writer / Media Session / MFT ti serve effettivamente
  2. Decidi in anticipo l’appartamento e la politica di richiamata
  3. Gestire con attenzione la negoziazione media type e la durata degli oggetti

Non è necessario capire tutto dall’inizio. Inizia con l’idea che “Media Foundation è una piattaforma di elaborazione multimediale, con COM profondamente incorporato nelle sue superfici di confine,” e sia la documentazione che il codice diventeranno molto più facili da seguire.

9. Riferimenti

Articoli recenti con gli stessi tag per approfondire argomenti vicini.

Queste pagine collocano l’argomento in un contesto più ampio di servizi e decisioni.

L’articolo è direttamente collegato ai servizi seguenti.

Domande frequenti

Domande che ricorrono nelle consulenze sull’argomento dell’articolo.

Perché Media Foundation utilizza COM?
Media Foundation è una piattaforma di elaborazione multimediale, ma i confini tra i suoi componenti (sorgenti, trasformazioni, sink, oggetti di attivazione, attributi e callback) sono espressi come interfacce COM. Ecco perché gli argomenti IUnknown, HRESULT, GUIDs e gli appartamenti emergono naturalmente mentre lo usi. Media Foundation non è puro COM, però: concetti come Media Session, topologie e orologio di presentazione sono funzionalità a livello di piattaforma costruite sulla base dei contratti COM.
Dovrei iniziare con Source Reader, Sink Writer o Media Session?
Inizia con Source Reader se desideri ottenere fotogrammi o campioni da un file o da una fotocamera: carica i decodificatori secondo necessità. Utilizza Sink Writer per scrivere l'audio o il video generato su file, raggruppando il codificatore e il dissipatore multimediale. Passare a Media Session solo quando è necessario un adeguato controllo della riproduzione, ricerca o sincronizzazione audio-video, poiché è necessaria la comprensione delle topologie. MFTs arriva per ultimo, quando devi inserire trasformazioni personalizzate.
Perché mi servono sia CoInitializeEx che MFStartup?
Perché inizializzano due livelli diversi: CoInitializeEx inizializza la libreria COM per il thread e MFStartup inizializza la piattaforma Media Foundation. COM l'inizializzazione da sola non è sufficiente. In pratica, dovresti anche decidere in anticipo quale thread utilizza Media Foundation, se quel thread è STA o MTA e chi possiede le corrispondenti responsabilità di arresto di MFShutdown e CoUninitialize, perché lasciare questo vago causa confusione sui callback e sull'integrazione dell'UI in un secondo momento.
Su quali thread vengono eseguiti i callback Media Foundation?
L'elaborazione asincrona di Media Foundation utilizza code di lavoro e i thread della coda di lavoro sono MTA, quindi i callback come IMFSourceReaderCallback arrivano sui thread MTA. L'implementazione diventa più semplice se il lato applicativo funziona anche nel MTA. Non toccare mai gli oggetti STA del thread dell'UI direttamente da un callback: rendi l'implementazione del callback thread-safe ed esegui il marshalling solo dei risultati nel thread dell'UI quando sono necessari aggiornamenti.

Profilo dell’autore

Pagina di presentazione dell’autore dell’articolo.

Go Komura

Rappresentante di KomuraSoft LLC

Specializzato nello sviluppo di software Windows, nella consulenza tecnica e nell’analisi dei malfunzionamenti, soprattutto nei progetti con sistemi esistenti e guasti difficili da riprodurre.

Torna al blog