草庐IT

c++ - 使用 DirectShow 将视频捕获为 AVI 文件

coder 2024-06-13 原文

我正在尝试使用 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 错误

有什么建议吗?

谢谢

最佳答案

看看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 :

All of the filters specified by pSource, pIntermediate, and pSink must be added to the graph prior to calling the method.

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


如果上述步骤不能解决您的问题,您可以尝试其他几种方法。

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

连接。每个设备都是不同的。可能是您的设备无法直接连接到多路复用器。在这种情况下,我们将不得不找出原因,并确定您是否需要连接额外的过滤器(如解码器)。 GraphEdit是找到这些过滤器的一种非常快速的方法。

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

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


如果您的代码仍然无法正常工作,您应该向我提供更多信息。使用GraphEdit的时候,你达到目标了吗? ?您的 VDM 电视卡过滤器看起来如何(引脚、媒体类型)?

关于c++ - 使用 DirectShow 将视频捕获为 AVI 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13743076/

有关c++ - 使用 DirectShow 将视频捕获为 AVI 文件的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 使用 RubyZip 生成 ZIP 文件时设置压缩级别 - 2

    我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看ruby​​zip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d

  3. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  4. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  5. ruby - 在 Ruby 中使用匿名模块 - 2

    假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于

  6. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  7. ruby-on-rails - 在 Rails 中将文件大小字符串转换为等效千字节 - 2

    我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,

  8. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  9. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  10. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

随机推荐