使用 USB DV 视频设备
[与此页面关联的功能 DirectShow 是一项旧功能。 它已被 MediaPlayer、 IMFMediaEngine 和 媒体基金会中的音频/视频捕获取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能使用 MediaPlayer、 IMFMediaEngine 和 Media Foundation 中的音频/视频捕获 ,而不是 DirectShow。 如果可能,Microsoft 建议重写使用旧 API 的现有代码以使用新 API。]
本主题介绍如何为捕获 DV 视频的通用串行总线 (USB) 视频设备编写应用程序。
标准 DV 格式的数据速率约为每秒 25 兆位, (Mbps) 。 首次引入 USB 时,它没有足够的带宽来支持 DV 视频。 但是,USB 2.0 最多可支持 480 Mbps,这对于 DV 视频来说已经足够了。 2003 年发布的 USB 视频设备类 (UVC) 规范定义了 USB DV 视频设备的有效负载格式。 Windows XP Service Pack 2 中引入了适用于 UVC 设备的 Windows 驱动程序模型 (WDM) 类驱动程序。
在大多数情况下,UVC 驱动程序支持与适用于 IEEE 1394 设备的 MSDV 驱动程序相同的编程模型。 为 MSDV 编写的应用程序只需进行少量修改即可支持 UVC 设备。
UVC 驱动程序的行为与 MSDV 驱动程序在以下方面不同:
若要确定正在使用哪个驱动程序,请调用 IAMExtDevice::get_DevicePort。 MSDV 驱动程序返回DEV_PORT_1394标志,UVC 驱动程序返回DEV_PORT_USB标志。
设备节点
在 USB 术语中,终结点是数据进入或离开设备的点。 终结点具有数据流方向,无论是从设备到主机) 的输入 (,还是从主机到设备) 输出 (。 将这些方向视为相对于主机可能会有所帮助。 输入将转到主机;输出来自主机。 下图说明了这两个终结点。
在 UVC 设备中,设备的功能在逻辑上划分为称为单元和终端的组件。 一个单元接收一个或多个数据流作为输入,并只提供一个流作为输出。 终端是数据流的起点或终点。 USB 终结点对应于终端,但方向是相反的:输入终结点由输出终端表示,反之亦然。 下图显示了终端和终结点之间的关系。
此外,并非每个终端都对应一个 USB 终结点。 术语终结点专门指 USB 连接,设备可以通过非 USB 连接发送或接收数据。 例如,摄像机是输入终端,LCD 屏幕是输出终端。
在 KS 代理筛选器中,单位和终端表示为筛选器中的节点。 术语节点比术语单位和终端更通用,因为非 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;
}
相关主题