VS2010基于DirectShows实现视频预览控件

     之前用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



你可能感兴趣的:(视频,mfc,VS2010,控件,directshow)