50
Programmation Multimédia Master d’Informatique 2005-2006 Université de Reims Champagne-Ardennes Page 1/50 Programmation Multimédia Cours de programmation DirectShow Pascal Mignot / Pascal Gardeur Partie 1 : Programmation COM Chaîne de caractères sous Windows Principes directeur de DirectShow Caractéristique d’un graphe Construction de Graphes Contrôles élémentaires d’un graphe Informations supplémentaires sur un graphe de capture de flux.

Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

  • Upload
    others

  • View
    10

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 1/50

Programmation Multimédia

Cours de programmation DirectShow Pascal Mignot / Pascal Gardeur

Partie 1 : • Programmation COM

• Chaîne de caractères sous Windows

• Principes directeur de DirectShow

• Caractéristique d’un graphe

• Construction de Graphes

• Contrôles élémentaires d’un graphe

• Informations supplémentaires sur un graphe de capture de flux.

Page 2: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 2/50

COM

1. COM = Component Object Model Définition : modèle de programmation orienté objet conçu pour promouvoir l’interopérabilité logicielle. Il permet la coopération entre plusieurs applications ou « composants », même si ils ont été codés dans des langages de programmations différents, par des fabricants différents ou sont utilisés sur des machines possédant des systèmes d’exploitation différents.

Un objet COM: • dérive de IUnknown (objet de base). • Objet COM : agrégation d’objets dérivés de IUnknown.

Exemple : IUnkown

D E

A C B

F si G = D ∪ E + C + F, alors G peut être manipulé avec l’une des interfaces quelconques d’un des objets qu’il contient ou dont il dérive.

Comme pour les objets C++ standard, chaque classe a ses méthodes spécifiques (propres).

• Un identificateur est associé :

o à chaque classe (class-id ou CLSID). o à chaque interface (interface-id ou IID).

• La cohérence des références à l’objet lui-même où à l’une des parties qui le compose est

gérée grâce à des compteurs de référence. • Utilisation d’un objet COM:

o Initialisation : l’interface COM doit être initialisé. o Création de l’instance d’un objet : à partir de sa CLSID et de son IID. o A partir de cette instance, on peut récupérer l’interface spécifique à l’une des classes dont

il dérive. � Chaque interface étant associée à une classe, il est possible d’acquérir l’une des

interfaces quelconques sur l’un des objets qui compose l’objet lui-même. � Le compteur de référence permet de multiplier le nombre d’instances de cet

objet en conservant des références correctes. � De façon similaire, le compteur de référence peut être utilisé pour conserver des

références sur l’une des sous-parties de l’objet. Exemple : cas de la surface dans une texture, …

o Le relâchement des interfaces permet de libérer un objet dès qu’on n’y fait plus référence. o Fin d’un programme : l’interface COM doit être désinitialisée.

Page 3: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 3/50

2. Fonctions de base pour l’interface COM a. Initialisation de l’interface COM

HRESULT CoInitialize(NULL); Attention, pour une application multithreads, utiliser CoInitializeEx pour gérer la façon dont les objets pourront être partagés (voir les articles techniques sur le site à ce sujet).

Au retour :

S_OK / S_FALSE : réussi / déjà été initialisée. RPC_E_CHANGED_MODE : déjà été initialisée avec un mode de gestion différent.

b. Création d’une instance d’un objet

STDAPI CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv);

Paramètres

Rclsid : [in] Identificateur de la classe. pUnkOuter : [in] On utilisera en général toujours NULL. dwClsContext : [in] CLSCTX_INPROC_SERVER, (Permet IMoniker::BindToObject). riid : [in] Identificateur de l’interface. ppv : [out] Pointeur sur l’instance de l’objet COM créé (ou NULL si erreur).

Valeurs de retour

S_OK : réussi. REGDB_E_CLASSNOTREG / CLASS_E_NOAGGREGATION / E_NOINTERFACE

c. Désinitialisation de l’interface COM

void CoUninitialize(void); Libère tous les objets COM du thread courant.

Exemple:

ICaptureGraphBuilder2 *pCaptureGraph = NULL; HRESULT h = CoInitialize(NULL); ... h = CoCreateInstance(CLSID_CaptureGraphBuilder2,

NULL,CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void **)&pCaptureGraph);

... pCaptureGraph->Release(); CoUninitialize();

Page 4: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 4/50

3. Interface IUnknown Une fois l’objet COM créé, il expose toujours les méthodes suivantes :

a. Ajout de référence :

Incrémenter de 1 le compteur de référence de l’objet.

IUnknown::AddRef Syntaxe : ULONG AddRef(VOID);

Retour : le nouveau compteur de référence (pour tests).

Remarque : à la création de l’interface, le compteur vaut 1. A chaque nouvelle obtention d’une interface ou d’appels à la méthode AddRef , le compteur est incrémenté de 1. Utiliser la méthode Release pour le décrémenter

b. Relâchement de référence:

Décrémenter de 1 le compteur de référence de l’objet.

IUnknown::Release Syntaxe : ULONG Release(VOID);

Retour : le nouveau compteur de référence (pour tests).

Remarque : l’objet se désaloue automatiquement lorsque son compteur arrive à 0. L’appel à cette méthode doit succéder l’appel à AddRef, QueryInterface ou CoCreateInstance.

c. Gestion des interfaces : Cette méthode permet de déterminer si l’objet supporte une interface particulière. Si cela est le cas, permet d’acquérir l’interface sur l’objet agrégé (pointeur), et incrémente son compteur de référence.

IUnknown::QueryInterface Syntaxe : HRESULT QueryInterface(REFIID riid, LPVOID *ppvObj); Paramètres

riid : Identificateur de l’interface demandée. ppvObj : Adresse du pointeur de l’interface.

Retour S_OK : réussi. E_NOINTERFACE : Pas d’interface (peut dépendre des composants).

Remarques Si l’application n’a plus besoin de l’interface, il faut appeler le méthode Release.

Exemple:

// l’interface COM doit avoir été initialisée. HRESULT h; ICaptureGraphBuilder2 *pCaptureGraph = NULL; IGraphBuilder *pGraph = NULL; IMediaControl *pControl = NULL;

Page 5: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 5/50

h = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, (void **)&pCaptureGraph);

h = pCaptureGraph->GetFiltergraph(&pGraph); h = pGraph->QueryInterface(IID_IMediaControl,(void **)&pControl); ... pControl->Release(); pGraph->Release(); pCaptureGraph->Release(); // l’interface COM doit être désinitialisée.

Page 6: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 6/50

Aparté : gestion des chaînes de caractères sous Windows

Cette partie est nécessaire car certaines méthodes que nous étudierons utilisent certaines conventions particulières sur les chaînes de caractères: Il y a 2 types de caractère sous Windows :

• Caractères ASCII classiques (8 bits) : Type associé : char • Caractères Unicode (16 bits, caractères étendus) : Type associé : wchar_t

On y ajoute en général le modificateur unsigned pour manipuler directement les codes. Le nom des types associés à utiliser sous Windows :

CHAR = char UCHAR = unsigned char WCHAR = wchar_t TCHAR = WCHAR si UNICODE est défini, CHAR sinon.

Exemple avec écriture des types: CHAR a = ‘a’; CHAR b[10] = "abcdef"; WCHAR c = L’a’; WCHAR d[10] = L"abcdef"; TCHAR e[10] = TEXT("abcdef"); Le type TCHAR permet de générer des codes avec un type caractère variant (par exemple dans une bibliothèque en fonction de la façon dont le code principal manipule ses caractères). Type des pointeurs associés: pointeur pointeur sur une constante CHAR LPSTR LPCSTR WCHAR LPWSTR LPCWSTR TCHAR LPTSTR LPCTSTR Fonctions de manipulation des chaînes: CHAR WCHAR TCHAR printf wprintf _tprintf sprintf swprintf _stprintf fprintf fwprintf _ftprintf Idem en remplaçant printf par scanf. Les fonctions génériques suivantes s’adaptent automatiquement en fonction des paramètres passés (ils doivent tous être de même type) et sont à remplacer par strcat StringCchCat ou StringCchCatEx strncat StringCchCatN ou StringCchCatNEx strcpy StringCchCopy ou StringCchCopyEx strncpy StringCchCopyN ou StringCchCopyNEx sprintf StringCchPrintf ou StringCchPrintfEx vsprintf StringCchVPrintf ou StringCchVPrintfEx strlen StringCchLength Ces fonctions sont sécurisées pour éviter les dépassements de buffer (risque de sécurité). Voir la documentation MSDN.

Page 7: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 7/50

Conversion entre char et wide char: MultiByteToWideChar(CP_ACP,0,inString,inSz,outWideString,outSz);

Convertit la chaîne inString (char) en outWideString (wide char). WideCharToMultiByte(CP_ACP,0,inWideString,inSz,outString,outSz,

&DefC,&UseDef); Convertit la chaîne inWideString (wide char) en outWideString (char).

avec CP_ACP : code page (ANSI) inString / inWideString : chaîne en entrée. outWideString / outString : chaîne en sortie. inSz : taille de la chaîne à convertir ou -1 pour s’arrêter à \0. outSz : taille de la chaîne en sortie (taille maximale du buffer). DefC : caractère par défaut à utiliser pour les caractères wide char

n’ayant pas équivalent en char (ou NULL : plus rapide). UseDef : utiliser le caractère par défaut (ou NULL).

Exemple:

WCHAR Win[10] = L“abcdef”, Wout[10]; CHAR Sin[10] = “abcdef”, Sout[10]; MultiCharToWideChar(CP_ACP, 0, Sin, -1, Wout, 10); WideCharToMultiByte(CP_ACP, 0, Win, -1, Sout, 10, NULL, NULL); Compilation en Unicode:

Faire: #define UNICODE

ou:

Page 8: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 8/50

Principes directeurs de DirectShow

Le graphe de filtres Le graphe de filtres constitue la base de toute programmation sous DirectShow. Il permet de gérer les flux de données de manière simple en structurant les traitements (appelés filtres) sous forme d’un graphe orienté non cyclique:

• Les nœuds du graphe représentent les traitements à effectuer. • Les arêtes (orientées) du graphe représentent la direction des flux entre les filtres.

Il est ainsi possible d’effectuer des graphes de traitement complexe avec une relative simplicité.

Rôle du graphe :

• assembler les filtres (les connecter en veillant à la compatibilité des E/S). • gérer les filtres (en acquièrant sur ceux-ci les interfaces nécessaires à leurs manipulations). • contrôler et synchroniser les flux dans le graphe.

Le graphe de filtres est donc un médiateur entre le programmeur et les données. Le programmeur opère donc à un niveau assez élevé de développement. Il n’a pas à se soucier de la manipulation bas-niveau de données audio et vidéo.

Le graphe est un objet de type COM.

Les filtres Conceptuellement, un filtre peut être considéré comme une boîte noire:

• qui appartient à une classe spécialisée en fonction du travail à accomplir. • avec des entrées et/ou des sorties.

o chacune de ces entrées/sorties est conceptualisée sous forme d’une borne de connexion (pin).

o une borne d’entrée permet de recevoir des données depuis un autre filtre. o une borne de sortie permet d’envoyer des données vers un autre filtre.

• avec une ou plusieurs interfaces spécialisées, associées à la classe du filtre qui permettent de contrôler et de configurer le comportement du filtre.

• la configuration interne du filtre est assurée par une interface graphique (interne) fournie par le filtre lui-même.

On distingue trois principaux types de filtres:

• Les filtres sources. • Les filtres de transformation. • Les filtres de rendu.

Page 9: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 9/50

Les filtres sont des objets COMs: la classe d’un filtre est définie par sa CLSID, les interfaces sont définies par leurs IIDs.

Les filtres sources Un filtre source n’a aucune borne d’entrée, et une ou plusieurs bornes de sortie.

Ce type de filtre produit un flux de données à partir:

• d’un support numérique (fichier, CD, DVD, …) • d’un périphérique d’acquisition (micro, webcam, caméscope numérique, tuner TV, etc…).

o S’il est directement supporté par Windows, le filtre correspondant existe déjà. o Pour tout nouveau matériel, à partir du moment où son pilote (driver) répond au modèle

WDM (Windows Driver Model), celui-ci installe également les filtres DirectShow nécessaires à l’utilisation du périphérique.

• d’une connexion réseau (streaming). • …

Il faut au minimum un filtre de ce type dans un graphe (pour « nourrir » le graphe avec des données). On peut utiliser autant de filtres d’entrés que de sources nécessaires à l’exécution de la tâche.

Les filtres de transformation Un filtre de transformation modifie le flux de données qui le traverse. Il reçoit ses données en provenance d’un ou de plusieurs autres filtres (sources/transformation). Il transmet le flux transformé vers un ou plusieurs autres filtres (transformation/rendu).

Il a donc au moins une borne d’entrée et au moins une borne de sortie.

Les filtres de transformations les plus couramment utilisés sont les suivants:

• parser = séparer les données contenue dans un flux pour former plusieurs flux de sortie (exemple: séparer l’audio de la vidéo).

• tee = dupliquer le flux d’entrée en plusieurs flux identique de sortie. • multiplexage = combiner plusieurs flux en un seul (combiner un flux vidéo et un flux audio

pour ensuite les enregistrer dans un ficher par exemple) • codec = convertir les données d’un flux en un autre format (compression / décompression). • sous-titrage = ajouter une couche de sous-titres à un flux vidéo. • montage et transition • …

L’installation de nouveaux Codecs rend disponible les méthodes de compression et décompression associées sous forme de nouveau filtre. Dans certains cas, il peut être nécessaire d’enregistrer manuellement ces filtres.

Les filtres de rendu Un filtre de rendu est placé en fin de chaîne de traitement et permet de terminer le traitement:

• par un rendu: • vidéo : création automatique (ou manuelle) d’une fenêtre (avec son handle) et rendu du flux

dans la fenêtre en utilisant DirectDraw. • audio : rendu automatique en utilisant DirectAudio.

Page 10: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 10/50

Toutes des ressources nécessaires sont automatiquement allouées et désallouées, la synchronisation vidéo/audio est assurée par le graphe.

• par l’enregistrement dans un fichier. • par sa transmission sur le réseau.

Ce type de filtre ne possède que des bornes d’entrée. Un graphe doit contenir au moins un filtre de rendu. On peut éventuellement multiplier le nombre de filtres de rendu:

• l’audio et la vidéo sont rendus par deux filtres de rendu différents (un filtre de rendu audio et un filtre de rendu vidéo).

• on peut effectuer un rendu vidéo tout en enregistrant ce même flux dans un fichier. • deux flux vidéo issus de deux chemins différents dans le graphe peuvent être rendu dans

deux fenêtres différentes.

Construction d’un graphe à partir de filtres Pour construire un graphe, les filtres peuvent être assemblés avec une grande liberté à partir du moment où les contraintes suivantes sont respectées:

• toute branche d’un graphe commence par un filtre d’entrée et se termine par un filtre de sortie (cohérence du graphe).

• les comptabilités entre les entrées et les sorties sont respectées. Chaque entrée/sortie n’accepte que certains types et format de flux de données. Autrement dit, une connexion entre deux filtres n’est possible que si il existe un pin de sortie sur le premier filtre et un pin d’entrée sur le sortie tels que:

o le type du flux de données est le même (audio, vidéo, …) o le format du flux en sortie est compatible avec le format du flux en entrée.

Un pin d’entrée d’un filtre peut supporter plusieurs formats différents.

Remarque: toutes les pins d’entrées/sortie des filtres n’ont pas besoin d’être connecté (certaines entrées/sorties peuvent être ignorées).

Exemple de graphe des filtres

Cette image d’exemple est issue de GraphEdit, un programme dont on va rapidement expliquer l’intérêt et le fonctionnement la section suivante.

Page 11: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 11/50

GraphEdit Le GraphEdit est un utilitaire fourni par Microsoft avec le SDK (Software Development Kit) de DirectX (sous forme d’extra).

Utilisation pour les tests et le maquettage

Il permet de s’habituer à la manipulation de graphes de filtre, de construire interactivement le graphe souhaité pour l’application envisagée et d’effectuer directement des tests afin de vérifier que le graphe envisagé est cohérent et donne le résultat cohérent.

Il est donc vivement conseillé, dans la mesure du possible, de toujours concevoir le graphe correspondant à l’application multimédia souhaitée avec GraphEdit avant d’écrire le code correspondant. Evidemment, le programme DirectShow du même graphe offre un niveau de flexibilité et de contrôle supérieur à celui proposé dans GraphEdit.

Les fonctionnalités d’aide à la conception de GraphEdit

• Lors de l’insertion d’un filtre, la liste des filtres disponibles est rangée par catégorie :

Le GUID du filtre de compression MP3 est : {33D9A761-90C8-11D0-BD43-00A0C911CE86}

ce qui va nous permettre d’y faire référence lors de notre programme DirectShow. Les filtres de base de DirectShow disposent déjà de leur GUID sous une forme plus

« humaine » (exemple: CLSID_VideoRenderer pour le filtre de rendu vidéo).

• Connexions entre filtres: simplement à la souris. • Connexion intelligente: lorsque l’on relie deux pins de format incompatible (mais de type

compatible) entre deux filtres, GraphEdit insère automatiquement les filtres nécessaires.

Page 12: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 12/50

Deux filtres ont été ajoutés :

• AVI Splitter : sépare les flux audio et vidéo (la borne non connectée correspond au flux audio).

• DV Video Decoder (convertisseur) qui prépare les données vidéo dans un format compatible avec le Video Renderer.

Le graphe est complet et peut fonctionner tel quel.

• Un rendu automatique peut s’effectuer à partir de n’importe quel pin de sortie en utilisant le menu contextuel.

GrapheEdit insère tous les filtres pour effectuer le rendu des flux provenant du filtre source (audio + vidéo si nécessaire) :

Contrôle interactif du graphe

GraphEdit permet de simuler de comportement du graphe en permettant son exécution de manière similaire à ce qui se fait avec un média player.

Page 13: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 13/50

Caractéristiques d'un graphe Cette partie est dédiée à l'extraction de l'ensemble des propriétés sur un graphe, à savoir:

• l'ensemble des filtres présents dans un graphe • pour chaque filtre: les propriétés et l'ensemble de ses bornes. • pour chaque borne: sa direction, son nom, éventuellement à quelle autre borne elle est

connectée, le format des données supportées.

C'est l'ensemble de ces méthodes qui permettent de connaître: • la topologie du graphe de filtres. • les propriétés générales de chaque filtre.

Interfaces associées:

Graphe : IFilterGraph, IGraphBuilder, IGraphBuilder2 Filtre : IBaseFilter ou toute interface qui en dérive Borne : IPin

1. Enumérateurs Les objets que nous allons étudier ont une méthode pour obtenir un objet énumérateur qui permet de lister ses composants internes:

• pour un graphe, les filtres qu'il contient. • pour un filtre, les bornes de ce filtre. • pour une borne, l'ensemble des formats de média supportés.

L'utilisation d'un énumérateur s'effectue dans les étapes suivantes (où les XXX sont à remplacer par les noms indiqués dans la table ci-dessous): 1. Faire appel à la méthode EnumXXXs pour récupérer l'interface sur l'énumérateur IEnumXXX. 2. Utiliser les méthodes Next/Skip/Reset de l'interface IEnumXXX pour énumérer les objets. Par la suite, on notera IEnumXXXs cette méthode d'énumération: elle permet d'obtenir une interface IEnumXXX permettant d'énumérer les objets de type XXX (voir la table ci-dessous).

IEnumXXX::Next

Syntaxe : HRESULT Next(ULONG cObjs, IXXX **ppObjs, ULONG *pcFetched); Paramètres

cObjs : [in] Nombre d’objets à récupérer. ppObjs : [out] tableau de taille cObjs qui sera rempli avec les pointeurs de IXXX. pcFetched : [out] Pointeur de la variable qui reçoit le nombre d’objets récupérés. (peut

être NULL si cObjs = 1). Retour : Valeur HRESULT (voir la doc: VFW_E_ENUM_OUT_OF_SYNC) Remarque : Cette méthode récupère cObjs pointeurs d’objets, à partir de la position

courante. Si la méthode réussie, les références des interfaces IXXX sont incrémentées. Il faut effectuer un Release sur les objets récupérés.

Classe

Méthode pour obtenir l'énumérateur

Enumérateur IEnumXXX

Objets récupérés lors de l'énumération IXXX

IFilterGraph EnumFilters IEnumFilters IBaseFilter IBaseFilter EnumPins IEnumPins IPin IPin EnumMediaTypes IEnumMediaTypes AM_MEDIA_TYPE

Page 14: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 14/50

IEnumXXX::Skip

Syntaxe : HRESULT Skip(ULONG cObjs); Paramètres

cPins : [in] Nombre d’objets à passer. Retour : Valeur HRESULT (voir la doc) Remarque : comme pour Next, l'énumération peut devenir inconsistante. Dans ce cas,

la méthode renvoie VFW_E_ENUM_OUT_OF_SYNC => faire un Reset.

IEnumXXX::Reset

Syntaxe : HRESULT Reset(void); Retour : S_OK;

2. Information sur un graphe

Cette méthode permet d'énumérer les filtres dans un graphe. Ceci va nous permettre: 1. de connaître le nombre de filtres dans un graphe. 2. de récupérer l'interface sur chaque filtre inséré dans le graphe. 3. pour chaque filtre récupéré, on pourra alors acquérir plus d'informations sur ce

filtre (voir section suivante).

IFilterGraph::EnumFilters Syntaxe : HRESULT EnumFilters(IEnumFilters **ppEnum); Paramètres

ppEnum : [out] Adresse du pointeur sur l’interface IEnumFilters. (Release!) Retour : Valeur HRESULT (voir la doc)

3. Information sur un filtre Les informations que l'on peut récupérer sur un filtre sont:

• le nom du filtre avec QueryFilterInfo. • l'ensemble des bornes avec EnumPins.

IBaseFilter::QueryFilterInfo Syntaxe : HRESULT QueryFilterInfo(FILTER_INFO *pInfo); Paramètres

pInfo : [out] Pointer sur une structure FILTER_INFO. Retour : Valeur HRESULT (voir la doc) Remarque : Release !

FILTER_INFO Structure

Syntaxe typedef struct _FilterInfo { WCHAR achName[MAX_FILTER_NAME]; IFilterGraph *pGraph; } FILTER_INFO;

Page 15: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 15/50

Membres achName : Nom du filtre. (terminé par NULL) pGraph : Si le filtre fait parti d’un graphe, contient son pointeur. NULL sinon.

Remarque : Si pGraph != NULL => Release !

Exemple: affichage des filtres insérés dans le graphe et renvoie le nombre de filtres. UINT ViewFilters(IFilterGraph *Graph) { UINT i=0; IBaseFilter *Filter = NULL; IEnumFilters *Enum = NULL;

FILTER_INFO Info; Graph->EnumFilters(&Enum); while(Enum->Next(1,&Filter,NULL) == S_OK) { Filter->QueryFilterInfo(&Info); printf("%d : %S\n",++i,Info.achName); SAFE_RELEASE(Info.pGraph); SAFE_RELEASE(Filter); } SAFE_RELEASE(Enum); return i; }

Exemple 2: utilisation de la fonction ci-dessus avec un IGraphBuilder (rappel: il dérive de IFilterGraph):

Méthode 1: utiliser les dynamic casts. Rappel: ceci n'est possible pour appeler une fonction f(A)

avec f(B) si et seulement si B dérive de A (l'inverse produit un résultat imprévisible). IGraphBuilder *pGraph; // pGraph est construit ici ViewGraphInfo(dynamic_cast<IFilterGraph*>(pGraph));

Méthode 2: utiliser QueryInterface pour rendre compatible les objets COM, en écrivant une surcharge de la fonction:

void ViewGraphInfo(IGraphBuilder *Graph) { IFilterGraph *FilterGraph; Graph->QueryInterface(IID_IFilterGraph,

(void **)&FilterGraph); ViewGraphInfo(FilterGraph); SAFE_RELEASE(FilterGraph);

} Avantage avec les objets COMs: devrait marcher dans les deux sens (que A dérive de B ou B dérive de A). Autrement dit, la fonction ViewFilter aurait pu être écrite pour des IGraphBuilder. Il aurait été possible dans la fonction de conversion de faire un QueryInterface sur le IFilterGraph (enrichissement des fonctionnalités de l'objet).

IBaseFilter::EnumPins Syntaxe : HRESULT EnumPins(IEnumPins **ppEnum); Paramètres

ppEnum : [out] Adresse du pointeur qui reçoit l’interface IEnumPins. Retour : Valeur HRESULT (voir la doc) Remarque : Release !

Page 16: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 16/50

4. Information sur les bornes d'un filtre

Pour toute borne (pin) d'un filtre, on a les informations suivantes: • le nom de la borne • la direction de passage du flux sur cette borne (entrée ou sortie) • l'ensemble des formats acceptés par cette borne.

Si la borne est connectée: • la borne (d'un autre filtre) sur laquelle elle est connectée. • le format du flux actuel.

Une borne contient en plus les informations techniques suivantes: • un pointeur vers le filtre auquel elle appartient. • une chaîne unique identifiant la borne (généralement quelques caractères).

L'interface IPin est exposée par les bornes d'entrée et de sortie.

Remarque: ne pas utiliser les méthodes de cette interface pour effectuer des connexions ou changer l'état de la borne. Utiliser plutôt les interfaces du graphe afin que les synchronisations nécessaires soient effectuées.

Les méthodes de IPin permettant d'obtenir des informations sur une borne sont les suivants:

IPin::QueryPinInfo Syntaxe : HRESULT QueryPinInfo(PIN_INFO *pInfo); Paramètres

pInfo : [out] Pointeur sur une structure PIN_INFO. Retour : Valeur HRESULT (voir la doc) Remarque : Release !

PIN_INFO Structure Syntaxe

typedef struct _PinInfo { IBaseFilter *pFilter; PIN_DIRECTION dir; WCHAR achName[MAX_PIN_NAME]; } PIN_INFO;

Membres pFilter : Pointeur sur l’interface IBaseFilter du filtre propriétaire. dir : Direction de la pin (PINDIR_INPUT ou PINDIR_OUTPUT). achName : Nom de la pin.

Remarque : Release ! (Voir la doc)

Method Description ConnectedTo Récupère la pin connectée à celle-ci. ConnectionMediaType Récupère le type de la connexion courante. QueryPinInfo Récupère des informations sur la pin (nom, filtre propriétaire, direction). QueryId Récupère l’identificateur de la pin. QueryAccept Détermine si la pin accepte un type spécifique. EnumMediaTypes Enumère les types disponibles sur la pin. QueryInternalConnections Récupère les pins connectés à l’intérieur du filtre. QueryDirection Récupère la direction de la pin (entrée ou sortie).

Page 17: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 17/50

Exemple: affichage des bornes d'un filtre. void ViewPinInfo(IPin *Pin) { PIN_INFO Info; Pin->QueryInfo(&Info); printf("%-3s:%s\n",(Info.dir==PINDIR_INPUT?"In":"Out"),

Info.achName); SAFE_RELEASE(Info.pFilter); } void ViewPins(IBaseFilter *Filter) { IEnumPins *Enum = NULL; IPin *Pin = NULL; Filter->EnumPins(&Enum); while(Enum->Next(1,&Pin,NULL) == S_OK) { ViewPinInfo(Pin); SAFE_RELEASE(Pin); } SAFE_RELEASE(Enum); }

IPin::QueryDirection Syntaxe : HRESULT QueryDirection(PIN_DIRECTION *pPinDir); Paramètres

pPinDir : [out] Pointeur sur la structure PIN_DIRECTION. Retour : Valeur HRESULT (voir la doc) Remarque : la valeur récupérée ici est la même que celle obtenue avec QueryPinInfo dans

la structure PIN_INFO.

IPin::QueryId Syntaxe : HRESULT QueryId(LPWSTR *Id); Paramètres

Id : [out] Pointeur sur une chaîne de caractère. Retour : Valeur HRESULT (voir la doc) Remarques : La chaîne est allouée en utilisant la fonction Win32 CoTaskMemAlloc. Il faut

la libérer avec CoTaskMemFree. l'Id d'une borne identifie de manière unique la borne.

Utiliser la fonction de désallocation indiquée ci-dessus. Exemple:

Exemple: lecture de l'Id LPWSTR Id; Pin->QueryId(&Id); printf("Id=[%S] ",Id); // rappel: c'est un wide char. CoTaskMemFree(Id);

IPin::ConnectedTo

Cette méthode va nous permettre pour une borne quelconque, de savoir si elle est connectée et sur quelle autre borne elle est connectée.

Syntaxe : HRESULT ConnectedTo(IPin **ppPin); Paramètres

ppPin : [out] Adresse du pointeur sur l’interface IPin de l’autre pin. (!= NULL).

Page 18: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 18/50

Retour S_OK : réussi VFW_E_NOT_CONNECTED : pin non connectée. E_POINTER : Argument du pointeur NULL.

Remarque : Release ! Exemple: pour une borne, affiche la connexion

void ViewLinkInfo(IPin *LocalPin) { IPin *Pin = NULL; FILTER_INFO FInfo; PIN_INFO PInfo; HRESULT h; h = LocalPin->ConnectedTo(&Pin); if (h == VFW_E_NOT_CONNECTED)

WriteInfo("-> NOT_CONNECTED"); else { Pin->QueryPinInfo(&PInfo); PInfo.pFilter->QueryFilterInfo(&FInfo); WriteInfo("-> %S[%S] ",

FInfo.achName,PInfo.achName); } } Voici la trace générée pour l'ensemble des filtres utilisées pour le rendu d'un fichier vidéo (sans son) par les fonctions ci-dessus. Les méthodes pour connaître le type de flux associé à une borne sont vues ci-dessous.

Filter in graph = 4 Filter : ruby.avi 1 Out Id=[Output] Output -> AVI Splitter[input pin] Type=Stream Filter : AVI Splitter 1 In Id=[input pin] input pin -> ruby.avi[Output] Type=Stream 2 Out Id=[Stream 00] Stream 00 -> AVI Decompressor[XForm In] Type=Video Filter : AVI Decompressor 1 Out Id=[Out] XForm Out -> Video Renderer[VMR Input0] Type=Video 2 In Id=[In] XForm In -> AVI Splitter[Stream 00] Type=Video Filter : Video Renderer 1 In Id=[VMR Input0] VMR Input0 -> AVI Decompressor[XForm Out] Type=Video

IPin::QueryInternalConnections Sur certains filtres, des connexions internes entre bornes sont faites (exemple: sur un filtre tuner, pour connecter le tuner ou l'entrée composite sur la sortie vidéo).

Syntaxe: HRESULT QueryInternalConnections(IPin **apPin,ULONG *nPin);

Paramètres

apPin : [out] Adresse d’un tableau de pointeurs d’IPin (à allouer avant l'appel).

nPin : [in, out] En entrée, spécifie la taille du tableau. En retour, la valeur est actualisée au nombre de pointeurs retournés.

Page 19: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 19/50

Retour : Valeur HRESULT (voir la doc)

Remarque : Release !

IPin::ConnectionMediaType Syntaxe : HRESULT ConnectionMediaType(AM_MEDIA_TYPE *pmt); Paramètre

pmt : [out] Pointeur sur une structure AM_MEDIA_TYPE. Retour : Valeur HRESULT (voir la doc) Remarque : Voir l'exemple de code après la description des formats pour voir

comment utiliser cette méthode.

IPin::EnumMediaTypes Syntaxe : HRESULT EnumMediaTypes(IEnumMediaTypes **ppEnum); Paramètres

ppEnum : [out] Adresse du pointeur sur l’interface IEnumMediaType. Retour S_OK : réussi E_OUTOFMEMORY : pas assez de mémoire E_POINTER : argument NULL. Remarques

o comme d'habitude IEnumMediaTypes (interface IEnumXXX), on utilise les méthodes Next/Step/Reset.

o Voir l'exemple de code après la description des formats pour voir comment utiliser cette méthode.

5. Formats supportés sur une borne AM_MEDIA_TYPE Structure

Syntaxe typedef struct _MediaType { GUID majortype; GUID subtype; BOOL bFixedSizeSamples; BOOL bTemporalCompression; ULONG lSampleSize; GUID formattype; IUnknown *pUnk; ULONG cbFormat; [size_is(cbFormat)] BYTE *pbFormat; } AM_MEDIA_TYPE; Membres

majortype : Globally Unique IDentifier (GUID) : le type majeur du flux. subtype : Le type mineur du flux. bFixedSizeSamples : Information seulement. bTemporalCompression : Information seulement. lSampleSize : Taille du sample en bytes. (0 pour les données compressées). Formattype : Structure utilisée:

Format type Format structure FORMAT_None None. FORMAT_DvInfo DVINFO

Page 20: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 20/50

FORMAT_MPEGVideo MPEG1VIDEOINFO FORMAT_MPEG2Video MPEG2VIDEOINFO FORMAT_VideoInfo VIDEOINFOHEADER FORMAT_VideoInfo2 VIDEOINFOHEADER2 FORMAT_WaveFormatEx WAVEFORMATEX GUID_NULL None

pUnk : Non utilisé. cbFormat : Staille du block du format, en bytes. pbFormat : Pointeur sur le block du format.

Remarques Quand deux pins veulent se connecter, ils négocient un type, défini par la structure AM_MEDIA_TYPE. Si elles ne se mettent pas d’accord sur un type, elles ne peuvent pas se connecter. Le type majeur défini la catégorie générale (vidéo, audio, …). Le type mineur précise le type majeur (Par exemple, les types mineurs d’une vidéo sont 8-bit, 16-bit, 24-bit, et 32-bit RGB). Exemple : if (pmt->formattype == FORMAT_VideoInfo) { // Check the buffer size. if (pmt->cbFormat >= sizeof(VIDEOINFOHEADER)) { VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat); /* Access VIDEOINFOHEADER members through pVih. */ } }

Major Types GUID Description MEDIATYPE_AnalogAudio Analog audio. MEDIATYPE_AnalogVideo Analog video. MEDIATYPE_Audio Audio. MEDIATYPE_AUXLine21Data Line 21 data. Used by closed captions. MEDIATYPE_File File. (Obsolete) MEDIATYPE_Interleaved Interleaved audio and video. Used for Digital Video (DV). MEDIATYPE_LMRT Obsolete. Do not use. MEDIATYPE_Midi MIDI format. MEDIATYPE_MPEG2_PES MPEG-2 PES packets. MEDIATYPE_MPEG2_SECTION MPEG-2 section data MEDIATYPE_ScriptCommand Data is a script command, used by closed captions. MEDIATYPE_Stream Byte stream with no time stamps. MEDIATYPE_Text Text. MEDIATYPE_Timecode Timecode data. MEDIATYPE_URL_STREAM Obsolete. Do not use. MEDIATYPE_Video Video.

Audio Subtypes (voir la documentation pour les autres sous-types) GUID Description MEDIASUBTYPE_PCM PCM audio. MEDIASUBTYPE_PCMAudioObsolete Obsolete. Do not use. MEDIASUBTYPE_MPEG1Packet MPEG1 Audio packet. MEDIASUBTYPE_MPEG1Payload MPEG1 Audio Payload. MEDIASUBTYPE_MPEG2_AUDIO MPEG-2 audio data

Page 21: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 21/50

MEDIASUBTYPE_DVD_LPCM_AUDIO DVD audio data MEDIASUBTYPE_MPEG2_AUDIO MPEG-2 audio data MEDIASUBTYPE_DRM_Audio Corresponds to WAVE_FORMAT_DRM. MEDIASUBTYPE_IEEE_FLOAT Corresponds to WAVE_FORMAT_IEEE_FLOAT MEDIASUBTYPE_DOLBY_AC3 Dolby data MEDIASUBTYPE_DOLBY_AC3_SPDIF Dolby AC3 over SPDIF. MEDIASUBTYPE_RAW_SPORT ~ MEDIASUBTYPE_DOLBY_AC3_SPDIF. MEDIASUBTYPE_SPDIF_TAG_241h ~ MEDIASUBTYPE_DOLBY_AC3_SPDIF.

Exemples:

o fonction libérant une structure AM_MEDIA_TYPE allouée (par exemple pour IPin::ConnectionMediaType)

void FreeMediaType(AM_MEDIA_TYPE& mt) { SAFE_RELEASE(mt.pUnk); if (mt.cbFormat != 0) CoTaskMemFree((PVOID)mt.pbFormat); ZeroMemory(&mt,sizeof(AM_MEDIA_TYPE)); }

o fonction libérant un pointeur sur une structure de type AM_MEDIA_TYPE allouée par une méthode d'énumération des formats

(par exemple lors d'un IEnumMediaTypes::Next) void DeleteMediaType(AM_MEDIA_TYPE *pmt) { if (pmt == NULL) return; FreeMediaType(*pmt); CoTaskMemFree((PVOID)pmt); }

o fonction affichant le major type du média d'une borne connectée (pour les 2 types les plus courants parmi les 16 possibles):

void ViewPinFormatMajorType(IPin *Pin) { AM_MEDIA_TYPE mt; HRESULT s = Pin->ConnectionMediaType(&mt); if (s==VFW_E_NOT_CONNECTED) printf("NotConnected"); GUID major = mt.majortype; FreeMediaType(mt); if (IsEqualGUID(major,MEDIATYPE_Audio)) printf("Audio"); else if (IsEqualGUID(major,MEDIATYPE_Video)) printf("Video"); }

o fonction récupérant la première borne libre de direction et de type souhaitée sur un filtre:

IPin *GetPin(IBaseFilter *Filter,PIN_DIRECTION Dir,GUID FmtType){ IEnumPins *Enum = NULL; IEnumMediaTypes *EnumFmt = NULL; IPin *Pin = NULL, *Pin2=NULL; AM_MEDIA_TYPE *pmt; PIN_DIRECTION PinDir;

Page 22: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 22/50

BOOL bFmtType = FALSE; Filter->EnumPins(&Enum); while(Enum->Next(1,&Pin,NULL) == S_OK) { Pin->QueryDirection(&PinDir); if (PinDir != Dir) { SAFE_RELEASE(Pin); continue; } BOOL Connected

= (Pin->ConnectedTo(&Pin2) != VFW_E_NOT_CONNECTED); SAFE_RELEASE(Pin2); if (Connected) { SAFE_RELEASE(Pin); continue; } Pin->EnumMediaTypes(&EnumFmt); while( EnumFmt->Next(1,&pmt,NULL) == S_OK ) { bFmtType = IsEqualGUID(pmt->majortype,FmtType); DeleteMediaType(pmt); if (bFmtType) break; } SAFE_RELEASE(EnumFmt); if (bFmtType) break; SAFE_RELEASE(Pin); } SAFE_RELEASE(Enum); return Pin; }

6. Pour aller plus loin Pour rechercher des filtres en fonction de critères de recherche, utiliser la méthode: IFilterMapper2::EnumMatchingFilters Ne pas tenter d'utiliser cette méthode avant d'avoir abordé la partie sur l'énumération des devices sur un système.

Page 23: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 23/50

Construction de Graphe

1. Principes

Pour construire un graphe il faut:

1. ajouter les filtres nécessaires à ceux utilisables par le graphe: IFilterGraph::AddFilter : ajouter un filtre IGraphBuilder::AddSourceFilter : ajouter un filtre qui lit un media. IFilterGraph::RemoveFilter : supprimer un filtre (avec déconnexion) attention, ces méthodes ne construisent pas le graphe (pas de connexion).

2. récupérer les filtres et les bornes que l'on souhaite utiliser pour les connexions: IFilterGraph::FindFilterByName : pour récupérer un filtre à partir de son nom. IPin::QueryAccept : pour savoir si un type de média est supporté par la borne d'un filtre. voir aussi les fonctions de récupération d'information sur un graphe pour énumérer et récupérer les filtres et les bornes.

3. effectuer les connections: IFilterGraph::ConnectDirect : connexion de deux bornes (sans intermédiaire). IFilterGraph::Disconnect : déconnexion d'une borne IGraphBuilder::Connect : connexion intelligente de deux bornes (en ajoutant éventuellement les filtres intermédiaires nécessaires).

ICaptureGraphBuilder2::RenderStream : connexion ‘semi’ intelligente de deux filtres (en ajoutant éventuellement un filtre intermédiaire spécifié).

Les méthodes de construction du graphe suivantes permettent également de construire automatiquement les graphes de rendu pour:

IGraphBuilder::Render : en spécifiant la borne de sortie dont il faut effectuer le rendu. IGraphBuilder::RenderFile : en spécifiant le nom d'un fichier multimédia.

Règles générales de construction d'un graphe:

1. Un filtre doit avoir été inséré dans le graphe avant de tenter de connecter ses bornes avec un autre filtre dans le graphe.

2. Il faut arrêter le graphe pour pouvoir le modifier.

Page 24: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 24/50

2. Création d'un filtre

IBaseFilter *pFilter = NULL; CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pFilter);

où clsid est la classe du filtre que l'on veut créer et qui est défini par:

• soit un identifieur de type CLSID_xxx, où xxx est le nom du filtre (voir la liste à l'entrée "DirectShow Filters" dans les "DirectShow Reference").

• soit le GUID associé à la classe que l'on peut créer de la façon suivante si le filtre résulte de l'installation de composants logiciels supplémentaires (comme des codecs):

i) consulter dans GraphEdit le GUID du filtre que l'on souhaite utiliser: Exemple: MPEG layer 3 Compressor : {33D9A761-90C8-11D0-BD43-00A0C911CE86}

ii) définir le GUID associé: DEFINE_GUID(CLSID_MP3Compress, 0x33D9A761, 0x 90C8, 0x11D0, 0xBD, 0x43, 0x00, 0xA0, 0xC9, 0x11, 0xCE, 0x86);

2. Méthodes de construction

Méthodes présentées dans cette section:

IFilterGraph AddFilter Ajouter un filtre dans le graphe. RemoveFilter Supprimer un filtre dans le graphe. FindFilterByName Trouver un filtre dans le graphe en spécifiant son nom. ConnectDirect Connecter deux pins directement. Disconnect Déconnecter une pin.

IGraphBuilder (hérite de IFilterGraph) Connect Connecter deux pins (avec intelligence). Render Ajouter une chaîne au filtre pour rendre le flux. RenderFile Construit un graphe qui joue un fichier. AddSourceFilter Ajouter un fichier source au graphe.

IBaseFilter EnumPins Enumère les pins de ce filtre. FindPin Trouver un pin spécifique.

IPin QueryAccept Détermine si la pin accepte un type spécifié.

Page 25: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 25/50

a. Gestion des filtres dans le graphe IFilterGraph::AddFilter

Syntaxe : HRESULT AddFilter(IBaseFilter *pFilter, LPCWSTR pName); Paramètres

pFilter : [in] Le filtre doit avoir été alloué avec CoCreateInstance. pName : [in] Ce nom est le nom "humain" que l'on donne au filtre.

Retour : Valeur HRESULT (voir la doc)

IGraphBuilder::AddSourceFilter Syntaxe : HRESULT AddSourceFilter(LPCWSTR lpwstrFileName,

LPCWSTR lpwstrFilterName, IBaseFilter **ppFilter);

Paramètres lpwstrFileName : [in] Nom du fichier à charger. lpwstrFilterName: [in] Nom du filtre source. ppFilter : [out] Le filtre est créé lors de l'appel.

Retour : Valeur HRESULT (voir la doc) Remarque : Release !

IFilterGraph::RemoveFilter Syntaxe : HRESULT RemoveFilter(IBaseFilter *pFilter); Paramètres

pFilter : [in] Filtre à supprimer. Retour : Valeur HRESULT (voir la doc) Remarque : Il n’est pas nécessaire de le déconnecter avant, mais le graphe doit être dans

l’état Stop. Sinon, la déconnexion (automatique) des pins risque d’échouer.

b. Récupération des filtres et des bornes

IFilterGraph::FindFilterByName Le nom recherché est le nom humain du filtre. Syntaxe HRESULT FindFilterByName(LPCWSTR pName,IBaseFilter **ppFilter); Paramètres

pName : [in, string] Nom du filtre à rechercher. ppFilter : [out] Adresse du pointeur sur le filtre, NULL si pas trouvé.

Retour : Valeur HRESULT (voir la doc) Remarque : Release !.

IBaseFilter::EnumPins = voir la partie "Information et structure d'un graphe de filtres".

Page 26: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 26/50

IBaseFilter::FindPin Syntaxe : HRESULT FindPin(LPCWSTR Id, IPin **ppPin ); Paramètres

Id : [in] Nom de la pin à rechercher. ppPin : [out] Adresse du pointeur sur la pin, NULL si pas trouvé.

Retour : Valeur HRESULT (voir la doc) Remarque : Release !

IPin::QueryAccept

Syntaxe : HRESULT QueryAccept(const AM_MEDIA_TYPE *pmt); Paramètre

pmt : [in] Pointeur sur AM_MEDIA_TYPE. Retour : Valeur HRESULT (voir la doc) Remarque : Test avec S_OK; ne pas utiliser la macro SUCCEEDED.

c. Connexions IGraphBuilder::Connect

Syntaxe : HRESULT Connect(IPin *ppinOut, IPin *ppinIn); Paramètres

ppinOut : [in] Pointeur sur la pin de sortie. ppinIn : [in] Pointeur sur la pin d’entrée.

Retour : Valeur HRESULT (voir la doc) Remarque : Cette méthode connecte deux pins directement ou non, en ajoutant des filtres

intermédiaires si nécessaire. Elle commence par essayer une connexion directe. Si elle échoue, elle va essayer tous les filtres déjà présents (dans n’importe quel ordre) dans le graphe ayant une entrée déconnectée. Si elle échoue de nouveau, elle va rechercher les filtres dans le registre (dans l’ordre de merit).

IFilterGraph::ConnectDirect

Syntaxe : HRESULT ConnectDirect(IPin *ppinOut, IPin *ppinIn, const AM_MEDIA_TYPE *pmt);

Paramètres ppinOut : [in] Pointeur sur la pin de sortie. ppinIn : [in] Pointeur sur la pin d’entrée. pmt : [in] Optionel, peut être NULL.

Retour : Valeur HRESULT (voir la doc)

IFilterGraph::Disconnect

Syntaxe : HRESULT Disconnect(IPin *ppin); Paramètre

ppin : [in] Pointeur sur la pin à déconnecter. Retour : Valeur HRESULT (voir la doc)

Page 27: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 27/50

Remarque : La méthode ne casse pas complètement la connexion. Pour ce faire, les deux bouts doivent être déconnectés. Pour supprimer le filtre entièrement, utiliser RemoveFilter.

d. Rendu automatique IGraphBuilder::Render

Syntaxe : HRESULT Render(IPin *ppinOut); Paramètre

ppinOut : [in] Pointeur sur une pin de sortie. Retour : Valeur HRESULT (voir la doc)

IGraphBuilder::RenderFile Syntaxe HRESULT RenderFile(LPCWSTRlpwstrFile, LPCWSTR lpwstrPlayList); Paramètres

lpwstrFile : [in] Nom du fichier à lire. lpwstrPlayList : [in] Réservé : doit être NULL.

Retour : Valeur HRESULT (voir la doc) Remarque : Si vous l’appelez une seconde fois, les fichiers seront joués en même temps. Exemple

hr = pGraph->RenderFile(L"C:\\Media\\Example.avi", 0); hr = pGraph->RenderFile(L"http://example.microsoft.com/Example.avi", 0);

e. Exemples

Rendu automatique: Un rendu simple de n’importe quel fichier audio/vidéo peut être effectué avec DirectShow. Ce rendu s’effectue en 3 étapes:

1. Création d’un graphe de filtres (classe FilterGraph) avec l’interface IGraphBuilder.

2. Appel de la méthode RenderFile du IGraphBuilder pour construire automatiquement le graphe nécessaire au rendu du filtre.

3. Récupération de l’interface IMediaControl sur le graphe de filtres, et utilisation de cette interface pour contrôler le flux (méthodes Run, Stop, Pause).

WCHAR *fname = L "ruby.avi"; IGraphBuilder *pGraph; CoInitialize (NULL); CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,

IID_IGraphBuilder, (void **) &pGraph); pGraph ->RenderFile(fname, NULL); // la partie ci-dessous sera traitée dans la section suivante IMediaControl *pMCtrl; pGraph->QueryInterface(IID_IMediaControl, (void **)&pMCtrl); pMCtrl->Run();

Page 28: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 28/50

MessageBox (NULL, “Cliquer pour terminer”, “DirectShow”, MB_OK); pMCtrl->Stop(); pMCtrl->Release(); // libération des ressources pGraph->Release(); CoUninitialize();

Ce programme est le même si on remplace le fichier avi par n’importe quel autre fichier audio (mp3) ou vidéo (divX).

Le graphe généré est le suivant:

Rendu manuel:

On reconstruit manuellement le graphe ci-dessus:

o Récupération de la première borne d'une direction donnée sur un filtre: IPin *GetPin(IBaseFilter *Filter, PIN_DIRECTION RequestedDir) { IEnumPins *Enum = NULL; IPin *Pin = NULL; PIN_DIRECTION Dir; Filter->EnumPins(&Enum); while(Enum->Next(1,&Pin,NULL) == S_OK) { Pin->QueryDirection(&Dir); if (Dir == RequestedDir) break; SAFE_RELEASE(Pin); } SAFE_RELEASE(Enum); return Pin; }

o Code de construction du graphe (obtenue par les deux lignes indiquées en bleu dans la partie précédente):

IGraphBuilder *pGraph; CoInitialize (NULL);

CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC, IID_IGraphBuilder, (void **) &pGraph);

WCHAR *fname = L"ruby.avi"; IBaseFilter *InputFileFilter = NULL; IBaseFilter *AviSplitter = NULL; IBaseFilter *AviDecompressor = NULL; IBaseFilter *VideoRenderer = NULL; IPin *PinOut = NULL, *PinIn = NULL;

Page 29: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 29/50

// Création de la liste de filtres pGraph->AddSourceFilter(fname, fname, &InputFileFilter); CoCreateInstance(CLSID_AviSplitter, NULL, CLSCTX_INPROC_SERVER,

IID_IBaseFilter, (void **)&AviSplitter); CoCreateInstance(CLSID_AVIDec, NULL, CLSCTX_INPROC_SERVER,

IID_IBaseFilter, (void **)&AviDecompressor); CoCreateInstance(CLSID_VideoRenderer, NULL, CLSCTX_INPROC_SERVER,

IID_IBaseFilter, (void **)&VideoRenderer); // Insertion des filtres dans le graphe pGraph->AddFilter(AviSplitter, L"Avi Splitter"); pGraph->AddFilter(AviDecompressor, L"Avi Decompressor"); pGraph->AddFilter(VideoRenderer, L"Video Renderer"); ViewGraphInfo(pGraph); // connexions PinOut= GetPin(InputFileFilter,PINDIR_OUTPUT); PinIn = GetPin(AviSplitter,PINDIR_INPUT); pGraph->ConnectDirect(PinOut,PinIn,NULL); SAFE_RELEASE(PinOut); SAFE_RELEASE(PinIn); PinOut= GetPin(AviSplitter,PINDIR_OUTPUT); PinIn = GetPin(AviDecompressor,PINDIR_INPUT);

pGraph->ConnectDirect(PinOut,PinIn,NULL); SAFE_RELEASE(PinOut); SAFE_RELEASE(PinIn); PinOut= GetPin(AviDecompressor,PINDIR_OUTPUT); PinIn = GetPin(VideoRenderer,PINDIR_INPUT); pGraph->ConnectDirect(PinOut,PinIn,NULL); SAFE_RELEASE(PinOut); SAFE_RELEASE(PinIn);

Remarques: i) la construction automatique n'est pas toujours possible (elle peut dépendre du

contexte).

ii) La fonction IPin *GetPin(IBaseFilter *Filter, PIN_DIRECTION Dir, GUID FmtType) est utilisable dans ce cadre mais seulement sur les bornes de sortie. En effet, une bonne partie des filtres configurent leurs major/minor type seulement qu'une fois que leur entrée est fixée.

Construction automatique partielle:

Sur n'importe quelle borne d'un graphe, il est possible de lancer un rendu automatique avec la méthode Render sur n'importe quelle borne n'importe où dans un graphe:

WCHAR *fname = L"ruby.avi"; IBaseFilter *InputFileFilter = NULL; IPin *PinOut = NULL, *PinIn = NULL; h=pGraph->AddSourceFilter(fname, fname, &InputFileFilter); PinOut = GetPin(InputFileFilter,PINDIR_OUTPUT,MEDIATYPE_Stream); pGraph->Render(PinOut);

Page 30: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 30/50

Contrôle d'un Graphe

1. Le Média Controller

L'interface IMediaControl est faite pour contrôler le flux des données à travers le graphe. Elle est disponible sur tous les objets qui dérivent de IFilterGraph.

On acquière cette interface de la façon suivante: IMediaControl *pMCtrl; pGraph->QueryInterface(IID_IMediaControl, (void **)&pMCtrl);

où pGraph est un pointeur vers l'interface d'un graphe de filtres.

Cette interface expose les méthodes suivantes:

Run – Pause – Stop – StopWhenReady – GetState

Note: o un graphe peut être dans trois états différents: Run/Pause/Stop. o Son état est "Run" si le graphe a des données qui transitent dans tous ses filtres. o Son état est "Stop" si tous les filtres du graphe sont arrêtés. o Son état est "Pause" s'il est momentanément arrêté, ou s'il est dans n'importe quel

état intermédiaire (état de transition). Ce dernier cas arrive notamment lorsque les filtres sont en train de s'initialiser.

IMediaControl::Run Syntaxe : HRESULT Run(void); Retour

S_FALSE : Le graphe se prépare, mais certains filtres sont encore en transition. S_OK : Tous les filtres ont terminé leur transition.

Remarques : Si le graphe est arrêté, il passe d’abord en pause avant de se lancer. Il reste dans cet état tant qu’il n’y aura pas d’appel à Pause ou Stop. Quand le flux se termine (fin du fichier par exemple), le graphe continue de tourner mais ne transite aucune donnée. Vous pouvez donc récupérer cet évènement pour arrêter le graphe. Si vous arrêtez le graphe en cours de route et le relancez, la reprise se fera à la position actuelle et ne recommencera pas du début. Pour ce faire, utilisez l’interface IMediaSeeking.

IMediaControl::Pause Syntaxe : HRESULT Pause(void); Retour:

S_FALSE : Le graphe se prépare, mais certains filtres sont encore en transition. S_OK : Tous les filtres ont terminé leur transition.

Remarque : Tant que le graphe est en pause, les données circulent dans les filtres et sont traitées mais ne sont pas rendues.

Page 31: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 31/50

IMediaControl::Stop Syntaxe : HRESULT Stop(void); Retour : Valeur HRESULT (voir la doc) Remarque : Si le graphe est en cours d’exécution, il passe d’abord en pause.

IMediaControl::StopWhenReady Syntaxe : HRESULT StopWhenReady(void); Retour : Valeur HRESULT (voir la doc) Remarque : Cette méthode est utile si vous voulez changer la position dans le flux pendant

que le graphe est arrêté. (Voir la doc).

IMediaControl::GetState Quand vous utilisez cette méthode, le graphe peut être dans un état de transition. Dans ce cas, la méthode attend que la transition se termine ou que le timeout expire.

Syntaxe : HRESULT GetState(LONG msTimeout, OAFilterState *pfs); Paramètres

msTimeout : [in] Timeout, en millisecondes, ou INFINITE. pfs : [out] Pointeur sur FILTER_STATE.

Retour S_OK : Réussi. VFW_S_STATE_INTERMEDIATE : Toujours en transition. VFW_S_CANT_CUE : En pause mais ne peut plus produire de données. E_FAIL : Echec.

Remarque : Evitez un timeout INFINITE, car les threads ne peuvent pas traiter les messages. Spécifiez des temps réduits pour rester réactifs aux interactions avec l’utilisateur.

FILTER_STATE Enumeration Syntaxe

typedef enum _FilterState { State_Stopped,State_Paused, State_Running } FILTER_STATE;

Exemple: utilisation du média controller pour lancer l'exécution d'un graphe. IMediaControl *pMCtrl; pGraph->QueryInterface(IID_IMediaControl, (void **)&pMCtrl); pMCtrl->Run(); MessageBox (NULL, "Cliquer pour terminer", "DirectShow", MB_OK); pMCtrl->Stop(); pMCtrl->Release();

Le media controller est donc une interface très limitée qui ne permet notamment pas:

o de se déplacer dans le flux de données. Ceci est pris en charge par l'interface IMediaSeeking.

Page 32: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 32/50

o de gérer la répétition et plus généralement les évènements dans un flux. Ceci est pris en charge par les interfaces IMediaEvent et IMediaEventEx (avec gestionnaire d'événements Windows).

2. Position dans un flux Cette interface permet de se déplacer dans le flux de données. Tous les flux de données ne supportent pas ces changements: on l'utilise en général lors de la lecture d’un fichier.

Interface: IMediaPosition Exposée par: les graphes, les filtres. Exemple:

IMediaPosition *pMPos; pGraph->QueryInterface(IID_IMediaPosition, (void **)&pMPos);

Dans cette interface, le temps est mesuré en seconde, et son type associé est:

typedef double REFTIME; Remarque: une interface plus avancée IMediaSeeking permet des déplacements et une gestion plus avancée de la position, ou de changer le mode de déplacement dans le média (par frame, par octets, …).

a. Capacités de recherche

IMediaPosition::CanSeekForward et CanSeekBackward CanSeekForward : détermine si le graphe peut faire une recherche avant.

CanSeekBackward : détermine si le graphe peut faire une recherche arrière.

Syntaxe: HRESULT CanSeekForward(LONG *pCanSeek); HRESULT CanSeekBackward(LONG *pCanSeek);

Paramètres

pCanSeek : [out] OATRUE vrai si possible, et OAFALSE sinon.

b. Récupération des informations Les informations que l'on peut récupérer ici sont:

o la durée totale du flux. o la position courante dans le flux. o le point d'arrêt de lecture (par défaut, la fin du flux).

Attention: o les valeurs récupérées sont en seconde.

Page 33: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 33/50

o la ou les valeurs retournées ne dépendent pas de la vitesse de lecture ou de temps initial de la lecture (la valeur est en position absolue par rapport au flux).

o pour avoir des informations plus précises sur le flux, on utilisera l'interface IMediaDet.

IMediaPosition::get_Duration HRESULT get_Duration(REFTIME *plength);

IMediaPosition::get_CurrentPosition HRESULT get_CurrentPosition(REFTIME *pllTime);

IMediaPosition::get_StopTime HRESULT get_StopTime(REFTIME *pllTime);

c. Déplacement dans le flux

Cette interface peut être utilisée indépendamment de l'état du graphe.

IMediaPosition::put_CurrentPosition HRESULT put_CurrentPosition(REFTIME llTime);

La valeur llTime est comprise entre 0 et la durée totale du flux. La valeur de retour S_FALSE indique le graphe était encore en phase de transition au moment du retour de la méthode. Voir le résultat de CanSeekForward et de CanSeekBackward pour connaître quel sont les déplacement possible. On remarquera que:

o Si son état est Run, la graphe est passé en Pause le temps que le déplacement soit effectué, et la lecture reprend automatiquement.

o Si graphe est en pause, les filtres doivent vider leurs buffers avant de passer à une nouvelle position. Voir IPin::BeginFlush et IPin::EndFlush.

IMediaPosition::put_StopTime HRESULT put_StopTime(REFTIME llTime);

La valeur llTime est comprise entre 0 et la durée totale du flux.

Change la valeur à laquelle le flux s'arrête de jouer. Par défaut, cette marque est à la fin du flux. Elle est indépendante de la vitesse avec laquelle le flux est joué.

d. Cas des données à accès non direct Pour certains types de média (bandes, streaming, …), l'accès n'est pas direct. Ceci empêche donc des accès aléatoires à l'intérieur du flux. Deux méthodes sont utilisées pour gérer ces cas:

IMediaPosition::get_PrerollTime HRESULT get_PrerollTime(REFTIME *pllTime);

Page 34: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 34/50

Indique la quantité de données qui sera accumulée avant le début de la lecture. Il n'est par conséquent possible de faire un Put_CurrentPosition avec un temps de réponse acceptable que dans l'intervalle [t_courant, t_courant + llTime]. Si le filtre/graphe n'implémente pas cette méthode: pllTime=0 et retour=S_OK.

IMediaPosition::put_PrerollTime HRESULT put_PrerollTime(REFTIME llTime);

Fixe la quantité de données qui sera accumulée avant le début de la lecture.

e. Vitesse de lecture La vitesse de lecture permet de régler:

o la vitesse à laquelle le flux de données est lu. o le sens de lecture (en spécifiant une vitesse négative).

Sur un flux audio, une vitesse autre que 1.0 se traduit par un changement de pitch.

IMediaPosition::put_Rate HRESULT put_Rate(double dRate);

où dRate est la nouvelle vitesse de lecture (différente de 0). Si la valeur de retour est E_INVALIDARG : la valeur passée n'est pas supportée. Si cette valeur est négative, cela signifie que le filtre ne permet pas de jouer le flux à l'envers. Si cette valeur est positive, cela signifie que le filtre ne peut pas jouer le flux avec une vitesse autre que 1.

IMediaPosition::get_Rate HRESULT get_Rate(double *pdRate);

Récupère dans pdRate la vitesse actuelle de lecture.

3. Gestion des évènements

Cette interface permet de se déplacer dans le flux de données. Tous les flux de données ne supportent pas ces changements: on l'utilise en général lors de la lecture de fichier.

Interface: IMediaEvent Exposée par: les graphes. Exemple:

IMediaEvent *pMEvent; pGraph->QueryInterface(IID_IMediaEvent, (void **)&pMEvent);

Cette interface peut être utilisée indépendamment de l'état du graphe. Si son état est Run,

L'utilisation est la suivante:

Page 35: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 35/50

o lecture des évènements en mode bloquant ou non bloquant pour savoir quels sont les évènements intervenus lors de la lecture du flux.

o on peut avoir une gestion minimale des évènements appropriés à la lecture d'un flux.

A noter que l'interface IMediaEventEx permet de générer des évènements Windows associés à la lecture d'un média.

a. Principaux évènements gérés

De nombreux évènements peuvent être gérés. Nous ne citons ici que les plus courants:

o Evènement lors du déroulement du flux:

EC_COMPLETE : le position courante a atteint la position de fin (StopTime).

EC_QUALITY_CHANGE : le graphe perd des échantillons (par exemple des frames).

EC_STEP_COMPLETE : avancé image par image, le filtre a fini d'avancer du nombre de frame spécifié.

o Erreurs courantes:

EC_ERRORABORT : opération annulé à cause d'une erreur.

o Erreurs liées aux renderers:

EC_USERABORT : opération annulé car l'utilisateur a fermé la fenêtre de rendu (par exemple ALT-F4).

EC_FULLSCREEN_LOST : le vidéo-renderer est sorti du mode plein écran.

EC_WINDOW_DESTROYED : le vidéo-renderer a été détruit ou retiré du graphe.

o Erreurs liée à un changement d'état sur un périphérique:

EC_DEVICE_LOST : un périphérique plug'n play a été enlevé ou est devenu indisponible.

Voir la documentation pour les autres messages gérés.

Il est aussi possible de générer ses propres évènements avec IMediaEventSink.

b. Attente d'évènements

Ces méthodes permettent de gérer les évènements en provenance du graphe.

IMediaEvent::GetEvent Syntaxe : HRESULT GetEvent(long *lEventCode,

LONG_PTR *lParam1, LONG_PTR *lParam2, long msTimeout);

Paramètres lEventCode : [out] Pointeur sur l’évènement. lParam1 : [out] Pointeur sur la première variable de l’évènement. lParam2 : [out] Pointeur sur la deuxième variable de l’évènement.

Page 36: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 36/50

msTimeout : [in] Timeout, en millisecondes. Mettre 0 pour une attente non bloquante, et toute autre valeur pour une attente bloquante (en ms ou INFINITE jusqu'à l'arrivée d'un évènement).

Retour S_OK : Réussi, E_ABORT : Timeout expiré.

Remarque : Si vous utilisez cette méthode dans le même thread que celui qui gère les messages de Windows, spécifié uniquement des petits timeout Après la réception d’un évènement, libérez les ressources avec FreeEventParams.

IMediaEvent::FreeEventParams Syntaxe : HRESULT FreeEventParams(long lEventCode,

LONG_PTR lParam1, LONG_PTR lParam2); Paramètres

lEventCode : [in] Evènement. lParam1 : [in] Premier paramètre. lParam2 : [in] Deuxième paramètre.

Retour : S_OK. Remarque : Utilisez les mêmes variables que l’appel à GetEvent.

Exemples: 1) Lecture non bloquante d'un évènement:

hr = pEvent->GetEvent(&evCode, &param1, &param2, 0); // Handle the event (not shown). hr = pEvent->FreeEventParams(evCode, param1, param2);

2) Vide et traite de la liste des évènements en attente: while(SUCCEEDED(pEvent->GetEvent(&ev, &c1, &c2, 0))) {

pEvent->FreeEventParams(ev, c1, c2); switch(ev) { case EC_COMPLETE:

// Traitement de ce cas break;

// Traitement des autres cas }

} pEvent-> FreeEventParams(ev,c1,c2);

c. Attente bloquante de fin de lecture

Cette méthode attend la fin de lecture du flux. Elle ne répond à aucun autre message autre que EC_COMPLETE, et les messages d'erreur sont non récupérables.

IMediaEvent::WaitForCompletion Syntaxe : HRESULT WaitForCompletion(long msTimeout, long *pEvCode); Paramètres

msTimeout : [in] Timeout, en millisecondes. (0 ou INFINITE). pEvCode : [out] Pointeur sur l’évènement.

Retour S_OK : Réussi, E_ABORT : Timeout expiré,

VFW_E_WRONG_STATE : Le graphe n’est pas lancé.

Page 37: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 37/50

Exemple 1: IMédiaControl et IMédiaEvent pour contrôler la lecture d'un graphe. IMediaControl *pMCtrl; IMediaEvent *pMEvent; long EventCode = 0; pGraph->QueryInterface(IID_IMediaControl, (void **)&pMCtrl); pGraph->QueryInterface(IID_IMediaEvent, (void **)&pMEvent); pMCtrl->Run(); pMEvent->WaitForCompletion( INFINITE, &EventCode ); // tester EventCode pour savoir si la lecture s'est arrêtée en fin pMCtrl->Stop(); pMCtrl->Release();

Exemple 2: lecture en boucle : attendre la fin de la lecture avec WaitForCompletion , faire un put_CurrentPosition(0) et relancer la lecture.

Page 38: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 38/50

Information supplémentaire sur le flux et capture de flux

1. Classe Média Détecteur (IMediaDet)

Cette classe permet de manipuler un fichier contenant un flux indépendamment d'un graphe, et d'extraire des informations directement depuis le fichier.

Classe: CLSID_MediaDet Interface: IID_IMediaDet

Exemple: IMediaDet *pDet; hr = CoCreateInstance( CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER, IID_IMediaDet, (void**) &pDet ); Attention: pour utiliser cette classe, penser à inclure dans l'entête de votre fichier:

#pragma comment(lib,"strmiids.lib") #include <Qedit.h>

Une fois l'objet MediaDet créé, il faut lui affecter un fichier multimédia avec la méthode:

IMediaDet::put_Filename Ne pas appeler cette méthode deux fois sur le même objet. Pour utiliser cette interface sur deux fichiers différents, créer une instance séparé de l’objet IMediaDet.

Syntaxe : HRESULT put_Filename(LPWSTR newVal);

Paramètre : newVal : [in] Nom du fichier source.

Retour : S_OK ou la cause de l'erreur dans le HRESULT.

a. Aparté sur les formats de flux multimédia

Un ensemble d'informations très différentes peuvent être contenu dans un flux multimédia:

o de la vidéo (éventuellement plusieurs flux vidéo, par exemple le DVB).

o du son (éventuellement plusieurs pistes son)

o des sous-titres.

o …

Dans un flux, ces informations sont organisées sous forme de streams. Sous DirectShow, tous les streams contiennent un type de données différent.

Page 39: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 39/50

La séparation du contenu d'un stream ou de l'ensemble des streams est effectuée avec un splitter adapté au type de fichier.

b. Extraction des informations

Les informations peuvent être extraite stream par stream. On utilise les méthodes suivantes:

IMediaDet::get_OutputStreams Syntaxe : HRESULT get_OutputStreams(long *pVal);

Paramètre : pVal = [out, retval] Pointeur qui va recevoir le nombre de flux de sortis.

On ne récupère avec cette méthode que les flux audio et vidéo.

IMediaDet::put_CurrentStream Syntaxe : HRESULT put_CurrentStream(long newVal); Paramètres : newVal = [in] Numéro du flux.

où newval prend des valeurs de 0 à Val-1 (valeurs lues par get_OutputStreams).

Une fois que le nom du fichier et que le numéro du stream à examiner sont fixés, on peut alors extraire les informations suivantes du stream:

o Son nombre d'images par seconde (Frame Rate) : cette valeur n'a de sens que pour les flux vidéo.

o Sa durée (Stream Length) en seconde.

o Son type (son Major Type, comme déjà vu dans la structure AM_MEDIA_TYPE).

o La description complète du format (la structure AM_MEDIA_TYPE elle-même).

IMediaDet::get_FrameRate Le flux doit être une vidéo.

Syntaxe : HRESULT get_FrameRate(double *pVal);

Paramètre : pVal = [out, retval] Frame rate, en f/s.

Retour : S_OK ou la cause de l'erreur dans le HRESULT.

Page 40: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 40/50

IMediaDet::get_StreamLength Syntaxe : HRESULT get_StreamLength(double *pVal); Paramètre : pVal = [out, retval] Durée du flux, en secondes.

Retour : S_OK ou la cause de l'erreur dans le HRESULT.

IMediaDet::get_StreamType Syntaxe : HRESULT get_StreamType(GUID *pVal); Paramètre : pVal = [out, retval] GUID du flux.

Retour : S_OK ou la cause de l'erreur dans le HRESULT.

Pour l'audio, pVal = MEDIATYPE_Audio. Pour la vidéo, pVal = MEDIATYPE_Video

Exemple: récupération des informations de base sur les flux audio/vidéo contenu dans un fichier IMediaDet *pDet; LONG nstreams; h = CoCreateInstance( CLSID_MediaDet, NULL, CLSCTX_INPROC_SERVER,

IID_IMediaDet, (void**) &pDet ); h = pDet->put_Filename(fname); h = pDet->get_OutputStreams(&nstreams); printf("Nb Streams = %d\n",nstreams); for(LONG i=0;i<nstreams;i++) { double val; GUID major; // fixe le stream à examiner pDet->put_CurrentStream(i); printf("stream %d:\n",i); // durée du stream pDet->get_StreamLength(&val); printf("\tLength = %.2f seconds\n",val); // type de stream pDet->get_StreamType(&major); if (IsEqualGUID(major,MEDIATYPE_Audio))

printf("\tType = audio\n"); else if (IsEqualGUID(major,MEDIATYPE_Video))

printf("\tType = video\n"); else

printf("\tType = unknown\n"); // frame rate if (IsEqualGUID(major,MEDIATYPE_Video) { pDet->get_FrameRate(&val); printf("\tFrame rate = %.2f frames/second\n",val); }

Sortie obtenue par ce code sur un fichier avi avec video+audio: Nb Streams = 2 stream 0: Length = 106.68 seconds Type = video Frame rate = 25.00 frames/second stream 1: Length = 106.68 seconds Type = audio

Page 41: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 41/50

IMediaDet::get_StreamMediaType Syntaxe : HRESULT get_StreamMediaType(AM_MEDIA_TYPE *pVal); Paramètre : pVal = [out, retval] AM_MEDIA_TYPE utilisé.

Retour : S_OK ou la cause de l'erreur dans le HRESULT.

Rappel: pVal doit être libéré avec une fonction du type FreeMediaType. Un exemple d'utilisation de cette méthode est donné après la description des formats.

c. Rappel sur les structures de format structure WAVEFORMATEX déjà décrite dans le cours sur DirectSound, principalement:

wFormatTag : type d'audio (exemple: WAVE_FORMAT_PCM pour PCM) nChannels : nombre de canaux (mono=1, stéréo=2, …) nSamplesPerSec : fréquence d'échantillonnage en kHz (8, 11.025, 22.05, ...). wBitsPerSample : nombre de bits par échantillons (8, 16, …)

et si la structure WAVEFORMATEXTENSIBLE est disponible: wValidBitsPerSample : nombre de bits valides par échantillons dwChannelMask : description de l'assignation des canaux (HP par HP). SubFormat: sous-format du format.

Remarque: dans certains cas (audio compressé), certains champs n'ont pas de sens et leurs valeurs sont mises à 0.

Exemple: affichage des propriétés d'un flux audio void ViewSoundFormat(WAVEFORMATEX *Sfmt) { printf("\tAudio:%d channel(s)x%d bits@%d Hz (Format=%s)\n", Sfmt->nChannels,

Sfmt->wBitsPerSample, Sfmt->nSamplesPerSec, GetSoundFormatCodeString(Sfmt->wFormatTag));

}

où GetSoundFormatCodeString est une fonction qui convertit le FormatTag en chaîne de caractères (voir le code source de cette fonction sur le serveur).

structure VIDEOINFOHEADER

rcSource / rcTarget : rectangle source et destination (zone de rendu). dwBitRate : débit moyen de la vidéo en bit/s. dwBitErrorRate : débit d'erreur (i.e. de bit d'erreur), en bit/s. AvgTimePerFrame : temps moyen d'une frame, en 10ème de ms. bmiHeader : structure de type BITMAPINFOHEADER qui contient:

biWidth, biHeight : largeur et hauteur de la vidéo. biBitCount : nombre de bits par pixels.

Page 42: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 42/50

biCompression : code FOURCC qui spécifie la compression employée ou BI_RGB pour du RGB non compressé, et BI_BITFIELDS pour du RGB nom compressé avec masque (16bpp ou 32 bpp).

biSizeImage : taille en octet de l'image (0 en mode non compressé). biXPelsPerMeter, biYPelsPerMeter : pour fixer la taille des pixels (en

pixels par mètres). biClrUsed : nombre de couleurs utilisées dans palette de couleurs (8bpp). biClrImportant : nombre de couleurs importantes parmi les précédents

(si 0, elles le sont toutes).

Remarque : dans certains cas (par exemple vidéo compressée), certains champs n'ont pas de sens et leurs valeurs sont mises à 0.

code FOURCC : (FOUR Character Code) code stocké dans un dword sur 4 caractères. Tous les formats vidéo ont un code affecté. Ci-dessous, ceux enregistré par Microsoft:

Compressor Code Description

ANIM Intel - RDX AUR2 AuraVision - Aura 2 Codec - YUV 422 AURA AuraVision - Aura 1 Codec - YUV 411 BT20 Brooktree - MediaStream codec BTCV Brooktree - Composite Video codec CC12 Intel - YUV12 codec CDVC Canopus - DV codec

CHAM Winnov, Inc. -MM_WINNOV_CAVIARA_CHAMPAGNE

CPLA Weitek - 4:2:0 YUV Planar CVID Supermac - Cinepak CWLT reserved DUCK Duck Corp. - TrueMotion 1.0 DVE2 InSoft - DVE-2 Videoconferencing codec DXT1 reserved DXT2 reserved DXT3 reserved DXT4 reserved DXT5 reserved DXTC DirectX Texture Compression

FLJP D-Vision - Field Encoded Motion JPEG With LSI Bitstream Format

GWLT reserved H260 Intel - Conferencing codec H261 Intel - Conferencing codec H262 Intel - Conferencing codec H263 Intel - Conferencing codec H264 Intel - Conferencing codec H265 Intel - Conferencing codec H266 Intel - Conferencing codec H267 Intel - Conferencing codec H268 Intel - Conferencing codec H269 Intel - Conferencing codec I263 Intel - I263 I420 Intel - Indeo 4 codec IAN Intel - RDX ICLB InSoft - CellB Videoconferencing codec ILVC Intel - Layered Video ILVR ITU-T - H.263+ compression standard IRAW Intel - YUV uncompressed IV30 Intel - Indeo Video 3 codec IV31 Intel - Indeo Video 3.1 codec

IV32 Intel - Indeo Video 3 codec IV33 Intel - Indeo Video 3 codec IV34 Intel - Indeo Video 3 codec IV35 Intel - Indeo Video 3 codec IV36 Intel - Indeo Video 3 codec IV37 Intel - Indeo Video 3 codec IV38 Intel - Indeo Video 3 codec IV39 Intel - Indeo Video 3 codec IV40 Intel - Indeo Video 4 codec IV41 Intel - Indeo Video 4 codec IV42 Intel - Indeo Video 4 codec IV43 Intel - Indeo Video 4 codec IV44 Intel - Indeo Video 4 codec IV45 Intel - Indeo Video 4 codec IV46 Intel - Indeo Video 4 codec IV47 Intel - Indeo Video 4 codec IV48 Intel - Indeo Video 4 codec IV49 Intel - Indeo Video 4 codec IV50 Intel - Indeo 5.0 MP42 Microsoft - MPEG-4 Video Codec V2 MPEG Chromatic - MPEG 1 Video I Frame MRCA FAST Multimedia - Mrcodec MRLE Microsoft - Run Length Encoding MSVC Microsoft - Video 1 NTN1 Nogatech - Video Compression 1 qpeq Q-Team - QPEG 1.1 Format video codec RGBT Computer Concepts - 32 bit support RT21 Intel - Indeo 2.1 codec RVX Intel - RDX SDCC Sun Communications - Digital Camera Codec SFMC Crystal Net - SFM Codec SMSC Radius - proprietary SMSD Radius - proprietary SPLC Splash Studios - ACM audio codec SQZ2 Microsoft - VXtreme Video Codec V2 SV10 Sorenson - Video R1 TLMS TeraLogic - Motion Intraframe Codec TLST TeraLogic - Motion Intraframe Codec TM20 Duck Corp. - TrueMotion 2.0 TMIC TeraLogic - Motion Intraframe Codec

TMOT Horizons Technology - TrueMotion Video Compression Algorithm

TR20 Duck Corp. - TrueMotion RT 2.0 V422 Vitec Multimedia - 24 bit YUV 4:2:2 format

Page 43: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 43/50

(CCIR 601). V655 Vitec Multimedia - 16 bit YUV 4:2:2 format. VCR1 ATI - VCR 1.0 VIVO Vivo - H.263 Video Codec

VIXL Miro Computer Products AG - for use with the Miro line of capture cards.

VLV1 Videologic - VLCAP.DRV

WBVC Winbond Electronics - W9960 XLV0 NetXL, Inc. - XL Video Decoder YC12 Intel - YUV12 codec YUV8 Winnov, Inc. - MM_WINNOV_CAVIAR_YUV8 YUV9 Intel - YUV9 YUYV Canopus - YUYV compressor ZPEG Metheus - Video Zipper

The following list shows the FOURCC values for DIB (Device Independent Bitmap) compression: Compressor Code Description CYUV Creative Labs, Inc - Creative Labs YUV FVF1 Iterated Systems, Inc. - Fractal Video Frame IF09 Intel - Intel Intermediate YUV9 JPEG Microsoft - Still Image JPEG DIB MJPG Microsoft - Motion JPEG DIB Format PHMO IBM – Photomotion ULTI IBM – Ultimotion VDCT Vitec Multimedia - Video Maker Pro DIB VIDS Vitec Multimedia - YUV 4:2:2 CCIR 601 for V422 YU92 Intel – YUV

Pour une liste complète, voir www.fourcc.org Exemple: affichage du contenu de la structure VIDEOINFOHEADER.

typedef struct { union { DWORD fourcc; char cc[4]; } v; } fourcc;

void ViewVideoFormat(VIDEOINFOHEADER *Vfmt) { printf("\tVideo Format:\n"); printf("\t\tData rate = %d bits/sec\n",Vfmt->dwBitRate); printf("\t\tError rate=%dbits/sec\n",Vfmt->dwBitErrorRate); printf("\t\tAverage Frame Display Time = %.2f second\n",

float(Vfmt->AvgTimePerFrame) / 10000000.f); printf("\t\tDIB information:\n"); printf("\t\t\tImage size : %d x %d \n",

Vfmt->bmiHeader.biWidth,Vfmt->bmiHeader.biHeight); printf("\t\t\tBit/pixel: %d\n",Vfmt->bmiHeader.biBitCount); printf("\t\t\tCompression : "); fourcc f; f.v.fourcc = Vfmt->bmiHeader.biCompression; switch(Vfmt->bmiHeader.biCompression) { case BI_RGB: printf("Uncompressed RGB\n"); break; case BI_BITFIELDS: printf("Uncompressed RGB.\n"); break; default: printf("%c%c%c%c\n",f.v.cc[0],f.v.cc[1],f.v.cc[2],f.v.cc[3]); } printf("\t\t\tSize : %d\n",Vfmt->bmiHeader.biSizeImage); printf("\t\t\tPixels per meter : %d x %d\n",

Vfmt->bmiHeader.biXPelsPerMeter, Vfmt->bmiHeader.biYPelsPerMeter);

printf("\t\t\tColor indices in color table = %d\n", Vfmt->bmiHeader.biClrUsed);

printf("\t\t\tImportant color indices = %d\n", Vfmt->bmiHeader.biClrImportant);

}

Page 44: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 44/50

Exemple 2: affichage du type de media avec IMediaDet // récupération des informations détaillées AM_MEDIA_TYPE Type; pDet->get_StreamMediaType(&Type); // information sur les flux if (IsEqualGUID(major,MEDIATYPE_Audio))

ViewSoundFormat((WAVEFORMATEX *)Type.pbFormat); else if (IsEqualGUID(major,MEDIATYPE_Video)) { pDet->get_FrameRate(&val); printf("\tFrame rate = %.2f frames/second\n",val); ViewVideoFormat((VIDEOINFOHEADER *)Type.pbFormat); } else printf("\tInvalid format\n"); // affichage des informations sur les flux FreeMediaType(Type); Résultat dans le cas du même flux que précédemment (flux avec vidéo+audio):

Nb Streams = 2 stream 0: Length = 106.68 seconds Type = video Frame rate = 25.00 frames/second Video Format: Data rate = 0 bits/second Error rate = 0 bits/second Average Frame Display Time = 0.04 second DIB information: Image size : 576 x 320 Bit per pixel : 12 Compression : XVID Image size : 1105920 Pixels per meter : 0 x 0 Color indice in color table = 0 Important color indices = 0 stream 1: Length = 106.68 seconds Type = audio Audio: 2 channel(s) x 0 bits @ 48000 Hz (Format=MPEGLAYER3)

d. Capture de frames

On peut effectuer des captures de frame (à savoir: quelle est l'image dans le flux vidéo à un temps t donné) avec les méthodes suivantes:

IMediaDet::WriteBitmapBits Récupère l’image et l’enregistre dans un fichier dans un format RGB24.

Syntaxe : HRESULT WriteBitmapBits(double StreamTime, long Width, long Height, BSTR Filename);

Paramètres StreamTime : Temps à lequel capturer l’image. Width, Height : Taille de l’image en pixels. Filename : Chemin (24bpp BMP). Si le fichier existe, la méthode l’écrasera.

Retour : S_OK ou la cause de l'erreur dans le HRESULT.

On utilise cette méthode pour générer simplement quelques captures. Pour une utilisation plus intensive (par exemple lors de la lecture d'un flux), on utilisera l'interface ISampleGrabber (voir ci-dessous).

Page 45: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 45/50

Exemple: capture de 3 frames sur une vidéo (à 1/4, 1/2, et 3/4 de la vidéo). // Obtention de la durée de la video pDet->get_StreamLength(&StreamLength); // Obtention de la taille (hauteur/largeur) de la video AM_MEDIA_TYPE MediaType; pDet->get_StreamMediaType(&MediaType); VIDEOINFOHEADER *pVih = reinterpret_cast<VIDEOINFOHEADER*>(MediaType.pbFormat); width=pVih->bmiHeader.biWidth; height=pVih->bmiHeader.biHeight; // Capture de 3 frames hr = pDet->WriteBitmapBits((double)(StreamLength*1/4),width,height,L"preview1.bmp"); hr = pDet->WriteBitmapBits((double)(StreamLength*2/4),width,height,L"preview2.bmp"); hr = pDet->WriteBitmapBits((double)(StreamLength*3/4),width,height,L"preview3.bmp"); FreeMediaType(MediaType);

IMediaDet::GetBitmapBits Syntaxe HRESULT GetBitmapBits(double StreamTime, long *pBufferSize, char *pBuffer, long Width, long Height); Idem WriteBitmapBits mais le résultat est stocké dans un buffer pBuffer (structure BITMAPINFOHEADER + DIB bits) de taille pBufferSize. S'utilise en deux étapes:

1) faire appel à GetBitmapBits avec en remplaçant pBuffer par NULL. Au retour, pBufferSize contient au retour la taille du buffer à allouer.

2) allouer pBuffer, et refaire l'appel en passant NULL à la place de pBufferSize pour remplir pBuffer. Au retour, pBuffer pointe sur BITMAPINFOHEADER. pBuffer + sizeof(BITMAPINFOHEADER) est l'emplacement des DIB bits (= l'image).

On peut par exemple utiliser cette méthode pour générer des preview de flux vidéo (voir par exemple le Windows Movie Maker).

2. Sample Grabber

L'interface ISampleGrabber permet de capture des frames dans un graphe. Elle s'utilise de deux manières différentes:

o soit directement à partir de IMediaDet Remarque: IMediaDet crée un graphe contenant le SampleGrabber

o soit à l'intérieur d'un graphe comme un filtre.

Eviter d'utiliser IMediaDet si on dispose déjà d'un graphe contrôlant le flux concerné. On préfèrera dans ce cas insérer un ISampleGrabber dans ce filtre pour effectuer la tache demandée.

a. Utilisation depuis IMediaDet

Il est possible de récupérer un pointeur sur le ISamplerGrabber depuis l'interface IMediaDet en deux étapes:

Page 46: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 46/50

1. Utiliser IMediaDet::EnterBitmapGrabMode pour entrer en mode capture. Attention toutes les autres méthodes get/put de IMediaDet ne sont plus utilisables.

2. Utiliser IMediaDet::GetSampleGrabber pour récupérer l'interface du ISampleGrabber.

IMediaDet::EnterBitmapGrabMode Syntaxe : HRESULT EnterBitmapGrabMode(double StreamTime); Paramètre : StreamTime : Temps, en seconds.

Retour : S_OK ou la cause de l'erreur dans le HRESULT.

IMediaDet::GetSampleGrabber Syntaxe : HRESULT GetSampleGrabber(ISampleGrabber **pGRAB); Paramètre : ppVal : [out] Adresse du Pointeur sur l’interface ISampleGrabber.

Retour : S_OK ou la cause de l'erreur dans le HRESULT.

Remarque : Release ! Exemple: Dans l'exemple précédent, IMediaDet construit automatiquement le filtre suivant. La fonction GetSampleGrabber permet d’insérer le filtre ISampleGrabber dans le graphe.

Filter : Source 1 Out Id=[Output] Output -> AVI Splitter[input pin] Type=Stream

Filter : AVI Splitter 1 In Id=[input pin] input pin -> Source[Output] Type=Stream 2 Out Id=[Stream 00] Stream 00 -> DivX Decoder Filter[XForm In] Type=Video 3 Out Id=[Stream 01] Stream 01 -> NOT_CONNECTED Type=NotConnected

Filter : DivX Decoder Filter 1 In Id=[In] XForm In -> AVI Splitter[Stream 00] Type=Video 2 Out Id=[Out] XForm Out -> BitBucket[Input] Type=Video

Filter : BitBucket 1 In Id=[In] Input -> DivX Decoder Filter[XForm Out] Type=Video 2 Out Id=[Out] Output -> NullRenderer[In] Type=Video

Filter : NullRenderer 1In Id=[In] In -> BitBucket[Output] Type=Video

ISampleGrabber s'utilise alors comme indiqué ci-dessous.

b. Utilisation de ISampleGrabber comme un filtre

i. Création d'un filtre de type SampleGrabber Le sample grabber est un objet qui dérive de l'objet IBaseFilter:

• de classe CLSID_SampleGrabber • d'interface IID_ISampleGrabber

En trois étapes:

1. créer un filtre de classe SampleGrabber avec l'interface IBaseFilter. 2. insérer le filtre dans le graphe. 3. récupérer l'interface ISampleGrabber sur le filtre.

Page 47: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 47/50

Autrement dit: // pGraph est l'interface sur le GraphBuilder IBaseFilter *pGrabFilter = NULL; ISampleGrabber *pGrabber; CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pGrabFilter); pGraph->AddFilter(pGrabFilter, L"Sample Grabber"); pGrabFilter->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber); // il faut encore connecter les bornes aux autres filtres

Remarque: on aurait pu acquérir les interfaces dans le sens inverse (rappel des

fonctionnalité COM): // pGraph est l'interface sur le GraphBuilder IBaseFilter *pGrabFilter = NULL; ISampleGrabber *pGrabber; CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_ISampleGrabber, (void**)& pGrabber); pGrabber->QueryInterface(IID_IBaseFilter, (void**)& pGrabFilter); pGraph->AddFilter(pGrabFilter, L"Sample Grabber"); // il faut encore connecter les bornes aux autres filtres

ii. Type de média capturé par le SampleGrabber

Par défaut,:

• Le SampleGrabber n'a pas de type préféré: il pourra capturer n'importe quoi en adaptant automatiquement le type de sa borne d'entrée au filtre sur lequel il se connecte.

• Le flux sortant par sa borne de sortie est le même que celui de sa borne d'entrée (format inchangé).

Par conséquent, si dans un flux vidéo, on veut capturer des données dans un mode particulier, la méthode la plus simple pour le programmeur est de:

• fixer le type de média de la borne d'entrée du SampleGrabber.

• faire un "Intelligent Connect" sur la source.

On fait appel à la méthode suivante pour décider du format des données que l'on souhaite.

ISampleGrabber::SetMediaType Syntaxe : HRESULT SetMediaType(const AM_MEDIA_TYPE *pType); Paramètre : pType : Pointeur sur AM_MEDIA_TYPE.

Retour : S_OK.

Exemple: fixe le format de la capture d'image avec le grabber en 24bpp. AM_MEDIA_TYPE mt; ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE)); mt.majortype = MEDIATYPE_Video; mt.subtype = MEDIASUBTYPE_RGB24; pGrabber->SetMediaType(&mt);

Page 48: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 48/50

Remarque 1: Eviter de connecter un renderer derrière le SampleGrabber (et en particulier en vidéo-renderer) car il peut provoquer des problèmes de performance lors du rendu (saccades). La solution consiste alors à:

a) ajouter un splitter derrière la source (en général après le décoder).

b) faire le rendu à partir de la première borne du splitter.

c) faire la capture sur la deuxième borne, en plaçant un NULL renderer derrière le SampleGrabber afin de terminer cette branche du graphe.

Remarque 2: Utiliser GUID_NULL comme majortype et subtype lors d'un SetMediaType pour rendre de nouveau un SampleGrabber (non connecté) avec tout type d'entrée. Dans ce cas, et après connexion, on peut utiliser la méthode ISampleGrabber::GetConnectedMediaType pour connaître le type de la borne d'entrée du SampleGrabber (i.e. le format des données que l'on va recevoir).

iii. Utilisation d'un SampleGrabber

On utilise le SampleGrabber essentiellement en mode bufferisé: on effectue une copie de chaque sample avant de continuer sa distribution.

Les méthodes utilisées sont les suivantes:

ISampleGrabber::SetBufferSamples Syntaxe : HRESULT SetBufferSamples(BOOL BufferThem); Paramètre : BufferThem : Si TRUE, les données sont bufferisées.

ISampleGrabber::SetOneShot Syntaxe : HRESULT SetOneShot(BOOL OneShot); Paramètre : OneShot : Si TRUE, le graphe s’arrête dès la réception de la première image, et

l’évènement EC_COMPLETE est envoyé. Si FALSE, le graphe continu normalement. Retour : S_OK.

Cette méthode est utilisée pour récupérer des samples à des instants particuliers. En partant d'un graphe arrêté (IMediaControl::Stop):

1) appeler SetOneShot(TRUE) et SetBufferSamples(TRUE). 2) changer la position avec l'interface IMediaPosition sur le graphe. 3) lancer le graphe avec IMediaControl::Run. Le graphe se lance, effectue la capture et se stoppe immédiatement après.

Exemple: pour capturer le sample à la position courante du fichier. // Set one-shot mode and buffering. hr = pGrabber->SetOneShot(TRUE); hr = pGrabber->SetBufferSamples(TRUE); pControl->Run(); // Run the graph. pEvent->WaitForCompletion(INFINITE, &evCode); // ici: le sample est dans le buffer du Grabber

Page 49: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 49/50

ISampleGrabber::GetCurrentBuffer Syntaxe : HRESULT GetCurrentBuffer(long *pBufferSize, long *pBuffer); Paramètres

pBufferSize : [in, out] Taille du buffer. Si pBuffer = NULL, ce paramètre reçoit la taille requise du buffer, en bytes. Si pBuffer != NULL, ce paramètre devrait être égale à la taille du buffer. En sortie, ce paramètre reçoit le nombre de bytes copiés dans le buffer. (Peut être plus petit que la taille du buffer).

pBuffer : [out] Pointeur sur le buffer, ou NULL.

Retour : S_OK ou la cause de l'erreur dans le HRESULT.

Remarques: (1) la zone mémoire sur laquelle pointe pBuffer doit avoir été allouée. (2) ISampleGrabber::GetConnectedMediaType permet de récupérer les caractéristiques du

sample (notamment la taille de la vidéo). Si ISampleGrabber::SetMediaType a été appelé, les données devraient être de ce type.

(3) en pratique, on utilise cette méthode en deux passes: (a) ISampleGrabber::GetCurrentBuffer(&sz,NULL)

sz est la taille du buffer nécessaire pour récupérer les données (l'allouer si nécessaire).

(b) ISampleGrabber::GetCurrentBuffer(&sz,buff) où buff est le buffer sur la zone mémoire réservée pour récupérer les données.

Ce buffer peut être directement envoyé sur la UI avec le GDI.

iv. Utilisation avec un CallBack

Tel un évènement sous Windows, dès que le SampleGrabber reçoit une image, il peut générer un CallBack et exécuter une méthode correspondante.

ISampleGrabber::SetCallBack Syntaxe : HRESULT SetCallBack(ISampleGrabberCB *pCallback, long WhichMethodToCallback); Paramètres

pCallback : [in] Pointeur sur la callback contenant la méthode. (Ou NULL pour l’annuler).

WhichMethodToCallback : [in] Index spécifiant la méthode, 0 ou 1. 0 pour la méthode SampleCB 1 pour la méthode BufferCB

Retour : S_OK ou la cause de l'erreur dans le HRESULT.

Remarques : Si la méthode ne se termine pas rapidement, il peut y avoir un ralentissement. Modifier le contenu du buffer n’est pas recommandé. Il est préférable d’effectuer une copie du buffer ou utiliser directement un filtre de transformation.

Page 50: Programmation Multimédia Cours de programmation DirectShowmathinfo.univ-reims.fr/image/mmVideo/cours/DirectShow1.pdf · 2014-10-07 · Programmation Multimédia Master d’Informatique

Programmation Multimédia Master d’Informatique 2005-2006

Université de Reims Champagne-Ardennes Page 50/50

Exemple : IBaseFilter *pSampleGrabber = NULL; ISampleGrabber *pGrabber = NULL; ISampleGrabberCB *pCB = NULL;

// Callback du grabber class MyCallback : public ISampleGrabberCB { public: int id; STDMETHODIMP_(ULONG) AddRef() { return 2; } STDMETHODIMP_(ULONG) Release() { return 1; } STDMETHODIMP QueryInterface(REFIID riid, void ** ppv){ if(riid==IID_ISampleGrabberCB || riid==IID_IUnknown){ *ppv=(void*)static_cast<ISampleGrabberCB*>(this); return NOERROR;

} return E_NOINTERFACE;

}

STDMETHODIMP SampleCB(double STime, IMediaSample *pSample) { return 0; } STDMETHODIMP BufferCB( double STime, BYTE *pBuf, long BLen) { return 0 ; }

}

// on défini le filtre et son callback CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER,

IID_ISampleGrabber, (void**)& pGrabber); pGrabber->QueryInterface(IID_IBaseFilter, (void**)& pGrabFilter); pGraph->AddFilter(pGrabFilter, L"Sample Grabber"); pCB = new MyCallback; pSampleGrabber->QueryInterface(IID_ISampleGrabber,(void **)&pGrabber); pGrabber->SetBufferSamples(FALSE); pGrabber->SetOneShot(FALSE); pGrabber->SetCallback(pCB,1);

AM_MEDIA_TYPE mt; ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE)); mt.majortype = MEDIATYPE_Video; mt.subtype = MEDIASUBTYPE_RGB32; pGrabber->SetMediaType(&mt); MyFreeMediaType(mt);

// connexions

// release