Threads de streaming et d’application
[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.]
Toute application DirectShow contient au moins deux threads importants : le thread d’application et un ou plusieurs threads de diffusion en continu. Des exemples sont remis sur les threads de diffusion en continu, et des changements d’état se produisent sur le thread d’application. Le thread de diffusion en continu main est créé par un filtre source ou d’analyseur. D’autres filtres peuvent créer des threads de travail qui fournissent des exemples, qui sont également considérés comme des threads de diffusion en continu.
Certaines méthodes sont appelées sur le thread d’application, tandis que d’autres sont appelées sur un thread de streaming. Par exemple :
- Thread(s) de streaming : IMemInputPin::Receive, IMemInputPin::ReceiveMultiple, IPin::EndOfStream, IMemAllocator::GetBuffer.
- Thread d’application : IMediaFilter::P ause, IMediaFilter::Run, IMediaFilter::Stop, IMediaSeeking::SetPositions, IPin::BeginFlush, IPin::EndFlush.
- Soit : IPin::NewSegment.
Le fait d’avoir un thread de diffusion en continu distinct permet aux données de circuler dans le graphique pendant que le thread d’application attend l’entrée de l’utilisateur. Le danger de plusieurs threads, cependant, est qu’un filtre peut créer des ressources lorsqu’il s’interrompt (sur le thread d’application), les utiliser à l’intérieur d’une méthode de diffusion en continu et les détruire lorsqu’il s’arrête (également sur le thread d’application). Si vous n’êtes pas prudent, le thread de diffusion en continu peut essayer d’utiliser les ressources après leur destruction. La solution consiste à protéger les ressources à l’aide de sections critiques et à synchroniser les méthodes de diffusion en continu avec les changements d’état.
Un filtre a besoin d’une section critique pour protéger l’état du filtre. La classe CBaseFilter a une variable membre pour cette section critique, CBaseFilter::m_pLock. Cette section critique est appelée verrou de filtre. En outre, chaque broche d’entrée a besoin d’une section critique pour protéger les ressources utilisées par le thread de streaming. Ces sections critiques sont appelées verrous de streaming ; vous devez les déclarer dans votre classe d’épingle dérivée. Il est plus facile d’utiliser la classe CCritSec , qui encapsule un objet Windows CRITICAL_SECTION et peut être verrouillée à l’aide de la classe CAutoLock . La classe CCritSec fournit également des fonctions de débogage utiles. Pour plus d’informations, consultez Fonctions de débogage de section critique.
Lorsqu’un filtre s’arrête ou se vide, il doit synchroniser le thread d’application avec le thread de streaming. Pour éviter l’interblocage, il doit d’abord débloquer le thread de streaming, qui peut être bloqué pour plusieurs raisons :
- Il attend d’obtenir un exemple dans la méthode IMemAllocator::GetBuffer , car tous les exemples de l’allocateur sont en cours d’utilisation.
- Il attend qu’un autre filtre retourne à partir d’une méthode de diffusion en continu, telle que Receive.
- Il attend à l’intérieur de l’une de ses propres méthodes de diffusion en continu pour qu’une ressource soit disponible.
- Il s’agit d’un filtre de renderer en attente de l’heure de présentation de l’exemple suivant
- Il s’agit d’un filtre de renderer qui attend à l’intérieur de la méthode Receive pendant la pause.
Par conséquent, lorsque le filtre s’arrête ou se vide, il doit effectuer les opérations suivantes :
- Libérez tout échantillon qu’il contient pour une raison quelconque. Cela débloque la méthode GetBuffer .
- Revenez de n’importe quelle méthode de diffusion en continu aussi rapidement que possible. Si une méthode de diffusion en continu attend une ressource, elle doit cesser d’attendre immédiatement.
- Commencez à rejeter des exemples dans Receive, afin que le thread de streaming n’accède plus aux ressources. (La classe CBaseInputPin gère cela automatiquement.)
- La méthode Stop doit désengager tous les allocateurs du filtre. (La classe CBaseInputPin gère cela automatiquement.)
Le vidage et l’arrêt des deux se produisent sur le thread d’application. Un filtre s’arrête en réponse à la méthode IMediaControl::Stop . Le Gestionnaire de graphe de filtre émet la commande stop dans amont ordre, en commençant à partir des convertisseurs et en descendant jusqu’aux filtres sources. La commande stop se produit complètement à l’intérieur de la méthode CBaseFilter::Stop du filtre. Lorsque la méthode retourne, le filtre doit être dans un état arrêté.
Le vidage se produit généralement en raison d’une commande de recherche. Une commande de vidage démarre à partir du filtre source ou de l’analyseur et se déplace en aval. Le vidage se produit en deux étapes : la méthode IPin::BeginFlush informe un filtre d’ignorer toutes les données en attente et entrantes ; La méthode IPin::EndFlush indique au filtre qu’il accepte à nouveau les données. Le vidage nécessite deux étapes, car l’appel BeginFlush se trouve sur le thread d’application, pendant lequel le thread de streaming continue de fournir des données. Par conséquent, certains échantillons peuvent arriver après l’appel BeginFlush . Le filtre doit les ignorer. Tous les échantillons qui arrivent après l’appel EndFlush sont garantis comme étant nouveaux et doivent être remis.
Les sections qui suivent contiennent des exemples de code montrant comment implémenter les méthodes de filtre les plus importantes, telles que Pause, Réception, etc., de manière à éviter les interblocages et les conditions de concurrence. Toutefois, chaque filtre ayant des exigences différentes, vous devrez adapter ces exemples à votre filtre particulier.
Notes
Les classes de base CTransformFilter et CTransInPlaceFilter gèrent la plupart des problèmes décrits dans cet article. Si vous écrivez un filtre de transformation et que votre filtre n’attend pas les événements à l’intérieur d’une méthode de diffusion en continu ou que vous conservez des exemples en dehors de Receive, ces classes de base doivent être suffisantes.