http://blog.csdn.net/yangyk125/article/details/29216149
多媒体编程——摄像头录像预览
1、 新建MFC工程,选择对话框工程。
2、新建一个static控件。
3、修改ID,并且在OnInitDialog里面获取指针。
4、加一个成员函数,并且在OnInitDialog里面调用,作为我们的例子代码的放置位置。
5、添加完整头文件
- #include <qedit.h>
- #include <dshow.h>
- #include <dxtrans.h>
- #pragma comment(lib,"strmiids.lib")
- //DirectDraw
- #include <ddraw.h>
- #pragma comment(lib,"ddraw.lib")
- #pragma comment(lib,"dxguid.lib")
并且让Dialog同时继承类型 ISampleGrabberCB。
6、添加读取摄像头的代码
摄像头读取,使用的是DirectXSDK中的DirectShow组件
需要编写的代码如下
- void CCameraTestDlg::initCameraPreview()
- {
- HRESULT hr = CoInitialize(NULL);
- if(FAILED(hr))
- return ;
- IBaseFilter* pCameraFilter = NULL ;
- //检查并加载系统摄像头。
- CComPtr <IMoniker> pMoniker =NULL;
- ULONG cFetched;
- CComPtr <ICreateDevEnum> pDevEnum =NULL;
- hr = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,IID_ICreateDevEnum, (void **) &pDevEnum);
- if (FAILED(hr))
- return ;
- CComPtr <IEnumMoniker> pClassEnum = NULL;
- hr = pDevEnum->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
- if (FAILED(hr))
- return ;
- if (pClassEnum == NULL)
- return ;
- if (SUCCEEDED(pClassEnum->Next (1, &pMoniker, &cFetched)))
- {
- hr = pMoniker->BindToObject(0,0,IID_IBaseFilter, (void**)&pCameraFilter);
- if (FAILED(hr))
- return ;
- }
- if(pCameraFilter == NULL)
- return ;
- //加载DirectShow对象
- IGraphBuilder* pGraphBuilder = NULL ;
- hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,IID_IGraphBuilder, (void **) &pGraphBuilder);
- if (FAILED(hr))
- return ;
- // Create the capture graph builder
- ICaptureGraphBuilder2* pCaptureGraphBuilder = NULL ;
- hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,IID_ICaptureGraphBuilder2, (void **) &pCaptureGraphBuilder);
- if (FAILED(hr))
- return ;
- //Create sampleGrabber
- IBaseFilter* pSampleGrabberFilter = NULL ;
- hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC,IID_IBaseFilter, (void**)&pSampleGrabberFilter);
- if (FAILED(hr))
- return ;
- ISampleGrabber* pSampleGrabber = NULL ;
- hr = pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (void**)&pSampleGrabber);
- if (FAILED(hr))
- return ;
- //设置视频格式
- AM_MEDIA_TYPE mt;
- ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
- mt.majortype = MEDIATYPE_Video ;
- mt.subtype = MEDIASUBTYPE_RGB32;
- hr = pSampleGrabber->SetMediaType(&mt);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetOneShot(FALSE);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetBufferSamples(FALSE);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetCallback(this, 1);
- if (FAILED(hr))
- return ;
- // Obtain interfaces for media control and Video Window
- IMediaControl* pMediaControl = NULL ;
- hr = pGraphBuilder->QueryInterface(IID_IMediaControl,(LPVOID *) &pMediaControl);
- if (FAILED(hr))
- return ;
- // Attach the filter graph to the capture graph
- hr = pCaptureGraphBuilder->SetFiltergraph(pGraphBuilder);
- if (FAILED(hr))
- return ;
- // Add Capture filter to our graph.
- hr = pGraphBuilder->AddFilter(pCameraFilter, L"Video Capture");
- if (FAILED(hr))
- return ;
- hr = pGraphBuilder->AddFilter(pSampleGrabberFilter, L"Sample Grabber");
- if (FAILED(hr))
- return ;
- //将摄像头和采集连接
- hr = pCaptureGraphBuilder->RenderStream(NULL,&MEDIATYPE_Video,pCameraFilter,NULL,pSampleGrabberFilter);
- if (FAILED(hr))
- return ;
- //连接之后取得来源视频格式
- AM_MEDIA_TYPE amt;
- hr = pSampleGrabber->GetConnectedMediaType(&amt);
- if (FAILED(hr))
- return ;
- //获取摄像头采集画面的分辨率
- int width = 0 ;
- int height = 0 ;
- VIDEOINFOHEADER* pVideoHeader = reinterpret_cast<VIDEOINFOHEADER*>(amt.pbFormat);
- if(pVideoHeader)
- {
- width = pVideoHeader->bmiHeader.biWidth ;
- height= pVideoHeader->bmiHeader.biHeight ;
- if (amt.cbFormat != 0)
- {
- CoTaskMemFree((PVOID)mt.pbFormat);
- mt.cbFormat = 0;
- mt.pbFormat = NULL;
- }
- if (amt.pUnk != NULL)
- {
- mt.pUnk->Release();
- mt.pUnk = NULL;
- }
- }
- else
- return ;
- //调用run之后才真正开始得到图像。
- pMediaControl->Run();
- }
- HRESULT CCameraTestDlg::SampleCB(double SampleTime,IMediaSample *pSample)
- {
- return E_FAIL;
- }
- HRESULT CCameraTestDlg::BufferCB(double SampleTime,BYTE *pBuffer, long BufferLen)
- {
- OutputDebugString(_T("BufferCB image data!\n"));
- return E_FAIL;
- }
- HRESULT CCameraTestDlg::QueryInterface(REFIID riid,void** ppvObject)
- {
- return S_OK ;
- }
- ULONG CCameraTestDlg::AddRef(void)
- {
- return 0 ;
- }
- ULONG CCameraTestDlg::Release(void)
- {
- return 0 ;
- }
此刻运行,已经能看到OutputDebugString不停的输出了,说明已经获取到了数据。只不过还没添加渲染,看不到图像。
7、添加视频渲染初始化函数,initDrawVideo(int width, int height)
并且在初始化摄像头成功,获取了分辨率那个地方,调用它。
新增加一个渲染函数。
- void DisplayLine(LPBYTE pBuf,int nPixelBytes)
第一个参数是数据buf,第二个参数是字节数。
后面完整代码:
- // CameraTestDlg.h : 头文件
- //
- #pragma once
- //DirectShow
- #include <qedit.h>
- #include <dshow.h>
- #include <dxtrans.h>
- #pragma comment(lib,"strmiids.lib")
- //DirectDraw
- #include <ddraw.h>
- #pragma comment(lib,"ddraw.lib")
- #pragma comment(lib,"dxguid.lib")
- // CCameraTestDlg 对话框
- class CCameraTestDlg : public CDialog
- , public ISampleGrabberCB
- {
- // 构造
- public:
- CCameraTestDlg(CWnd* pParent = NULL); // 标准构造函数
- // 对话框数据
- enum { IDD = IDD_CAMERATEST_DIALOG };
- protected:
- virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
- // 实现
- protected:
- HICON m_hIcon;
- // 生成的消息映射函数
- virtual BOOL OnInitDialog();
- afx_msg void OnPaint();
- afx_msg HCURSOR OnQueryDragIcon();
- DECLARE_MESSAGE_MAP()
- protected:
- CStatic* m_pPlayControl ;
- LPDIRECTDRAW7 m_lpDDraw; // DirectDraw 对象指针
- LPDIRECTDRAWSURFACE7 m_lpDDSOverlay ; // DirectDraw 离屏表面指针
- DDSURFACEDESC2 m_stOverlayDdsd; // DirectDraw 表面描述
- LPDIRECTDRAWSURFACE7 m_lpDDSPrimary; // DirectDraw 主表面指针
- DDSURFACEDESC2 m_stPrimaryDdsd; // DirectDraw 表面描述
- LPDIRECTDRAWCLIPPER m_pDirectDrawCliper ;//
- void initCameraPreview() ;
- void initDrawVideo(int width, int height);
- void DisplayLine(LPBYTE pBuf,int nPixelBytes);
- public:
- HRESULT STDMETHODCALLTYPE SampleCB(double SampleTime,IMediaSample *pSample);
- HRESULT STDMETHODCALLTYPE BufferCB(double SampleTime,BYTE *pBuffer, long BufferLen);
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,void**ppvObject);
- ULONG STDMETHODCALLTYPE AddRef(void);
- ULONG STDMETHODCALLTYPE Release(void);
- };
实现文件代码如下:
- // CameraTestDlg.cpp : 实现文件
- //
- #include "stdafx.h"
- #include "CameraTest.h"
- #include "CameraTestDlg.h"
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #endif
- // CCameraTestDlg 对话框
- CCameraTestDlg::CCameraTestDlg(CWnd* pParent /*=NULL*/)
- : CDialog(CCameraTestDlg::IDD, pParent)
- {
- m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
- }
- void CCameraTestDlg::DoDataExchange(CDataExchange* pDX)
- {
- CDialog::DoDataExchange(pDX);
- }
- BEGIN_MESSAGE_MAP(CCameraTestDlg, CDialog)
- ON_WM_PAINT()
- ON_WM_QUERYDRAGICON()
- //}}AFX_MSG_MAP
- END_MESSAGE_MAP()
- // CCameraTestDlg 消息处理程序
- BOOL CCameraTestDlg::OnInitDialog()
- {
- CDialog::OnInitDialog();
- // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
- // 执行此操作
- SetIcon(m_hIcon, TRUE); // 设置大图标
- SetIcon(m_hIcon, FALSE); // 设置小图标
- this->m_pPlayControl = (CStatic*)GetDlgItem(IDC_STATIC_PLAYCONTROL);
- this->initCameraPreview();
- return TRUE; // 除非将焦点设置到控件,否则返回TRUE
- }
- // 如果向对话框添加最小化按钮,则需要下面的代码
- // 来绘制该图标。对于使用文档/视图模型的MFC 应用程序,
- // 这将由框架自动完成。
- void CCameraTestDlg::OnPaint()
- {
- if (IsIconic())
- {
- CPaintDC dc(this); // 用于绘制的设备上下文
- SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
- // 使图标在工作区矩形中居中
- int cxIcon = GetSystemMetrics(SM_CXICON);
- int cyIcon = GetSystemMetrics(SM_CYICON);
- CRect rect;
- GetClientRect(&rect);
- int x = (rect.Width() - cxIcon + 1) / 2;
- int y = (rect.Height() - cyIcon + 1) / 2;
- // 绘制图标
- dc.DrawIcon(x, y, m_hIcon);
- }
- else
- {
- CDialog::OnPaint();
- }
- }
- //当用户拖动最小化窗口时系统调用此函数取得光标
- //显示。
- HCURSOR CCameraTestDlg::OnQueryDragIcon()
- {
- return static_cast<HCURSOR>(m_hIcon);
- }
- void CCameraTestDlg::initCameraPreview()
- {
- HRESULT hr = CoInitialize(NULL);
- if(FAILED(hr))
- return ;
- IBaseFilter* pCameraFilter = NULL ;
- //检查并加载系统摄像头。
- CComPtr <IMoniker> pMoniker =NULL;
- ULONG cFetched;
- CComPtr <ICreateDevEnum> pDevEnum =NULL;
- hr = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,IID_ICreateDevEnum, (void **) &pDevEnum);
- if (FAILED(hr))
- return ;
- CComPtr <IEnumMoniker> pClassEnum = NULL;
- hr = pDevEnum->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
- if (FAILED(hr))
- return ;
- if (pClassEnum == NULL)
- return ;
- if (SUCCEEDED(pClassEnum->Next (1, &pMoniker, &cFetched)))
- {
- hr = pMoniker->BindToObject(0,0,IID_IBaseFilter, (void**)&pCameraFilter);
- if (FAILED(hr))
- return ;
- }
- if(pCameraFilter == NULL)
- return ;
- //加载DirectShow对象
- IGraphBuilder* pGraphBuilder = NULL ;
- hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,IID_IGraphBuilder, (void **) &pGraphBuilder);
- if (FAILED(hr))
- return ;
- // Create the capture graph builder
- ICaptureGraphBuilder2* pCaptureGraphBuilder = NULL ;
- hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,IID_ICaptureGraphBuilder2, (void **) &pCaptureGraphBuilder);
- if (FAILED(hr))
- return ;
- //Create sampleGrabber
- IBaseFilter* pSampleGrabberFilter = NULL ;
- hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC,IID_IBaseFilter, (void**)&pSampleGrabberFilter);
- if (FAILED(hr))
- return ;
- ISampleGrabber* pSampleGrabber = NULL ;
- hr = pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (void**)&pSampleGrabber);
- if (FAILED(hr))
- return ;
- //设置视频格式
- AM_MEDIA_TYPE mt;
- ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
- mt.majortype = MEDIATYPE_Video ;
- mt.subtype = MEDIASUBTYPE_RGB32;
- hr = pSampleGrabber->SetMediaType(&mt);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetOneShot(FALSE);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetBufferSamples(FALSE);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetCallback(this, 1);
- if (FAILED(hr))
- return ;
- // Obtain interfaces for media control and Video Window
- IMediaControl* pMediaControl = NULL ;
- hr = pGraphBuilder->QueryInterface(IID_IMediaControl,(LPVOID *) &pMediaControl);
- if (FAILED(hr))
- return ;
- // Attach the filter graph to the capture graph
- hr = pCaptureGraphBuilder->SetFiltergraph(pGraphBuilder);
- if (FAILED(hr))
- return ;
- // Add Capture filter to our graph.
- hr = pGraphBuilder->AddFilter(pCameraFilter, L"Video Capture");
- if (FAILED(hr))
- return ;
- hr = pGraphBuilder->AddFilter(pSampleGrabberFilter, L"Sample Grabber");
- if (FAILED(hr))
- return ;
- //将摄像头和采集连接
- hr = pCaptureGraphBuilder->RenderStream(NULL,&MEDIATYPE_Video,pCameraFilter,NULL,pSampleGrabberFilter);
- if (FAILED(hr))
- return ;
- //连接之后取得来源视频格式
- AM_MEDIA_TYPE amt;
- hr = pSampleGrabber->GetConnectedMediaType(&amt);
- if (FAILED(hr))
- return ;
- //获取摄像头采集画面的分辨率
- int width = 0 ;
- int height = 0 ;
- VIDEOINFOHEADER* pVideoHeader = reinterpret_cast<VIDEOINFOHEADER*>(amt.pbFormat);
- if(pVideoHeader)
- {
- width = pVideoHeader->bmiHeader.biWidth ;
- height= pVideoHeader->bmiHeader.biHeight ;
- if (amt.cbFormat != 0)
- {
- CoTaskMemFree((PVOID)mt.pbFormat);
- mt.cbFormat = 0;
- mt.pbFormat = NULL;
- }
- if (amt.pUnk != NULL)
- {
- mt.pUnk->Release();
- mt.pUnk = NULL;
- }
- }
- else
- return ;
- this->initDrawVideo(width, height);
- pMediaControl->Run();
- }
- HRESULT CCameraTestDlg::SampleCB(double SampleTime,IMediaSample *pSample)
- {
- return E_FAIL;
- }
- HRESULT CCameraTestDlg::BufferCB(double SampleTime,BYTE *pBuffer, long BufferLen)
- {
- this->DisplayLine(pBuffer, 32/8);
- return E_FAIL;
- }
- HRESULT CCameraTestDlg::QueryInterface(REFIID riid,void** ppvObject)
- {
- return S_OK ;
- }
- ULONG CCameraTestDlg::AddRef(void)
- {
- return 0 ;
- }
- ULONG CCameraTestDlg::Release(void)
- {
- return 0 ;
- }
- void CCameraTestDlg::initDrawVideo(int width, int height)
- {
- HRESULT hr = DirectDrawCreateEx(NULL, (LPVOID*)&m_lpDDraw,IID_IDirectDraw7, NULL);
- if (FAILED(hr))
- return ;
- // 设置协作层
- hr = m_lpDDraw->SetCooperativeLevel(this->m_hWnd, DDSCL_NORMAL);
- if (FAILED(hr))
- return ;
- // 创建主表面
- ZeroMemory(&m_stPrimaryDdsd, sizeof(DDSURFACEDESC2));
- m_stPrimaryDdsd.dwSize = sizeof(DDSURFACEDESC2);
- m_stPrimaryDdsd.dwFlags = DDSD_CAPS ;
- m_stPrimaryDdsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
- hr = m_lpDDraw->CreateSurface(&m_stPrimaryDdsd, &m_lpDDSPrimary, NULL);
- if (FAILED(hr))
- return ;
- // 创建缓存表面
- ZeroMemory(&m_stOverlayDdsd, sizeof(DDSURFACEDESC2));
- m_stOverlayDdsd.dwSize = sizeof(DDSURFACEDESC2);
- m_stOverlayDdsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_OFFSCREENPLAIN ;
- m_stOverlayDdsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT ;
- m_stOverlayDdsd.dwWidth = width;
- m_stOverlayDdsd.dwHeight = height;
- struct DDPIXELFORMATDEF : public DDPIXELFORMAT
- {
- DDPIXELFORMATDEF(UINT dwFlags,
- UINT dwFourCC = 0x0,
- UINT dwUnionBitCount = 0x0,
- UINT dwUnionBitMask1 = 0x0,UINT dwUnionBitMask2 = 0x0,
- UINT dwUnionBitMask3 = 0x0,UINT dwUnionBitMask4 = 0x0)
- {
- __super::dwFlags = dwFlags ;
- __super::dwFourCC = dwFourCC ;
- __super::dwRGBBitCount = dwUnionBitCount ;
- __super::dwRBitMask = dwUnionBitMask1 ;
- __super::dwGBitMask = dwUnionBitMask2 ;
- __super::dwBBitMask = dwUnionBitMask3 ;
- __super::dwRGBAlphaBitMask = dwUnionBitMask4 ;
- }
- };
- //上面摄像头那里强制的设置的RGB32,则这里就使用RGB32。
- m_stOverlayDdsd.ddpfPixelFormat = DDPIXELFORMATDEF(DDPF_RGB,0x0,32,0x00FF0000,0x0000FF00,0x000000FF,0xFF000000) ;
- m_stOverlayDdsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
- hr = m_lpDDraw->CreateSurface(&m_stOverlayDdsd, &m_lpDDSOverlay, NULL);
- if (FAILED(hr))
- {
- m_stOverlayDdsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY | DDSCAPS_OFFSCREENPLAIN ;//使用内存再次尝试创建。
- hr = m_lpDDraw->CreateSurface(&m_stOverlayDdsd, &m_lpDDSOverlay, NULL);
- if (FAILED(hr))
- return ;
- }
- hr = m_lpDDraw->CreateClipper(0,&m_pDirectDrawCliper,NULL);
- if (FAILED(hr))
- return ;
- hr = m_pDirectDrawCliper->SetHWnd(0,this->m_pPlayControl->m_hWnd);
- if (FAILED(hr))
- return ;
- hr = m_lpDDSPrimary->SetClipper(m_pDirectDrawCliper);
- if (FAILED(hr))
- return ;
- }
- void CCameraTestDlg::DisplayLine(LPBYTE pBuf,int nPixelBytes)
- {
- HRESULT hr; // DirectDraw 函数返回值
- hr = m_lpDDSOverlay->Lock(NULL,&m_stOverlayDdsd,DDLOCK_WAIT|DDLOCK_WRITEONLY,NULL);
- if (FAILED(hr))
- return ;
- LPBYTE p_YUV_RGB = pBuf ;
- int dwByteCountPerLine = m_stOverlayDdsd.dwWidth*nPixelBytes; //m_stOverlayDdsd.ddpfPixelFormat.dwRGBBitCount/8 ;
- //摄像头读取的画面上下是反过来的,这个就自己处理了,就在下面的循环里处理就好了。
- LPBYTE lpSurface = (LPBYTE)m_stOverlayDdsd.lpSurface;
- if(lpSurface)
- {
- for (int i=0;i<m_stOverlayDdsd.dwHeight;i++)
- {
- memcpy(lpSurface, p_YUV_RGB, dwByteCountPerLine);
- p_YUV_RGB += dwByteCountPerLine;
- lpSurface += m_stOverlayDdsd.lPitch;
- }
- }
- hr = m_lpDDSOverlay->Unlock(NULL);
- if (FAILED(hr))
- return ;
- RECT rect;
- ::GetClientRect(m_pPlayControl->m_hWnd,&rect);
- POINT point = {0,0};
- ::ClientToScreen(m_pPlayControl->m_hWnd,&point);
- rect.right = rect.right - rect.left + point.x;
- rect.left = point.x;
- rect.bottom = rect.bottom - rect.top + point.y;
- rect.top = point.y;
- hr = m_lpDDSPrimary->Blt(&rect, m_lpDDSOverlay, NULL, DDBLT_WAIT, NULL);
- if (FAILED(hr))
- return ;
- }
运行截图如下:
补充说明:
对于DirectDraw创建Surface的时候,显卡内存模式支持的像素格式比系统内存支持像素格式要少。比方说这个例子里面直接初始化的是RGB32的像素格式的,而其实系统显卡通常默认是不支持的,但是软件Surface是支持的。为了获得更快的渲染效率,通常实际开发的时候还是采用系统显卡支持的像素格式,比如YUYV NV12等等。
多媒体编程——摄像头录像预览
1、 新建MFC工程,选择对话框工程。
2、新建一个static控件。
3、修改ID,并且在OnInitDialog里面获取指针。
4、加一个成员函数,并且在OnInitDialog里面调用,作为我们的例子代码的放置位置。
5、添加完整头文件
- #include <qedit.h>
- #include <dshow.h>
- #include <dxtrans.h>
- #pragma comment(lib,"strmiids.lib")
- //DirectDraw
- #include <ddraw.h>
- #pragma comment(lib,"ddraw.lib")
- #pragma comment(lib,"dxguid.lib")
并且让Dialog同时继承类型 ISampleGrabberCB。
6、添加读取摄像头的代码
摄像头读取,使用的是DirectXSDK中的DirectShow组件
需要编写的代码如下
- void CCameraTestDlg::initCameraPreview()
- {
- HRESULT hr = CoInitialize(NULL);
- if(FAILED(hr))
- return ;
- IBaseFilter* pCameraFilter = NULL ;
- //检查并加载系统摄像头。
- CComPtr <IMoniker> pMoniker =NULL;
- ULONG cFetched;
- CComPtr <ICreateDevEnum> pDevEnum =NULL;
- hr = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,IID_ICreateDevEnum, (void **) &pDevEnum);
- if (FAILED(hr))
- return ;
- CComPtr <IEnumMoniker> pClassEnum = NULL;
- hr = pDevEnum->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
- if (FAILED(hr))
- return ;
- if (pClassEnum == NULL)
- return ;
- if (SUCCEEDED(pClassEnum->Next (1, &pMoniker, &cFetched)))
- {
- hr = pMoniker->BindToObject(0,0,IID_IBaseFilter, (void**)&pCameraFilter);
- if (FAILED(hr))
- return ;
- }
- if(pCameraFilter == NULL)
- return ;
- //加载DirectShow对象
- IGraphBuilder* pGraphBuilder = NULL ;
- hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,IID_IGraphBuilder, (void **) &pGraphBuilder);
- if (FAILED(hr))
- return ;
- // Create the capture graph builder
- ICaptureGraphBuilder2* pCaptureGraphBuilder = NULL ;
- hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,IID_ICaptureGraphBuilder2, (void **) &pCaptureGraphBuilder);
- if (FAILED(hr))
- return ;
- //Create sampleGrabber
- IBaseFilter* pSampleGrabberFilter = NULL ;
- hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC,IID_IBaseFilter, (void**)&pSampleGrabberFilter);
- if (FAILED(hr))
- return ;
- ISampleGrabber* pSampleGrabber = NULL ;
- hr = pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (void**)&pSampleGrabber);
- if (FAILED(hr))
- return ;
- //设置视频格式
- AM_MEDIA_TYPE mt;
- ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
- mt.majortype = MEDIATYPE_Video ;
- mt.subtype = MEDIASUBTYPE_RGB32;
- hr = pSampleGrabber->SetMediaType(&mt);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetOneShot(FALSE);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetBufferSamples(FALSE);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetCallback(this, 1);
- if (FAILED(hr))
- return ;
- // Obtain interfaces for media control and Video Window
- IMediaControl* pMediaControl = NULL ;
- hr = pGraphBuilder->QueryInterface(IID_IMediaControl,(LPVOID *) &pMediaControl);
- if (FAILED(hr))
- return ;
- // Attach the filter graph to the capture graph
- hr = pCaptureGraphBuilder->SetFiltergraph(pGraphBuilder);
- if (FAILED(hr))
- return ;
- // Add Capture filter to our graph.
- hr = pGraphBuilder->AddFilter(pCameraFilter, L"Video Capture");
- if (FAILED(hr))
- return ;
- hr = pGraphBuilder->AddFilter(pSampleGrabberFilter, L"Sample Grabber");
- if (FAILED(hr))
- return ;
- //将摄像头和采集连接
- hr = pCaptureGraphBuilder->RenderStream(NULL,&MEDIATYPE_Video,pCameraFilter,NULL,pSampleGrabberFilter);
- if (FAILED(hr))
- return ;
- //连接之后取得来源视频格式
- AM_MEDIA_TYPE amt;
- hr = pSampleGrabber->GetConnectedMediaType(&amt);
- if (FAILED(hr))
- return ;
- //获取摄像头采集画面的分辨率
- int width = 0 ;
- int height = 0 ;
- VIDEOINFOHEADER* pVideoHeader = reinterpret_cast<VIDEOINFOHEADER*>(amt.pbFormat);
- if(pVideoHeader)
- {
- width = pVideoHeader->bmiHeader.biWidth ;
- height= pVideoHeader->bmiHeader.biHeight ;
- if (amt.cbFormat != 0)
- {
- CoTaskMemFree((PVOID)mt.pbFormat);
- mt.cbFormat = 0;
- mt.pbFormat = NULL;
- }
- if (amt.pUnk != NULL)
- {
- mt.pUnk->Release();
- mt.pUnk = NULL;
- }
- }
- else
- return ;
- //调用run之后才真正开始得到图像。
- pMediaControl->Run();
- }
- HRESULT CCameraTestDlg::SampleCB(double SampleTime,IMediaSample *pSample)
- {
- return E_FAIL;
- }
- HRESULT CCameraTestDlg::BufferCB(double SampleTime,BYTE *pBuffer, long BufferLen)
- {
- OutputDebugString(_T("BufferCB image data!\n"));
- return E_FAIL;
- }
- HRESULT CCameraTestDlg::QueryInterface(REFIID riid,void** ppvObject)
- {
- return S_OK ;
- }
- ULONG CCameraTestDlg::AddRef(void)
- {
- return 0 ;
- }
- ULONG CCameraTestDlg::Release(void)
- {
- return 0 ;
- }
此刻运行,已经能看到OutputDebugString不停的输出了,说明已经获取到了数据。只不过还没添加渲染,看不到图像。
7、添加视频渲染初始化函数,initDrawVideo(int width, int height)
并且在初始化摄像头成功,获取了分辨率那个地方,调用它。
新增加一个渲染函数。
- void DisplayLine(LPBYTE pBuf,int nPixelBytes)
第一个参数是数据buf,第二个参数是字节数。
后面完整代码:
- // CameraTestDlg.h : 头文件
- //
- #pragma once
- //DirectShow
- #include <qedit.h>
- #include <dshow.h>
- #include <dxtrans.h>
- #pragma comment(lib,"strmiids.lib")
- //DirectDraw
- #include <ddraw.h>
- #pragma comment(lib,"ddraw.lib")
- #pragma comment(lib,"dxguid.lib")
- // CCameraTestDlg 对话框
- class CCameraTestDlg : public CDialog
- , public ISampleGrabberCB
- {
- // 构造
- public:
- CCameraTestDlg(CWnd* pParent = NULL); // 标准构造函数
- // 对话框数据
- enum { IDD = IDD_CAMERATEST_DIALOG };
- protected:
- virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
- // 实现
- protected:
- HICON m_hIcon;
- // 生成的消息映射函数
- virtual BOOL OnInitDialog();
- afx_msg void OnPaint();
- afx_msg HCURSOR OnQueryDragIcon();
- DECLARE_MESSAGE_MAP()
- protected:
- CStatic* m_pPlayControl ;
- LPDIRECTDRAW7 m_lpDDraw; // DirectDraw 对象指针
- LPDIRECTDRAWSURFACE7 m_lpDDSOverlay ; // DirectDraw 离屏表面指针
- DDSURFACEDESC2 m_stOverlayDdsd; // DirectDraw 表面描述
- LPDIRECTDRAWSURFACE7 m_lpDDSPrimary; // DirectDraw 主表面指针
- DDSURFACEDESC2 m_stPrimaryDdsd; // DirectDraw 表面描述
- LPDIRECTDRAWCLIPPER m_pDirectDrawCliper ;//
- void initCameraPreview() ;
- void initDrawVideo(int width, int height);
- void DisplayLine(LPBYTE pBuf,int nPixelBytes);
- public:
- HRESULT STDMETHODCALLTYPE SampleCB(double SampleTime,IMediaSample *pSample);
- HRESULT STDMETHODCALLTYPE BufferCB(double SampleTime,BYTE *pBuffer, long BufferLen);
- HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,void**ppvObject);
- ULONG STDMETHODCALLTYPE AddRef(void);
- ULONG STDMETHODCALLTYPE Release(void);
- };
实现文件代码如下:
- // CameraTestDlg.cpp : 实现文件
- //
- #include "stdafx.h"
- #include "CameraTest.h"
- #include "CameraTestDlg.h"
- #ifdef _DEBUG
- #define new DEBUG_NEW
- #endif
- // CCameraTestDlg 对话框
- CCameraTestDlg::CCameraTestDlg(CWnd* pParent /*=NULL*/)
- : CDialog(CCameraTestDlg::IDD, pParent)
- {
- m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
- }
- void CCameraTestDlg::DoDataExchange(CDataExchange* pDX)
- {
- CDialog::DoDataExchange(pDX);
- }
- BEGIN_MESSAGE_MAP(CCameraTestDlg, CDialog)
- ON_WM_PAINT()
- ON_WM_QUERYDRAGICON()
- //}}AFX_MSG_MAP
- END_MESSAGE_MAP()
- // CCameraTestDlg 消息处理程序
- BOOL CCameraTestDlg::OnInitDialog()
- {
- CDialog::OnInitDialog();
- // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
- // 执行此操作
- SetIcon(m_hIcon, TRUE); // 设置大图标
- SetIcon(m_hIcon, FALSE); // 设置小图标
- this->m_pPlayControl = (CStatic*)GetDlgItem(IDC_STATIC_PLAYCONTROL);
- this->initCameraPreview();
- return TRUE; // 除非将焦点设置到控件,否则返回TRUE
- }
- // 如果向对话框添加最小化按钮,则需要下面的代码
- // 来绘制该图标。对于使用文档/视图模型的MFC 应用程序,
- // 这将由框架自动完成。
- void CCameraTestDlg::OnPaint()
- {
- if (IsIconic())
- {
- CPaintDC dc(this); // 用于绘制的设备上下文
- SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
- // 使图标在工作区矩形中居中
- int cxIcon = GetSystemMetrics(SM_CXICON);
- int cyIcon = GetSystemMetrics(SM_CYICON);
- CRect rect;
- GetClientRect(&rect);
- int x = (rect.Width() - cxIcon + 1) / 2;
- int y = (rect.Height() - cyIcon + 1) / 2;
- // 绘制图标
- dc.DrawIcon(x, y, m_hIcon);
- }
- else
- {
- CDialog::OnPaint();
- }
- }
- //当用户拖动最小化窗口时系统调用此函数取得光标
- //显示。
- HCURSOR CCameraTestDlg::OnQueryDragIcon()
- {
- return static_cast<HCURSOR>(m_hIcon);
- }
- void CCameraTestDlg::initCameraPreview()
- {
- HRESULT hr = CoInitialize(NULL);
- if(FAILED(hr))
- return ;
- IBaseFilter* pCameraFilter = NULL ;
- //检查并加载系统摄像头。
- CComPtr <IMoniker> pMoniker =NULL;
- ULONG cFetched;
- CComPtr <ICreateDevEnum> pDevEnum =NULL;
- hr = CoCreateInstance (CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC,IID_ICreateDevEnum, (void **) &pDevEnum);
- if (FAILED(hr))
- return ;
- CComPtr <IEnumMoniker> pClassEnum = NULL;
- hr = pDevEnum->CreateClassEnumerator (CLSID_VideoInputDeviceCategory, &pClassEnum, 0);
- if (FAILED(hr))
- return ;
- if (pClassEnum == NULL)
- return ;
- if (SUCCEEDED(pClassEnum->Next (1, &pMoniker, &cFetched)))
- {
- hr = pMoniker->BindToObject(0,0,IID_IBaseFilter, (void**)&pCameraFilter);
- if (FAILED(hr))
- return ;
- }
- if(pCameraFilter == NULL)
- return ;
- //加载DirectShow对象
- IGraphBuilder* pGraphBuilder = NULL ;
- hr = CoCreateInstance (CLSID_FilterGraph, NULL, CLSCTX_INPROC,IID_IGraphBuilder, (void **) &pGraphBuilder);
- if (FAILED(hr))
- return ;
- // Create the capture graph builder
- ICaptureGraphBuilder2* pCaptureGraphBuilder = NULL ;
- hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,IID_ICaptureGraphBuilder2, (void **) &pCaptureGraphBuilder);
- if (FAILED(hr))
- return ;
- //Create sampleGrabber
- IBaseFilter* pSampleGrabberFilter = NULL ;
- hr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC,IID_IBaseFilter, (void**)&pSampleGrabberFilter);
- if (FAILED(hr))
- return ;
- ISampleGrabber* pSampleGrabber = NULL ;
- hr = pSampleGrabberFilter->QueryInterface(IID_ISampleGrabber, (void**)&pSampleGrabber);
- if (FAILED(hr))
- return ;
- //设置视频格式
- AM_MEDIA_TYPE mt;
- ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
- mt.majortype = MEDIATYPE_Video ;
- mt.subtype = MEDIASUBTYPE_RGB32;
- hr = pSampleGrabber->SetMediaType(&mt);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetOneShot(FALSE);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetBufferSamples(FALSE);
- if (FAILED(hr))
- return ;
- hr = pSampleGrabber->SetCallback(this, 1);
- if (FAILED(hr))
- return ;
- // Obtain interfaces for media control and Video Window
- IMediaControl* pMediaControl = NULL ;
- hr = pGraphBuilder->QueryInterface(IID_IMediaControl,(LPVOID *) &pMediaControl);
- if (FAILED(hr))
- return ;
- // Attach the filter graph to the capture graph
- hr = pCaptureGraphBuilder->SetFiltergraph(pGraphBuilder);
- if (FAILED(hr))
- return ;
- // Add Capture filter to our graph.
- hr = pGraphBuilder->AddFilter(pCameraFilter, L"Video Capture");
- if (FAILED(hr))
- return ;
- hr = pGraphBuilder->AddFilter(pSampleGrabberFilter, L"Sample Grabber");
- if (FAILED(hr))
- return ;
- //将摄像头和采集连接
- hr = pCaptureGraphBuilder->RenderStream(NULL,&MEDIATYPE_Video,pCameraFilter,NULL,pSampleGrabberFilter);
- if (FAILED(hr))
- return ;
- //连接之后取得来源视频格式
- AM_MEDIA_TYPE amt;
- hr = pSampleGrabber->GetConnectedMediaType(&amt);
- if (FAILED(hr))
- return ;
- //获取摄像头采集画面的分辨率
- int width = 0 ;
- int height = 0 ;
- VIDEOINFOHEADER* pVideoHeader = reinterpret_cast<VIDEOINFOHEADER*>(amt.pbFormat);
- if(pVideoHeader)
- {
- width = pVideoHeader->bmiHeader.biWidth ;
- height= pVideoHeader->bmiHeader.biHeight ;
- if (amt.cbFormat != 0)
- {
- CoTaskMemFree((PVOID)mt.pbFormat);
- mt.cbFormat = 0;
- mt.pbFormat = NULL;
- }
- if (amt.pUnk != NULL)
- {
- mt.pUnk->Release();
- mt.pUnk = NULL;
- }
- }
- else
- return ;
- this->initDrawVideo(width, height);
- pMediaControl->Run();
- }
- HRESULT CCameraTestDlg::SampleCB(double SampleTime,IMediaSample *pSample)
- {
- return E_FAIL;
- }
- HRESULT CCameraTestDlg::BufferCB(double SampleTime,BYTE *pBuffer, long BufferLen)
- {
- this->DisplayLine(pBuffer, 32/8);
- return E_FAIL;
- }
- HRESULT CCameraTestDlg::QueryInterface(REFIID riid,void** ppvObject)
- {
- return S_OK ;
- }
- ULONG CCameraTestDlg::AddRef(void)
- {
- return 0 ;
- }
- ULONG CCameraTestDlg::Release(void)
- {
- return 0 ;
- }
- void CCameraTestDlg::initDrawVideo(int width, int height)
- {
- HRESULT hr = DirectDrawCreateEx(NULL, (LPVOID*)&m_lpDDraw,IID_IDirectDraw7, NULL);
- if (FAILED(hr))
- return ;
- // 设置协作层
- hr = m_lpDDraw->SetCooperativeLevel(this->m_hWnd, DDSCL_NORMAL);
- if (FAILED(hr))
- return ;
- // 创建主表面
- ZeroMemory(&m_stPrimaryDdsd, sizeof(DDSURFACEDESC2));
- m_stPrimaryDdsd.dwSize = sizeof(DDSURFACEDESC2);
- m_stPrimaryDdsd.dwFlags = DDSD_CAPS ;
- m_stPrimaryDdsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
- hr = m_lpDDraw->CreateSurface(&m_stPrimaryDdsd, &m_lpDDSPrimary, NULL);
- if (FAILED(hr))
- return ;
- // 创建缓存表面
- ZeroMemory(&m_stOverlayDdsd, sizeof(DDSURFACEDESC2));
- m_stOverlayDdsd.dwSize = sizeof(DDSURFACEDESC2);
- m_stOverlayDdsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_OFFSCREENPLAIN ;
- m_stOverlayDdsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH | DDSD_PIXELFORMAT ;
- m_stOverlayDdsd.dwWidth = width;
- m_stOverlayDdsd.dwHeight = height;
- struct DDPIXELFORMATDEF : public DDPIXELFORMAT
- {
- DDPIXELFORMATDEF(UINT dwFlags,
- UINT dwFourCC = 0x0,
- UINT dwUnionBitCount = 0x0,
- UINT dwUnionBitMask1 = 0x0,UINT dwUnionBitMask2 = 0x0,
- UINT dwUnionBitMask3 = 0x0,UINT dwUnionBitMask4 = 0x0)
- {
- __super::dwFlags = dwFlags ;
- __super::dwFourCC = dwFourCC ;
- __super::dwRGBBitCount = dwUnionBitCount ;
- __super::dwRBitMask = dwUnionBitMask1 ;
- __super::dwGBitMask = dwUnionBitMask2 ;
- __super::dwBBitMask = dwUnionBitMask3 ;
- __super::dwRGBAlphaBitMask = dwUnionBitMask4 ;
- }
- };
- //上面摄像头那里强制的设置的RGB32,则这里就使用RGB32。
- m_stOverlayDdsd.ddpfPixelFormat = DDPIXELFORMATDEF(DDPF_RGB,0x0,32,0x00FF0000,0x0000FF00,0x000000FF,0xFF000000) ;
- m_stOverlayDdsd.ddpfPixelFormat.dwSize = sizeof(DDPIXELFORMAT);
- hr = m_lpDDraw->CreateSurface(&m_stOverlayDdsd, &m_lpDDSOverlay, NULL);
- if (FAILED(hr))
- {
- m_stOverlayDdsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY | DDSCAPS_OFFSCREENPLAIN ;//使用内存再次尝试创建。
- hr = m_lpDDraw->CreateSurface(&m_stOverlayDdsd, &m_lpDDSOverlay, NULL);
- if (FAILED(hr))
- return ;
- }
- hr = m_lpDDraw->CreateClipper(0,&m_pDirectDrawCliper,NULL);
- if (FAILED(hr))
- return ;
- hr = m_pDirectDrawCliper->SetHWnd(0,this->m_pPlayControl->m_hWnd);
- if (FAILED(hr))
- return ;
- hr = m_lpDDSPrimary->SetClipper(m_pDirectDrawCliper);
- if (FAILED(hr))
- return ;
- }
- void CCameraTestDlg::DisplayLine(LPBYTE pBuf,int nPixelBytes)
- {
- HRESULT hr; // DirectDraw 函数返回值
- hr = m_lpDDSOverlay->Lock(NULL,&m_stOverlayDdsd,DDLOCK_WAIT|DDLOCK_WRITEONLY,NULL);
- if (FAILED(hr))
- return ;
- LPBYTE p_YUV_RGB = pBuf ;
- int dwByteCountPerLine = m_stOverlayDdsd.dwWidth*nPixelBytes; //m_stOverlayDdsd.ddpfPixelFormat.dwRGBBitCount/8 ;
- //摄像头读取的画面上下是反过来的,这个就自己处理了,就在下面的循环里处理就好了。
- LPBYTE lpSurface = (LPBYTE)m_stOverlayDdsd.lpSurface;
- if(lpSurface)
- {
- for (int i=0;i<m_stOverlayDdsd.dwHeight;i++)
- {
- memcpy(lpSurface, p_YUV_RGB, dwByteCountPerLine);
- p_YUV_RGB += dwByteCountPerLine;
- lpSurface += m_stOverlayDdsd.lPitch;
- }
- }
- hr = m_lpDDSOverlay->Unlock(NULL);
- if (FAILED(hr))
- return ;
- RECT rect;
- ::GetClientRect(m_pPlayControl->m_hWnd,&rect);
- POINT point = {0,0};
- ::ClientToScreen(m_pPlayControl->m_hWnd,&point);
- rect.right = rect.right - rect.left + point.x;
- rect.left = point.x;
- rect.bottom = rect.bottom - rect.top + point.y;
- rect.top = point.y;
- hr = m_lpDDSPrimary->Blt(&rect, m_lpDDSOverlay, NULL, DDBLT_WAIT, NULL);
- if (FAILED(hr))
- return ;
- }
运行截图如下:
补充说明:
对于DirectDraw创建Surface的时候,显卡内存模式支持的像素格式比系统内存支持像素格式要少。比方说这个例子里面直接初始化的是RGB32的像素格式的,而其实系统显卡通常默认是不支持的,但是软件Surface是支持的。为了获得更快的渲染效率,通常实际开发的时候还是采用系统显卡支持的像素格式,比如YUYV NV12等等。