次の方法で共有


USB DV ビデオ デバイスの操作

[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayer、IMFMediaEngine、Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayerIMFMediaEngineAudio/Video Capture を使用することを強くお勧めします。 Microsoft は、レガシ API を使用する既存のコードを、可能であれば新しい API を使用するように書き換えるよう提案しています。]

このトピックでは、DV ビデオをキャプチャするユニバーサル シリアル バス (USB) ビデオ デバイス用のアプリケーションを作成する方法について説明します。

Standard DV 形式のデータ レートは、1 秒あたり約 25 メガビット (Mbps) です。 USB が最初に導入されたとき、DV ビデオをサポートするのに十分な帯域幅がありませんでした。 ただし、USB 2.0 では最大 480 Mbps をサポートできます。これは DV ビデオでは十分です。 2003 年にリリースされた USB Video Device Class (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 用語では、エンドポイントとは、データがデバイスに入ったり離れたりするポイントです。 エンドポイントには、入力 (デバイスからホストへ) または出力 (ホストからデバイス) のデータ フローの方向があります。 これらの指示は、ホストに対する相対的な方向と考えるのに役立つ場合があります。 入力はホストに送信されます。出力はホストから取得されます。 次の図は、2 つのエンドポイントを示しています。

usb エンドポイント

UVCデバイスでは、デバイスの機能は論理的にユニットと端子と呼ばれるコンポーネントに分割されます。 ユニットは 1 つ以上のデータ ストリームを入力として受け取り、出力として 1 つのストリームを配信します。 ターミナルは、データ ストリームの開始点または終了ポイントです。 USB エンドポイントはターミナルに対応していますが、方向は逆になります。入力エンドポイントは出力ターミナルで表され、その逆も同様です。 次の図は、ターミナルとエンドポイントの関係を示しています。

uvc ユニットと端子

また、すべてのターミナルが USB エンドポイントに対応しているわけではありません。 エンドポイントという用語は、特に USB 接続を指し、デバイスは USB 以外の接続を介してデータを送受信できます。 たとえば、ビデオ カメラは入力ターミナルで、LCD 画面は出力ターミナルです。

KS プロキシ フィルターでは、単位とターミナルはフィルター内のノードとして表されます。 ノードという用語は、USB 以外のデバイスにもノードを含めることができるため、ユニットとターミナルという用語よりも一般的です。 フィルター内のノードに関する情報を取得するには、 フィルターに対して IKsTopologyInfo インターフェイスのクエリを実行します。 ノードの種類は GUID によって識別されます。 セレクター ノードは、2 つ以上の入力を切り替えることができるノードです。 セレクター ノードは 、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 のデジタル ビデオ