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还有其他常见成员函数,如保存滤波器链表。
相关文章推荐
- Android Studio 获取数字签名信息(SHA1)
- js 多语言
- JAVA接口的一些理解描述
- iOS利用通知(NSNotification)进行传值
- thinkphp碰到的问题
- OpenGL
- 沙盒路径
- PHP使用SimpleElement创建和解析xml文件
- HASP加密锁外壳加密速成
- Go1.5使用sftp下载文件
- python vimrc的安装,并用pep8检测python代码
- 关于Azure Storage 的一点研究
- 机器学习算法面试—口述(3):贝叶斯分类器
- iOS推送AppDelegate跳转任意VC
- 如何优雅地搞砸你的app
- hdu 5410 CRB and His Birthday(01背包+完全背包)
- android学习笔记4 - Visibility控件的可见性
- 京东收货地址地市下拉框数据抓取
- java.util.ResourceBundle 和java.util.properties 读取配置文件区别
- 微软操作系统内部版本号大全