Introduction à Media Foundation - Comprendre l'API à travers le prisme COM

· Mis à jour le: · · Media Foundation, COM, C++, Développement Windows

Quand on commence à utiliser Media Foundation, on a facilement l’impression que « je pensais utiliser les API vidéo et audio de Windows, et voilà que tout devient soudain une histoire de COM ». CoInitializeEx, MFStartup, IMFSourceReader, IMFMediaType, IMFTransform, IMFActivate, HRESULT, les GUID, etc. arrivent tous en même temps, l’atmosphère prend brusquement des allures de Win32/COM, et ce qu’est réellement Media Foundation devient difficile à percevoir.

Plutôt que de couvrir l’ensemble de Media Foundation comme un dictionnaire, cet article se concentre sur trois points.

  • Pourquoi les notions COM apparaissent-elles naturellement lorsqu’on utilise Media Foundation ?
  • À quels endroits la teinte COM s’intensifie-t-elle ?
  • Par où commencer au départ - Source Reader / Sink Writer / Media Session / MFT ?

Les exemples de code sont basés sur le C++, mais la façon de penser reste globalement la même lorsqu’on y accède via un wrapper depuis .NET ou un autre langage.

Table des matières

  1. La conclusion d’abord (en une phrase)
  2. Les tableaux de repère à consulter en premier
    • 2.1. Que toucher selon ce que l’on veut faire
    • 2.2. Où l’API prend-elle un visage COM
    • 2.3. Les termes à connaître avant tout
  3. Vue d’ensemble de Media Foundation (schéma)
  4. Les points où Media Foundation prend un visage COM
    • 4.1. CoInitializeEx et MFStartup côte à côte à l’initialisation
    • 4.2. Les échanges d’objets sont centrés sur les interfaces
    • 4.3. L’apparition des Activation Objects
    • 4.4. Les paramètres et les informations de type centrés sur IMFAttributes et les GUID
    • 4.5. L’asynchrone, les callbacks et les threads gérés à la manière COM
    • 4.6. Mais Media Foundation ≠ COM
  5. Guide rapide pour choisir
    • 5.1. Commencer par le Source Reader
    • 5.2. Écrire dans un fichier avec le Sink Writer
    • 5.3. Gérer la lecture et la synchronisation avec la Media Session
    • 5.4. Insérer des composants personnalisés avec un MFT
  6. Liste de vérification pour la pratique
  7. Extraits de code
    • 7.1. Initialisation
    • 7.2. Créer un Source Reader en mode synchrone
    • 7.3. Créer un Source Reader en mode asynchrone
    • 7.4. Énumérer et instancier des MFT avec MFTEnumEx
  8. Conclusion
  9. Références

1. La conclusion d’abord (en une phrase)

  • Media Foundation est une plateforme destinée à traiter la vidéo et l’audio ; l’API dans son ensemble n’est pas, en tant que telle, purement du COM
  • Cependant, les frontières entre source, transform, sink, activation, attributes et callback sont exprimées via des interfaces COM, si bien que les notions d’IUnknown, HRESULT, GUID et apartment apparaissent naturellement lorsqu’on l’utilise
  • Il est plus simple de commencer par le Source Reader / Sink Writer, de passer à la Media Session lorsque le contrôle de la lecture devient nécessaire, puis au MFT lorsqu’un convertisseur personnalisé est requis

En somme, Media Foundation est une plateforme de traitement multimédia dont les frontières sont profondément imprégnées de COM.

En gardant cela à l’esprit dès le départ, la question « pourquoi cela prend-il soudain un visage COM ? » devient beaucoup plus claire.

2. Les tableaux de repère à consulter en premier

2.1. Que toucher selon ce que l’on veut faire

Consulter d’abord ce tableau facilite le choix du point d’entrée.

Ce que vous voulez faire À toucher en premier Intensité COM Remarque
Récupérer des trames / échantillons depuis un fichier ou une caméra Source Reader Moyenne Prend en charge le decoder si nécessaire
Écrire l’audio / la vidéo généré(e) dans un fichier Sink Writer Moyenne Peut regrouper l’encoder et le media sink si nécessaire
Gérer la lecture, l’arrêt, le seek, la synchronisation A/V et le contrôle de qualité Media Session Élevée Nécessite de comprendre la topology et la session
Insérer un convertisseur personnalisé ou un composant de type codec MFT Élevée Raisonner autour d’IMFTransform
Examiner les candidats énumérés avant d’instancier uniquement ceux dont vous avez besoin IMFActivate Élevée Ce qui est renvoyé peut être un activation object plutôt que l’objet réel

2.2. Où l’API prend-elle un visage COM

Point Ce qui apparaît À comprendre en premier
Initialisation CoInitializeEx, MFStartup L’initialisation COM et l’initialisation Media Foundation sont distinctes
Création et transmission d’objets IMFSourceReader, IMFMediaType, IMFTransform La plupart sont des pointeurs d’interface + HRESULT
Paramètres IMFAttributes, GUID Les valeurs de configuration et les informations de type sont exprimées en paires clé/valeur + GUID
Énumération et création différée IMFActivate, ActivateObject Le résultat de l’énumération n’est pas toujours l’objet réel
Asynchrone IMFSourceReaderCallback, work queue Il faut être attentif aux callbacks et aux apartments
Contrôle de la lecture topology, Media Session Le flux global du pipeline est un concept propre à Media Foundation

2.3. Les termes à connaître avant tout

Terme Signification ici
Media Source Le point d’entrée qui alimente le pipeline en données multimédias : fichiers, réseau, périphériques de capture, etc.
MFT Media Foundation Transform. Le modèle commun pour les décodeurs, encodeurs, convertisseurs vidéo, etc.
Media Sink La destination des données multimédias : affichage à l’écran, sortie audio, écriture de fichier, etc.
Media Session Le mécanisme qui gère le flux de l’ensemble du pipeline ; responsable de la lecture et de la synchronisation
Topology Le schéma de connexion décrivant comment relier source, transform et sink
Activation Object Un objet auxiliaire permettant de créer l’objet réel ultérieurement ; représenté par IMFActivate
Attributes Un magasin clé/valeur indexé par des GUID ; largement utilisé dans l’ensemble de Media Foundation

Avoir déjà ce vocabulaire en tête réduit considérablement les points d’achoppement lors de la lecture de la documentation.

3. Vue d’ensemble de Media Foundation (schéma)

Vu dans son ensemble, Media Foundation est avant tout une histoire de pipeline multimédia. L’aspect COM est important, mais il est plus facile de s’organiser en regardant d’abord la vue d’ensemble.

Modèle où l'application gère les données directementSource Reader (+ decoder)Media SourceApplicationSink Writer (+ encoder)Media SinkModèle utilisant le pipeline completMFTMedia SourceMedia SinkMedia Session

Media Foundation propose globalement deux modes d’utilisation.

  • Le modèle utilisant le pipeline complet
    • Vous connectez source, transform et sink, et la Media Session gère le flux de données et la synchronisation A/V
  • Le modèle où l’application gère les données directement
    • Vous extrayez les données d’une source avec le Source Reader, puis les transmettez à un sink avec le Sink Writer

Le second est plus facile à aborder lorsque vous souhaitez traiter vous-même les trames ou les échantillons. À l’inverse, si vous voulez confier la lecture et la synchronisation à la plateforme, le premier est la voie naturelle.

Ce qu’il faut retenir, c’est que Media Foundation est, au fond, une plateforme de traitement multimédia - ce qui diffère légèrement de la sensation de manipuler directement un ensemble d’objets COM.

Cependant, dès que l’on commence à observer les frontières entre ces composants, le visage COM s’intensifie soudainement. Le chapitre suivant passe ces points en revue un par un.

4. Les points où Media Foundation prend un visage COM

4.1. CoInitializeEx et MFStartup côte à côte à l’initialisation

C’est le premier endroit où beaucoup de gens ressentent une étrangeté. Avant même de parler d’ouvrir un fichier ou de capturer depuis une caméra, CoInitializeEx et MFStartup apparaissent.

  • CoInitializeEx initialise la bibliothèque COM
  • MFStartup initialise la plateforme Media Foundation

Autrement dit, l’initialisation COM seule ne suffit pas - l’initialisation côté Media Foundation est également nécessaire. C’est là qu’on comprend : « ce n’est pas juste une API vidéo, il y a en dessous un contrat basé sur COM assez substantiel ».

En pratique, décider des points suivants à ce stade facilite grandement la suite.

  • Quel thread utilise Media Foundation ?
  • Ce thread doit-il être en STA ou en MTA ?
  • Qui a la responsabilité de MFStartup / MFShutdown ainsi que de CoInitializeEx / CoUninitialize ?

Si l’on avance en laissant cette conception dans le flou, cela devient source de confusion plus tard, au niveau des callbacks et de l’intégration avec l’interface utilisateur.

4.2. Les échanges d’objets sont centrés sur les interfaces

En parcourant les API de Media Foundation, on constate que la plupart des valeurs de retour et des paramètres de sortie sont des interfaces COM.

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

Ce qui est caractéristique, c’est que non seulement les données elles-mêmes, mais aussi les informations de type et les objets de configuration sont exprimés via des interfaces.

Par exemple :

  • IMFTransform est l’interface représentant un MFT
  • IMFAttributes est un magasin clé/valeur
  • IMFMediaType hérite d’IMFAttributes et constitue une « description du format multimédia »

Même quelque chose d’aussi proche d’une donnée de configuration qu’un media type est ainsi porté par une interface COM. C’est ici que le contexte d’IUnknown, QueryInterface, AddRef / Release et HRESULT s’introduit naturellement.

IUnknownIMFAttributesIMFMediaTypeIMFActivateIMFSourceReaderIMFTransform

Arrivé ici, on commence à percevoir : « Media Foundation est une API multimédia, mais la façon dont elle exprime ses frontières est résolument COM ».

4.3. L’apparition des Activation Objects

C’est dans les activation objects que le caractère COM de Media Foundation se manifeste le plus fortement.

IMFActivate est un objet auxiliaire permettant de créer l’objet réel ultérieurement. Intuitivement, il est plus simple de le voir comme quelque chose de proche d’une class factory COM.

Dans les situations où il apparaît, la valeur retournée par une API d’énumération n’est pas « l’objet réel directement utilisable », mais, dans un premier temps, un tableau d’IMFActivate*. On instancie ensuite uniquement ce dont on a besoin avec ActivateObject.

IMFTransform / sink, etc.IMFActivateAPI d'énumérationApplicationIMFTransform / sink, etc.IMFActivateAPI d'énumérationApplicationAppelle l'énumérationTableau d'IMFActivate*Consulte les attributsActivateObject(...)L'objet COM réel

Cette structure s’accorde bien avec le fait que Media Foundation est conçu pour découvrir a posteriori des composants interchangeables et les assembler.

De plus, comme l’activation object lui-même peut porter des attributes, le déroulement tend naturellement à suivre l’ordre suivant : « examiner d’abord les attributs des candidats », « les configurer si nécessaire », « les instancier plus tard ». C’est là encore très COM.

4.4. Les paramètres et les informations de type centrés sur IMFAttributes et les GUID

En manipulant Media Foundation, on arrive à un point où les paramètres semblent soudain n’être qu’un amas de GUID. Au centre de cela se trouve IMFAttributes, un magasin clé/valeur indexé par des GUID. Il est utilisé extrêmement fréquemment dans l’ensemble de Media Foundation.

IMFMediaType est particulièrement important : il hérite d’IMFAttributes et porte les informations de format multimédia sous forme d’attributs.

Il s’agit par exemple des informations suivantes.

  • Le major type (audio ou vidéo)
  • Le subtype (H.264, AAC, RGB32, PCM, etc.)
  • La taille des trames
  • La fréquence d’images
  • La fréquence d’échantillonnage
  • Le nombre de canaux
IMFMediaTypeMF_MT_MAJOR_TYPEMF_MT_SUBTYPETaille / FPS / fréquence d'échantillonnage, etc.

On a facilement l’impression d’une « forêt de GUID » à ce stade, mais ce qui se passe réellement est assez direct.

  • Les paramètres sont portés par un magasin d’attributs
  • Le media type est lui aussi exprimé comme un magasin d’attributs
  • Le format est négocié entre source, transform et sink en consultant ces attributs

Il s’agit simplement du fait que des interfaces de type COM et des GUID sont utilisés pour exprimer les paramètres et les informations de type.

4.5. L’asynchrone, les callbacks et les threads gérés à la manière COM

Ce qui est facile à négliger dans la pratique de Media Foundation, c’est le traitement asynchrone et le modèle de threads.

Par exemple, le Source Reader est par défaut en mode synchrone. En mode synchrone, ReadSample bloque. Selon l’état du fichier, du réseau ou du périphérique, cette attente peut devenir sensiblement perceptible.

Pour passer en mode asynchrone, vous transmettez un callback lors de la création du Source Reader. Le déroulement consiste à préparer un objet implémentant IMFSourceReaderCallback, à le définir dans l’attribut MF_SOURCE_READER_ASYNC_CALLBACK, puis à créer le reader.

Un point encore un peu plus important est l’apartment. Le traitement asynchrone de Media Foundation utilise une work queue, et le thread de la work queue est en MTA. Par conséquent, l’implémentation devient plus simple si l’application est également traitée en MTA.

IMFSourceReaderCallbackMF work queue (MTA)Source ReaderThread applicatifIMFSourceReaderCallbackMF work queue (MTA)Source ReaderThread applicatifReadSample(...)Retourne immédiatementTraite en interneOnReadSample(...)

Voici les points à surveiller autour des callbacks.

  • Ne pas toucher directement, depuis le callback, aux objets STA du thread d’interface utilisateur
  • Rendre l’implémentation du callback thread-safe
  • Si une mise à jour de l’interface est nécessaire, ne renvoyer que le résultat vers le thread d’interface utilisateur
  • Fixer d’emblée dans son esprit la question : « de quel thread proviennent les callbacks de Media Foundation ? »

Media Foundation n’absorbe pas automatiquement les contraintes des objets STA à votre place. Il est donc plus simple de s’organiser en orientant le worker qui utilise Media Foundation vers le MTA, et en construisant un pont explicite vers l’interface utilisateur.

4.6. Mais Media Foundation ≠ COM

Après avoir lu jusqu’ici, on est tenté de penser que « Media Foundation n’est finalement rien d’autre que du COM ». Mais ce n’est pas tout à fait exact.

Media Foundation possède des concepts propres à la plateforme qui ne se réduisent pas aux généralités de COM.

  • MFStartup / MFShutdown
  • Media Session
  • Topology
  • Le topology loader
  • La presentation clock
  • Source Reader / Sink Writer

Ce sont là les propres attributions de Media Foundation : comment faire circuler le pipeline multimédia.

Par exemple, dans la Media Session, lorsque l’application transmet une partial topology, le topology loader complète les transforms nécessaires pour la résoudre en une full topology. Il ne s’agit pas là d’une généralité COM, mais d’une fonctionnalité que Media Foundation possède en tant que plateforme de traitement multimédia.

Partial TopologySource -> OutputTopology LoaderFull TopologySource -> Decoder MFT -> Output

Media Foundation utilise COM pour exprimer les contrats entre ses composants, tout en fonctionnant, par-dessus, comme une plateforme de traitement multimédia. Garder cette vision à deux niveaux évite de s’y perdre.

5. Guide rapide pour choisir

Pour décider du premier point d’entrée, le schéma suivant suffit souvent.

Lire des trames / échantillonsÉcrire dans un fichierBesoin de contrôle de lecture ou de sync A/VInsérer un convertisseur personnaliséCe que vous voulez faireDe quoi avez-vous besoin en premier ?Source ReaderSink WriterMedia SessionMFT

5.1. Commencer par le Source Reader

Le Source Reader est un point d’entrée très accessible lorsqu’on veut extraire des données de fichiers ou de périphériques.

Il convient par exemple aux cas suivants.

  • Récupérer des trames depuis un fichier vidéo
  • Décoder un fichier audio pour en extraire des échantillons
  • Récupérer des trames depuis une caméra
  • Connecter une source Media Foundation à votre propre pipeline de traitement

Le Source Reader charge un decoder si nécessaire et transmet les données à l’application. En revanche, il ne prend pas en charge la gestion de la presentation clock, la synchronisation A/V, ni le rendu à l’écran lui-même.

Il est plus simple de le voir comme un point d’entrée pour « obtenir des données », et non pour « effectuer une lecture ».

5.2. Écrire dans un fichier avec le Sink Writer

Le Sink Writer est le point d’entrée lorsqu’on veut écrire de l’audio ou de la vidéo dans un fichier.

Voici des usages typiques.

  • Enregistrer des trames générées dans un fichier vidéo
  • Encoder et écrire des échantillons audio
  • Convertir des données lues vers un autre format avant de les enregistrer

Le Sink Writer trouve et charge un encoder si nécessaire, et gère le flux de données vers le media sink. Il est souvent combiné avec le Source Reader, mais les deux sont des composants indépendants ; il n’est pas obligatoire de les utiliser ensemble.

5.3. Gérer la lecture et la synchronisation avec la Media Session

Si l’objectif n’est pas « extraire des données d’un fichier » mais assurer une véritable lecture, il est plus naturel de raisonner autour de la Media Session.

La Media Session entre en jeu lorsque vous avez des besoins comme ceux-ci.

  • Gérer la lecture / l’arrêt / le seek
  • Confier la synchronisation audio-vidéo à la plateforme
  • Gérer le pipeline en incluant le contrôle de qualité et les changements de format
  • Composer le flux source / transform / sink à l’aide d’une topology

En entrant dans cette couche, on se rapproche du « cœur de Media Foundation » davantage qu’avec le Source Reader / Sink Writer. Les concepts propres à Media Foundation - topology, session event, etc. - se multiplient en conséquence.

5.4. Insérer des composants personnalisés avec un MFT

Le MFT est le modèle commun de Media Foundation pour les transforms.

On y entre dans des situations comme celles-ci.

  • Créer son propre décodeur ou encodeur
  • Insérer des composants de traitement vidéo ou audio dans le pipeline
  • Énumérer les codecs et convertisseurs pour les choisir soi-même
  • Exercer un contrôle plus poussé que la résolution automatique par défaut

Dans le monde du MFT, les contrats de type COM occupent une place très importante : IMFTransform, IMFActivate, la media type negotiation, la gestion des samples / buffers. C’est pourquoi plutôt que d’entrer directement par le MFT comme premier point d’entrée, il est plus clair de d’abord déterminer lequel du Source Reader, du Sink Writer ou de la Media Session est réellement nécessaire.

6. Liste de vérification pour la pratique

Pour finir, voici en un seul tableau les points à vérifier en premier dans la pratique.

Élément Ce qu’il faut vérifier Ce qui risque de se produire en cas d’oubli
Responsabilité d’initialisation Décider où appeler CoInitializeEx et MFStartup, et qui gère la finalisation Initialisation manquée, confusion dans l’ordre de finalisation
Apartment Décider à l’avance si le thread qui touche MF sera en STA ou en MTA Confusion autour des callbacks, conflits avec l’interface utilisateur
Mode du Source Reader Décider synchrone ou asynchrone au moment de la création ReadSample bloque de façon inattendue ; impossible de changer par la suite
Media type negotiation Énumérer les formats de sortie et indiquer explicitement celui réellement utilisé MF_E_INVALIDMEDIATYPE, réception d’un format différent de celui attendu
Durée de vie des objets Clarifier les responsabilités de Release, Unlock, ShutdownObject Fuites mémoire, buffers retenus, incohérences à la fermeture
Activation object Distinguer si le résultat de l’énumération est l’objet réel ou un IMFActivate Échec en supposant à tort qu’un QueryInterface est possible
Topology Savoir si l’on manipule une partial topology ou une full topology Se retrouver bloqué en supposant que « ça devrait se connecter automatiquement »
Vérification des erreurs Vérifier systématiquement HRESULT, les stream flags et les events Passer à côté d’un échec partiel
Intégration UI Ne jamais toucher l’interface directement depuis un callback ; ne renvoyer que le résultat vers le thread d’interface utilisateur Blocages, conditions de concurrence, bogues difficiles à diagnostiquer

Les trois points suivants sont particulièrement prioritaires.

  1. Ne pas se tromper sur l’API d’entrée initiale
    • Déterminer d’abord lequel du Source Reader, du Sink Writer ou de la Media Session est réellement nécessaire
  2. Décider l’apartment en premier
    • Si l’UI en STA et la work queue de Media Foundation doivent cohabiter, décider d’abord comment construire le pont entre elles
  3. Ne pas négliger la media type negotiation
    • Avancer sur la base d’un « c’est probablement ce format-là » devient très confus par la suite

7. Extraits de code

On ne présente pas ici des exemples complets, mais seulement des extraits suffisants pour repérer où apparaît le visage COM.

7.1. Initialisation

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();
}

Ici, CoInitializeEx et MFStartup sont placés côte à côte. C’est le tout premier endroit où l’atmosphère COM s’épaissit soudainement lorsqu’on travaille avec Media Foundation.

Dans certaines implémentations, une autre couche peut déjà être responsable de l’initialisation COM. Même dans ce cas, il est plus sûr de fixer à l’avance qui porte cette responsabilité.

7.2. Créer un Source Reader en mode synchrone

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;

    // Extraire l'IMFMediaBuffer de pSample et le traiter

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

Voici ce qui devient visible ici.

  • Le reader comme le media type sont des interfaces COM
  • Les paramètres sont basés sur des GUID
  • Les valeurs de retour sont des HRESULT
  • En mode synchrone, ReadSample bloque

Même un simple « je veux juste lire une trame » prend, à la frontière de Media Foundation, un visage résolument COM.

7.3. Créer un Source Reader en mode asynchrone

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;
}

Ici, pour activer le mode asynchrone, le callback est placé dans les attributs avant que le reader ne soit créé.

Autrement dit :

  • Le callback lui-même est une interface COM
  • La configuration asynchrone passe par IMFAttributes
  • Le mode est fixé au moment de la création

voilà la forme que cela prend.

En pratique, il est important de rendre l’implémentation d’IMFSourceReaderCallback thread-safe et de ne pas y introduire directement d’objets d’interface utilisateur.

7.4. Énumérer et instancier des MFT avec 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;
}

Ici, le résultat de l’énumération n’est pas renvoyé d’emblée sous forme d’IMFTransform*, mais sous forme d’IMFActivate**. Ce n’est qu’en appelant ActivateObject que l’on obtient enfin l’IMFTransform réel.

Ce déroulement illustre remarquablement bien cette sensation propre à Media Foundation de « prendre soudain un visage COM ».

8. Conclusion

Ce n’est pas un hasard si les sujets liés à COM se multiplient soudainement lorsqu’on travaille avec Media Foundation.

  • Media Foundation est une plateforme de traitement multimédia
  • Ses frontières - source, transform, sink, activation, callback, etc. - sont exprimées via des interfaces COM
  • C’est pourquoi les sujets IUnknown, HRESULT, GUID, apartment et callback apparaissent naturellement
  • Cependant, le cœur de Media Foundation est un pipeline multimédia doté d’une Media Session et de topologies - ce n’est pas une simple reformulation de COM

En pratique, réfléchir dans l’ordre suivant permet de bien s’organiser.

  1. Déterminer avant tout lequel du Source Reader, du Sink Writer, de la Media Session ou du MFT est nécessaire
  2. Décider en amont la politique concernant l’apartment et les callbacks
  3. Traiter avec soin la media type negotiation et la durée de vie des objets

Il n’est pas nécessaire de tout comprendre dès le départ. En gardant d’abord à l’esprit que « Media Foundation est une plateforme de traitement multimédia, avec COM profondément intégré à ses frontières », la documentation comme le code deviennent beaucoup plus faciles à suivre.

9. Références

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Cet article est directement lié aux services suivants.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

Qu'est-ce que Media Foundation ? Est-ce différent de COM ?
Media Foundation est une plateforme de traitement multimédia de Windows pour gérer la vidéo et l'audio ; l'API dans son ensemble n'est pas purement du COM en tant que tel. Cependant, les frontières entre les composants tels que source, transform, sink, activation, attributes et callback sont exprimées via des interfaces COM, si bien que les notions d'IUnknown, HRESULT, GUID et apartment apparaissent naturellement lorsqu'on l'utilise. Il est plus exact de la considérer comme « une plateforme de traitement multimédia dont les frontières sont profondément imprégnées de COM ».
Pourquoi faut-il à la fois MFStartup et CoInitializeEx ?
Parce que leurs rôles sont différents. CoInitializeEx initialise la bibliothèque COM, tandis que MFStartup initialise la plateforme Media Foundation. L'initialisation COM seule ne suffit pas ; l'initialisation côté Media Foundation est également nécessaire. En pratique, décider à l'avance quel thread utilise Media Foundation, s'il doit être en STA ou en MTA, et qui a la responsabilité de MFStartup / MFShutdown ainsi que de CoInitializeEx / CoUninitialize facilite grandement la gestion ultérieure des callbacks et de l'intégration avec l'interface utilisateur.
Comment choisir entre Source Reader, Sink Writer, Media Session et MFT ?
Si vous voulez extraire des trames ou des échantillons d'un fichier ou d'une caméra, le Source Reader est le point d'entrée. Si vous voulez écrire de l'audio ou de la vidéo générés dans un fichier, c'est le Sink Writer. Si vous voulez confier à la plateforme la lecture, l'arrêt, le déplacement (seek), la synchronisation audio/vidéo et le contrôle de qualité, il vaut mieux raisonner autour de la Media Session. Pour insérer vos propres décodeurs ou convertisseurs dans le pipeline, vous passez au MFT, mais comme les contrats de type COM y sont très présents, il est recommandé de d'abord vérifier lequel des trois premiers est réellement nécessaire.
À quoi faut-il faire attention avec les callbacks asynchrones de Media Foundation ?
Le traitement asynchrone de Media Foundation utilise une work queue dont le thread est en MTA ; l'implémentation devient donc plus simple si le côté application se rapproche également du MTA. Il est important de rendre l'implémentation d'IMFSourceReaderCallback thread-safe et de ne jamais toucher directement, depuis le callback, aux objets STA du thread d'interface utilisateur. Si une mise à jour de l'interface est nécessaire, ne renvoyez que le résultat vers le thread d'interface utilisateur. Il faut également noter que le mode synchrone ou asynchrone du Source Reader est fixé à la création et ne peut plus être modifié par la suite.

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog