DirectXShow开发-实现视频预览和捕获静态图片功能出现的问题

mf1983 2014-03-21 04:35:33
最近在做基于DirectXShow的视频预览及捕获静态图片的工作,使用VS2005,CLR C++开发
要完成的功能:
1 一个pictureBox中实现视频预览
2 通过button按钮事件从视频预览的video stream中捕获一张图片数据,并在另一个pictureBox中显示出来
3 捕获的同时视频预览窗口可以有暂时的停顿,捕获完毕要继续实现视频预览的功能

查阅了MSDN,也参考了DirectXShow的demo(Microsoft DirectX 9.0 SDK (Summer 2004)\Samples\C++\DirectShow目录下的Capture和Editing下的demo),采用了Sample Grabber Filter,使用SetBufferSamples( TRUE ) ;SetOneShot( FALSE )的模式.

问题是:
1 单独实现视频预览或单独实现从capture video stream中捕获一张图片都没问题
2 在预览视频的同时捕获图片出现问题,GetCurrentBuffer时出错

IMediaControl::Run之后使用GetCurrentBuffer获取图片大小出错,通过调试发现是因为Filter Graph的状态不对,MSDN中说调用GetCurrentBuffer方法的时候“Do not call this method while the filter graph is running.”。那我应该何时调用呢?在Run后调用Stop和Pause方法都不行,调用IMediaEvent::GetEvent方法测试,使用以下代码:

while (hr = pEvent->GetEvent(&evCode, ¶m1, ¶m2, 0), SUCCEEDED(hr))
{
//add the evCode to a int array[]
}

发现一共返回了5个evCode,从array[0] --array[4]分别是:EC_PALETTE_CHANGED,EC_VIDEO_SIZE_CHANGED,EC_CLOCK_CHANGED,EC_PAUSED,EC_PAUSED
然后调用GetCurrentBuffer依然出错

难道除了Sleep就没有别的方法了么?
SetBufferSamples( TRUE )之后DirectShow到底做了什么?对Filter Graph有什么影响?如何知道何时调用GetCurrentBuffer?有没有详细的资料?

附代码:

HRESULT DSComment::CreatFilterGraph(){

HRESULT hr;
cli::pin_ptr<IGraphBuilder *> ppGraphManager = &pGraphManager;
cli::pin_ptr<ICaptureGraphBuilder2 *> ppCapture = &pCapture;

if(!ppGraphManager || !ppCapture){
return E_POINTER;
}

hr = CoCreateInstance (
CLSID_FilterGraph,
NULL,
CLSCTX_INPROC_SERVER, //for add grephedit
IID_IGraphBuilder,
(void **) ppGraphManager
);
if (FAILED(hr)){
MessageBox::Show("Initial IFilterGraph2 failed!");
return E_NOINTERFACE;
}



// Create the capture graph builder
hr = CoCreateInstance (
CLSID_CaptureGraphBuilder2,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICaptureGraphBuilder2,
(void **) ppCapture
);
if (FAILED(hr)){
MessageBox::Show("Initial ICaptureGraphBuilder2 failed!");
return E_NOINTERFACE;
}
return S_OK;
}


HRESULT DSComment::CreatGrabberFilter(){

HRESULT hr;
cli::pin_ptr<IBaseFilter *> ppGrabberBaseFilter = &pGrabberBaseFilter;

hr = CoCreateInstance(
CLSID_SampleGrabber,
NULL,
CLSCTX_INPROC_SERVER,
IID_IBaseFilter,
(void**)ppGrabberBaseFilter
);


if (FAILED(hr))
{
MessageBox::Show("Couldn't create sample grabber filter base!");
return E_NOINTERFACE;
}


cli::pin_ptr<ISampleGrabber *> ppGrabber = &pGrabber;

pGrabberBaseFilter->QueryInterface(IID_ISampleGrabber, (void**)ppGrabber);

AM_MEDIA_TYPE mt;
ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
mt.majortype = MEDIATYPE_Video;
mt.subtype = MEDIASUBTYPE_RGB24;
hr = pGrabber->SetMediaType(&mt);
FreeMediaType(mt);

return hr;
}


HRESULT DSComment::CreatCaptureFilter(){

HRESULT hr;
ULONG cFetched;
const char* CAPTUREDEVNAME;

CAPTUREDEVNAME = "USB 视频设备";


cli::pin_ptr<IBaseFilter *> ppCaptureBaseFilter = &pCaptureBaseFilter;

// Create the system device enumerator
ICreateDevEnum *pDevEnum =NULL;

hr = CoCreateInstance (
CLSID_SystemDeviceEnum,
NULL,
CLSCTX_INPROC,
IID_ICreateDevEnum,
(void **) &pDevEnum
);
if (FAILED(hr)){
MessageBox::Show("Couldn't create system enumerator!");
return E_NOINTERFACE;
}

// Create an enumerator for the video capture devices
IEnumMoniker *pClassEnum = NULL;

hr = pDevEnum->CreateClassEnumerator(
CLSID_VideoInputDeviceCategory,
&pClassEnum,
0
);

if (FAILED(hr)){
MessageBox::Show("Couldn't create class enumerator!");
return E_NOINTERFACE;
}

// If there are no enumerators for the requested type, then
// CreateClassEnumerator will succeed, but pClassEnum will be NULL.
if (pClassEnum == NULL){
MessageBox::Show("No video capture device was detected.\r\n\r\n \
This sample requires a video capture device, such as a USB WebCam,\r\n \
to be installed and working properly. The sample will now close. \
No Video Capture Hardware");
return E_FAIL;
}

IMoniker *pMoniker = NULL;

// Use the first video capture device on the device list.
// Note that if the Next() call succeeds but there are no monikers,
// it will return S_FALSE (which is not a failure). Therefore, we
// check that the return code is S_OK instead of using SUCCEEDED() macro.
if (S_OK == (pClassEnum->Next (1, &pMoniker, &cFetched))){

// Bind Moniker to a filter object
hr = pMoniker->BindToObject(0,0,IID_IBaseFilter, (void**)ppCaptureBaseFilter);

IPropertyBag *pPropBag = NULL;
hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pPropBag);
if (FAILED(hr)){
pMoniker->Release();
return E_NOINTERFACE;
}

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)){
String^ strCaptureDevName = String(var.bstrVal).ToString();
if(!strCaptureDevName->CompareTo(String(CAPTUREDEVNAME).ToString())){
hr = S_OK;
}else{
hr = E_FAIL;
return hr;
}
}
pPropBag->Release();
}else{
MessageBox::Show("Unable to access video capture device!");
return E_FAIL;
}


pMoniker->Release();
pClassEnum->Release();
pDevEnum->Release();
return hr;

}



HRESULT DSComment::BuiltFilterGraph(){

HRESULT hr;
pGraphManager->AddFilter(pCaptureBaseFilter, L"Capture Device");
pGraphManager->AddFilter(pGrabberBaseFilter, L"Sample Grabber Filter");

pCapture->SetFiltergraph(pGraphManager);

pCapture->RenderStream(
&PIN_CATEGORY_PREVIEW,
&MEDIATYPE_Video,
pCaptureBaseFilter,
pGrabberBaseFilter,
NULL

);


cli::pin_ptr<IVideoWindow *> ppVW = &pVW;
cli::pin_ptr<IMediaControl *> ppMC = &pMC;
cli::pin_ptr<IMediaEventEx *> ppME = &pME;

hr = pGraphManager->QueryInterface(IID_IMediaControl,(LPVOID *) ppMC);
if (FAILED(hr)){
MessageBox::Show("Initial IMediaControl failed!");
return E_NOINTERFACE;
}

hr = pGraphManager->QueryInterface(IID_IMediaEvent, (LPVOID *) ppME);
if (FAILED(hr)){
MessageBox::Show("Initial IMediaEventEx failed!");
return E_NOINTERFACE;
}

hr = pGraphManager->QueryInterface(IID_IVideoWindow, (LPVOID *) ppVW);
if (FAILED(hr)){
MessageBox::Show("Initial IVideoWindow failed!");
return E_NOINTERFACE;
}

}

BYTE* DSComment::SnapStillImage(long* size){

HRESULT hr;

pMC->Stop();

hr = pGrabber->SetBufferSamples( TRUE );

// Only grab one at a time, stop stream after
// grabbing one sample
//
hr = pGrabber->SetOneShot( FALSE );

pMC->Run();


//××××××××××××××××××××××××××××××××××××××××××××××××××××××××
//就是在这里除了Sleep难道没有别的方法可以保证GetCurrentBuffer成功??
//×××××××××××××××××××××××××××××××××××××××××××××××××××××××××××

long cbBuffer;
hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);
if (FAILED(hr))
{
MessageBox::Show("GetCurrentBuffer Fail!");
}

BYTE *pBuffer = (BYTE*)CoTaskMemAlloc(cbBuffer);
if (!pBuffer)
{
MessageBox::Show("CoTaskMemAlloc Fail!");
}

hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);
if (FAILED(hr))
{
MessageBox::Show("GetCurrentBuffer Fail!");

}

hr = pGrabber->SetOneShot( FALSE );

AM_MEDIA_TYPE mt;

hr = pGrabber->GetConnectedMediaType(&mt);
if (FAILED(hr))
{
MessageBox::Show("GetConnectedMediaType Fail!");

}

VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)mt.pbFormat;

long cbBMI = mt.cbFormat - SIZE_PREHEADER;

long captureSize = cbBMI + cbBuffer + sizeof(BITMAPFILEHEADER);
*size = captureSize;
BYTE *pCaptrue = (BYTE*)CoTaskMemAlloc(captureSize);

BITMAPFILEHEADER bmf = { };


bmf.bfType = 'MB';
bmf.bfSize = captureSize;
bmf.bfOffBits = sizeof(bmf) + cbBMI;

memcpy(pCaptrue, &bmf, sizeof(bmf));

memcpy((pCaptrue + sizeof(bmf)), &pVih->bmiHeader, cbBMI);
memcpy((pCaptrue + sizeof(bmf) + cbBMI), pBuffer, cbBuffer);

return pCaptrue;

}
...全文
317 4 打赏 收藏 转发到动态 举报
写回复
用AI写文章
4 条回复
切换为时间正序
请发表友善的回复…
发表回复
mf1983 2014-03-26
  • 打赏
  • 举报
回复
好的,多谢指教,我再研究下
曹大夯 2014-03-25
  • 打赏
  • 举报
回复
用回调函数,在回调函数里面读取Image Buffer是不会影响视频Preview的,除非你在回调函数里面做太多事,有可能影响视频的流畅性。
曹大夯 2014-03-24
  • 打赏
  • 举报
回复
一般情况下,Sample Grabber是用ISampleGrabber::SetCallback method 来获取Snap Image Buffer的。 例子提供的Graph,其Render是Null的,不需要实际显示视频;而你的应用程序,Render不是Null Render,所以应该用Call Back,在CallBack里面获取图像。
mf1983 2014-03-24
  • 打赏
  • 举报
回复
多谢Huntercao的指教 其实我刚开始的时候也加过NullRenderer,但是我觉得后面这个Render类型不是最重要的。 在上面的代码中我用系统自动添加的video render,设置

SetBufferSamples( TRUE ) ;
SetOneShot( TRUE )
也能抓图,只是预览视频的功能停止了。看MSDN上说setoneshot后会给后面的render发一个IPin::endstream(可能不准确,大意是告诉后面的render没有vidoe stream了),这个能否通过操作IPin继续恢复视频预览的功能? 对于GetCurrentBuffer,是不是除了sleep,只能配合setoneshot(TRUE),然后设置IMediaControl::waitforcomple(INFINITE, &EvCode)来完成sample的buffer?不能通过IMediaEvent::getevente或IMediaControl::getstate的方法获得么? 我现在最想知道的是GetCurrentBuffer这条路是否能走的通,实在不行就转callback了,之前也尝试过callback模式,有点问题没有继续深入~~~ 再次感此Huntercao的指教
内容概要:本文档系统整合了基于MATLAB/Simulink的风力涡轮机雷达信号仿真、电力系统优化、新能源调控及多领域智能算法应用资源,涵盖风电功率平抑、混合储能协同调频、综合能源系统调度、无人机三维路径规划、电动汽车参与调度、电氢氨耦合系统优化等前沿科研方向。资源包提供大量可复现的Matlab代码、Simulink仿真模型、数据集及配套论文,涉及GWO、PSO、WOA、HHO等多种智能优化算法,以及LSTM、CNN、GRU等深度学习模型在负荷预测、故障诊断、信号处理中的应用,尤其聚焦于风电与雷达信号交互、储能功率分配、虚拟电厂运行、微电网多时间尺度优化等关键技术的仿真实现。; 适合人群:具备一定MATLAB编程基础,从事电力系统、新能源、智能优化、信号处理、雷达仿真、综合能源管理等相关领域的科研人员及研究生;工作1-3年相关方向的工程师。; 使用场景及目标:①开展风力发电系统与雷达信号交互仿真研究;②复现高水平期刊论文中的优化调度、故障诊断、功率预测模型;③进行无人机路径规划、储能系统设计、综合能源系统优化等课题研究与论文撰写;④借助成熟的代码框架快速搭建仿真模型,提升科研效率与工程实践能力。; 阅读建议:建议按主题分类浏览资源列表,优先选择标注“复现”“顶刊”“EI”等高价值项目,结合提供的网盘链接下载完整代码与数据,配合Simulink仿真模型与说明文档进行调试与二次开发,注重算法实现与实际工程问题的深度融合。
日常处理文本内容时,你是否也常常被这些琐碎又烦人的操作消耗大量时间:从网页复制来的文章满是空行和多余空格,要一段段手动清理;从不同渠道收集的文档繁简混杂,格式五花八门,想统一规范却只能逐篇调整;面对数千行的数据,想按行排序、去重、添加行号,却找不到一个趁手的小工具;更别提Base64编解码、URL转义这些开发中高频使用的操作,每次都得打开在线网站反复折腾——这些看似零散的文本处理需求,单独去搜工具费时费力,用Word又杀鸡用牛刀,始终缺少一款能把这些功能聚合在一起、打开即用的桌面工具。而 大飞哥软件自习室——文本批量整理工具 v2.0正是为整合这些高频文本处理需求而生的专业效率工具,它集文件管理、文本清洗、格式转换、查找替换、批量插入、排序打乱、编码解码、实时统计等近三十项实用功能于一身,采用多标签页设计支持同时编辑多个文档,所有操作均支持撤销与重做,让你在写代码、整理素材、编辑文案时再也不用东拼西凑找工具,一个软件搞定全部文本处理需求。 软件的核心功能模块围绕文本处理的完整工作流展开,覆盖了从“粗加工”到“精处理”的各个环节。文本清洗模块提供了行首尾去空格、清除所有空格、删除空行、合并多余空格、去除重复行、清除HTML标签等六项功能,一键将脏乱差的原始文本快速规整为干净可用的素材。文本转换模块则涵盖了繁简互转、标点中英文互转、全半角互转、全部大写/小写/首字母大写等选项,尤其适合需要批量处理多语言或不同格式规范的文本内容。查找替换模块不仅支持普通文本替换,更提供了正则表达式和区分大小写选项,查找、替换、全部替换、选区替换四种模式灵活应对不同场景。批量插入文本模块更是独树一帜——用户可预设两组插入内容,选择插入到当前段首/段尾或所有段首/段尾,四个单选按钮搭配两个输入框,实现八种插入组合,为批量处理结构化文本提供了极大的便利。

21,620

社区成员

发帖
与我相关
我的任务
社区描述
硬件/嵌入开发 驱动开发/核心开发
社区管理员
  • 驱动开发/核心开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧