使用DirectShow将视频捕获到AVI文件

时间:2012-12-06 11:53:54

标签: c++ windows video directshow video-capture

我正在尝试使用directshow编写一个C ++应用程序,将视频捕获保存到文件中。 代码中的步骤是:  1.创建Capture Graph Builder  2.创建系统设备枚举器  3.创建系统设备枚举器 - 以获取捕获筛选器  4.为视频捕获类别创建枚举器  5.创建查询以捕获视频

附加代码

// gets the device filter
HRESULT getDeviceFilter(REFCLSID clsid, int order, IBaseFilter **pCap)
{

ICreateDevEnum *pDevEnum = NULL;
IEnumMoniker *pEnum = NULL;

// Create the System Device Enumerator.
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
                              CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
                              reinterpret_cast<void**>(&pDevEnum));

if (SUCCEEDED(hr))
{
    // Create an enumerator for the video capture category.
    hr = pDevEnum->CreateClassEnumerator( clsid, &pEnum, 0);
}

IMoniker *pMoniker = NULL;

if (pEnum->Next(1, &pMoniker, NULL) == S_OK)
    hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)pCap);

return hr;
}


int main()
{
IGraphBuilder *pGraph = 0;
ICaptureGraphBuilder2 *pBuild = 0;
IBaseFilter *pCap = 0;
HRESULT hr = CoInitialize(NULL);

// Create the Capture Graph Builder.
hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, 
                              NULL, 
                              CLSCTX_INPROC_SERVER, 
                                  IID_ICaptureGraphBuilder2, 
                              (void**)&pBuild );


ICreateDevEnum *pDevEnum = NULL;
IEnumMoniker *pEnum = NULL;

// Create the System Device Enumerator.
hr = CoCreateInstance(CLSID_SystemDeviceEnum, 
                      NULL,
                      CLSCTX_INPROC_SERVER, 
                      IID_ICreateDevEnum,
                      reinterpret_cast<void**>(&pDevEnum));



IBaseFilter *pMux = 0;
hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi,  // Specifies AVI for the target file.
                               L"C:\\Example.avi", // File name.
                               &pMux,              //     Receives a pointer to the mux.
                               NULL);              //     (Optional) Receives a pointer to the file sink.


// gets the first device, VDM tv card
hr = getDeviceFilter(CLSID_VideoInputDeviceCategory, 0, &pCap);


hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, // Pin category.
                          &MEDIATYPE_Video,      // Media type.
                          pCap,                  // Capture filter.
                          NULL,                  // Intermediate filter (optional).
                          pMux);                 // Mux or file sink filter.

// Release the mux filter.
pMux->Release();

IConfigAviMux *pConfigMux = NULL;
hr = pMux->QueryInterface(IID_IConfigAviMux, (void**)&pConfigMux);
if (SUCCEEDED(hr))
{
    pConfigMux->SetMasterStream(1);
    pConfigMux->Release();
}

return 0;
}

然而,在调用 RenderStream 时,我收到 E_INVALIDARG 错误

有什么建议吗?

由于

1 个答案:

答案 0 :(得分:1)

看看this topic。看来你错过了一些步骤。

首先,您没有在任何地方使用pGraph。您应该创建一个图形管理器,然后使用SetFilterGraph向图形管理器提供指向图形管理器的指针来初始化图形构建器。

// Create the Filter Graph Manager.
hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,
    IID_IGraphBuilder, (void**)&pGraph);
if (SUCCEEDED(hr))
{
    // Initialize the Capture Graph Builder.
    pBuild->SetFiltergraph(pGraph);

    // ...
}

其次,您使用的是不受图表管理器管理的过滤器。引自here

  

在调用方法之前,必须将pSource,pIntermediate和pSink指定的所有过滤器添加到图形中。

您必须使用AddFilter将过滤器pCappMux添加到之前创建的图表管理器中。您应该在调用RenderStream之前执行此操作。这是因为RenderStream最终会在管理器上调用连接方法。


如果上述步骤无法解决您的问题,您可以尝试其他一些方法。

设备过滤器。您使用的是CLSID_VideoInputDeviceCategory的第一台设备,但是您确定这是正确的设备吗?网络摄像头等也包含在此类别中。确保没有连接相同类别的其他设备,然后重试。

<强>连接即可。每个设备都不同。可能是您的设备无法直接连接到多路复用器。在这种情况下,我们必须找出原因,并确定是否需要连接其他过滤器(如解码器)。 GraphEdit是查找这些过滤器的快捷方式。

引脚类别/媒体类型。根据我的经验,E_INVALIDARG是由RenderStream的前2个参数引起的90%的时间。尝试将引脚类别或媒体类型设置为NULL

系统设备枚举器:正如您所描述的那样,您正在创建两次系统设备枚举器。这对我来说似乎很奇怪,为什么不使用一个,用于两个目的?


如果您的代码仍然不起作用,您应该向我提供更多信息。使用GraphEdit时,您是否实现了目标?您的VDM电视卡过滤器如何(引脚,媒体类型)?