设为首页 加入收藏

TOP

9.3.3 视频图像捕获类CVMR_Capture的实现(2)
2013-10-07 00:41:08 来源: 作者: 【 】 浏览:51
Tags:9.3.3 视频 图像 捕获 CVMR_Capture 实现

9.3.3  视频图像捕获类CVMR_Capture的实现(2)

上述程序实现枚举系统所有的视频采集设备,然后把FriendlyName信息添加到组合框中,这里的设备枚举与"使用经典采集技术实现视频捕获"实例的设备枚举相同。

构建滤波器链表,添加各个滤波器、链接并运行链表。应用程序调用该函数实现视频采集、图像预览。

HRESULT CVMR_Capture::Init(int iDeviceID,HWND hWnd, int iWidth, int iHeight)
{
HRESULT hr;
//再次调用函数,释放已经建立的链表
CloseInterfaces();
//创建IGraphBuilder
hr = CoCreateInstance(CLSID_FilterGraph, NULL,  CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&m_pGB);
if (SUCCEEDED(hr))
{
//创建VMR并添加到Graph中
InitializeWindowlessVMR(hWnd);
//把指定的设备捆绑到一个滤波器
if(!BindFilter(iDeviceID, &m_pDF))
return S_FALSE;
//添加采集设备滤波器到Graph中
hr = m_pGB->AddFilter(m_pDF, L"Video Capture");
if (FAILED(hr)) return hr;
//获取捕获滤波器的引脚
IEnumPins  *pEnum;
m_pDF->EnumPins(&pEnum);
hr |= pEnum->Reset();
hr |= pEnum->Next(1, &m_pCamOutPin, NULL);
//获取媒体控制和事件接口
hr |= m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC);
hr |= m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pME);
//设置窗口通知消息处理
//hr = pME->SetNotifyWindow((OAHWND)hWnd, WM_GRAPHNOTIFY, 0);
//匹配视频分辨率,对视频显示窗口设置
hr |= InitVideoWindow(hWnd,iWidth, iHeight);
//为捕获图像帧申请内存
m_nFramelen=iWidth*iHeight*3;
m_pFrame=(BYTE*) new BYTE[m_nFramelen];  
//运行Graph,捕获视频
m_psCurrent = STOPPED;
hr |= m_pGB->Render(m_pCamOutPin);
hr |= m_pMC->Run();
if (FAILED(hr)) return hr;
m_psCurrent = RUNNING;
}
return hr;
}


上述是一个独立而又完整的使用VMR-9技术实现视频的采集、存储任务的程序。首先如果再次调用该函数,则关闭所有接口、释放有关资源;接着创建IGraphBuilder作为滤波器链表管理器;然后添加VMR滤波器到链表中,把指定的采集设备索引与捕获滤波器捆绑,并把该滤波器添加到链表中;接着获取捕获滤波器的引脚、获取媒体控制接口和事件接口;设置窗口通知消息处理,根据输入的视频分辨率匹配采集设备的分辨率;最后使用自动渲染功能Render方法把滤波器链表链接起来,使用媒体控制接口的方法Run开始运行媒体。以下分别是该函数的子功能实现。

创建VMR滤波器,并添加到Graph链表中。

/*创建VMR,添加、设置VMR*/
HRESULT CVMR_Capture::InitializeWindowlessVMR(HWND hWnd)
{
IBaseFilter* pVmr = NULL;
//创建VMR
HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL,
CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr);
if (SUCCEEDED(hr))
{
//添加VMR到滤波器链表中
hr = m_pGB->AddFilter(pVmr, L"Video Mixing Renderer");
if (SUCCEEDED(hr))
{
//设置无窗口渲染模式
IVMRFilterConfig* pConfig;
hr = pVmr->QueryInterface(IID_IVMRFilterConfig, (void**)&pConfig);
if( SUCCEEDED(hr))
{
pConfig->SetRenderingMode(VMRMode_Windowless);
pConfig->Release();
}
//设置传入的窗口为显示窗口
hr = pVmr->QueryInterface(IID_IVMRWindowlessControl, (void**)&m_pWC);
if( SUCCEEDED(hr))
{
m_pWC->SetVideoClippingWindow(hWnd); //设置视频剪辑窗口
}
}
pVmr->Release();
}
return hr;
}
首先使用CoCreateInstance创建VMR的接口pVmr,然后把VMR滤波器添加到滤波器链表中。设置视频显示为无窗口模式,首先在pVmr接口下查询IVMRFilterConfig接口,以参数VMRMode_Windowless使用SetRenderingMode方法设置完成。最后设置传入的窗口为视频剪辑窗口。

 设置捕获图像帧的格式,遍历所有格式是否有预定格式,若没有则以默认格式捕获。

HRESULT CVMR_Capture::InitVideoWindow(HWND hWnd,int width, int height)
{
HRESULT hr;         //返回值
RECT rcDest;        //矩形区域
IAMStreamConfig *pConfig;      //流配置接口
IEnumMediaTypes *pMedia;      //枚举媒体类型接口
AM_MEDIA_TYPE *pmt = NULL, *pfnt = NULL;   //媒体类型
hr = m_pCamOutPin->EnumMediaTypes( &pMedia ); //获取捕获设备的所有媒体类型
if(SUCCEEDED(hr))
{
//把视频的所有格式遍历一遍,看是否有预定的格式
while(pMedia->Next(1, &pmt, 0) == S_OK)
{
if( pmt->formattype == FORMAT_VideoInfo )
{
VIDEOINFOHEADER *vih = (VIDEOINFOHEADER *)pmt->pbFormat;
//当前的格式是否与预定格式相同,即宽和高是否相同
if( vih->bmiHeader.biWidth == width && vih->bmiHeader.biHeight      == height )
{
pfnt = pmt;  //记录当前媒体格式
break;   //退出循环
}
DeleteMediaType( pmt ); //格式不匹配,则删除当前查询的媒体格式

}
pMedia->Release();
}
//获取流配置接口
hr = m_pCamOutPin->QueryInterface( IID_IAMStreamConfig, (void **) &pConfig );
if(SUCCEEDED(hr))
{
//有预定的媒体格式
if( pfnt != NULL )
{
hr=pConfig->SetFormat( pfnt );
DeleteMediaType( pfnt );
}
//没有预定的格式,读取默认媒体格式
hr = pConfig->GetFormat( &pfnt );
if(SUCCEEDED(hr))
{
m_nWidth = ((VIDEOINFOHEADER *)pfnt->pbFormat)->bmiHeader.biWidth;
//读取高
m_nHeight = ((VIDEOINFOHEADER *)pfnt->pbFormat)->bmiHeader.biHeight;
//读取宽
DeleteMediaType( pfnt );
}
}
//获取传入窗口的区域,以设置显示窗口
::GetClientRect (hWnd,&rcDest);
hr = m_pWC->SetVideoPosition(NULL, &rcDest); //设置视频窗口位置
return hr;
}
/* 删除媒体类型*/
void CVMR_Capture::DeleteMediaType(AM_MEDIA_TYPE *pmt)
{
if (pmt == NULL) {       //为空则直接返回
return;
}
if (pmt->cbFormat != 0) {     //格式块大小不为零
//释放由CoTaskMemAlloc或CoTaskMemRealloc申请的内存块
CoTaskMemFree((PVOID)pmt->pbFormat);
pmt->cbFormat = 0;
pmt->pbFormat = NULL;
}
if (pmt->pUnk != NULL) {      //IUnknown接口不为空
pmt->pUnk->Release();     //释放资源
pmt->pUnk = NULL;
}
      CoTaskMemFree((PVOID)pmt);
}

无论采用CoTaskMemAlloc函数还是采用CreateMediaType函数分配的内存都可以用这个函数来释放。

 从VMR中获取一帧图像数据,转换目标颜色空间为RGB24。m_pFrame指向RGB24格式的数据。

/* 从VMR中获取一帧图像*/
DWORD CVMR_Capture::GrabFrame()
{
if(m_pWC ) {
BYTE* lpCurrImage = NULL;
//获取当前的图像数据,实际的数据内容是BMP位图格式
if(m_pWC->GetCurrentImage(&lpCurrImage) == S_OK)
{
//读取图像帧数据lpCurrImage
LPBITMAPINFOHEADER  pdib = (LPBITMAPINFOHEADER) lpCurrImage;
//判断图像的宽度和高度是否正确
if(m_pFrame==NULL || (pdib->biHeight * pdib->biWidth * 3) !=m_nFramelen )
{
if(m_pFrame!=NULL)  delete []m_pFrame;       //删除以前申请的内存
m_nFramelen = pdib->biHeight * pdib->biWidth * 3; //重新申请内存
m_pFrame = new BYTE [pdib->biHeight * pdib->biWidth * 3] ;
//申请的内存大小
}   
if(pdib->biBitCount == 32)
{
DWORD  dwSize=0, dwWritten=0;   
BYTE *pTemp32;
pTemp32=lpCurrImage + sizeof(BITMAPINFOHEADER);
//由于采集的是32位RGB,目标要求24位,需要进行转换
this->Convert24Image(pTemp32, m_pFrame, pdib->biSizeImage);
}
//释放该图像lpCurrImage
CoTaskMemFree(lpCurrImage);
}
else {  return -1;  }
}else{  return -1;  }
return m_nFramelen;
}

上述程序实现获取当前显示图像,如果格式不正确则重新申请空间。由于采集的图像多数为32位,所以需要转换成24位以便于算法直接处理,捕获的图像帧放置在类CVMR_Capture的成员变量m_pFrame中,该函数返回图像帧的数据大小。

 颜色空间转换ARGB32到RGB24。

/* ARGB32 to RGB24 */
bool CVMR_Capture::Convert24Image(BYTE *p32Img, BYTE *p24Img,DWORD dwSize32)
{
if(p32Img != NULL && p24Img != NULL && dwSize32>0)  //确认指针合法
{
DWORD dwSize24;
dwSize24=(dwSize32 * 3)/4;         //RGB32与RGB24的像素点空间只差了一个字节
BYTE *pTemp=p32Img,*ptr=p24Img;
for (DWORD index = 0; index < dwSize32/4 ; index++)
{         
unsigned char r = *(pTemp++);  unsigned char g = *(pTemp++);
unsigned char b = *(pTemp++);   (pTemp++); //跳过alpha分量
*(ptr++) = b;  *(ptr++) = g;  *(ptr++) = r; 记录RGB 3分量
}
} else {
return false; //指针不合法
}
return true;
}

RGB32与RGB24格式只差一个分量alpha,所以转换时把每个像素点的该分量丢掉,即可得到RGB24格式。另外类CVMR_Capture还有其他常见成员函数,如保存滤波器链表。

【责任编辑:云霞 TEL:(010)68476606】

回书目   上一节   下一节

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇9.3.3 视频图像捕获类CVMR_Captur.. 下一篇9.3.2 GraphEdit模拟实现视频捕获..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: