之前用VFW实现视频预览控件,使用的时候经常会弹出视频源选择窗口,为了解决这个问题,采用最新的DirectShow来实现视频预览,而且VFW目前基本上已经不使用了,DirectX的功能比较强大。因需求比较单一,还是只实现简单的视频预览功能,并没有实时存储视频。实现过程完全参照MSDN实现。要使用DirectShow的功能首先需要包含头文件
#include<dshow.h>,并引入库 #pragma comment(lib,"strmiids.lib")
一、控件界面
界面还是和上篇VFW实现方法一样。
视频预览大致流程是“枚举视频设备 -> 初始化并绑定视频采集过滤器 -> 将视频导入特定的流中” ,具体实现方法参考MSDN上的DirectShow部分。
为显示界面添加CPreviewWindow类,下面是PreviewWindow.h的实现。
#pragma once #include "afxwin.h" #include <atlbase.h> #include <windows.h> #include <dshow.h> #include <vector> using namespace std; #ifndef SAFE_RELEASE #define SAFE_RELEASE( x ) if ( NULL != x ) { x->Release( ); x = NULL; } #endif #pragma comment(lib,"strmiids.lib") typedef struct Device { CString Description; CString FrindlyName; int WaveInID; CString DevicePath; }Device,*PDevice; // CPreviewWindow 对话框 class CPreviewWindow : public CDialogEx { DECLARE_DYNAMIC(CPreviewWindow) public: CPreviewWindow(CWnd* pParent = NULL); // 标准构造函数 virtual ~CPreviewWindow(); HWND GetHwnd();//返回对话框类句柄 // 对话框数据 enum { IDD = IDD_VIDEOVIEW }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 DECLARE_MESSAGE_MAP() public: CStatic m_video; public: //CVideoCapture(void); //~CVideoCapture(void); HRESULT EnumDevices(REFGUID category);//枚举设备 HRESULT InitCaptureGraphBuilder();<span style="white-space:pre"> </span>//初始化视频采集过滤器 BOOL InitPreview(); void StartPreview(); void StopPreview(); void CancelPreview(); void PausePreview(); public: IEnumMoniker *pEnum; IMoniker *pMoniker; vector<Device> m_device; IBaseFilter *pCap;//视频采集过滤器 //IBaseFilter *SmartTee;//Smart Tee Filter //IBaseFilter *pRender;//Video Render Filter ICaptureGraphBuilder2 *pBuilder;//视频采集过滤器图表 IGraphBuilder *pGraph;//过滤器图表管理器 IVideoWindow *pVideoWindow; IMediaControl *pControl;//用户命令接口,用来控制过滤器图表 IMediaEvent *pEvent;//过滤器图表事件接口 //HWND hWnd; afx_msg void OnSizing(UINT fwSide, LPRECT pRect); };枚举视频设备实现如下:
HRESULT CPreviewWindow::EnumDevices(REFGUID category) { // Create the System Device Enumerator. ICreateDevEnum *pDevEnum; HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,NULL,CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pDevEnum)); if(SUCCEEDED(hr)) { // Create an enumerator for the category. hr = pDevEnum->CreateClassEnumerator(category,&pEnum,0); if(hr == S_FALSE) { pDevEnum->Release(); return VFW_E_NOT_FOUND;// The category is empty. Treat as an error. } //show device list Device dev; CString str; while(pEnum->Next(1,&pMoniker,NULL) == S_OK) { IPropertyBag *pPropBag; HRESULT hr = pMoniker->BindToStorage(0,0,IID_PPV_ARGS(&pPropBag)); if(FAILED(hr)) { pMoniker->Release(); continue; } VARIANT var; VariantInit(&var); // Get description or friendly name. hr = pPropBag->Read(L"Description",&var,0); if(SUCCEEDED(hr)) { dev.Description = var.bstrVal; VariantClear(&var); } hr = pPropBag->Read(L"FriendlyName",&var,0); if(SUCCEEDED(hr)) { dev.FrindlyName = var.bstrVal; //MessageBox(dev.FrindlyName); OutputDebugString(var.bstrVal); VariantClear(&var); } hr = pPropBag->Write(L"FrindlyName",&var); hr = pPropBag->Read(L"WaveInID",&var,0); if(SUCCEEDED(hr)) { dev.WaveInID = var.lVal; VariantClear(&var); } hr = pPropBag->Read(L"DevicePath",&var,0); if(SUCCEEDED(hr)) { dev.DevicePath = var.bstrVal; VariantClear(&var); } m_device.push_back(dev);//增加设备 pPropBag->Release(); pMoniker->Release(); } pDevEnum->Release(); } return hr; }初始化视频采集过滤器:
HRESULT CPreviewWindow::InitCaptureGraphBuilder() { // Create the Capture Graph Builder HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2,NULL, CLSCTX_INPROC_SERVER,IID_ICaptureGraphBuilder2,(void**)&pBuilder); if(SUCCEEDED(hr)) { // Create the Filter Graph Manager. hr = CoCreateInstance(CLSID_FilterGraph,0,CLSCTX_INPROC_SERVER,IID_IGraphBuilder,(void**)&pGraph); if(SUCCEEDED(hr)) { pBuilder->SetFiltergraph(pGraph); return S_OK; } else { pBuilder->Release(); } } return hr; }初始化好视频采集过滤器之后就可以将其绑定到视频源上采集数据,那么采集到的数据如何显示到界面上呢?这是最关键的问题。
首先定义一个IVideoWindow类型的对象,该对象用于连接视频图像数据与MFC窗口界面。下面就实现这个功能:
BOOL CPreviewWindow::InitPreview() { if(pMoniker == NULL) EnumDevices(CLSID_VideoInputDeviceCategory);//枚举设备 HRESULT hr = pMoniker->BindToObject(0,0,IID_IBaseFilter,(void**)&pCap); if(SUCCEEDED(hr)) { hr = pGraph->AddFilter(pCap,L"Capture Filter"); } hr = pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_Video,pCap,NULL,NULL); hr = pGraph->QueryInterface(IID_IVideoWindow,(void**)&pVideoWindow); if(SUCCEEDED(hr)) { pVideoWindow->put_Owner((OAHWND)this->m_hWnd);//设定视频窗口为主窗口的一个子窗口 pVideoWindow->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN);//设定窗口模式 CRect rectClient; ::GetClientRect(this->m_hWnd,rectClient); pVideoWindow->SetWindowPosition(0,0,rectClient.right,rectClient.bottom);//设定窗口大小 } else { return FALSE; } hr = pGraph->QueryInterface(IID_IMediaControl,(void**)&pControl); if(FAILED(hr)) { return FALSE; } return TRUE; }
定义一个IMediaControl pControl对象来控制视频的预览,下面就具体实现视频预览的控制方法:
void CPreviewWindow::PausePreview() { if(pControl) pControl->Pause();//暂停 } void CPreviewWindow::StopPreview() { if(pControl) pControl->Stop();//停止 pVideoWindow->put_Visible(FALSE); } void CPreviewWindow::StartPreview() { pVideoWindow->put_Visible(OATRUE);//设定可视 pControl->Run();//开始预览 }
在控件CVideoPreviewCtrl类中添加如下代码:
public:
CPreviewWindow m_PreviewWin;
BOOL InitFlag;
void EnumDevices(void);
void StartPreview(void);
void PausePreview(void);
void StopPreview(void);
重写OnCreate方法:
int CVideoPreviewCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (COleControl::OnCreate(lpCreateStruct) == -1)
return -1;
// TODO: 在此添加您专用的创建代码
if(!m_PreviewWin.Create(IDD_VIDEOVIEW,this))
MessageBox(L"创建对话框失败");
m_PreviewWin.InitCaptureGraphBuilder();
InitFlag = m_PreviewWin.InitPreview();
return 0;
}
添加接口供外部调用:
void CVideoPreviewCtrl::StartPreview(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加调度处理程序代码
//MessageBox(L"开始预览");
//m_PreviewWin.EnumDevices(CLSID_VideoInputDeviceCategory);
if(InitFlag)
m_PreviewWin.StartPreview();
}
void CVideoPreviewCtrl::PausePreview(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加调度处理程序代码
m_PreviewWin.PausePreview();
}
void CVideoPreviewCtrl::StopPreview(void)
{
AFX_MANAGE_STATE(AfxGetStaticModuleState());
// TODO: 在此添加调度处理程序代码
m_PreviewWin.StopPreview();
}
测试方法参见VFW实现。
CVideoWindow.cpp