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


6ème leçon: Capturer un flux vidéo


La présente leçon est la suite immédiate de la leçon 5. Nous pousuivons le projet commencé, lequel comportait des fonctions de prévisualisation, et nous y rajoutons maintenant de quoi enregistrer le fichier capturé sur disque.On suppose  pour cela qu'une caméra numérique au format DV est branchée sur une carte firewire et allumée en mode VCR ou Camera, et qu'on sait construire le graphe de prévisualiation comme vu à la leçon5..

1. Enregistrer le flux vidéo

Nous partirons de la situation où une prévisualisation avec son a été choisie. Je vous rappelle qu'alors nous avons construit le graphe suivant:

On y observe une broche de la catégorie Capture sur le Smart Tee. C'est celle-ci que nous allons exploiter avec un renderstream pour enregistrer le flux souhaité. Pour rester simple, nous allons d'abord l'enregistrer en DV de type I, ce qui est aussi plus économique en ressources machines. Si votre configuration est assez puissante, vous pourrez plus loin voir comment construire un graphe qui enregistre le résultat en un fichier DV de type II . Si vous avez compris la leçon 4, cela devrait être faisable pour vous sans attendre que je vous donne la solution.

La fonction que nous créons  pour cela est la suivante:

function TCaptureModule.buildDV1graph (vw:IBaseFilter;fichier:widestring):Boolean;

 

{La fonction prend un paramètre qui est de nouveau le filtre de prévisualisation, comme vu dans la leçon 5, puis le nom du fichier à enregistrer, avec son chemin. L'appel de la fonction se fait donc selon l'exemple suivant:

if capturemodule1.buildcapturegraph(videowindow1 as IbaseFilter,'f:\capture\trucmuche.avi') then begin...}

    var

      cg:ICaptureGraphBuilder2;

      avimux:IBaseFilter;

      writersink:IFileSinkFilter;

      hr:HResult;

{Les variables avimux et writersink sont des pointeurs sur des interfaces qui seront fournies par certaines fonctions de l'interface ICaptureGraphBuilder2.}

    begin

      result:=False;

    try

    try

      if not buildpreviewgraph(vw) then exit;

{On commence par appeler la fonction de preview vue à la leçon 5. Si elle ne marche pas on quitte la fonction}

      capturegraph.stop;

      cg:=CaptureGraph as ICaptureGraphBuilder2;

      hr:=cg.SetOutputFileName(MEDIASUBTYPE_Avi,pwidechar(fichier),

        avimux,writersink);

{La fonction SetOutputFileName de l'interface ICaptureGraphBuilder2 permet de fixer le nom du fichier de sortie. Nous lui donnons donc le widestring reçu en paramètre. Elle fournit un IBaseFilter avimux qu'il faut garder pour l'utiliser plus loin dans renderstream. Voyez dans msdn la référence  de setoutputfilename.}

      if hr<>S_Ok then exit;

      hr:=cg.RenderStream(@PIN_CATEGORY_CAPTURE,@MEDIATYPE_Interleaved, CaptureFiltre as IBaseFilter,nil,avimux);

{On appelle donc renderstream en demandant une broche de type Capture, et donnant un flux qui entrelace video et audio. On demande de construire le graphe en partant de la source CaptureFiltre, pour arriver au filtre avimux qui génère l'écriture sur disque (ce dernier continuera ensuite par le writersink créé par setoutputfilename pour déterminer le nom du fichier, mais il est inutile de le repréciser dans renderstream, car la connexion existe déjà grâce à setoutputfilename}

      result:=succeeded(hr);

    except

    end;

    finally

      cg:=nil;

    end;

    end;

Le résultat de cette fonction , quand elle est appelée avec succès par exemple pour enregistrer la capture dans un fichier trucmuche.avi donne le graphe suivant:

On voit que le renderstream a trouvé la bonne broche de la catégorie Capture sur le filtre Smart Tee en partant du filtre CaptureFiltre, et qu'il a ajouté deux filtres: Mux qui est en fait le filtre Avi Mux, qui encapsule le flux DV audio+vidéo unique dans le format avi, et le writersink qui a pris ici le nom de "trucmuche.avi" et qui enregistre ce fichier sur le disque.

2. Impossible de faire un graphe de capture "économique"

Je rappelle que jusqu'ici nous avons utilisé la fonction preview de la leçon5. Celle-ci avait été construite sur la broche DV A/V Out pour bénéficier de l'audio. Tout cela nous donne un graphe où il y a pas mal de travail pour le processeur et la mémoire vive. Si votre configuration n'est pas assez puissante, cela risque de "ramer" un peu.

On pourrait se demander si on peut faire un graphe de capture en partant du graphe de prévisualisation "économique" que nous avons construit à la leçon 5. Il n'en est rien pour une raison très spécifique: le driver msdv qui construit la source de capture, ici notre CaptureFiltre, ne sait pas rendre des flux sur ses deux broches de sortie à la fois. Il faut donc choisir entre la broche video DV Vid Out qui a servi pour le graphe de capture économique sans le son, et la broche DV A/V Out qui contient audio et vidéo. Pour vous en convaincre, reportez-vous à la référence du driver Microsoft msdv. Par conséquent, si on veut enregistrer le son, ce qui me paraît presque toujours le cas, il faut brancher les flux de preview et de capture sur la seconde broche, et utiliser un smart tee. Tout au plus peut-on économiser la branche de rendu de l'audio pendant la préview si on veut économiser les ressources machines, et le cas échéant éviter du larsen. (Mais on peut aussi mettre le volume du graphe à 0 pour cela).

3. Trouver un filtre dans un graphe

Nous avons vu à la leçon5 comment trouver un filtre DV Splitter, dans un graphe, lorsque nous avons voulu chercher sa broche audio pour la rendre. Lorsqu'on travaille avec renderstream qui introduit des filtres dans un graphe, sans qu'on les ait créé directement en leur donnant un nom, il est souvent utile de chercher ainsi un filtre particulier dans un graphe. Faisons-en une fonction qui pourra être réutilisée:

function TCaptureModule.trouverFiltre(nom:string;var bf:IBaseFilter):Boolean;

{Attention au var devant bf qui est indispensable pour qu'au retour bf pointe bien sur une interface IbaseFilter, malgré notre fl.free}

      var fl:TFilterList;

      i:integer;

    begin

      result:=False;

    try

    try

      fl:=TFilterlist.Create(capturegraph as IFilterGraph);

      if fl.Count=0 then exit;

      for i:=0 to fl.count-1 do begin

        if fl.filterinfo[i].achName=nom then break;

      end;//for

      if i<fl.Count then begin

        result:=true;

        bf:=fl[i];

      end;

    except

    end;//try except

    finally

      fl.Free;

    end;//try finally

    end;

4. Enregistrer en DV de type II

Je vous avais suggéré plus haut de construire vous-même un graphe de capture en DV de type II. Pour les paresseux, ou ceux qui ont encore des difficultés, je donne la fonction correspondante ici, car il me paraît plus pratique et plus naturel de capturer en type II. Je ne sais d'ailleurs pas quels sont les esprits tordus qui ont inventé la capture en un seul flux mêlant audio et vidéo, et qui sont responsables de beaucoup d'ennuis que rencontrent les vidéastes. Voir mon freeware DVdate pour pouvoir jongler entre les type I et II, ou encore la leçon 4 qui vous permettait de construire votre propre convertisseur.

Le principe est d'insérer un filtre nommé DVSplitter (autre que celui inséré automatiquement dans la partie preview) et de relier ses deux broches de sortie à celles d'un filtre avi muxer. Ajoutez donc un filtre DV Splitter sur le capturemodule. La fonction est alors la suivante:

function TCaptureModule.buildDV2graph(vw:IBaseFilter;fichier:widestring):Boolean;

{La fonction prend les mêmes paramètres que buildDV1graph.}

    var

      cg:ICaptureGraphBuilder2;

      avimux,smarttee:IBaseFilter;

      writersink:IFileSinkFilter;

      hr:HResult;

    begin

      result:=False;

    try

    try

      DVSplitter.filtergraph:=nil;

{Utile pour que notre filtre ne soit pas accaparé par renderstream dans la partie preview}

      if not buildpreviewgraph(vw) then exit;

      capturegraph.stop;

      cg:=CaptureGraph as ICaptureGraphBuilder2;

      hr:=cg.SetOutputFileName(MEDIASUBTYPE_Avi, pwidechar(fichier), avimux,writersink);

      if hr<>S_Ok then exit;

      if not trouverFiltre('Smart Tee',smarttee) then exit;

      DVSplitter.filtergraph:=capturegraph;

      hr:=cg.RenderStream(nil,nil,smarttee ,DVSplitter as IbaseFilter,avimux);

      if failed(hr) then exit;

      hr:=cg.renderstream(nil,@MEDIATYPE_Audio,DVSplitter,nil,avimux);

      if failed(hr) then exit;

      result:=true;

    except

    end;//try except

    finally

      cg:=nil;

    end;//try finally

    end;

Si vous regardez le graphe de capture résultant dans graphedit, vous obtenez ceci:

 

5. Conclusion

 Au terme de cette leçon, vous avez appris à construire des graphes de capture, en utilisant les puissantes fonctions de l'interface ICaptureGraphBuilder2. D'une part renderstream qui vous était déjà familière pour relier deux filtres, et qui sait ajouter lui-même les filtres nécessaires (toujours l'intelligence de directshow) et vous pouvez même préciser la nature des broches à relier. D'autre part setoutputfilename qui construit aisément toute la partie d'écriture d'un fichier avi sur le disque. Nous avons construit aussi une fonction "maison" pour retrouver un filtre dans le graphe lorsqu'on connaît seulement son nom. Enfin, vous avez commencé à examiner de plus près la source de capture DV, et entreaperçu quelques unes de ses particularités comme l'impossibilité de rendre les deux broches à la fois. Il y a évidemment beaucoup plus à découvrir sur le format dv dans directshow en parcourant les références dans msdn

Pour mettre en oeuvre ces fonctions dans une application delphi, il vous reste à construire une interface avec l'utilisateur. Comme d'habitude je vous livre en téléchargement une petite démo rudimentaire que vous pourrez améliorer. Pour vous donner une idée de ce qu'on peut faire, regardez mon freeware CaptureFlux.

 

Ici on peut élécharger le code source de cette leçon (dans une version delphi 7).

Leçon 5 pink05_back.gif pink05_next.gif Leçon 7   

retour vers le haut de la page