Si c'est un succès, alors
un graphe a été construit, et
en principe il restitue le son via un filtre
Default DirectSound Device. Voici par
exemple un graphe très classique pour
un fichier DV avi.
On voit ici que si l'on veut
trouver le son, il faudra se brancher sur la
broche Stream 01 de Avi splitter
juste avant le Default DirectSound Device
qu'il faudra enlever, se débarasser
aussi de tout ce qui est en aval de Stream
00 car cela concerne la video qu'on ne veut
ici pas perdre de temps à traiter.
- éventuellement
si c'est un Sink Filter qui écrit
dans un fichier, apparaît ensuite:
- SINK
nom du fichier: le fichier qui est ouvert
en mode WRITE pour recueillir le flux, par
exemple SINK
"D:\Fichier.wav"
- data:
des données binaires, dont la longueur
est précisée, la plupart du
temps dix 0, quand il n'y a pas de données
spéciales, donc 0000000000
La deuxième
section, sous le titre CONNECTIONS,
comprend la liste des
connexions qui ont été établies,
chacune donnée sous la forme:
- filtre
d'origine: indiqué par son numéro
dans le liste des filtres précédente
(4 caractères), par exemple 0002
- broche
d'origine: indiquée par son nom
entre guillemets, par exemple "Stream
00".
- filtre
de destination: indiqué de nouveau
par son numéro dans la liste des
filtres, par exemple
0003
- broche
de destination:indiquée par son
nom entre guillemets, par exemple "In".
- taille
des samples: 10 chiffres, par exemple
0000000376
- major
type du media: donné par son
GUID, par exemple {73646976-0000-0010-8000-00AA00389B71}
pour
Vidéo
- subtype
du media: donné par son GUID,
par exemple {64737664-0000-0010-8000-00AA00389B71}
pour dvsd
- flags:
deux indicateurs qui valent 0 ou 1 (False
ou True) selon que les samples sont de taille
fixe ou non, puis selon que le media utilise
de la compression temporelle ou non
- format:
des données de format, comprenant
notamment le GUID du format, par exemple
{05589F80-C356-11CE-BF01-00AA0055595A}
pour le format dvsd 720x576,24bits
Si le nombre
de sections indiqué avant FILTERS est
0002,
alors le fichier ne comporte pas la section
suivante.S'il est 0003,
alors on rencontre une section introduite par
CLOCK, qui
définit les paramètres d'horloge
pour synchroniser les flux du graphe, sous la
forme:
Le fichier est
terminé par la chaîne END
Je me suis amusé
à faire un petit programme qui
analyse les fichiers GRF en tentant d'en retirer
les informations ci-dessus. L'utilité
principale est de retrouver les GUID des filtres
ou des medias utilisés, lorsque le friendlyname
n'est pas suffisamment explicite. Téléchargez-le
ici en version 1.1 en Français
(292Ko), sans aucune garantie de ma part, et utilisez-le
à vos risques et périls.
retour
vers le haut de la page
2. Sauver un graphe depuis
delphi
La méthode
pour espionner une application directshow, c'est
de lancer Graphedit, puis de taper CTRL+G pour
ouvrir l'un des graphes actifs. Cela suppose
que le graphe de votre application directshow
s'est enregistré dans le système,
comme on le fait dans delphi en mettant la propriété
graphedit du filtergraph à true.
Vous pouvez ainsi voir quel
est le graphe l'application, et
vérifier s'il correspond à vos
attentes. Le problème est que cette méthode
n'est pas très souple et en particulier
ne
permet pas de visualiser des graphes intermédiaires
pour comprendre comment le graphe se construit.
La fonction SaveGRF
répond à cette préoccupation.
En l'appelant n'importe où dans votre
programme delphi, vous enregistrez un fichier
GRF représentant le graphe, et vous pouvez
ensuite (éventuellement en différé)
l'ouvrir dans GraphEdit pour voir l'état
du graphe au moment où vous l'avez souhaité.
Je la donne ici sans commentaires, car elle
repose sur des concepts de programmation COM
qui dépassent l'objet de ce tutorial.
Ceux-ci sont définis dans l'unité
ActiveX.dcu de delphi7. Rajoutez donc ActiveX
dans la clause uses de votre unité.
La fonction
SaveGRF prend comme argument d'abord un nom de fichier
(avec son chemin) sous lequel sera sauvé
le graphe. Il a en principe une extension *.grf.
Le second est un composant Tfiltergraph, celui
dont on veut sauver le graphe. Donc un appel
typique de la fonction sera du type:
saveGRF('d:\Mes
Documents\graphe1.grf',filtergraph1);
En sortie, la
fonction retourne True si tout s'est
déroulé sans erreur, False
dans le cas contraire, et vous pouvez alors
ouvrir le fichier obtenu dans GraphEdit
ou dans GRFshow.
{--------------------------------------------------------------------------}
function
saveGRF(fichier:widestring;fg:TFiltergraph):Boolean;
var
hr:Hresult;
pStorage:IStorage;
pStream:IStream;
pPersist:IPersistStream;
begin
result:=false;
try
try
hr:=
StgCreateDocfile(pwidechar(fichier),STGM_CREATE
or STGM_TRANSACTED or STGM_READWRITE or
STGM_SHARE_EXCLUSIVE,0, pStorage);
if
FAILED(hr) then exit;
hr:=
pStorage.CreateStream('ActiveMovieGraph',STGM_WRITE
or STGM_CREATE or STGM_SHARE_EXCLUSIVE,0,
0, pStream);
if
FAILED(hr) then exit;
fg.QueryInterface(IPersistStream,pPersist);
hr:=
pPersist.Save(pStream, TRUE);
if
FAILED(hr) then exit;
pStorage.Commit(STGC_DEFAULT);
if
FAILED(hr) then exit;
result:=true;
except
end;
finally
pStorage:=nil;
pStream:=nil;
pPersist:=nil;
end;
end;
{--------------------------------------------------------------------------}
retour
vers le haut de la page
3. Charger un graphe dans delphi
L'opération
inverse consiste à charger un fichier
GRF dans un Tfiltergraph implanté
dans une application delphi. Cette opération
est parfois très intéressante.
Quand vous avez manuellement mis au point un
graphe dans GraphEdit et bichonné
toutes ses liaisons, vous le sauvez sous un
format GRF et pouvez le charger dans votre
application, sans avoir besoin de programmer
à la main toutes les connexions. C'est
la fonction loadGRF qui permet cela.
Là encore je la donne sans commentaires
sur les concepts de la programmation COM, si
ce n'est de rappeler qu'il faut mettre ActiveX
dans la clause uses de votre unité.
En entrée
la fonction prend comme arguments le nom d'un
fichier GRF (avec son chemin), puis le nom du
composant
TFiltergraph que l'on veut charger. En
sortie, si tout s'est bien déroulé
elle retourne True, sinon False. Dans
le cas True le graphe est en principe
construit et peut éventuellement être joué par
Play, par exemple. Un appel typique sera
donc de la forme suivante:
if
loadGRF('graphe1.grf,filtergraph1) then
filtergraph1.play;
Vous voyez donc
que c'est le chaînon manquant entre GraphEdit
et delphi. Toutes les applications directshow
sous delphi pourraient ainsi être
écrites en trois temps: 1) construire
le graphe avec GraphEdit, 2) construire
l'interface utilisateur dans delphi en la testant grâce
à loadGRF, 3) programmer dans
delphi la construction du graphe pour
ne pas être obligé de distribuer
l'application avec un fichier GRF (et éventuellement
utiliser des méthodes de construction
de graphe qui ne préjugent pas des filtres
installés sur le système de l'utilisateur).
{--------------------------------------------------------------------------}
begin
result:=false;
try
try
if
StgIsStorageFile(pwidechar(fichier))<>S_Ok
then exit;
hr:=
StgOpenStorage(pwidechar(fichier), nil,
STGM_TRANSACTED or STGM_READ or STGM_SHARE_DENY_WRITE,0,
0, pStorage);
if
FAILED(hr) then exit;
hr:=fg.QueryInterface(IPersistStream,pPersist);
if
FAILED(hr) then exit;
hr:=
pStorage.OpenStream('ActiveMovieGraph',
0, STGM_READ or STGM_SHARE_EXCLUSIVE, 0,
pStream);
if
FAILED(hr) then exit;
hr:=
pPersist.Load(pStream);
if
FAILED(hr) then exit;
result:=true;
except
end;
finally
pStorage:=nil;
pStream:=nil;
pPersist:=nil;
end;
end;
{--------------------------------------------------------------------------}
retour
vers le haut de la page
4. Les erreurs de directshow
Dans
les fonctions ci-dessus, vous avez vu apparaître
la variable hr de type Hresult. C'est
l'occasion de commenter la gestion des erreurs
dans directshow. En régle générale
une fonction directshow renvoie un code
d'erreur de type Hresult. En fait HResult
est un entier (de type Longint). En hexadecimal,
si vous l'écrivez sous la forme
inttohex(hr,8), cela
donne une représentation du type $80040209, par exemple
pour la pénible erreur qui dit "L'opération
ne peut pas être effectuée car
les broches ne sont pas connectées
($80040209)".
Les erreurs spécifiques
de directshow sont consultables ici.
Plusieurs fonctions sont fournies pour les traiter; les plus utiles sont FAILED dont
vous voyez des utilisations ci-dessus et SUCCEEDED
. Elles permettent de classer les codes HResult
en deux catégories selon que le code
correspond à un succès ou un
échec. En effet certains codes d'erreurs
correspondent à un succès, par
exemple ceux qui commencent par VFW_S_ et bien
entendu S_Ok qui est le code de succès
par excellence.
Il est possible
de traduire en langage courant la plupart de
ces codes d'erreurs grâce à la
petite fonction que je vous donne ci-dessous.
Typiquement on pourrait l'utiliser dans une
boite de dialogue, par exemple:
if
failed(hr) then messagedlg(geterrortext(hr),mtError,[mbOk],0);
Elle devrait être incluse dans tout
programme directshow sous delphi qui se respecte.
{--------------------------------------------------------------------------}
function
geterrortext(hr:Hresult):string;
var
buf:array[0..255]of char;
begin
AMGetErrortextA(hr,@buf,255);
result:=buf;
end;
{--------------------------------------------------------------------------}
retour
vers le haut de la page
5. Conclusion
Au terme de cette
7ème leçon, vous connaissez mieux
les fichiers GRF de Graphedit, vous savez
sauver par programme un graphe depuis delphi,
et vous savez aussi charger par programme un graphe vers delphi.
Vous avez appris à récupérer
les erreurs de directshow, leur code et leur traduction en
texte. Comme d'habitude, vous pouvez télécharger
ci-dessous une application rudimentaire qui
met en oeuvre ces notions. Elle comprend une
unité grf.pas où vous avez les trois
fonctions données dans cette leçon.
Ces fonctions ont de plus été
sécurisées grâce à
geterrortext. Le programme principal
vous propose un bouton load qui charge
un fichier GRF et essaie de le faire jouer.
Le bouton save charge un fichier multimedia,
construit le graphe pour le rendre, et ensuite
sauve le graphe en question au format GRF. Ce
n'est évidemment qu'un aperçu
très anecdotique de l'intérêt
de ces trois fonctions... A vous de trouver
mieux.
Ici
on peut élécharger le code source de cette
leçon (dans une version delphi 7).
Leçon
6
retour
vers le haut de la page