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, &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 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