Pour suivre les cinquième
et sixième leçons, vous devez avoir de bonnes notions de
delphi, avoir installé DSPack et
connaître les concepts évoqués
dans les leçons précédentes (
voir 1 2
3 4).
Nous allons
travailler à capturer une séquence
vidéo depuis une caméra vidéo
DV et l'enregistrer sur le disque dur. Comme
amorcé à la leçon
4, on s'attachera simplement à créer les fonctions
de construction de graphe, logées dans
un datamodule, et on laissera le lecteur compléter
lui-même l'interface avec l'utilisateur.
Dans la présente leçon 5
on construira seulement les fonctions de prévisualisation.
Dans la leçon 6 on complètera
le graphe pour pouvoir enregistrer le fichier
sur le disque.
1. Sélectionner la source de
capture vidéo
Pour commencer,
créez un nouveau projet delphi, ajoutez-lui
un nouveau datamodule - qu'on appelera CaptureModule.
Pour être tranquille, ajoutez dans
la clause Uses du datamodule les trois
unités fournies par Henri Gourvest, à
savoir DSpack, DSutil et directshow9.
Ajoutez aussi dans l'évènement
destroy du datamodule, le code de fermeture
de graphe comme vu dans les leçons précédentes.
Dans la fenêtre du datamodule, faites
glisser un composant filtergraph - qu'on appelera
CaptureGraph dans sa propriété name-
et un composant filtre - qu'on appellera CaptureFiltre.
Comme vu lors des leçons précédentes,
mettez la propriété filtergraph
de CaptureFiltre à CaptureGraph.
On pourrait ensuite fixer à la main sa
propriété BaseFilter. Pour
une fois, il faudrait ne pas aller dans les
filtres de directshow, mais chercher dans la
catégorie Video Capture Sources,
et y choisir l'une des sources de capture disponibles.
Dans cette catégorie
de filtres, on trouve des choses assez différentes,
comme on le voit chez moi. On peut trouver aussi
bien des webcam, des cartes d'acquisition analogiques
que des caméras numériques DV
branchées sur une carte Firewire. A noter
que les cartes d'acquisition analogiques (par
exemple Pinnacle DCxx
MJPEG Capture Filter)
figurent ici même si rien n'est connecté
dessus, alors que la caméra DV n'apparaît
sous le titre Microsoft
DV cameras and VCR
que si elle est branchée et allumée. C'est la raison pour laquelle
il est en fait peu judicieux de fixer la propriété
BaseFilter dès la création
de l'application, car vous ne savez pas si
la caméra sera branchée au moment
où elle sera lancée. Les
applications de capture vidéo
laissent en général l'utilisateur choisir la source
de capture après avoir lancé le programme.
Pour cela il faut énumérer
dans une liste les sources de capture disponibles
et ensuite fixer le BaseFilter de CaptureFiltre
par programme, en fonction de la source qu'il
aura choisie.
C'est l'objet
des deux premières fonctions que nous implanterons dans
le datamodule CaptureModule. D'une part
GetCaptureSources qui liste les sources
de capture disponibles au moment où on
l'appelle, et d'autre part SetCaptureSource
qui met la propriété Basefilter
de notre filtre CaptureFiltre à
la valeur choisie. Les deux fonctions renvoient
True si tout a bien marché et
False s'il y a eu une erreur.
function
TCaptureModule.GetCaptureSources(SourcesList:TStrings):Boolean;
{Notre
fonction doit remplir une variable SourcesList
de type TStrings avec tous les noms de
sources de capture trouvées. Typiquement
quand vous appelez cette fonction, vous lui
fournissez comme paramètre les items
d'une combobox de votre programme principal.
De cette manière l'utilisateur pourra
choisir sa source en la sélectionnant
dans la combobox.}
var
i:
integer;
SysDev:TSysDevEnum;
{TSysDevEnum
est une classe définie dans DSutil,
qui permet d'énumérer une catégorie
de filtres présents sur le système.
Voyez dans l'aide de DSPack les
propriétés et méthodes
de TSysDevEnum.}
{Dans la méthode create
de TSysDevEnum, on définit la catégorie des
sources de capture à énumérer,
ici
par le paramètre CLSID_VideoInputDeviceCategory.
Vous
pouvez essayer avec d'autres catégories,
comme par exemple CLSID_VideoCompressorCategory
ou CLSID_AudioInputDeviceCategory etc...
qui sont définies dans l'unité
directshow9.}
except
end;
finally
end;
end;
Si la fonction GetCaptureSources a bien
marché, la fonction SetCaptureSource
permet de choisir la source qui deviendra active
dans notre graphe, à travers notre filtre
CaptureFiltre
{Cette fonction
prend comme paramètre un nom de source
de capture. C'est-à-dire typiquement
l'un des noms chargés par la fonction
GetCaptureSources dans une liste.}
try
CaptureGraph.active:=false;
SysDev:=
TSysDevEnum.Create(CLSID_VideoInputDeviceCategory);
for
i:=0 to SysDev.CountFilters-1 do begin
if
SysDev.Filters[i].FriendlyName=SourceName
then break;
end;//for
i
if
i=SysDev.CountFilters then exit;
{Elle
recharge un représentant de la classe
TSysDevEnum avec tous les noms des sources
de capture présentes sur le système,
et cherche dans la liste celui qui porte le
nom souhaité.}
{Elle fixe alors la propriété
BaseFilter de notre CaptureFiltre,
en ayant recours à la notion de Moniker,
concept de la programmation COM qu'il serait trop long d'expliquer ici. Si
cela vous intéresse, voir par exemple
la théorie
fournie par Microsoft ici.}
finally
end;
//finally
end;
2. Construire un graphe
de prévisualisation
A partir d'ici, on suppose que
l'utilisateur a choisi un camescope numérique
DV en sélectionnant Microsoft
DV cameras and VCR ou
son équivalent dans une autre langue.
En Français on trouve parfois Caméra et magnétoscope numériques DV Microsoft
comme me l'a signalé Frank Allimant.
On dispose alors d'un filtre CaptureFiltre
qui est inscrit dans le graphe CaptureGraph
et dont le BaseFilter a été
chargé pour qu'il indique la bonne source
de capture. Si on a activé la propriété
GraphEdit du CaptureGraph, alors
on peut voir le graphe suivant, (en tapant CTRL-G
sur la fenêtre de GraphEdit):
Le filtre CaptureFiltre fournit en sortie
deux broches (en Anglais pins). La seconde
DV A/V Out donne exactement ce qui est
entré, c'est-à-dire un flux de
format interleaved comprenant aussi bien l'audio
que la vidéo (voir la leçon
4 sur les DV de type I pour comprendre).
La première DV Vid Out donne
un flux de format video, qui ne comprend donc
plus le son. Pour voir les propriétés
d'une broche, cliquez dessus avec le bouton
droit et regardez sous Pin Proprerties....
Pour obtenir
une prévisualisation "économique",
c'est-à-dire qui ne sollicite pas trop
le processeur, vous pouvez "rendre"
le flux de la broche DV
Vid Out qui présente
la vidéo capturée seule. Essayez
de le faire vous-même, par exemple en
sélectionnant la broche dans une pinlist,
à l'instar de ce que nous avons vu dans
la leçon 4, et en
appliquant à ce pin une fonction render,
fonction qui est dans l'interface IGraphBuilder
de notre filtergraph. Le problème
de cette méthode est que l'on ne traite
pas le son. Cependant si l'enjeu est d'éviter
de perdre des frames dans une configuration
pas hyper-rapide, c'est la méthode la
plus adaptée. Beaucoup de logiciels de
capture ne donnent pas le son pendant la prévisualisation.
Nous allons cependant
utiliser une fonction
spécialisée des graphes de capture
qui s'appelle renderstream et l'appliquer
à la seconde broche, de manière
à pouvoir traiter aussi bien l'image
que le son. Commencez par regarder la référence
de renderstream
dans msdn.
Pour pouvoir l'utiliser, il faut invoquer l'interface
ICaptureGraphBuilder2 de CaptureGraph, ce
qui suppose que vous ayiez ajusté une
nouvelle propriété du graphe CaptureGraph
qui est le mode, en le mettant non plus
à gmNormal comme c'est le cas
par défaut, mais à gmCapture.
Une fois cela
fait, la fonction de construction de la prévisualisation
peut être écrite comme suit:
function
TCaptureModule.buildpreviewgraph(vw:IBaseFilter):Boolean;
{Cette
fonction prend comme argument un IBaseFilter,
qui est typiquement la videowindow dans
laquelle la prévisualisation doit intervenir.
Bien entendu, la propriété filtergraph
de cette videowindow doit avoir été
fixée à CaptureGraph.On
appelera la fonction par une instruction du type:
Le
résultat de la fonction est True
si tout s'est bien passé, et False
sinon.}
{HResult
est le type des codes d'erreur que fournit directshow.
En général quand une fonction
directshow a bien marché, elle renvoie
le message d'erreur S_Ok. Ici on verra
cependant que le résultat attendu est
différent.}
{Les
paramètres de renderstream permettent
d'abord d'indiquer à quelle catégorie
de pins on veut s'adresser: ici on précise
qu'on veut rendre un pin de catégorie preview. On
verra dans la leçon 6 une autre catégorie,
qui est celle de capture. Le second paramètre précise
le type de Media que l'on veut rendre. On trouverait @MEDIATYPE_Video ou @MEDIATYPE_Audio
sur une carte analogique. Mais ici
on s'intéresse à la broche DV
A/V Out qui donne vidéo et audio
entrelacées. On choisit donc @MEDIATYPE_Interleaved.
Les trois
paramètres suivants précisent
le filtre de départ, ici bien évidemment
c'est notre CaptureFiltre; puis un filtre intermédiaire
à utiliser, ici nil pour n'en imposer
aucun; et enfin le filtre d'arrivée,
ici vw qui est le IBaseFilter fourni par notre
fonction buildpreviewgraph.}
{Après avoir
appelé renderstream, le code d'erreur
attendu est VFW_S_NoPreviewpin. En effet,
renderstream part de notre CaptureFiltre
et y cherche une broche de type preview.
Ce type de broches existe sur les cartes analogiques,
mais pas sur les sources DV. D'où un
message d'erreur, qui ne traduit en fait pas
une erreur. Bien mieux, n'en trouvant pas sur
notre CaptureFiltre, renderstream
se débrouille pour en créer un
en ajoutant au graphe un filtre spécial
qui s'appelle Smart Tee, et qui a pour
seule fonction de dupliquer le flux initial
en un flux
de type preview et un flux de type capture.
}
CaptureGraph.Play;
result:=true;
except
end;//except
Tout
cela peut paraître assez obscur, mais
s'éclaire si on utilise graphedit pour
visualiser les choses. On aura obtenu après
la fonction buildpreviewgraph, le graphe suivant
- souvenez-vous qu'il faut mettre la propriété
graphedit de CaptureGraph à true pour
pouvoir visualiser le graphe de notre application
delphi dans graphedit.
On voit ici que
le filtre Smart tee a été
automatiquement ajouté, et qu'il comporte
deux broches de type Preview et capture. Le
reste est maintenant bien connu: DV Splitter
sépare les flux audio et video, DV
Video Decoder décompresse la video
dvsd et on l'envoie ensuite dans une
videowindow qui s'appelle ici vw.
La différence avec la première
méthode qui partait de DV Vid Out
est l'ajout de Smart Tee et de DV
Splitter. Cela prend incontestablement un
peu plus de temps et peut être rédhibitoire
sur les petites configurations. Mais l'avantage
est que cela va permettre d'ajouter un rendu
du son. C'est la présence d'une broche
AudOut00 dans le filtre DV Splitter
qui va nous permettre de conclure sur ce point.
3. Rendre le son pendant la prévisualisation
L'idée
est d'appliquer maintenant la fonction renderstream
en partant du filtre DV
Splitter, et en demandant
un media de type @MEDIATYPE_Audio.
Les autres paramètres de renderstream
sont mis à nil
de manière à laisser directshow
faire au mieux. Il choisira notamment comme
filtre d'arrivée le filtre par défaut
de rendu du son. Le plus dur dans ce morceau
de programme (qui a vocation à s'insérer
dans le précédent) est de trouver
par programme le filtre de départ DV
Splitter. Voici comment
je procède:
Dans les déclarations
de variables de la fonction précédente,
ajouter:
fl:TFilterList;
i:integer;
fo:TFilterInfo;
Puis dans l'implémentation
de notre fonction, avant CaptureGraph.play,
ajouter le code suivant:
{TFilterList
est une classe qui permet d'énumérer
les filtres contenus dans un graphe. Il fonctionne
un peu comme TPinlist vu dans la leçon
précédente.}
{Filterinfo
est une fonction qui s'applique à une
TFilterList (voir l'aide de DSPack) et qui met
dans fo différentes informations sur
le filtre d'index i, en particulier une propriété
achName qui n'est autre que le nom familier
du filtre.}
{
Une fois qu'on a trouvé le bon filtre,
c''est-à-dire celui dont le nom est DV
Splitter, on appelle renderstream, en laissant
la catégorie du pin à nil, en précisant
le format du media comme étant de l'audio,
et en prenant bien sûr notre filtre DV
Splitter comme point de départ, sans
fixer de point d'arrivée qui est laissé
à l'appréciation de directshow...}
Le
graphe résultant de tout cela est maintenant
le suivant:
ce qui était
bien le résultat souhaité.
4. Prévisualisation
économique
Maintenant que
nous maîtrisons bien la fonction renderstream,
on peut l'utiliser pour la prévisualisation
"économique", qui n'utilise
pas le son. Vous avez sûrement deviné
comment l'on fait, et je vous livre le code
sans commentaire:
function
TCaptureModule.economicpreviewgraph(vw:IBaseFilter):Boolean;
begin
try
hr:=(CaptureGraph
as ICaptureGraphBuilder2).RenderStream(nil,@MEDIATYPE_Video,CaptureFiltre
as IBaseFilter,nil,vw);
if
hr<>S_OK then exit;
CaptureGraph.Play;
result:=true;
except
end;//try
except
end;
GraphEdit confirme
le résultat, qui est effectivement plus
économique en ressources du processeur
que celui obtenu précédemment,
mais qui ne comporte pas de son.
Si vous utilisez
ces différents programmes pour faire
une prévisualisation de ce que vous voulez
enregistrer, vous remarquerez sans doute deux
choses: D'une part, on peut prévisualiser
aussi bien ce qui vient en Live d'un camescope
DV que ce qui vient de la cassette (mode VCR).
D'autre part l'image présente des zébrures
d'entrelacement dans les mouvements rapides,
sauf si votre fenêtre videowindow
de prévisualisation ne dépasse
pas 288 pixels de haut, ce qui explique sans
doute que beaucoup de logiciels de capture
ne comportent qu'une petite fenêtre de
prévisualisation. Dans mon freeware CaptureFlux
j'ai sensiblement amélioré les
choses, permettant dans certains cas même
une prévisualisation fluide en plein
écran pour les heureux possesseurs d'une
configuration musclée.
5. Conclusion
Au terme de cette
leçon, vous avez appris à lister
toutes les sources de capture présentes
sur votre configuration, et à choisir
celle qui vous intéresse pour ajouter
le filtre correspondant à un filtergraph
. Je vous ai donné les deux fonctions
fondamentales que vous pourrez utiliser pour
beaucoup d'usages de ce type, à condition
de construire vous-même votre interface
avec l'utilisateur. Vous avez ensuite vu deux
manières de construire un graphe de
prévisualisation pour une source de capture
DV, l'une "économique" sans
le son, l'autre permettant d'avoir le son prendant
la prévisualisation. Pour construire
ces graphes on a appris à passer un filtergraph
en mode Capture, à invoquer son
interface ICaptureGraphBuilder2, à
utiliser la puissante fonction renderstream.
On a aussi vu au passage comment lister les
filtres d'un graphe, et obtenir leurs propriétés,
notamment leur nom. On a développé
quelques considérations sur les codes
d'erreur de directshow, sur les broches de type
Preview ou Capture, et sur le format des media
qui passent à travers ces broches.
Dans la leçon
suivante, on continuera à développer
les questions de capture, en enregistrant maintenant
dans un fichier sur disque le flux vidéo
capturé. On peut télécharger
ci-dessous le code source des fonctions utilisées
dans cette leçon (dans une version delphi
7), avec une interface extrêmemnt sommaire
que je vous laisse le soin d'améliorer.
Ici
on peut élécharger le code source de cette
leçon (dans une version delphi 7).
Leçon
4
Leçon 6
retour
vers le haut de la page
|