Utilisation de périphériques vidéo USB DV
[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 des applications pour les périphériques vidéo USB (Universal Serial Bus) qui capturent des vidéos DV.
Le format DV standard a un débit de données d’environ 25 mégabits par seconde (Mbits/s). Lorsque l’usb a été introduit pour la première fois, il n’avait pas suffisamment de bande passante pour prendre en charge la vidéo DV. Toutefois, USB 2.0 peut prendre en charge jusqu’à 480 Mbits/s, ce qui est plus que suffisant pour la vidéo DV. La spécification USB Video Device Class (UVC), publiée en 2003, définit le format de charge utile pour les périphériques vidéo USB DV. Un pilote de classe WDM (Windows Driver Model) pour les appareils UVC a été introduit dans Windows XP Service Pack 2.
À la plupart des égards, le pilote UVC prend en charge le même modèle de programmation que le pilote MSDV pour les appareils IEEE 1394. Les applications écrites pour MSDV ne doivent nécessiter que des modifications mineures pour prendre en charge les appareils UVC.
Le pilote UVC se comporte différemment du pilote MSDV dans les domaines suivants :
- Mode appareil
- Format de flux
- Signal Format
- Recherche d’emplacement de bande
- Transmettre DV d’un fichier à une bande.
Pour déterminer le pilote utilisé, appelez IAMExtDevice::get_DevicePort. Le pilote MSDV retourne l’indicateur DEV_PORT_1394 et le pilote UVC renvoie l’indicateur DEV_PORT_USB.
Nœuds d’appareil
Dans la terminologie USB, les points de terminaison sont les points où les données entrent ou quittent l’appareil. Un point de terminaison a une direction de flux de données, entrée (de l’appareil à l’hôte) ou sortie (de l’hôte à l’appareil). Il peut être utile de considérer ces directions comme étant relatives à l’hôte. L’entrée est envoyée à l’hôte ; la sortie provient de l’hôte. Le diagramme suivant illustre les deux points de terminaison.
Dans un appareil UVC, les fonctions de l’appareil sont logiquement divisées en composants appelés unités et terminaux. Une unité reçoit un ou plusieurs flux de données en tant qu’entrée et fournit exactement un flux en sortie. Un terminal est le point de départ ou de fin d’un flux de données. Les points de terminaison USB correspondent aux terminaux, mais les directions sont inversées : un point de terminaison d’entrée est représenté par un terminal de sortie, et vice versa. Le diagramme suivant montre la relation entre les terminaux et les points de terminaison.
En outre, tous les terminaux ne correspondent pas à un point de terminaison USB. Le terme point de terminaison fait spécifiquement référence aux connexions USB, et un appareil peut envoyer ou recevoir des données via des connexions non USB. Par exemple, une caméra vidéo est un terminal d’entrée et un écran LCD un terminal de sortie.
Dans le filtre proxy KS, les unités et les terminaux sont représentés en tant que nœuds à l’intérieur du filtre. Le terme nœud est plus général que les termes unité et terminal, car les périphériques non USB peuvent également avoir des nœuds. Pour obtenir des informations sur les nœuds d’un filtre, interrogez le filtre pour l’interface IKsTopologyInfo . Les types de nœuds sont identifiés par des GUID. Les nœuds sélecteurs sont des nœuds qui peuvent basculer entre deux entrées ou plus. Les nœuds de sélecteur exposent l’interface ISelector .
Le code suivant teste si une broche de sortie sur un filtre reçoit l’entrée d’un nœud d’un type donné.
// Structure to hold topology information.
struct TopologyConnections
{
KSTOPOLOGY_CONNECTION *connections; // Array of connections
DWORD count; // Number of elements in the array
};
/////////////////////////////////////////////////////////////////////
// Name: GetTopologyConnections
// Desc: Gets the topology information from a filter.
//
// pTopo: Pointer to the filter's IKsTopologyInfo interface.
// connectInfo: Pointer to a TopologyConnections structure. The
// function fills in this structure.
//
// Note: If the function succeeds, call CoTaskMemFree to free the
// pConnectInfo->connections array.
/////////////////////////////////////////////////////////////////////
HRESULT GetTopologyConnections(
IKsTopologyInfo *pTopo,
TopologyConnections *pConnectInfo
)
{
DWORD count;
HRESULT hr = pTopo->get_NumConnections(&count);
if (FAILED(hr))
{
return hr;
}
pConnectInfo->count = count;
pConnectInfo->connections = NULL;
if (count > 0)
{
// Allocate an array for the connection information.
SIZE_T cb = sizeof(KSTOPOLOGY_CONNECTION) * count;
KSTOPOLOGY_CONNECTION *pConnections =
(KSTOPOLOGY_CONNECTION*) CoTaskMemAlloc(cb);
if (pConnections == NULL)
{
return E_OUTOFMEMORY;
}
// Fill the array.
for (DWORD ix = 0; ix < count; ix++)
{
hr = pTopo->get_ConnectionInfo(ix, &pConnections[ix]);
if (FAILED(hr))
{
break;
}
}
if (SUCCEEDED(hr))
{
pConnectInfo->connections = pConnections;
}
else
{
CoTaskMemFree(pConnections);
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////
// Name: IsNodeDownstreamFromNode
// Desc: Searches upstream from a node for a specified node type.
//
// pTopo: Pointer to the filter's IKsTopologyInfo interface.
// connectInfo: Contains toplogy information. To fill in this
// structure, call GetTopologyConnections.
// nodeID: ID of the starting node in the search.
// nodeType: Type of node to find.
// pIsConnected: Receives true if connected, or false otherwise.
//
// Note: If the source node matches the type, this function returns
// true without searching upstream.
/////////////////////////////////////////////////////////////////////
HRESULT IsNodeDownstreamFromNode(
IKsTopologyInfo *pTopo,
const TopologyConnections& connectInfo,
DWORD nodeID,
const GUID& nodeType,
bool *pIsConnected
)
{
*pIsConnected = false;
// Base case for recursion: check the source node.
GUID type;
HRESULT hr = pTopo->get_NodeType(nodeID, &type);
if (FAILED(hr))
{
return hr;
}
if (type == nodeType)
{
*pIsConnected = true;
return S_OK;
}
// If the source node is a selector, get the input node.
CComPtr<ISelector> pSelector;
hr = pTopo->CreateNodeInstance(nodeID, __uuidof(ISelector),
(void**)&pSelector);
if (SUCCEEDED(hr))
{
DWORD sourceNodeID;
hr = pSelector->get_SourceNodeId(&sourceNodeID);
if (SUCCEEDED(hr))
{
// Recursive call with the selector's input node.
return IsNodeDownstreamFromNode(pTopo, connectInfo,
sourceNodeID, nodeType, pIsConnected);
}
}
else if (hr == E_NOINTERFACE)
{
hr = S_OK; // This node is not a selector. Not a failure.
}
else
{
return hr;
}
// Test all of the upstream connections on this pin.
for (DWORD ix = 0; ix < connectInfo.count; ix++)
{
if ((connectInfo.connections[ix].ToNode == nodeID) &&
(connectInfo.connections[ix].FromNode != KSFILTER_NODE))
{
// FromNode is connected to the source node.
DWORD fromNode = connectInfo.connections[ix].FromNode;
// Recursive call with the upstream node.
bool bIsConnected;
hr = IsNodeDownstreamFromNode(pTopo, connectInfo,
fromNode, nodeType, &bIsConnected);
if (FAILED(hr))
{
break;
}
if (bIsConnected)
{
*pIsConnected = true;
break;
}
}
}
return hr;
}
/////////////////////////////////////////////////////////////////////
// Name: GetNodeUpstreamFromPin
// Desc: Finds the node connected to an output pin.
//
// connectInfo: Contains toplogy information. To fill in this
// structure, call GetTopologyConnections.
// nPinIndex: Index of the output pin.
// pNodeID: Receives the ID of the connected node.
/////////////////////////////////////////////////////////////////////
HRESULT GetNodeUpstreamFromPin(
const TopologyConnections& connectInfo,
UINT nPinIndex,
DWORD *pNodeID
)
{
bool bFound = false;
for (DWORD ix = 0; ix < connectInfo.count; ix++)
{
if ((connectInfo.connections[ix].ToNode == KSFILTER_NODE) &&
(connectInfo.connections[ix].ToNodePin == nPinIndex))
{
*pNodeID = connectInfo.connections[ix].FromNode;
bFound = true;
break;
}
}
if (bFound)
{
return S_OK;
}
else
{
return E_FAIL;
}
}
/////////////////////////////////////////////////////////////////////
// Name: IsPinDownstreamFromNode
// Desc: Tests whether an output pin gets data from a node of
// a specified type.
//
// pFilter: Pointer to the filter's IBaseFilter interface.
// UINT: Index of the output pin to test.
// nodeType: Type of node to find.
// pIsConnected: Receives true if connected; false otherwise.
/////////////////////////////////////////////////////////////////////
HRESULT IsPinDownstreamFromNode(
IBaseFilter *pFilter,
UINT nPinIndex,
const GUID& nodeType,
bool *pIsConnected
)
{
CComQIPtr<IKsTopologyInfo> pTopo(pFilter);
if (pTopo == NULL)
{
return E_NOINTERFACE;
}
// Get the topology connection information.
TopologyConnections connectionInfo;
HRESULT hr = GetTopologyConnections(pTopo, &connectionInfo);
if (FAILED(hr))
{
return hr;
}
// Find the node upstream from this pin.
DWORD nodeID;
hr = GetNodeUpstreamFromPin(connectionInfo, nPinIndex, &nodeID);
if (SUCCEEDED(hr))
{
bool isConnected;
hr = IsNodeDownstreamFromNode(pTopo, connectionInfo,
nodeID, nodeType, &isConnected);
if (SUCCEEDED(hr))
{
*pIsConnected = isConnected;
}
}
CoTaskMemFree(connectionInfo.connections);
return hr;
}
Rubriques connexes