您的位置:首页 > 其它

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

2014-12-16 23:14 411 查看
之前用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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: