您的位置:首页 > 其它

directshow capture 一个很好用的文件

2015-08-25 15:31 375 查看
<span style="font-family: Arial, Helvetica, sans-serif;">VMR_Capture.h</span>
//////////////////////////////////////////////////////////////////////
//
//  This class is designed to provide simple interface for
//  simultaneous Video Capture & Preview using DirectShow
//
//////////////////////////////////////////////////////////////////////
//
//    References: MS DirectShow Samples
//
//
//////////////////////////////////////////////////////////////////////
//
//    This class was written by Sagar K.R .
//  Use of this class is not restricted in any
//    way whatsoever.Please report the bugs to krssagar@firsteccom.co.kr
//
//    Special thanks to all the members at The Code Project!
//    (www.codeproject.com)
//
//////////////////////////////////////////////////////////////////////

// VMR_Capture.h: interface for the CVMR_Capture class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_VMR_CAPTURE_H__186091F3_30FA_4FAA_AC8B_EF25E8463B9A__INCLUDED_)
#define AFX_VMR_CAPTURE_H__186091F3_30FA_4FAA_AC8B_EF25E8463B9A__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <atlbase.h>
#include <dshow.h>
#include <d3d9.h>
#include <vmr9.h>

//#define WM_GRAPHNOTIFY  WM_USER+13
enum PLAYSTATE {Stopped, Paused, Running, Init};

class CVMR_Capture
{
public:

CVMR_Capture();
int EnumDevices(HWND hList);
HRESULT Init(int iDeviceID,HWND hWnd,int iWidth,int iHeight);
DWORD GetFrame(BYTE ** pFrame);//获得图片储存于 pFrame中//Call these functions to get the captured buffer.GrabFrame(); will grab the image. Store it in a buffer and return value is the size of the buffer.User can get the buffer pointer by calling GetFrame(BYTE ** pFrame) which returns the size of the buffer.
BOOL Pause();
DWORD ImageCapture(LPCTSTR szFile);
DWORD GrabFrame();//抓取图片    用法(源于本例):DWORD dwSize;

//dwSize=this->m_VMRCap.GrabFrame ();
//if(dwSize>0)
//{
//    BYTE *pImage;
//    this->m_VMRCap.GetFrame (&pImage);
//    this->m_ctrlCaptureIMG .ShowImage (pImage);
//}

virtual ~CVMR_Capture();

protected:

IGraphBuilder *m_pGB ;
IMediaControl *m_pMC;
IMediaEventEx *m_pME ;
//IMediaEvent *pME ;

IVMRWindowlessControl9 *m_pWC;
IPin * m_pCamOutPin;
IBaseFilter *m_pDF;

PLAYSTATE m_psCurrent;

int m_nWidth;
int m_nHeight;

BYTE *m_pFrame;
long m_nFramelen;

bool BindFilter(int deviceId, IBaseFilter **pFilter);
HRESULT InitializeWindowlessVMR(HWND hWnd);
HRESULT InitVideoWindow(HWND hWnd,int width, int height);//Adjust the Display Video Size according to the size of the display window
void StopCapture();
void CloseInterfaces(void);

void DeleteMediaType(AM_MEDIA_TYPE *pmt);
bool Convert24Image(BYTE *p32Img,BYTE *p24Img,DWORD dwSize32);

private:

};

#endif // !defined(AFX_VMR_CAPTURE_H__186091F3_30FA_4FAA_AC8B_EF25E8463B9A__INCLUDED_)

//----------------------------------------------------------------------------------------------//
VMR_Capture.cpp
//////////////////////////////////////////////////////////////////////
//
//  This class is designed to provide simple interface for
//  simultaneous Video Capture & Preview using DirectShow
//
//////////////////////////////////////////////////////////////////////
//
//    References: MS DirectShow Samples
//
//
//////////////////////////////////////////////////////////////////////
//
//    This class was written by Sagar K.R .
//  Use of this class is not restricted in any
//    way whatsoever.Please report the bugs to krssagar@firsteccom.co.kr
//
//    Special thanks to all the members at The Code Project!
//    (www.codeproject.com)
//
//////////////////////////////////////////////////////////////////////

// VMR_Capture.cpp: implementation of the CVMR_Capture class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "VMR_Capture.h"

#define SAFE_RELEASE(x) { if (x) x->Release(); x = NULL; }

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CVMR_Capture::CVMR_Capture()
{
CoInitialize(NULL);
m_pGB = NULL;
m_pMC = NULL;
m_pME = NULL;
m_pWC = NULL;
m_pDF =NULL;
m_pCamOutPin =NULL;
m_pFrame=NULL;
m_nFramelen=0;

m_psCurrent=Stopped;
}

CVMR_Capture::~CVMR_Capture()
{
CloseInterfaces();
CoUninitialize( );
}
HRESULT CVMR_Capture::Init(int iDeviceID,HWND hWnd, int iWidth, int iHeight)
{
HRESULT hr;
// Get the interface for DirectShow's GraphBuilder
hr=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
IID_IGraphBuilder, (void **)&m_pGB);

if(SUCCEEDED(hr))
{
// Create the Video Mixing Renderer and add it to the graph
InitializeWindowlessVMR(hWnd);
// Bind Device Filter.  We know the device because the id was passed in
if(!BindFilter(iDeviceID, &m_pDF))
return S_FALSE;

hr=m_pGB->AddFilter(m_pDF, L"Video Capture");
if (FAILED(hr))
return hr;

CComPtr<IEnumPins> pEnum;
m_pDF->EnumPins(&pEnum);

hr = pEnum->Reset();
hr = pEnum->Next(1, &m_pCamOutPin, NULL);

// QueryInterface for DirectShow interfaces
hr = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC);

hr = m_pGB->QueryInterface(IID_IMediaEventEx, (void **)&m_pME);

// Have the graph signal event via window callbacks for performance
//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];

// Run the graph to play the media file

m_psCurrent=Stopped;

hr = m_pGB->Render(m_pCamOutPin);
hr = m_pMC->Run();
m_psCurrent=Running;

}
return hr;

}

HRESULT CVMR_Capture::InitializeWindowlessVMR(HWND hWnd)//此段知识点参见我的博客
{
IBaseFilter* pVmr = NULL;

// Create the VMR and add it to the filter graph.
HRESULT hr = CoCreateInstance(CLSID_VideoMixingRenderer, NULL,//注意这个CLSID_VideoMixingRenderer
CLSCTX_INPROC, IID_IBaseFilter, (void**)&pVmr);
if (SUCCEEDED(hr))
{
hr = m_pGB->AddFilter(pVmr, L"Video Mixing Renderer");
if (SUCCEEDED(hr))
{
// Set the rendering mode and number of streams.
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);//查询接口 貌似 ...使用IVMRWindowlessControl9必须要这样?
if( SUCCEEDED(hr))
{
m_pWC->SetVideoClippingWindow(hWnd);

}
}
pVmr->Release();
}

return hr;
}

bool CVMR_Capture::BindFilter(int deviceId, IBaseFilter **pFilter)
{
if (deviceId < 0)
return false;

// enumerate all video capture devices
CComPtr<ICreateDevEnum> pCreateDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
{

return false;
}

CComPtr<IEnumMoniker> pEm;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
&pEm, 0);
if (hr != NOERROR)
{
return false;
}

pEm->Reset();
ULONG cFetched;
IMoniker *pM;
int index = 0;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK, index <= deviceId)
{
IPropertyBag *pBag;
hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
if(SUCCEEDED(hr))
{
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR)
{
if (index == deviceId)
{
pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
}
SysFreeString(var.bstrVal);
}
pBag->Release();
}
pM->Release();
index++;
}
return true;
}

HRESULT CVMR_Capture::InitVideoWindow(HWND hWnd,int width, int height)
{

// Set the grabbing size
// First we iterate through the available media types and
// store the first one that fits the requested size.
// If we have found one, we set it.
// In any case we query the size of the current media type
// to have this information for clients of this class.
//     Gerhard Reitmayr <reitmayr@i ...............>

HRESULT hr;
RECT rcDest;

CComPtr<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;
// printf("Size %i  %i\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight );
if( vih->bmiHeader.biWidth == width && vih->bmiHeader.biHeight == height )
{
pfnt = pmt;

// printf("found mediatype with %i %i\n", vih->bmiHeader.biWidth, vih->bmiHeader.biHeight );
//char test[100];
//sprintf(test,"Width=%d\nHeight=%d",vih->bmiHeader.biWidth, vih->bmiHeader.biHeight);
//MessageBox(test);
break;
}
DeleteMediaType( pmt );
}
}
pMedia->Release();
}
hr = m_pCamOutPin->QueryInterface( IID_IAMStreamConfig, (void **) &pConfig );
if(SUCCEEDED(hr))
{
if( pfnt != NULL )
{
hr=pConfig->SetFormat( pfnt );

//if(SUCCEEDED(hr))
//MessageBox("OK");

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::StopCapture()
{
HRESULT hr;

if((m_psCurrent == Paused) || (m_psCurrent == Running))
{
LONGLONG pos = 0;
hr = m_pMC->Stop();
m_psCurrent = Stopped;
// Display the first frame to indicate the reset condition
hr = m_pMC->Pause();
}

}

void CVMR_Capture::CloseInterfaces(void)
{
HRESULT hr;

// Stop media playback
if(m_pMC)
hr = m_pMC->Stop();
m_psCurrent = Stopped;

// Disable event callbacks

//    SAFE_RELEASE(pME);

// Release and zero DirectShow interfaces
if(m_pCamOutPin)
m_pCamOutPin->Disconnect ();

SAFE_RELEASE(m_pCamOutPin);
SAFE_RELEASE(m_pMC);
SAFE_RELEASE(m_pGB);
SAFE_RELEASE(m_pWC);
SAFE_RELEASE(m_pDF);

//delete allocated memory
if(m_pFrame!=NULL)
delete []m_pFrame;

}

//Capture RAW IMAGE BITS 24bits/pixel
DWORD CVMR_Capture::ImageCapture(LPCTSTR szFile)
{
BYTE *pImage;
DWORD dwSize,dwWritten;
dwSize=this->GrabFrame ();
this->GetFrame (&pImage);
//储存到哪里?那个文件?
HANDLE hFile = CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_READ, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);

if (hFile == INVALID_HANDLE_VALUE)
return FALSE;

WriteFile(hFile, (LPCVOID)pImage , m_nFramelen, &dwWritten, 0);
// Close the file
CloseHandle(hFile);

return dwWritten;
}

void CVMR_Capture::DeleteMediaType(AM_MEDIA_TYPE *pmt)
{
// allow NULL pointers for coding simplicity

if (pmt == NULL) {
return;
}

if (pmt->cbFormat != 0) {
CoTaskMemFree((PVOID)pmt->pbFormat);

// Strictly unnecessary but tidier
pmt->cbFormat = 0;
pmt->pbFormat = NULL;
}
if (pmt->pUnk != NULL) {
pmt->pUnk->Release();
pmt->pUnk = NULL;
}

CoTaskMemFree((PVOID)pmt);
}

DWORD CVMR_Capture::GrabFrame()
{
long lOut=-1;
if(m_pWC )
{
BYTE* lpCurrImage = NULL;

// Read the current video frame into a byte buffer.  The information
// will be returned in a packed Windows DIB and will be allocated
// by the VMR.
if(m_pWC->GetCurrentImage(&lpCurrImage) == S_OK)
{

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);

//change from 32 to 24 bit /pixel
this->Convert24Image(pTemp32, m_pFrame, pdib->biSizeImage);

}

CoTaskMemFree(lpCurrImage);    //free the image
}
else
{
return lOut;
}

}
else
{
return lOut;
}

return lOut=m_nFramelen;

}

bool CVMR_Capture::Convert24Image(BYTE *p32Img, BYTE *p24Img,DWORD dwSize32)
{

if(p32Img != NULL && p24Img != NULL && dwSize32>0)
{

DWORD dwSize24;

dwSize24=(dwSize32 * 3)/4;

BYTE *pTemp,*ptr;
//pTemp=p32Img + sizeof(BITMAPINFOHEADER); ;
pTemp=p32Img;

ptr=p24Img + dwSize24-1 ;

int ival=0;
for (DWORD index = 0; index < dwSize32/4 ; index++)
{
unsigned char r = *(pTemp++);
unsigned char g = *(pTemp++);
unsigned char b = *(pTemp++);
(pTemp++);//skip alpha

*(ptr--) = b;
*(ptr--) = g;
*(ptr--) = r;

}
}
else
{
return false;
}

return true;
}

BOOL CVMR_Capture::Pause()
{
if (!m_pMC)
return FALSE;

if(((m_psCurrent == Paused) || (m_psCurrent == Stopped)) )
{
this->StopCapture();
if (SUCCEEDED(m_pMC->Run()))
m_psCurrent = Running;

}
else
{
if (SUCCEEDED(m_pMC->Pause()))
m_psCurrent = Paused;
}

return TRUE;
}

DWORD CVMR_Capture::GetFrame(BYTE **pFrame)
{
if(m_pFrame && m_nFramelen)
{
*pFrame=m_pFrame;
}

return m_nFramelen;
}

int  CVMR_Capture::EnumDevices(HWND hList)
{
if (!hList)
return  -1;

int id = 0;

// enumerate all video capture devices
CComPtr<ICreateDevEnum> pCreateDevEnum;
//    ICreateDevEnum *pCreateDevEnum;
HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
IID_ICreateDevEnum, (void**)&pCreateDevEnum);
if (hr != NOERROR)
{

return -1;
}

CComPtr<IEnumMoniker> pEm;
hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
&pEm, 0);
if (hr != NOERROR)
{

return -1 ;
}

pEm->Reset();
ULONG cFetched;
IMoniker *pM;
while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)
{
IPropertyBag *pBag;
hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
if(SUCCEEDED(hr))
{
VARIANT var;
var.vt = VT_BSTR;
hr = pBag->Read(L"FriendlyName", &var, NULL);
if (hr == NOERROR)
{
TCHAR str[2048];

id++;
WideCharToMultiByte(CP_ACP,0,var.bstrVal, -1, str, 2048, NULL, NULL);
//注意函数EnumDevices没有BindToObject,只是单纯的将视频采集设备的friendname显示在下拉框中

(long)SendMessage(hList, CB_ADDSTRING, 0,(LPARAM)str);

SysFreeString(var.bstrVal);
}
pBag->Release();
}
pM->Release();
}
return id;
}




原文地址:http://blog.sina.com.cn/s/blog_690aa1740100okum.html

http://blog.chinaunix.net/uid-20801802-id-1839127.html

为完成采集视频、预览视频和捕获图像帧的任务,我们封装了CVMR_Capture类,使用VMR技术流畅显示和捕捉图像帧。根据GraphEdit的滤波器内容和链表的操作,编程实现使用VMR技术显示、捕捉图像。
 类CVMR_Capture定义在头文件VMR_Capture.h中。
enum PLAYER_STATE {INIT,RUNNING,PAUSED,STOPPED};
class CVMR_Capture
{
public:
 CVMR_Capture();                                  //类构造器
 virtual ~CVMR_Capture();                        //类析构器
 int EnumDevices(HWND hList);               //枚举设备
 HRESULT Init(int iDeviceID,HWND hWnd,int iWidth,int iHeight); 

     //根据设备索引号初始化
 DWORD GetFrame(BYTE ** pFrame);                //获取捕获的图像帧
 BOOL Pause();                              //暂停预览、捕获
 DWORD GrabFrame();                         //截获图像
 void CloseInterfaces(void);                    //关闭接口,释放资源
 void SaveGraph(CString wFileName);           //保存滤波器链表
protected:
 IGraphBuilder          *m_pGB;             //滤波器链表管理器
 IMediaControl          *m_pMC;             //媒体控制接口
 IMediaEventEx          *m_pME;             //媒体事件接口
 IVMRWindowlessControl9 *m_pWC;             //VMR-9接口
 IPin                   *m_pCamOutPin;      //视频采集滤波器引脚
 IBaseFilter                *m_pDF;             //视频采集滤波器
 PLAYER_STATE            m_psCurrent;
 int        m_nWidth;                           //图像帧宽度
 int        m_nHeight;                          //图像帧高度
 BYTE   *m_pFrame;                          //捕获的图像帧数据指针
 long   m_nFramelen;                           //捕获的图像帧数据大小
 bool BindFilter(int deviceId, IBaseFilter **pFilter);      //设备与滤波器捆绑
 HRESULT InitializeWindowlessVMR(HWND hWnd);    //初始化无窗口模式的VMR
 HRESULT InitVideoWindow(HWND hWnd,int width, int height);  //初始化视频窗口
 void StopCapture();
 void DeleteMediaType(AM_MEDIA_TYPE *pmt);                        //删除媒体类型
 bool Convert24Image(BYTE *p32Img,BYTE *p24Img,DWORD dwSize32); //颜色空间转换
private:
};
CVMR_Capture类封装了使用VMR技术的成员函数和成员变量,包括类的构造器、析构器,滤波器链表管理器接口、媒体控制接口、媒体事件接口,图像帧的宽度、高度及操作VMR的保护成员函数等。
 类CVMR_Capture有关初始化、析构等,类实现VMR_Capture.cpp文件中。
//释放资源的宏
#define RELEASE_POINTER (x) { if (x) x->Release(); x = NULL; }
/*类构造器*/
CVMR_Capture::CVMR_Capture()
{
 CoInitialize(NULL);                          //初始化COM库
//接口指针清空
 m_pGB = NULL;      m_pMC = NULL;       
m_pME = NULL;       m_pWC = NULL;
 m_pDF =NULL;             m_pCamOutPin =NULL; 
m_pFrame=NULL;        m_nFramelen=0;
 m_psCurrent=STOPPED;
}
/*类析构器*/
CVMR_Capture::~CVMR_Capture()
{
 CloseInterfaces();                           //清空指针、释放资源
 CoUninitialize( );                       //卸载COM库
}
/*释放所有资源,断开链接*/
void CVMR_Capture::CloseInterfaces(void)
{
     HRESULT hr;
      //停止媒体回放
     if(m_pMC) hr = m_pMC->Stop();
     m_psCurrent = STOPPED;
     //释放并清零接口指针
 if(m_pCamOutPin)
      m_pCamOutPin->Disconnect();       //断开引脚的链接
RELEASE_POINTER(m_pCamOutPin);          //视频采集滤波器引脚清空
RELEASE_POINTER(m_pMC);                     //媒体控制接口清空
RELEASE_POINTER(m_pGB);                     //滤波器链表管理器接口清空
RELEASE_POINTER(m_pWC);                 //VMR-9接口清空
RELEASE_POINTER(m_pDF);                 //视频采集滤波器接口清空
 //释放分配的内存
 if(m_pFrame!=NULL)
       delete []m_pFrame;                   //释放图像空间缓存
}
上述程序完成资源释放、指针清空等工作,在退出应用程序时调用此函数。
 枚举本地系统的采集设备。
/* 枚举本地系统的采集设备*/
int  CVMR_Capture::EnumDevices(HWND hList)
{
 if (!hList) return  -1;
 int id = 0;
        //枚举所有视频采集设备
       ICreateDevEnum *pCreateDevEnum;
        //生成设备枚举器 
 HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
                         CLSCTX_INPROC_SERVER,
                         IID_ICreateDevEnum, (void**)&pCreateDevEnum);
    if (hr != NOERROR) return -1;
       IEnumMoniker *pEm;
       //创建视频类枚举器
    hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,       &pEm, 0);
    if (hr != NOERROR) return -1;
    pEm->Reset();                                            //复位设备
    ULONG cFetched;
    IMoniker *pM;
    while(hr = pEm->Next(1, &pM, &cFetched), hr==S_OK)   //根据索引获取设备
    {
     IPropertyBag *pBag;
     //获取属性集
     hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
     if(SUCCEEDED(hr))
     {
           VARIANT var;
           var.vt = VT_BSTR;                              //保存二进制数据
           hr = pBag->Read(L"FriendlyName", &var, NULL);

               //以FriendlyName获取设备信息
           if (hr == NOERROR)
           {
                   id++;
                   //把该设备的信息添加到组合框
                   (long)SendMessage(hList, CB_ADDSTRING, 0,(LPARAM)var. 

                       bstrVal);
                   SysFreeString(var.bstrVal);          //释放资源
            }
            pBag->Release();                                //释放资源
     }
     pM->Release();                                     //释放资源
    }
 return id;
}
上述程序实现枚举系统所有的视频采集设备,然后把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还有其他常见成员函数,如保存滤波器链表。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: