Поделиться через


Работа с 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 (WDM) для устройств UVC появился в Windows XP в пакете обновления 2 (SP2).

В большинстве случаев драйвер UVC поддерживает ту же модель программирования, что и драйвер MSDV для устройств IEEE 1394. Приложения, написанные для MSDV, должны требовать только незначительных изменений для поддержки устройств UVC.

Драйвер UVC работает по-разному от драйвера MSDV в следующих областях:

Чтобы определить, какой драйвер используется, вызовите IAMExtDevice::get_DevicePort. Драйвер MSDV возвращает флаг DEV_PORT_1394, а драйвер UVC возвращает флаг DEV_PORT_USB.

узлы устройств

В терминологии USB конечные точки — это точки, в которых данные входят или покидают устройство. Конечная точка имеет направление потока данных, входные данные (от устройства до узла) или выходные данные (от узла до устройства). Это может помочь думать об этих направлениях как относительно хоста. Входные данные поступают к хосту; выходные данные выдаются от хоста. На следующей схеме показаны две конечные точки.

конечных точек USB

На устройстве UVC функции устройства логически делятся на компоненты, называемые единицами и терминалами. Единица получает один или несколько потоков данных в качестве входных данных и предоставляет ровно один поток в виде выходных данных. Терминал — это отправная точка или конечная точка потока данных. Конечные точки USB соответствуют терминалам, но направления противоположны: входная конечная точка представлена выходным терминалом и наоборот. На следующей схеме показана связь между терминалами и конечными точками.

uvc единиц и терминалов

Кроме того, не каждый терминал соответствует 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;
}

Цифровое видео в DirectShow