Partager via


Comment écrire un filtre source pour DirectShow

[La fonctionnalité associée à cette page, DirectShow, est une fonctionnalité héritée. Il a été remplacé par MediaPlayer, IMFMediaEngine et Audio/Video Capture in Media Foundation. Ces fonctionnalités ont été optimisées pour Windows 10 et Windows 11. Microsoft recommande vivement que le nouveau code utilise MediaPlayer, IMFMediaEngine et Audio/Video Capture dans Media Foundation au lieu de DirectShow, si possible. Microsoft suggère que le code existant qui utilise les API héritées soit réécrit pour utiliser les nouvelles API si possible.]

Cette rubrique explique comment écrire un filtre source personnalisé pour DirectShow.

Notes

Cette rubrique décrit uniquement les sources push ; il ne décrit pas les sources d’extraction, telles que le filtre de lecteur asynchrone ou les filtres de fractionnement qui se connectent à des sources d’extraction. Pour connaître la distinction entre les sources push et les sources d’extraction, consultez Data Flow pour les développeurs de filtres.

Modèle de diffusion en continu DirectShow

Lorsque vous écrivez un filtre source, il est important de comprendre qu’une source push n’est pas la même chose qu’une source active. Une source dynamique obtient des données d’une source externe, telle qu’une caméra ou un flux réseau. En règle générale, une source dynamique ne peut pas contrôler le débit entrant des données. Si les filtres en aval ne consomment pas les données assez rapidement, la source doit supprimer des échantillons.

Toutefois, une source push n’a pas besoin d’être une source active. Par exemple, une source push peut lire des données à partir d’un fichier local. Dans ce cas, les filtres de convertisseur en aval déterminent la vitesse à laquelle ils consomment les données de la source, en fonction de l’horloge de référence et des exemples d’horodatages. Le filtre source fournit des exemples aussi rapidement que possible, mais le flux de données réel est limité par les convertisseurs. Les mécanismes de gestion du flux de données sont décrits dans Data Flow pour les développeurs de filtres.

Chaque broche de sortie sur le filtre source crée un thread appelé thread de diffusion en continu. L’épingle fournit des exemples sur le thread de diffusion en continu. En règle générale, tout le décodage, le traitement et le rendu se produisent sur ce thread, bien que certains filtres en aval puissent créer des threads supplémentaires pour mettre en file d’attente leurs exemples de sortie.

Le thread de streaming exécute une boucle avec la structure suivante :

until (stopped)
  1. Get a media sample from the allocator.
  2. Fill the sample with data.
  3. Time stamp the sample. 
  4. Deliver the sample downstream.

Si aucun exemple n’est disponible, l’étape 1 se bloque jusqu’à ce qu’un exemple devienne disponible. L’étape 4 peut également bloquer ; par exemple, il peut se bloquer pendant que le graphique est suspendu.

La boucle s’exécute aussi rapidement que possible, mais elle est limitée par la vitesse à laquelle le filtre de renderer affiche chaque exemple. En supposant que le graphique de filtre a une horloge de référence, la vitesse est déterminée par les heures de présentation sur les échantillons. S’il n’y a pas d’horloge de référence, le convertisseur consomme des échantillons aussi rapidement que possible.

Utilisation de CSource et CSourceStream

Les classes de base DirectShow incluent deux classes qui prennent en charge les sources push : CSource et CSourceStream.

  • CSource est la classe de base du filtre et implémente l’interface IBaseFilter .
  • CSourceStream est la classe de base pour les broches de sortie et implémente l’interface IPin .

Broches de sortie

Un filtre source peut avoir plusieurs broches de sortie. Dans la méthode de constructeur de votre filtre, créez une ou plusieurs broches dérivées de CSourceStream (une broche par flux de sortie). Vous n’avez pas besoin de stocker les pointeurs vers les broches ; les broches s’ajoutent automatiquement au filtre lors de leur création.

Formats de sortie

La broche de sortie gère la négociation de format avec les méthodes CSourceStream suivantes :

Méthode Description
GetMediaType Obtient un type de média à partir de l’épingle de sortie.
L’épingle doit proposer au moins un type de média, car le filtre en aval peut ne proposer aucun type. Dans la plupart des cas, le filtre en aval est un décodeur ou un convertisseur, selon que le filtre source fournit des données compressées ou non compressées. Un filtre de renderer nécessite généralement un type de média complet, contenant toutes les informations de format nécessaires au rendu du flux. Pour un décodeur, la quantité d’informations requises dans le type de média dépend en grande partie du format d’encodage.
CheckMediaType Vérifie si la broche de sortie accepte un type de média donné. La substitution de cette méthode est facultative, selon la façon dont vous implémentez GetMediaType.

La méthode GetMediaType est surchargée :

Si la broche de sortie du filtre source prend en charge exactement un format multimédia, vous devez remplacer (1) pour initialiser l’objet CMediaType avec ce format. Conservez l’implémentation par défaut de (2) et laissez également l’implémentation par défaut de CheckMediaType.

Si l’épingle prend en charge plusieurs formats, remplacez (2). Initialisez l’objet CMediaType en fonction de la valeur de la variable d’index. L’épingle doit retourner les formats sous forme de liste triée. Dans ce cas, vous devez également remplacer checkMediaType pour case activée le type de média par rapport à votre liste de formats.

Pour les formats vidéo non compressés, n’oubliez pas que le filtre en aval peut proposer des formats avec différentes valeurs de foulée. Votre filtre doit accepter toute valeur stride valide. Pour plus d’informations, consultez BITMAPINFOHEADER.

Vous devez également remplacer la méthode virtuelle pure CBaseOutputPin::D ecideBufferSize . Utilisez cette méthode pour définir la taille des exemples de mémoires tampons.

Diffusion en continu

La classe CSourceStream crée le thread de streaming pour l’épingle. La procédure thread est implémentée dans la méthode CSourceStream::D oBufferProcessingLoop . Cette méthode appelle la méthode pure-virtuelle CSourceStream::FillBuffer , que la classe dérivée doit remplacer. Cette méthode est l’endroit où l’épingle remplit la mémoire tampon avec des données. Par exemple, si votre filtre diffuse une vidéo non compressée, c’est là que vous dessinez les trames vidéo.

La classe de base démarre et arrête automatiquement la boucle de thread au bon moment, lorsque le filtre s’interrompt ou s’arrête. Dans ce cas, la classe CSourceStream appelle certaines méthodes pour notifier votre classe dérivée :

Vous pouvez remplacer ces méthodes si vous devez ajouter une gestion spéciale. Sinon, les implémentations par défaut retournent simplement S_OK.

Recherche

Si vous avez un filtre source avec une broche de sortie, vous pouvez utiliser la classe CSourceSeeking comme point de départ pour implémenter la recherche. Héritez de votre classe pin de CSourceStream et de CSourceSeeking.

Notes

CSourceSeeking n’est pas recommandé pour un filtre avec plusieurs broches de sortie. Le main problème est qu’une seule broche doit répondre aux demandes de recherche. En général, cela nécessite une communication entre les broches et le filtre.

La classe CSourceSeeking gère le taux de lecture, l’heure de début, l’heure d’arrêt et la durée. Votre classe dérivée doit définir l’heure et la durée d’arrêt initiales. Chaque fois que l’une de ces valeurs change, la méthode CSourceSeeking::ChangeRate, CSourceSeeking::ChangeStart ou CSourceSeeking::ChangeStop est appelée, selon le cas. Les méthodes sont toutes des méthodes virtuelles pures. La classe pin dérivée remplace ces méthodes pour effectuer les opérations suivantes :

  1. Appelez IPin::BeginFlush sur la broche en aval. Ainsi, les filtres en aval libèrent les échantillons qu’ils conservent et rejettent de nouveaux échantillons.
  2. Appelez CSourceStream::Stop pour arrêter le thread de diffusion en continu. Le filtre source suspend la production de nouvelles données.
  3. Appelez IPin::EndFlush sur la broche en aval. Cela indique aux filtres en aval d’accepter de nouvelles données.
  4. Appelez IPin::NewSegment avec les nouvelles heures et fréquences de début et d’arrêt.
  5. Définissez la propriété de discontinuité sur l’exemple suivant.

Pour plus d’informations, consultez Prise en charge de la recherche dans un filtre source.

Si votre filtre prend en charge la recherche, la position du flux est désormais indépendante de l’heure de présentation. Après une recherche, les horodatages sont réinitialisés à zéro. La formule générale pour les horodatages est la suivante :

  • exemple d’heure de début = temps écoulé / taux de lecture
  • exemple d’heure de fin = heure de début de l’exemple + (heure par image /taux de lecture)

time s’est écoulé est le temps écoulé depuis le début de l’exécution du filtre ou depuis la dernière commande seek.

Formats de temps pour la recherche

Par défaut, les commandes de recherche sont en unités de 100 nanosecondes. Votre filtre source peut prendre en charge des formats de temps supplémentaires, tels que la recherche par numéro d’image. Chaque fois que le format est identifié par un GUID ; consultez GUID de format d’heure.

Pour prendre en charge les formats de temps supplémentaires, vous devez implémenter les méthodes suivantes sur votre broche de sortie :

Si l’application définit un nouveau format d’heure, tous les paramètres de position dans les méthodes IMediaSeeking sont interprétés en termes de nouveau format d’heure. Par exemple, si le format d’heure est des images, la méthode IMediaSeeking::GetDuration doit retourner la durée dans les images.

Dans la pratique, peu de filtres DirectShow prennent en charge des formats de temps supplémentaires, et par conséquent, peu d’applications DirectShow utilisent cette fonctionnalité.

Écriture de filtres sources