流媒体之DirectShow——基本任务


 

一:视频显示

1.1 Renderer介绍

DirectShow使用的renderer有以下几种:

  • Video Renderer filter: 支持所有DirectX的平台,没有系统要求,为默认renderer;
  • Video Mixing Renderer Filter 7(VMR-7): WindowsXP以后版本支持,加入新特性;
  • Video Mixing Renderer Filter 9(VMR-9): 比VMR7更先进,但对系统要求更高;
  • Enhanced Video Renderer (EVR): Windows Vista之后版本支持。
    总的来说:EVR 针对Windows Vista及之后版本,VMR-9针对Windows Vista之前版本。
    窗口模式:
  • Windowed Mode: 窗口模式,renderer带有自己的显示窗口,可以根据需要进行窗口嵌套;
  • Windowless Mode: 无窗口模式,renderer没有自带窗口,可以直接在其他窗口显示。
    Video Renderer filter只支持窗口模式; VMR-7VMR-9 都支持;EVR 只支持无窗口模式。

1.2 Renderer选择

Renderer的选择主要根据Window版本确定,可根据需要选择:

FilterRemarks
Enhanced Video Renderer (EVR)Uses Direct3D 9. Requires Windows Vista or later.
Video Mixing Renderer 9 (VMR-9)Uses Direct3D 9. Requires Windows XP or later.
Video Mixing Filter 7 (VMR-7)Uses DirectDraw. Requires Windows XP or later.
Overlay MixerSupports hardware overlays through DirectDraw.
Legacy Video Renderer filter.Uses DirectDraw or (rarely) GDI

1.3 Windowed Mode

窗口模式拥有自己的窗口,可以进行窗口嵌套,常规步骤:

  1. 查询IVideoWindow接口;
  2. 设置父窗口;
  3. 设置新的窗口风格;
  4. 为窗口在父窗口设置相应的位置;
  5. 通知窗口相应的WM_MOVE 事件。
// Query IVideoWindow
IVideoWindow *pVidWin = NULL;
pGraph->QueryInterface(IID_IVideoWindow, (void **)&pVidWin);

// Set parent
pVidWin->put_Owner((OAHWND)hwnd);

// Set window style
pVidWin->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS);

// Set position
RECT rc;
GetClientRect(hwnd, &rc);
pVidWin->SetWindowPosition(0, 0, rc.right, rc.bottom);

// Deal event
// (Inside your WindowProc)
case WM_MOVE:
    pVidWin->NotifyOwnerMessage((OAHWND)hWnd, msg, wParam, lParam);
    break;

// Clear up
pControl->Stop();
pVidWin->put_Visible(OAFALSE);
pVidWin->put_Owner(NULL);

1.4 Windowless Mode

常规步骤如下:

  1. 配置VMR为Windowless模式;
    VMR-7:
    • 创建Filter Graph Manager;
    • 创建VMR-7 并添加到filter graph;
    • 通过IVMRFilterConfig::SetRenderingMode设置VMRMode_Windowless标志;
    • 查询VMR-7的 IVMRWindowlessControl接口;
    • 调用IVMRWindowlessControl::SetVideoClippingWindow设置窗口。
      VMR-9:
    • 创建Filter Graph Manager;
    • 创建VMR-9 并添加到filter graph;
    • 通过 IVMRFilterConfig9::SetRenderingMode设置VMR9Mode_Windowless标志;
    • 查询VMR-9的IVMRWindowlessControl9接口;
    • 调用IVMRWindowlessControl9::SetVideoClippingWindow设置窗口。
  2. 处理filter graph的剩余部分;
  3. 指定视频位置:
    VMR7:
    • IVMRWindowlessControl::SetVideoPosition指定源矩形和目标矩形区域;
    • 源矩形必须小于等于视频大小,视频大小可以通过IVMRWindowlessControl::GetNativeVideoSize查询。
      VMR9:
    • IVMRWindowlessControl9::SetVideoPosition指定源矩形和目标矩形区域;
    • 源矩形必须小于等于视频大小,视频大小可以通过IVMRWindowlessControl9::GetNativeVideoSize。
  4. 处理窗口消息:
    VMR-7:
    • WM_PAINT: 调用 IVMRWindowlessControl::RepaintVideo绘制最新视频帧;
    • WM_DISPLAYCHANGE: 调用IVMRWindowlessControl::DisplayModeChanged重置视频分辨率或色彩格式;
    • WM_SIZE or WM_WINDOWPOSCHANGED: 重新计算位置,调用 IVMRWindowlessControl::SetVideoPosition 更新。
      VMR-9:
    • WM_PAINT: 调用 IVMRWindowlessControl9::RepaintVideo 绘制最新视频帧;
    • **WM_DISPLAYCHANGE: ** 调用 IVMRWindowlessControl9::DisplayModeChanged重制视频分辨率或色彩格式;
    • WM_SIZE or **WM_WINDOWPOSCHANGED: **重新计算位置,调用IVMRWindowlessControl9::SetVideoPosition 更新。
       
       

二:处理消息

DirectShow的消息处理流程:

// 自定义消息类型
#define WM_GRAPHNOTIFY  WM_APP + 1

// 注册窗口消息
IMediaEventEx *g_pEvent = NULL;
g_pGraph->QueryInterface(IID_IMediaEventEx, (void **)&g_pEvent);
g_pEvent->SetNotifyWindow((OAHWND)g_hwnd, WM_GRAPHNOTIFY, 0);

// 在窗口的消息循环中处理注册的消息
case WM_GRAPHNOTIFY:
    HandleGraphEvent();
    break;

// 处理DirectShow消息
void HandleGraphEvent()
{
    // Disregard if we don't have an IMediaEventEx pointer.
    if (g_pEvent == NULL)
    {
        return;
    }
    // Get all the events
    long evCode;
    LONG_PTR param1, param2;
    HRESULT hr;
    while (SUCCEEDED(g_pEvent->GetEvent(&evCode, &param1, &param2, 0)))
    {
        g_pEvent->FreeEventParams(evCode, param1, param2);
        switch (evCode)
        {
        case EC_COMPLETE:  // Fall through.
        case EC_USERABORT: // Fall through.
        case EC_ERRORABORT:
            CleanUp();
            PostQuitMessage(0);
            return;
        }
    } 
}

 
 

三:设备枚举

3.1 系统枚举

// 创建设备枚举句柄
HRESULT hr;
ICreateDevEnum *pSysDevEnum = NULL;
hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
    IID_ICreateDevEnum, (void **)&pSysDevEnum);
if (FAILED(hr)) {
    return hr;
}

// 获得视频枚举器
IEnumMoniker *pEnumCat = NULL;
hr = pSysDevEnum->CreateClassEnumerator(CLSID_VideoCompressorCategory, &pEnumCat, 0);
if (hr == S_OK) {
    // 循环枚举
    IMoniker *pMoniker = NULL;
    ULONG cFetched;
    while(pEnumCat->Next(1, &pMoniker, &cFetched) == S_OK)
    {
        IPropertyBag *pPropBag;
        hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, 
            (void **)&pPropBag);
        if (SUCCEEDED(hr))
        {
             VARIANT var;
        	VariantInit(&var);

        	// Get description or friendly name.
        	hr = pPropBag->Read(L"Description", &var, 0);
        	if (FAILED(hr))
        	{
         		hr = pPropBag->Read(L"FriendlyName", &var, 0);
        	}
        	if (SUCCEEDED(hr))
        	{
            	printf("%S\n", var.bstrVal);
            	VariantClear(&var); 
        	}

        	hr = pPropBag->Write(L"FriendlyName", &var);

        	// WaveInID applies only to audio capture devices.
        	hr = pPropBag->Read(L"WaveInID", &var, 0);
        	if (SUCCEEDED(hr))
        	{
            	printf("WaveIn ID: %d\n", var.lVal);
            	VariantClear(&var); 
        	}

        	hr = pPropBag->Read(L"DevicePath", &var, 0);
        	if (SUCCEEDED(hr))
        	{
            	// The device path is not intended for display.
            	printf("Device path: %S\n", var.bstrVal);
            	VariantClear(&var); 
        	}

            // To create an instance of the filter, do the following:
            IBaseFilter *pFilter;
            hr = pMoniker->BindToObject(NULL, NULL, IID_IBaseFilter,
                (void**)&pFilter);
            // Now add the filter to the graph. 
            //Remember to release pFilter later.
            pPropBag->Release();
        }
        pMoniker->Release();
    }
    pEnumCat->Release();
}
pSysDevEnum->Release();

CLSID_VideoCompressorCategory表示枚举的设备类型,根据需要修改。例如:CLSID_AudioInputDeviceCategory,CLSID_VideoInputDeviceCategory等。

每个IMoniker的IPropertyBag包含的内容如下:

PropertyDescriptionVARIANT Type
“FriendlyName”The name of the device.VT_BSTR
“Description”A description of the device.VT_BSTR
“DevicePath”A unique string that identifies the device. (Video capture devices only.)VT_BSTR
“WaveInID”The identifier for an audio capture device. (Audio capture devices only.)VT_I4

3.2 Filter Mapper枚举


 
 

四:Filter Graph内部枚举

Filter Graph -> Filters -> Pins -> Media Types

4.1 枚举Filters

在Filter Graph中枚举存在的Filters:

HRESULT EnumFilters (IFilterGraph *pGraph) 
{
    IEnumFilters *pEnum = NULL;
    IBaseFilter *pFilter;
    ULONG cFetched;

    // 查询所有Filters
    HRESULT hr = pGraph->EnumFilters(&pEnum);
    if (FAILED(hr)) return hr;

    // 遍历枚举到的Filter信息
    while(pEnum->Next(1, &pFilter, &cFetched) == S_OK)
    {
        FILTER_INFO FilterInfo;
        hr = pFilter->QueryFilterInfo(&FilterInfo);
        if (FAILED(hr))
        {
            MessageBox(NULL, TEXT("Could not get the filter info"),
                TEXT("Error"), MB_OK | MB_ICONERROR);
            continue;  // Maybe the next one will work.
        }

#ifdef UNICODE
        MessageBox(NULL, FilterInfo.achName, TEXT("Filter Name"), MB_OK);
#else
        char szName[MAX_FILTER_NAME];
        int cch = WideCharToMultiByte(CP_ACP, 0, FilterInfo.achName,
            MAX_FILTER_NAME, szName, MAX_FILTER_NAME, 0, 0);
        if (cch > 0)
            MessageBox(NULL, szName, TEXT("Filter Name"), MB_OK);
#endif

        // The FILTER_INFO structure holds a pointer to the Filter Graph
        // Manager, with a reference count that must be released.
        if (FilterInfo.pGraph != NULL)
        {
            FilterInfo.pGraph->Release();
        }
        pFilter->Release();
    }

    pEnum->Release();
    return S_OK;
}

4.2 枚举Pin

枚举单个Filter包含的Pins:

HRESULT GetPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin)
{
    IEnumPins  *pEnum = NULL;
    IPin       *pPin = NULL;
    HRESULT    hr;

    if (ppPin == NULL)
    {
        return E_POINTER;
    }

    // 查询所有的Pins
    hr = pFilter->EnumPins(&pEnum);
    if (FAILED(hr))
    {
        return hr;
    }
    
    // 遍历Pin信息(方向等)
    while(pEnum->Next(1, &pPin, 0) == S_OK)
    {
        PIN_DIRECTION PinDirThis;
        hr = pPin->QueryDirection(&PinDirThis);
        if (FAILED(hr))
        {
            pPin->Release();
            pEnum->Release();
            return hr;
        }
        if (PinDir == PinDirThis)
        {
            // Found a match. Return the IPin pointer to the caller.
            *ppPin = pPin;
            pEnum->Release();
            return S_OK;
        }
        // Release the pin for the next time through the loop.
        pPin->Release();
    }
    // No more pins. We did not find a match.
    pEnum->Release();
    return E_FAIL;  
}

4.3 枚举Media Types

枚举Pin包含的Media Types:

// Given a pin, find a preferred media type 
//
// pPin         Pointer to the pin.
// majorType    Preferred major type (GUID_NULL = don't care).
// subType      Preferred subtype (GUID_NULL = don't care).
// formatType   Preferred format type (GUID_NULL = don't care).
// ppmt         Receives a pointer to the media type. Can be NULL.
//
// Note: If you want to check whether a pin supports a desired media type,
//       but do not need the format details, set ppmt to NULL.
//
//       If ppmt is not NULL and the method succeeds, the caller must
//       delete the media type, including the format block. 

HRESULT GetPinMediaType(
    IPin *pPin,             // pointer to the pin
    REFGUID majorType,      // desired major type, or GUID_NULL = don't care
    REFGUID subType,        // desired subtype, or GUID_NULL = don't care
    REFGUID formatType,     // desired format type, of GUID_NULL = don't care
    AM_MEDIA_TYPE **ppmt    // Receives a pointer to the media type. (Can be NULL)
    )
{
    *ppmt = NULL;

    IEnumMediaTypes *pEnum = NULL;
    AM_MEDIA_TYPE *pmt = NULL;
    BOOL bFound = FALSE;
    
    HRESULT hr = pPin->EnumMediaTypes(&pEnum);
    if (FAILED(hr))
    {
        return hr;
    }

    while (hr = pEnum->Next(1, &pmt, NULL), hr == S_OK)
    {
        if ((majorType == GUID_NULL) || (majorType == pmt->majortype))
        {
            if ((subType == GUID_NULL) || (subType == pmt->subtype))
            {
                if ((formatType == GUID_NULL) || 
                    (formatType == pmt->formattype))
                {
                    // Found a match. 
                    if (ppmt)
                    {
                        *ppmt = pmt;  // Return it to the caller
                    }
                    else
                    {
                        _DeleteMediaType(pmt);
                    }
                    bFound = TRUE;
                    break;
                }
            }
        }
        _DeleteMediaType(pmt);
    }

    SafeRelease(&pEnum);
    if (SUCCEEDED(hr))
    {
        if (!bFound)
        {
            hr = VFW_E_NOT_FOUND;
        }
    }
    return hr;
}

 
 

五:Graph建立

5.1 Filter通过CLSID加入Graph

HRESULT AddFilterByCLSID(
    IGraphBuilder *pGraph,      // Pointer to the Filter Graph Manager.
    REFGUID clsid,              // CLSID of the filter to create.
    IBaseFilter **ppF,          // Receives a pointer to the filter.
    LPCWSTR wszName             // A name for the filter (can be NULL).
    )
{
    *ppF = 0;

    IBaseFilter *pFilter = NULL;
    
    HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, 
        IID_PPV_ARGS(&pFilter));
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pGraph->AddFilter(pFilter, wszName);
    if (FAILED(hr))
    {
        goto done;
    }

    *ppF = pFilter;
    (*ppF)->AddRef();

done:
    SafeRelease(&pFilter);
    return hr;
}

上述步骤为:根据CLSID创建Filter -> 将Filter加入Graph。
有时候也可以使用更简单的方法(但不是一直有效):

IBaseFilter *pMux;
hr = AddFilterByCLSID(pGraph, CLSID_AviDest, L"AVI Mux", &pMux, NULL); 
if (SUCCEEDED(hr))
{
    /* ... */
   pMux->Release();
}

5.2 查找Pin

Pin有连接状态和方向两种属性。通过以下接口可以查询:
查询Pin是否连接:

HRESULT IsPinConnected(IPin *pPin, BOOL *pResult)
{
    IPin *pTmp = NULL;
    HRESULT hr = pPin->ConnectedTo(&pTmp);
    if (SUCCEEDED(hr))
    {
        *pResult = TRUE;
    }
    else if (hr == VFW_E_NOT_CONNECTED)
    {
        // The pin is not connected. This is not an error for our purposes.
        *pResult = FALSE;
        hr = S_OK;
    }

    SafeRelease(&pTmp);
    return hr;
}

查询Pin方向:

// Query whether a pin has a specified direction (input / output)
HRESULT IsPinDirection(IPin *pPin, PIN_DIRECTION dir, BOOL *pResult)
{
    PIN_DIRECTION pinDir;
    HRESULT hr = pPin->QueryDirection(&pinDir);
    if (SUCCEEDED(hr))
    {
        *pResult = (pinDir == dir);
    }
    return hr;
}

按照连接状态和方向查询:

// Match a pin by pin direction and connection state.
HRESULT MatchPin(IPin *pPin, PIN_DIRECTION direction, BOOL bShouldBeConnected, BOOL *pResult)
{
    assert(pResult != NULL);

    BOOL bMatch = FALSE;
    BOOL bIsConnected = FALSE;

    HRESULT hr = IsPinConnected(pPin, &bIsConnected);
    if (SUCCEEDED(hr))
    {
        if (bIsConnected == bShouldBeConnected)
        {
            hr = IsPinDirection(pPin, direction, &bMatch);
        }
    }

    if (SUCCEEDED(hr))
    {
        *pResult = bMatch;
    }
    return hr;
}

按要求查找Filter中的符合条件的Pin:

// Return the first unconnected input pin or output pin.
HRESULT FindUnconnectedPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin)
{
    IEnumPins *pEnum = NULL;
    IPin *pPin = NULL;
    BOOL bFound = FALSE;

    HRESULT hr = pFilter->EnumPins(&pEnum);
    if (FAILED(hr))
    {
        goto done;
    }

    while (S_OK == pEnum->Next(1, &pPin, NULL))
    {
        hr = MatchPin(pPin, PinDir, FALSE, &bFound);
        if (FAILED(hr))
        {
            goto done;
        }
        if (bFound)
        {
            *ppPin = pPin;
            (*ppPin)->AddRef();
            break;
        }
        SafeRelease(&pPin);
    }

    if (!bFound)
    {
        hr = VFW_E_NOT_FOUND;
    }

done:
    SafeRelease(&pPin);
    SafeRelease(&pEnum);
    return hr;
}

5.3 Filters连接

主要有以下几种连接方式:

5.3.1 Output Pin -> Filter

即已知上级的输出Pin和下级Filter:

// Connect output pin to filter.

HRESULT ConnectFilters(
    IGraphBuilder *pGraph, // Filter Graph Manager.
    IPin *pOut,            // Output pin on the upstream filter.
    IBaseFilter *pDest)    // Downstream filter.
{
    IPin *pIn = NULL;
        
    // Find an input pin on the downstream filter.
    HRESULT hr = FindUnconnectedPin(pDest, PINDIR_INPUT, &pIn);
    if (SUCCEEDED(hr))
    {
        // Try to connect them.
        hr = pGraph->Connect(pOut, pIn);
        pIn->Release();
    }
    return hr;
}
5.3.2 Filter -> Input Pin

即已知上级Filter和下级的输入Pin:

// Connect filter to input pin.

HRESULT ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pSrc, IPin *pIn)
{
    IPin *pOut = NULL;
        
    // Find an output pin on the upstream filter.
    HRESULT hr = FindUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut);
    if (SUCCEEDED(hr))
    {
        // Try to connect them.
        hr = pGraph->Connect(pOut, pIn);
        pOut->Release();
    }
    return hr;
}
5.3.3 Filter -> Filter

即已知上级Filter和下级Filter:

// Connect filter to filter

HRESULT ConnectFilters(IGraphBuilder *pGraph, IBaseFilter *pSrc, IBaseFilter *pDest)
{
    IPin *pOut = NULL;

    // Find an output pin on the first filter.
    HRESULT hr = FindUnconnectedPin(pSrc, PINDIR_OUTPUT, &pOut);
    if (SUCCEEDED(hr))
    {
        hr = ConnectFilters(pGraph, pOut, pDest);
        pOut->Release();
    }
    return hr;
}

5.4 查找Filter和Pin的Interface

Filter和Pin都属于COM组件,都有相应的Interface用于控制。通过以下方法可以查找需要的Interface:

5.4.1 查找Filter的Interface
HRESULT FindFilterInterface(
    IGraphBuilder *pGraph, // Pointer to the Filter Graph Manager.
    REFGUID iid,           // IID of the interface to retrieve.
    void **ppUnk)          // Receives the interface pointer.
{
    if (!pGraph || !ppUnk) return E_POINTER;

    HRESULT hr = E_FAIL;
    IEnumFilters *pEnum = NULL;
    IBaseFilter *pF = NULL;
    if (FAILED(pGraph->EnumFilters(&pEnum)))
    {
        return E_FAIL;
    }
    // Query every filter for the interface.
    while (S_OK == pEnum->Next(1, &pF, 0))
    {
        hr = pF->QueryInterface(iid, ppUnk);
        pF->Release();
        if (SUCCEEDED(hr))
        {
            break;
        }
    }
    pEnum->Release();
    return hr;
}
5.4.2 查找Pin的Interface
HRESULT FindPinInterface(
    IBaseFilter *pFilter,  // Pointer to the filter to search.
    REFGUID iid,           // IID of the interface.
    void **ppUnk)          // Receives the interface pointer.
{
    if (!pFilter || !ppUnk) return E_POINTER;

    HRESULT hr = E_FAIL;
    IEnumPins *pEnum = 0;
    if (FAILED(pFilter->EnumPins(&pEnum)))
    {
        return E_FAIL;
    }
    // Query every pin for the interface.
    IPin *pPin = 0;
    while (S_OK == pEnum->Next(1, &pPin, 0))
    {
        hr = pPin->QueryInterface(iid, ppUnk);
        pPin->Release();
        if (SUCCEEDED(hr))
        {
            break;
        }
    }
    pEnum->Release();
    return hr;
}
5.4.3 查找任意的Interface
HRESULT FindInterfaceAnywhere(
    IGraphBuilder *pGraph, 
    REFGUID iid, 
    void **ppUnk)
{
    if (!pGraph || !ppUnk) return E_POINTER;
    HRESULT hr = E_FAIL;
    IEnumFilters *pEnum = 0;
    if (FAILED(pGraph->EnumFilters(&pEnum)))
    {
        return E_FAIL;
    }
    // Loop through every filter in the graph.
    IBaseFilter *pF = 0;
    while (S_OK == pEnum->Next(1, &pF, 0))
    {
        hr = pF->QueryInterface(iid, ppUnk);
        if (FAILED(hr))
        {
            // The filter does not expose the interface, but maybe
            // one of its pins does.
            hr = FindPinInterface(pF, iid, ppUnk);
        }
        pF->Release();
        if (SUCCEEDED(hr))
        {
            break;
        }
    }
    pEnum->Release();
    return hr;
}

5.5 查询Filter的相邻Filter

查找与Filter相连的其他Filter,基本就是遍历整个Graph。详情参考:
Find a Filters Peer

5.6 删除Graph的所有Filter

即遍历Graph和安全释放Filter:

// Stop the graph.
pControl->Stop();

// Enumerate the filters in the graph.
IEnumFilters *pEnum = NULL;
HRESULT hr = pGraph->EnumFilters(&pEnum);
if (SUCCEEDED(hr))
{
    IBaseFilter *pFilter = NULL;
    while (S_OK == pEnum->Next(1, &pFilter, NULL))
     {
         // Remove the filter.
         pGraph->RemoveFilter(pFilter);
         // Reset the enumerator.
         pEnum->Reset();
         pFilter->Release();
    }
    pEnum->Release();
}

5.7 使用Capture Graph Builder辅助构建Graph

由于Graph的结构过于复杂,DirectShow可以使用Capture Graph Builder来辅助构建一些特殊的Graph,例如采集系统等。可以认为Capture Graph Builder构建的是特殊的Filter Graph。具体操作:

  1. 构建Capture Graph Builder,并与Filter Graph关联;
  2. 连接Filters;
  3. 查找Filters和Pins的Interface;
  4. 查找Pins。
5.7.1 创建并关联Filter Graph
IGraphBuilder *pGraph = NULL;
ICaptureGraphBuilder2 *pBuilder = NULL;

// Create the Filter Graph Manager.
HRESULT hr =  CoCreateInstance(CLSID_FilterGraph, NULL,
    CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&pGraph);

if (SUCCEEDED(hr))
{
    // Create the Capture Graph Builder.
    hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL,
        CLSCTX_INPROC_SERVER, IID_ICaptureGraphBuilder2, 
        (void **)&pBuilder);
    if (SUCCEEDED(hr))
    {
        pBuilder->SetFiltergraph(pGraph);
    }
};
5.7.2 连接Filters

Capture Graph Builder通过 ICaptureGraphBuilder2::RenderStream 连接Filters。暂时先不讨论此函数的前两个参数:

// Connect A -> B -> C
RenderStream(NULL, NULL, A, B, C);

// Connect A -> C
RenderStream(NULL, NULL, A, NULL, C);

// Connect A -> B -> C -> D -> E
RenderStream(NULL, NULL, A, B, C);
RenderStream(NULL, NULL, C, D, E);

最后一个参数为NULL则表示自动指定一个Renderer。现在来讨论前两个参数:

  • 第一个参数:只用于Capture Filter,指明输出Pin类型,可选值:PIN_CATEGORY_CAPTUREPIN_CATEGORY_PREVIEW;如果Capture Filter没有这两种类型的Pin,那么RenderStream会自动提供一个 Smart Tee Filter来提供Capture Pin和Preview Pin;(Preview Pin会根据需要丢帧,优先会保证Capture Pin的帧采集);
  • 第二个参数:媒体类型,取值:MEDIATYPE_Audio,MEDIATYPE_VideoMEDIATYPE_Interleaved(DV)
5.7.3 查找Filters和Pins的Interface
// eg. to find IAMStreamConfig interface
IAMStreamConfig *pConfig = NULL;
HRESULT hr = pBuild->FindInterface(
    &PIN_CATEGORY_PREVIEW, 
    &MEDIATYPE_Video,
    pVCap, 
    IID_IAMStreamConfig, 
    (void**)&pConfig
);
if (SUCCESSFUL(hr))
{
    /* ... */
    pConfig->Release();
}
5.7.4 查找Pins
// eg. find unconnected output pin
IPin *pPin = NULL;
hr = pBuild->FindPin(
    pCap,                   // Pointer to the filter to search.
    PINDIR_OUTPUT,          // Search for an output pin.
    &PIN_CATEGORY_PREVIEW,  // Search for a preview pin.
    &MEDIATYPE_Video,       // Search for a video pin.
    TRUE,                   // The pin must be unconnected. 
    0,                      // Return the first matching pin (index 0).
    &pPin);                 // This variable receives the IPin pointer.
if (SUCCESSFUL(hr))
{
    /* ... */
    pPin->Release();
}

 
 

六:在Filter Graph中Seek

Seeking the Filter Graph
 
 

七:设置Graph时钟

Setting the Graph Clock
 
 

八:显示属性页

IBaseFilter *pFilter;
/* Obtain the filter's IBaseFilter interface. (Not shown) */
ISpecifyPropertyPages *pProp;
HRESULT hr = pFilter->QueryInterface(IID_ISpecifyPropertyPages, (void **)&pProp);
if (SUCCEEDED(hr)) 
{
    // Get the filter's name and IUnknown pointer.
    FILTER_INFO FilterInfo;
    hr = pFilter->QueryFilterInfo(&FilterInfo); 
    IUnknown *pFilterUnk;
    pFilter->QueryInterface(IID_IUnknown, (void **)&pFilterUnk);

    // Show the page. 
    CAUUID caGUID;
    pProp->GetPages(&caGUID);
    pProp->Release();
    OleCreatePropertyFrame(
        hWnd,                   // Parent window
        0, 0,                   // Reserved
        FilterInfo.achName,     // Caption for the dialog box
        1,                      // Number of objects (just the filter)
        &pFilterUnk,            // Array of object pointers. 
        caGUID.cElems,          // Number of property pages
        caGUID.pElems,          // Array of property page CLSIDs
        0,                      // Locale identifier
        0, NULL                 // Reserved
    );

    // Clean up.
    pFilterUnk->Release();
    FilterInfo.pGraph->Release(); 
    CoTaskMemFree(caGUID.pElems);
}

 
 
参考:
Basic DirectShow Tasks

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值