Programmation Multimédia Cours de programmation DirectShow
Transcription
Programmation Multimédia Cours de programmation DirectShow
Programmation Multimédia Master d’Informatique 2005-2006 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 1/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 A D C E 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é : • 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 à chaque classe (class-id ou CLSID). o à chaque interface (interface-id ou IID). o o Page 2/50 Le relâchement des interfaces permet de libérer un objet dès qu’on n’y fait plus référence. Fin d’un programme : l’interface COM doit être désinitialisée. Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 pUnkOuter dwClsContext riid ppv : [in] Identificateur de la classe. : [in] On utilisera en général toujours NULL. : [in] CLSCTX_INPROC_SERVER, (Permet IMoniker::BindToObject). : [in] Identificateur de l’interface. : [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 3/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 HRESULT h; ICaptureGraphBuilder2 IGraphBuilder IMediaControl Page 4/50 avoir été initialisée. *pCaptureGraph = NULL; *pGraph = NULL; *pControl = NULL; Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 5/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 UCHAR WCHAR TCHAR = char = unsigned char = wchar_t = WCHAR si UNICODE est défini, CHAR sinon. Exemple avec écriture des types: CHAR CHAR WCHAR WCHAR TCHAR a = ‘a’; b[10] = "abcdef"; c = L’a’; d[10] = L"abcdef"; 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 printf wprintf sprintf swprintf fprintf fwprintf Idem en remplaçant printf par scanf. TCHAR _tprintf _stprintf _ftprintf 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 6/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 7/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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. • 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. • 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. On distingue trois principaux types de filtres: • • • Les filtres sources. Les filtres de transformation. Les filtres de rendu. Page 8/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 9/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 10/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 11/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 12/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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). Classe IFilterGraph IBaseFilter IPin Méthode pour obtenir l' énumérateur EnumFilters EnumPins EnumMediaTypes Enumérateur IEnumXXX IEnumFilters IEnumPins IEnumMediaTypes Objets récupérés lors de l' énumération IXXX IBaseFilter IPin AM_MEDIA_TYPE IEnumXXX::Next Syntaxe : HRESULT Paramètres cObjs ppObjs pcFetched Retour Remarque Page 13/50 Next(ULONG cObjs, IXXX **ppObjs, ULONG *pcFetched); : [in] Nombre d’objets à récupérer. : [out] tableau de taille cObjs qui sera rempli avec les pointeurs de IXXX. : [out] Pointeur de la variable qui reçoit le nombre d’objets récupérés. (peut être NULL si cObjs = 1). : Valeur HRESULT (voir la doc: VFW_E_ENUM_OUT_OF_SYNC) : 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. Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 Page 14/50 typedef struct _FilterInfo { WCHAR achName[MAX_FILTER_NAME]; IFilterGraph *pGraph; } FILTER_INFO; Université de Reims Champagne-Ardennes Programmation Multimédia Membres achName pGraph Remarque Master d’Informatique 2005-2006 : Nom du filtre. (terminé par NULL) : Si le filtre fait parti d’un graphe, contient son pointeur. NULL sinon. : 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 15/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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: Method ConnectedTo ConnectionMediaType QueryPinInfo QueryId QueryAccept EnumMediaTypes QueryInternalConnections QueryDirection Description Récupère la pin connectée à celle-ci. Récupère le type de la connexion courante. Récupère des informations sur la pin (nom, filtre propriétaire, direction). Récupère l’identificateur de la pin. Détermine si la pin accepte un type spécifique. Enumère les types disponibles sur la pin. Récupère les pins connectés à l’intérieur du filtre. Récupère la direction de la pin (entrée ou sortie). 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) Page 16/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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); CoTaskMemFree(Id); // rappel: c'est un wide char. 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 17/50 Université de Reims Champagne-Ardennes Programmation Multimédia Retour S_OK VFW_E_NOT_CONNECTED E_POINTER Remarque : Release ! Master d’Informatique 2005-2006 : réussi : pin non connectée. : Argument du pointeur NULL. 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 18/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 19/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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, 16bit, 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 MEDIATYPE_AnalogAudio MEDIATYPE_AnalogVideo MEDIATYPE_Audio MEDIATYPE_AUXLine21Data MEDIATYPE_File MEDIATYPE_Interleaved MEDIATYPE_LMRT MEDIATYPE_Midi MEDIATYPE_MPEG2_PES MEDIATYPE_MPEG2_SECTION MEDIATYPE_ScriptCommand MEDIATYPE_Stream MEDIATYPE_Text MEDIATYPE_Timecode MEDIATYPE_URL_STREAM MEDIATYPE_Video Description Analog audio. Analog video. Audio. Line 21 data. Used by closed captions. File. (Obsolete) Interleaved audio and video. Used for Digital Video (DV). Obsolete. Do not use. MIDI format. MPEG-2 PES packets. MPEG-2 section data Data is a script command, used by closed captions. Byte stream with no time stamps. Text. Timecode data. Obsolete. Do not use. Video. Audio Subtypes (voir la documentation pour les autres sous-types) GUID MEDIASUBTYPE_PCM MEDIASUBTYPE_PCMAudioObsolete MEDIASUBTYPE_MPEG1Packet MEDIASUBTYPE_MPEG1Payload MEDIASUBTYPE_MPEG2_AUDIO Page 20/50 Description PCM audio. Obsolete. Do not use. MPEG1 Audio packet. MPEG1 Audio Payload. MPEG-2 audio data Université de Reims Champagne-Ardennes Programmation Multimédia MEDIASUBTYPE_DVD_LPCM_AUDIO MEDIASUBTYPE_MPEG2_AUDIO MEDIASUBTYPE_DRM_Audio MEDIASUBTYPE_IEEE_FLOAT MEDIASUBTYPE_DOLBY_AC3 MEDIASUBTYPE_DOLBY_AC3_SPDIF MEDIASUBTYPE_RAW_SPORT MEDIASUBTYPE_SPDIF_TAG_241h Master d’Informatique 2005-2006 DVD audio data MPEG-2 audio data Corresponds to WAVE_FORMAT_DRM. Corresponds to WAVE_FORMAT_IEEE_FLOAT Dolby data Dolby AC3 over SPDIF. ~ MEDIASUBTYPE_DOLBY_AC3_SPDIF. ~ 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 21/50 Université de Reims Champagne-Ardennes Programmation Multimédia BOOL Master d’Informatique 2005-2006 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 22/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 Construction de Graphe 1. Principes Pour construire un graphe il faut: 1. ajouter les filtres nécessaires à ceux utilisables par le 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). graphe: 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 23/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 2. Création d' un filtre IBaseFilter *pFilter = NULL; CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void **)&pFilter); on veut créer et qui est défini par: où clsid est la classe du filtre que l' • 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 RemoveFilter FindFilterByName ConnectDirect Disconnect Ajouter un filtre dans le graphe. Supprimer un filtre dans le graphe. Trouver un filtre dans le graphe en spécifiant son nom. Connecter deux pins directement. Déconnecter une pin. IGraphBuilder (hérite de IFilterGraph) Connect Render RenderFile AddSourceFilter Connecter deux pins (avec intelligence). Ajouter une chaîne au filtre pour rendre le flux. Construit un graphe qui joue un fichier. Ajouter un fichier source au graphe. IBaseFilter EnumPins FindPin Enumère les pins de ce filtre. Trouver un pin spécifique. QueryAccept Détermine si la pin accepte un type spécifié. IPin Page 24/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 25/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 IBaseFilter::FindPin Syntaxe : HRESULT FindPin(LPCWSTR Id, IPin **ppPin ); Paramètres Id ppPin Retour Remarque : [in] Nom de la pin à rechercher. : [out] Adresse du pointeur sur la pin, NULL si pas trouvé. : Valeur HRESULT (voir la doc) : 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 26/50 Université de Reims Champagne-Ardennes Programmation Multimédia Remarque Master d’Informatique 2005-2006 : 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 27/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 IBaseFilter IBaseFilter IBaseFilter IBaseFilter IPin Page 28/50 *fname = L"ruby.avi"; *InputFileFilter = NULL; *AviSplitter = NULL; *AviDecompressor = NULL; *VideoRenderer = NULL; *PinOut = NULL, *PinIn = NULL; Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 // 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 29/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 o o o un graphe peut être dans trois états différents: Run/Pause/Stop. Son état est "Run" si le graphe a des données qui transitent dans tous ses filtres. Son état est "Stop" si tous les filtres du graphe sont arrêtés. 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 30/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 31/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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: Exposée par: IMediaPosition 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 32/50 Université de Reims Champagne-Ardennes Programmation Multimédia o o Master d’Informatique 2005-2006 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). 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 33/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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: Exposée par: IMediaEvent 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 34/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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, Paramètres lEventCode lParam1 lParam2 Page 35/50 LONG_PTR *lParam1, LONG_PTR *lParam2, long msTimeout); : [out] Pointeur sur l’évènement. : [out] Pointeur sur la première variable de l’évènement. : [out] Pointeur sur la deuxième variable de l’évènement. Université de Reims Champagne-Ardennes Programmation Multimédia msTimeout Retour Master d’Informatique 2005-2006 : [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). 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, ¶m1, ¶m2, 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 36/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 37/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 38/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 où Syntaxe : HRESULT put_CurrentStream(long newVal); Paramètres : newVal = [in] Numéro du flux. 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 AM_MEDIA_TYPE). Type, comme déjà vu dans la structure 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 39/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 40/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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)); } GetSoundFormatCodeString est une fonction qui convertit le FormatTag en chaîne où 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 41/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 ANIM AUR2 AURA BT20 BTCV CC12 CDVC CHAM CPLA CVID CWLT DUCK DVE2 DXT1 DXT2 DXT3 DXT4 DXT5 DXTC FLJP GWLT H260 H261 H262 H263 H264 H265 H266 H267 H268 H269 I263 I420 IAN ICLB ILVC ILVR IRAW IV30 IV31 Page 42/50 Description Intel - RDX AuraVision - Aura 2 Codec - YUV 422 AuraVision - Aura 1 Codec - YUV 411 Brooktree - MediaStream codec Brooktree - Composite Video codec Intel - YUV12 codec Canopus - DV codec Winnov, Inc. MM_WINNOV_CAVIARA_CHAMPAGNE Weitek - 4:2:0 YUV Planar Supermac - Cinepak reserved Duck Corp. - TrueMotion 1.0 InSoft - DVE-2 Videoconferencing codec reserved reserved reserved reserved reserved DirectX Texture Compression D-Vision - Field Encoded Motion JPEG With LSI Bitstream Format reserved Intel - Conferencing codec Intel - Conferencing codec Intel - Conferencing codec Intel - Conferencing codec Intel - Conferencing codec Intel - Conferencing codec Intel - Conferencing codec Intel - Conferencing codec Intel - Conferencing codec Intel - Conferencing codec Intel - I263 Intel - Indeo 4 codec Intel - RDX InSoft - CellB Videoconferencing codec Intel - Layered Video ITU-T - H.263+ compression standard Intel - YUV uncompressed Intel - Indeo Video 3 codec Intel - Indeo Video 3.1 codec IV32 IV33 IV34 IV35 IV36 IV37 IV38 IV39 IV40 IV41 IV42 IV43 IV44 IV45 IV46 IV47 IV48 IV49 IV50 MP42 MPEG MRCA MRLE MSVC NTN1 qpeq RGBT RT21 RVX SDCC SFMC SMSC SMSD SPLC SQZ2 SV10 TLMS TLST TM20 TMIC TMOT TR20 V422 Intel - Indeo Video 3 codec Intel - Indeo Video 3 codec Intel - Indeo Video 3 codec Intel - Indeo Video 3 codec Intel - Indeo Video 3 codec Intel - Indeo Video 3 codec Intel - Indeo Video 3 codec Intel - Indeo Video 3 codec Intel - Indeo Video 4 codec Intel - Indeo Video 4 codec Intel - Indeo Video 4 codec Intel - Indeo Video 4 codec Intel - Indeo Video 4 codec Intel - Indeo Video 4 codec Intel - Indeo Video 4 codec Intel - Indeo Video 4 codec Intel - Indeo Video 4 codec Intel - Indeo Video 4 codec Intel - Indeo 5.0 Microsoft - MPEG-4 Video Codec V2 Chromatic - MPEG 1 Video I Frame FAST Multimedia - Mrcodec Microsoft - Run Length Encoding Microsoft - Video 1 Nogatech - Video Compression 1 Q-Team - QPEG 1.1 Format video codec Computer Concepts - 32 bit support Intel - Indeo 2.1 codec Intel - RDX Sun Communications - Digital Camera Codec Crystal Net - SFM Codec Radius - proprietary Radius - proprietary Splash Studios - ACM audio codec Microsoft - VXtreme Video Codec V2 Sorenson - Video R1 TeraLogic - Motion Intraframe Codec TeraLogic - Motion Intraframe Codec Duck Corp. - TrueMotion 2.0 TeraLogic - Motion Intraframe Codec Horizons Technology - TrueMotion Video Compression Algorithm Duck Corp. - TrueMotion RT 2.0 Vitec Multimedia - 24 bit YUV 4:2:2 format Université de Reims Champagne-Ardennes Programmation Multimédia (CCIR 601). Vitec Multimedia - 16 bit YUV 4:2:2 format. ATI - VCR 1.0 Vivo - H.263 Video Codec Miro Computer Products AG - for use with the Miro line of capture cards. Videologic - VLCAP.DRV V655 VCR1 VIVO VIXL VLV1 Master d’Informatique 2005-2006 WBVC XLV0 YC12 YUV8 YUV9 YUYV ZPEG Winbond Electronics - W9960 NetXL, Inc. - XL Video Decoder Intel - YUV12 codec Winnov, Inc. - MM_WINNOV_CAVIAR_YUV8 Intel - YUV9 Canopus - YUYV compressor Metheus - Video Zipper The following list shows the FOURCC values for DIB (Device Independent Bitmap) compression: Compressor Code CYUV FVF1 IF09 JPEG MJPG PHMO ULTI VDCT VIDS YU92 Description Creative Labs, Inc - Creative Labs YUV Iterated Systems, Inc. - Fractal Video Frame Intel - Intel Intermediate YUV9 Microsoft - Still Image JPEG DIB Microsoft - Motion JPEG DIB Format IBM – Photomotion IBM – Ultimotion Vitec Multimedia - Video Maker Pro DIB Vitec Multimedia - YUV 4:2:2 CCIR 601 for V422 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 43/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 44/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 45/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 ISampleGrabber. pour récupérer l' interface du 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 46/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 47/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 48/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 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 49/50 Université de Reims Champagne-Ardennes Programmation Multimédia Master d’Informatique 2005-2006 Exemple : IBaseFilter ISampleGrabber ISampleGrabberCB *pSampleGrabber = NULL; *pGrabber = NULL; *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 … Page 50/50 Université de Reims Champagne-Ardennes