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;

}
...全文
328 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的指教
源码下载地址: https://pan.quark.cn/s/07e7935aff27 在Android应用开发过程中,`ImageView`作为一个基础组件被广泛运用,其主要功能是展示各种图像资源,这些资源可以来源于本地存储或通过网络获取。本文将详细阐述如何动态地变更`ImageView`所显示的图片内容,并借助自定义监听机制来监控和干预图片的加载流程。此类技术在应用开发中具有很高的实用价值,特别是在处理网络用户头像、商品展示图以及实时数据更新的场景下。 我们首先需要明确`ImageView`的基本操作方式。在Android平台,我们常通过`setImageResource()`、`setImageBitmap()`以及`setImageDrawable()`等函数来设定`ImageView`的图像内容。然而,这些方法主要适用于已加载至内存中的图像文件,对于网络图片,则需要借助更为复杂的处理逻辑。 在实现动态下载并更新`ImageView`中的图像时,我们普遍采用`AsyncTask`或`Loader`来执行后台任务,以此防止主线程被阻塞。本文将重点介绍自定义监听器的构建方法。自定义监听器能够让我们依据图片加载的进程或状态来执行特定的任务,例如显示加载进度指示器或处理加载失败的情况。 自定义监听器通常定义为一个接口,其中包含了若干个回调方法,例如`onStart()`、`onProgressUpdate()`、`onSuccess()`、`onError()`等。以下是一个示例接口定义: ```java public interface ImageDownloaderListener { void onStart(); void onProgress(int ...
代码转载自:https://pan.quark.cn/s/8e8053c4e35c uni-app是由DCloud(即海豚浏览器背后的公司)设计的一款基于Vue.js技术的跨平台前端框架。其核心思想是让开发者仅需编写一次代码,就能达成多平台的兼容性,涵盖了iOS、Android、H5、小程序以及快应用等多个不同的终端设备。这种“编写一次,随处运行”的特性显著提升了开发效能,减少了维护开销,特别适用于构建规模庞大且结构复杂的应用程序。 【Vue.js基础】 uni-app的开发根基是Vue.js,这是一款具备轻量级MVVM(Model-View-ViewModel)架构的前端框架。Vue.js借助声明式的数据结合以及组件化机制,简化了前端构建过程。在uni-app环境中,Vue的模板语法、计算属性、指令、组件等要素均被保留并应用于开发跨平台应用。开发者需要充分掌握Vue的基础概念和操作方法,例如数据绑定(v-bind、v-model)、条件性渲染(v-if、v-else)、循环(v-for)、事件处理(v-on)等。 【uni-app组件系统】 uni-app配备了一套内容丰富的原生组件集合,这些组件依托于各平台的原生API,能够实现接近原生应用的交互效果。比如,按钮(button)、图像(image)、列表(list)、导航栏(navigator)、滑动视图(swiper)等。开发者可以通过设定配置属性、响应事件来调整组件的功能表现和视觉样式,达成自定义功能的目标。 【uni-app插件市场】 在具备内置组件之外,uni-app还拥有一个功能强大的插件市场,其中提供了众多第三方组件和API,覆盖了地图、支付、推送、广告等多种功能范畴。开发者可以便捷地集成这些...

21,620

社区成员

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

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