Mes contributions sur Delphi:


Cliquez sur la photo pour m'envoyer un e-mail:


Visitez les autres sections du site:

shome.gif


 Visit my English pages


Site optimisé pour un affichage en 1024x768.

 Dernière mise à Jour  de cette page

le vendredi 29 avril 2005

 

      film.gif


7ème leçon: Les fichiers GRF


A la leçon 1 nous avons appris à utiliser GraphEdit. Puis à la leçon 3 nous avons vu comment GraphEdit peut espionner une application pour voir quel est le graphe de l'application. Nous avons vu que cela peut être utilisé pour déboguer des applications delphi. Dans cette leçon nous allons approfondir notre connaissance de Graphedit et des relations entre Graphedit et delphi et découvrir trois fonctions utiles. Pour suivre cette leçon, il faut donc avoir étudié les leçons précédentes, tout spécialement les leçons 1 et 3 , et avoir des bases de programmation delphi.


1. Les fichiers GRF et l'utilitaire gratuit GRFshow

Si vous avez utilisé Graphedit comme indiqué à la leçon1 ou à la leçon3, vous avez sûrement constaté qu'une fois un graphe construit dans la fenêtre de Graphedit, on peut le sauver sous forme d'un fichier *.grf.  Utilisez pour cela la commande de menu File/Save as Graph (.GRF)... ou File/Save (.GRF). Le fichier ainsi enregistré sur le disque dur peut être réutilisé plus tard dans Graphedit, avec la commande de menu File/Open Graph(.GRF)...

Je ne connais pas les spécifications complètes des fichiers GRF. On trouve quelques indications dans msdn de Microsoft. Attention toutefois au fait que depuis la version 9 de directX il n'est plus possible de rendre un fichier GRF avec renderfile comme on pouvait le faire avec les versions anciennes de directX avant 8.1 C'est indiqué ici. On verra plus loin la méthode qui marche avec delphi sous directX9.

On voit au moins que les fichiers GRF sont des fichiers contenant un flux binaire (stream) que Graphedit sait charger. Ce flux porte un nom interne qui est ActiveMovieGraph.

A l'intérieur de ce flux apparaissent, deux ou trois sections, selon une valeur indiquée sous la forme 0002 ou 0003. Elles comportent les informations principales relatives au graphe

La première section porte le nom FILTERS, et décrit la liste des filtres qui sont dans le graphe. Pour chacun d'eux on trouve les indications suivantes:

    • numéro du filtre: donné avec 4 chiffres, par exemple 0001
    • GUID du filtre: c'est-à-dire un code du type {1B544C20-FD0B-11CE-8C63-00AA0044B51E}. Ce code est stocké dans le registre pour tous les filtres directshow installés sur votre système, et peut servir à retrouver le filtre. Faites une recherche avec regedit pour le GUID ci-dessus, et vous verrez que c'est le GUID de avi splitter.

     

      • éventuellement si c'est un Source Filter qui charge un fichier, apparaît ensuite:
      • SOURCE nom du fichier: le fichier qui est chargé dans le filtre source, donné par son nom (avec éventuellement le chemin complet), par exemple SOURCE "C:\SomeFile.avi"

       

        • é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:

      • required: un indicateur qui vaut 0 ou 1 (False ou True) selon que la synchronisation est requise ou non
      • clockID: un numéro à 4 chiffres, souvent 0000 ou encore un GUID.

    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. Il me sert aussi beaucoup à analyser des fichiers GRF que m'envoient des internautes, mais qu'il m'est impossible d'ouvrir dans graphedit car certains des filtres utilisés ne sont pas installés sur mon ordinateur. 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).

    {---------------------------------------------------------------------}

      function loadGRF(fichier:widestring;fg:TFiltergraph):Boolean;

      var  hr:HResult;

        pStorage:IStorage;

        pPersist:IPersiststream;

        pStream:IStream;

      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 pink05_back.gif   

    retour vers le haut de la page