Работа с видеоустройствами USB DV
[Функция, связанная с этой страницей DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngine, и аудио/ видео захвата в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует использовать в новом коде MediaPlayer, IMFMediaEngine и аудио/видеозахват в Media Foundation вместо DirectShow, когда это возможно. Корпорация Майкрософт предлагает переписать существующий код, в котором используются устаревшие API, чтобы по возможности использовать новые API.]
В этом разделе описывается, как создавать приложения для видеоустройств универсальной последовательной шины (USB), которые захватывают видео DV.
Стандартный формат DV имеет скорость передачи данных около 25 мегабит в секунду (Мбит/с). Когда USB был впервые представлен, у него не было достаточной пропускной способности для поддержки DV-видео. Однако USB 2.0 может поддерживать до 480 Мбит/с, что более чем достаточно для dv-видео. Спецификация класса видеоустройств USB (UVC), выпущенная в 2003 году, определяет формат полезных данных для видеоустройств USB DV. В Windows XP с пакетом обновления 2 (SP2) появился драйвер класса Windows Driver Model (WDM) для устройств UVC.
В большинстве случаев драйвер UVC поддерживает ту же модель программирования, что и драйвер MSDV для устройств IEEE 1394. Приложения, написанные для MSDV, должны требовать лишь незначительных изменений для поддержки устройств UVC.
Драйвер UVC ведет себя иначе, чем драйвер MSDV в следующих областях:
- Режим устройства
- Формат потока
- Формат сигнала
- Поиск по расположению ленты
- Передача DV из файла на ленту.
Чтобы определить, какой драйвер используется, вызовите IAMExtDevice::get_DevicePort. Драйвер MSDV возвращает флаг DEV_PORT_1394, а драйвер UVC — флаг DEV_PORT_USB.
Узлы устройств
В терминологии USB конечные точки — это точки, в которые данные входят или покидают устройство. Конечная точка имеет направление потока данных: входные данные (от устройства к узлу) или выходные данные (от узла к устройству). Это может помочь думать, что эти направления относятся к узлу. Входные данные идут на узел; Выходные данные поступают от узла. На следующей схеме показаны две конечные точки.
На устройстве UVC функции устройства логически разделены на компоненты, называемые единицами и терминалами. Единица получает один или несколько потоков данных в качестве входных данных и доставляет ровно один поток в качестве выходных данных. Терминал — это начальная или конечная точка потока данных. Конечные точки USB соответствуют терминалам, но направления обратно: входная конечная точка представлена выходным терминалом и наоборот. На следующей схеме показана связь между терминалами и конечными точками.
Кроме того, не каждый терминал соответствует конечной точке USB. Термин конечная точка относится конкретно к USB-подключениям, и устройство может отправлять или получать данные через подключения, отличные от USB. Например, видеокамера является входным терминалом, а ЖК-экран — выходным терминалом.
В фильтре прокси-сервера KS единицы и терминалы представлены в виде узлов внутри фильтра. Термин node является более общим, чем термины единица и терминал, так как устройства без USB также могут иметь узлы. Чтобы получить сведения об узлах в фильтре, запросите фильтр для интерфейса IKsTopologyInfo . Типы узлов определяются идентификаторами GUID. Узлы селектора — это узлы, которые могут переключаться между двумя или более входными данными. Узлы селектора предоставляют интерфейс ISelector .
Следующий код проверяет, получает ли выходной контакт фильтра входные данные от узла заданного типа.
// 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;
}
Связанные темы