Просмотр исходного кода

【软件版本】V
【模块名称】
【问题原因】将摄像头相关文件加入,并完全下拉框设备显示。
【修改描述】
【测试结果】

sat23 3 лет назад
Родитель
Сommit
9979f3ff50

+ 269 - 0
FieldTestTool/FieldTestTool/CaptureVideo.cpp

@@ -0,0 +1,269 @@
+#include "StdAfx.h"
+#include "CaptureVideo.h"
+
+CCaptureVideo::CCaptureVideo()
+{
+	//COM Library Intialization
+	if(FAILED(CoInitialize(NULL))) /*, COINIT_APARTMENTTHREADED)))*/
+	{
+		AfxMessageBox("CoInitialize Failed!\r\n");
+		return;
+	}
+	m_hWnd = NULL;
+	m_pVW = NULL;
+	m_pMC = NULL;
+	m_pGB = NULL;
+	m_pCapture = NULL;
+}
+
+CCaptureVideo::~CCaptureVideo()
+{
+	// Stop media playback
+	if(m_pMC)m_pMC->Stop();
+	if(m_pVW){
+		m_pVW->put_Visible(OAFALSE);
+		m_pVW->put_Owner(NULL);
+	}
+	SAFE_RELEASE(m_pCapture);
+	SAFE_RELEASE(m_pMC);
+	SAFE_RELEASE(m_pGB);
+	SAFE_RELEASE(m_pBF);
+	SAFE_RELEASE(m_pGrabber);
+	CoUninitialize( );
+}
+
+int CCaptureVideo::EnumDevices(CMFCRibbonComboBox *pCombobox)
+{
+	if (!pCombobox)
+		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 *pEnum;
+	hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEnum, 0);
+	if (hr != NOERROR)
+		return -1;
+
+    CString strCurName;
+    int nIndex = pCombobox->GetCurSel();
+    if ( nIndex != -1 )
+        strCurName = pCombobox->GetItem(nIndex);
+
+	pEnum->Reset();
+	ULONG cFetched;
+	IMoniker *pM;
+    pCombobox->RemoveAllItems();
+	while(hr = pEnum->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);
+				//::SendMessage(hList, CB_ADDSTRING, 0,(LPARAM)str);
+                pCombobox->AddItem(str);
+				SysFreeString(var.bstrVal);
+			}
+			pBag->Release();
+		}
+		pM->Release();
+	}
+
+	pEnum->Release();
+	pEnum = NULL;
+
+    // 如果之前有选择,恢复;
+    if ( nIndex != -1 )
+        pCombobox->SelectItem(strCurName);
+
+	return id;
+}
+
+HRESULT CCaptureVideo::Init(int iDeviceID, HWND hWnd)
+{
+	HRESULT hr;
+	hr = InitCaptureGraphBuilder();
+	if (FAILED(hr)){
+		AfxMessageBox("Failed to get video interfaces!");
+		return hr;
+	}
+	// Bind Device Filter. We know the device because the id was passed in
+	if(!BindFilter(iDeviceID, &m_pBF))return S_FALSE;
+	hr = m_pGB->AddFilter(m_pBF, L"Capture Filter");
+	// hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,
+	// m_pBF, NULL, NULL);
+	// create a sample grabber
+	//hr = m_pGrabber.CoCreateInstance( CLSID_SampleGrabber );
+	hr = CoCreateInstance( CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_ISampleGrabber, (void**)&m_pGrabber );
+
+	if( !m_pGrabber ){
+		AfxMessageBox("Fail to create SampleGrabber, maybe qedit.dll is not registered?");
+		return hr;
+	}
+	CComQIPtr< IBaseFilter, &IID_IBaseFilter > pGrabBase( m_pGrabber );
+	//设置视频格式
+	AM_MEDIA_TYPE mt;
+	ZeroMemory(&mt, sizeof(AM_MEDIA_TYPE));
+	mt.majortype = MEDIATYPE_Video;
+	mt.subtype = MEDIASUBTYPE_RGB24;
+	hr = m_pGrabber->SetMediaType(&mt);
+	if( FAILED( hr ) ){
+		AfxMessageBox("Fail to set media type!");
+		return hr;
+	}
+	hr = m_pGB->AddFilter( pGrabBase, L"Grabber" );
+	if( FAILED( hr ) ){
+		AfxMessageBox("Fail to put sample grabber in graph");
+		return hr;
+	}
+	// try to render preview/capture pin
+	hr = m_pCapture->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,m_pBF,pGrabBase,NULL);
+	if( FAILED( hr ) )
+		hr = m_pCapture->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,m_pBF,pGrabBase,NULL);
+	if( FAILED( hr ) ){
+		AfxMessageBox("Can’t build the graph");
+		return hr;
+	}
+
+	hr = m_pGrabber->GetConnectedMediaType( &mt );
+	if ( FAILED( hr) ){
+		AfxMessageBox("Failt to read the connected media type");
+		return hr;
+	}
+
+	VIDEOINFOHEADER * vih = (VIDEOINFOHEADER*) mt.pbFormat;
+	mCB.lWidth = vih->bmiHeader.biWidth;
+	mCB.lHeight = vih->bmiHeader.biHeight;
+	FreeMediaType(mt);
+	hr = m_pGrabber->SetBufferSamples( FALSE );
+	hr = m_pGrabber->SetOneShot( FALSE );
+	hr = m_pGrabber->SetCallback( &mCB, 1 );
+	//设置视频捕捉窗口
+	m_hWnd = hWnd ;
+	SetupVideoWindow();
+	hr = m_pMC->Run();//开始视频捕捉
+	if(FAILED(hr))
+	{
+		AfxMessageBox("Couldn’t run the graph!");
+		return hr;
+	}
+	return S_OK;
+}
+
+bool CCaptureVideo::BindFilter(int deviceId, IBaseFilter **pFilter)
+{
+	if (deviceId < 0)
+		return false;
+	// enumerate all video capture devices
+	ICreateDevEnum* pCreateDevEnum;
+	HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, (void**)&pCreateDevEnum);
+	if (hr != NOERROR)
+	{
+		return false;
+	}
+	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++;
+	}
+	pEm->Release();
+	pEm = NULL;
+
+	return true;
+}
+
+HRESULT CCaptureVideo::InitCaptureGraphBuilder()
+{
+	HRESULT hr;
+	// 创建IGraphBuilder接口
+	hr=CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IGraphBuilder, (void **)&m_pGB);
+	// 创建ICaptureGraphBuilder2接口
+	hr = CoCreateInstance (CLSID_CaptureGraphBuilder2 , NULL, CLSCTX_INPROC,
+		IID_ICaptureGraphBuilder2, (void **) &m_pCapture);
+	if (FAILED(hr))return hr;
+	m_pCapture->SetFiltergraph(m_pGB);
+	hr = m_pGB->QueryInterface(IID_IMediaControl, (void **)&m_pMC);
+	if (FAILED(hr))return hr;
+	hr = m_pGB->QueryInterface(IID_IVideoWindow, (LPVOID *) &m_pVW);
+	if (FAILED(hr))return hr;
+	return hr;
+}
+
+HRESULT CCaptureVideo::SetupVideoWindow()
+{
+	HRESULT hr;
+	hr = m_pVW->put_Owner((OAHWND)m_hWnd);
+	if (FAILED(hr))return hr;
+	hr = m_pVW->put_WindowStyle(WS_CHILD | WS_CLIPCHILDREN);
+	if (FAILED(hr))return hr;
+	ResizeVideoWindow();
+	hr = m_pVW->put_Visible(OATRUE);
+	return hr;
+}
+
+void CCaptureVideo::ResizeVideoWindow()
+{
+	if (m_pVW){
+		//让图像充满整个窗口
+		CRect rc;
+		::GetClientRect(m_hWnd,&rc);
+		m_pVW->SetWindowPosition(0, 0, rc.right, rc.bottom);
+	}
+}
+
+void CCaptureVideo::GrabOneFrame(BOOL bGrab)
+{
+	bOneShot = bGrab;
+}
+
+void CCaptureVideo::FreeMediaType(AM_MEDIA_TYPE& mt)
+{
+	if (mt.cbFormat != 0) {
+		CoTaskMemFree((PVOID)mt.pbFormat);
+		// Strictly unnecessary but tidier
+		mt.cbFormat = 0;
+		mt.pbFormat = NULL;
+	}
+	if (mt.pUnk != NULL) {
+		mt.pUnk->Release();
+		mt.pUnk = NULL;
+	}
+}

+ 45 - 0
FieldTestTool/FieldTestTool/CaptureVideo.h

@@ -0,0 +1,45 @@
+#ifndef __CAPTURE_VIDEO__
+#define __CAPTURE_VIDEO__
+
+#pragma once
+
+#ifndef SAFE_RELEASE
+#define SAFE_RELEASE( x ) \
+	if ( NULL != x ) \
+	{ \
+		x->Release( ); \
+		x = NULL; \
+	}
+#endif
+#include "SampleGrabberCB.h"
+
+class CSampleGrabberCB;
+class CCaptureVideo
+{
+	friend class CSampleGrabberCB;
+public:
+	// 截图;
+	void GrabOneFrame(BOOL bGrab);
+	// 初始化设备;
+	HRESULT Init(int iDeviceID,HWND hWnd);
+	// 枚举设备;
+	int EnumDevices(CMFCRibbonComboBox *pCombobox);
+	CCaptureVideo();
+	virtual ~CCaptureVideo();
+private:
+	HWND m_hWnd;
+	IGraphBuilder *m_pGB;
+	ICaptureGraphBuilder2* m_pCapture;
+	IBaseFilter* m_pBF;
+	IMediaControl* m_pMC;
+	IVideoWindow* m_pVW;
+	ISampleGrabber *m_pGrabber;
+protected:
+	void FreeMediaType(AM_MEDIA_TYPE& mt);
+	bool BindFilter(int deviceId, IBaseFilter **pFilter);
+	void ResizeVideoWindow();
+	HRESULT SetupVideoWindow();
+	HRESULT InitCaptureGraphBuilder();
+};
+
+#endif

+ 20 - 0
FieldTestTool/FieldTestTool/FieldTestTool.vcproj

@@ -449,6 +449,26 @@
 				>
 			</File>
 		</Filter>
+		<Filter
+			Name="Camera"
+			>
+			<File
+				RelativePath=".\CaptureVideo.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\CaptureVideo.h"
+				>
+			</File>
+			<File
+				RelativePath=".\SampleGrabberCB.cpp"
+				>
+			</File>
+			<File
+				RelativePath=".\SampleGrabberCB.h"
+				>
+			</File>
+		</Filter>
 		<File
 			RelativePath=".\ReadMe.txt"
 			>

+ 79 - 24
FieldTestTool/FieldTestTool/MainFrm.cpp

@@ -163,6 +163,14 @@ int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
 
     SetTimer(0,100,NULL);
 
+    RegisterDeviceChange();
+
+    // 初始化摄像头下拉框;
+    CMFCRibbonBar* pRibbon = GetRibbonBar();
+    ASSERT_VALID(pRibbon);
+    CMFCRibbonComboBox* pCameraCombo = DYNAMIC_DOWNCAST(CMFCRibbonComboBox, pRibbon->FindByID(ID_RIBBON_CAMERA_COMBOBOX));
+    m_cap.EnumDevices(pCameraCombo);    
+
 	return 0;
 }
 
@@ -772,6 +780,33 @@ void CMainFrame::OnUpdateSwitcherCheckboxB(CCmdUI* pCmdUI)
     pCmdUI->SetCheck(m_bSwitcherCheckB);
 }
 
+bool CMainFrame::RegisterDeviceChange()
+{
+    // HID设备的GUID,可在设备管理器中查询,经查结果如下:
+    // {745a17a0-74d3-11d0-b6fe-00a0c90f57da}
+    // 注:使用真实的HID的GUID,反正检测不出来,如果是其它的GUID,所有USB事件都能检测出
+    const GUID GUID_DEVINTERFACE_LIST[] = {
+        { 0xA5DCBF10, 0x6530, 0x11D2,{ 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } },  // USB设备
+        { 0x53f56307, 0xb6bf, 0x11d0,{ 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b } },  // 磁盘(U盘)
+        { 0x4D1E55B2, 0xF16F, 0x11CF,{ 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } },  // HID
+        { 0x745A17A0, 0x74D3, 0x11D0,{ 0xB6, 0xFE, 0x00, 0xA0, 0xC9, 0x0F, 0x57, 0xDA } },  // 另一个HID
+        { 0xad498944, 0x762f, 0x11d0,{ 0x8d, 0xcb, 0x00, 0xc0, 0x4f, 0xc3, 0x35, 0x8c } },  // 网卡;
+        { 0x4d36e96c, 0xe325, 0x11ce,{ 0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18 } }
+    }; 
+
+    // 注册HID事件
+    DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface;
+
+    memset(&DevBroadcastDeviceInterface, 0, sizeof(DEV_BROADCAST_DEVICEINTERFACE));
+    DevBroadcastDeviceInterface.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
+    DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
+    // 可以循环注册所有列出的GUID,此处只使用一种
+    DevBroadcastDeviceInterface.dbcc_classguid = GUID_DEVINTERFACE_LIST[5];
+    HDEVNOTIFY hDevNotify = RegisterDeviceNotification(this->GetSafeHwnd(), &DevBroadcastDeviceInterface, DEVICE_NOTIFY_WINDOW_HANDLE);
+
+    return hDevNotify ? true : false;
+}
+
 BOOL CMainFrame::OnDeviceChange(UINT nEventType, DWORD_PTR dwData)
 {
     CMFCRibbonBar* pRibbon = GetRibbonBar();
@@ -781,8 +816,12 @@ BOOL CMainFrame::OnDeviceChange(UINT nEventType, DWORD_PTR dwData)
     CMFCRibbonComboBox* pSwitcherCombo = DYNAMIC_DOWNCAST(CMFCRibbonComboBox, pRibbon->FindByID(ID_RIBBON_SWITCHER_COMBOBOX));
     ASSERT_VALID(pInfradeCombo);
     ASSERT_VALID(pSwitcherCombo);
+#ifdef _DEBUG
+    if ( dwData != 0 )
+        TRACE("dwData 不为空\r\n");
+#endif
 
-    //DEV_BROADCAST_DEVICEINTERFACE* pdbd = (DEV_BROADCAST_DEVICEINTERFACE*)dwData;
+    DEV_BROADCAST_DEVICEINTERFACE* pdbd = (DEV_BROADCAST_DEVICEINTERFACE*)dwData;
     DEV_BROADCAST_PORT *pbdp = (DEV_BROADCAST_PORT*)dwData;
     if ( pbdp && _tcsstr(pbdp->dbcp_name, _T("COM")) )
     {
@@ -860,6 +899,15 @@ BOOL CMainFrame::OnDeviceChange(UINT nEventType, DWORD_PTR dwData)
             }
         }
     }
+    else
+    {
+        //DEV_BROADCAST_HDR* phdr = (DEV_BROADCAST_HDR*)dwData;
+        // 初始化摄像头下拉框;
+        CMFCRibbonBar* pRibbon = GetRibbonBar();
+        ASSERT_VALID(pRibbon);
+        CMFCRibbonComboBox* pCameraCombo = DYNAMIC_DOWNCAST(CMFCRibbonComboBox, pRibbon->FindByID(ID_RIBBON_CAMERA_COMBOBOX));
+        m_cap.EnumDevices(pCameraCombo);
+    }
 
     TRACE(_T("OnDeviceChange\n"));
 
@@ -1006,30 +1054,7 @@ bool CMainFrame::IsUSBSwitchPort(CString strCOMPort)
 
     return false;
 }
-/*
-void CMainFrame::SwitchUSB(BOOL bCheckA)
-{
-    if ( m_SerialUSB.IsOpen() )
-    {
-        m_SerialUSB.SetTimeouts(0,2,50,1,150);
-        // 封装发送包;
-        byte szSendData[MAX_SWITCH_LEN] = {0};
-        _stprintf_s((TCHAR*)szSendData, MAX_SWITCH_LEN, _T("*SET PORT %c\r"), bCheckA ? 'A':'B');
 
-        // 发送单信号;
-        if ( m_SerialUSB.WriteSync(szSendData, _tcslen((TCHAR*)szSendData)) )
-        {
-            memset(szSendData, 0, MAX_SWITCH_LEN);
-            m_SerialUSB.ReadSync(szSendData, MAX_SWITCH_LEN);
-            TRACE("切换U盘成功\n");
-        }
-        else
-        {
-            TRACE("切换U盘失败\n");
-        }
-    }
-}
-*/
 void CMainFrame::OnTimer(UINT_PTR nIDEvent)
 {
     // TODO: 在此添加消息处理程序代码和/或调用默认值
@@ -1076,3 +1101,33 @@ void CMainFrame::OnTimer(UINT_PTR nIDEvent)
 
     CMDIFrameWndEx::OnTimer(nIDEvent);
 }
+
+BOOL CMainFrame::PreTranslateMessage(MSG* pMsg)
+{
+    // TODO: 在此添加专用代码和/或调用基类
+    if (pMsg->message == WM_KEYDOWN)
+    {
+        if (pMsg->wParam == VK_F3)
+        {
+            static BOOL bTopWnd = FALSE;
+            if (bTopWnd == FALSE)
+            {
+                SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);//窗口置顶
+                CString strVersionInfo = _T("视频采集卡 - 前置窗口(请按F3取消或开启前置)");
+                SetWindowText(strVersionInfo);
+            }
+            else
+            {
+                SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);//取消窗口置顶	
+                CString strVersionInfo = _T("视频采集卡 - 取消前置(请按F3取消或开启前置)");
+                SetWindowText(strVersionInfo);
+            }
+
+            bTopWnd = !bTopWnd;
+
+            // 必须退出,否则执行2次;
+            return TRUE;
+        }
+    }
+    return CMDIFrameWndEx::PreTranslateMessage(pMsg);
+}

+ 5 - 0
FieldTestTool/FieldTestTool/MainFrm.h

@@ -16,6 +16,7 @@
 #include "CalendarBar.h"
 #include "Resource.h"
 #include "PropertiesWnd.h"
+#include "CaptureVideo.h"
 
 class COutlookBar : public CMFCOutlookBar
 {
@@ -93,6 +94,8 @@ protected:
     afx_msg void OnSwitcherCheckboxB();
     afx_msg void OnUpdateSwitcherCheckboxB(CCmdUI* pCmdUI);
 
+    // ×¢²áÉ豸±ä»¯¼àÌý;
+    bool RegisterDeviceChange();
     afx_msg BOOL OnDeviceChange(UINT nEventType, DWORD_PTR dwData);
 	DECLARE_MESSAGE_MAP()
     
@@ -105,6 +108,7 @@ public:
 
 	int FindFocusedOutlookWnd(CMFCOutlookBarTabCtrl** ppOutlookWnd);
 
+    CCaptureVideo m_cap;
     CBaseSerial m_SerialTW;
     CBaseSerial m_SerialUSB;
     std::vector<TString> m_vtPorts;
@@ -122,6 +126,7 @@ public:
 	CMFCOutlookBarTabCtrl* m_pCurrOutlookWnd;
 	CMFCOutlookBarPane*    m_pCurrOutlookPage;
     afx_msg void OnTimer(UINT_PTR nIDEvent);
+    virtual BOOL PreTranslateMessage(MSG* pMsg);
 };
 
 

+ 55 - 0
FieldTestTool/FieldTestTool/SampleGrabberCB.cpp

@@ -0,0 +1,55 @@
+#include "StdAfx.h"
+#include "SampleGrabberCB.h"
+
+BOOL bOneShot = FALSE;
+CSampleGrabberCB mCB;
+CSampleGrabberCB::CSampleGrabberCB(void)
+{
+	_tcscpy_s(m_szFileName, _T("c:\\donaldo.bmp"));
+}
+
+CSampleGrabberCB::~CSampleGrabberCB(void)
+{
+}
+
+STDMETHODIMP CSampleGrabberCB::BufferCB( double dblSampleTime, BYTE * pBuffer, long lBufferSize )
+{
+	if( !bOneShot )
+		return 0;
+
+	if (!pBuffer)
+		return E_POINTER;
+	SaveBitmap(pBuffer, lBufferSize);
+	bOneShot = FALSE;
+	return 0;
+}
+
+//创建位图文件
+BOOL CSampleGrabberCB::SaveBitmap(BYTE * pBuffer, long lBufferSize )
+{
+	HANDLE hf = CreateFile(
+		m_szFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL,
+		CREATE_ALWAYS, NULL, NULL );
+	if( hf == INVALID_HANDLE_VALUE )return 0;
+	// 写文件头
+	BITMAPFILEHEADER bfh;
+	memset( &bfh, 0, sizeof( bfh ) );
+	bfh.bfType = 'MB';
+	bfh.bfSize = sizeof( bfh ) + lBufferSize + sizeof( BITMAPINFOHEADER );
+	bfh.bfOffBits = sizeof( BITMAPINFOHEADER ) + sizeof( BITMAPFILEHEADER );
+	DWORD dwWritten = 0;
+	WriteFile( hf, &bfh, sizeof( bfh ), &dwWritten, NULL );
+	// 写位图格式
+	BITMAPINFOHEADER bih;
+	memset( &bih, 0, sizeof( bih ) );
+	bih.biSize = sizeof( bih );
+	bih.biWidth = lWidth;
+	bih.biHeight = lHeight;
+	bih.biPlanes = 1;
+	bih.biBitCount = 24;
+	WriteFile( hf, &bih, sizeof( bih ), &dwWritten, NULL );
+	// 写位图数据
+	WriteFile( hf, pBuffer, lBufferSize, &dwWritten, NULL );
+	CloseHandle( hf );
+	return 0;
+}

+ 42 - 0
FieldTestTool/FieldTestTool/SampleGrabberCB.h

@@ -0,0 +1,42 @@
+#ifndef __SAMPLE_GRABBER_CB__
+#define __SAMPLE_GRABBER_CB__
+#pragma once
+
+#include <Dshow.h>
+#include <Qedit.h>
+#pragma comment(lib, "Strmiids.lib")
+
+extern BOOL bOneShot;//È«¾Ö±äÁ¿
+class CSampleGrabberCB :public ISampleGrabberCB
+{
+public:
+	CSampleGrabberCB(void);
+	~CSampleGrabberCB(void);
+
+public:
+	long lWidth;
+	long lHeight;
+	TCHAR m_szFileName[MAX_PATH];// λͼÎļþÃû³Æ
+	STDMETHODIMP_(ULONG) AddRef() { return 2; }
+	STDMETHODIMP_(ULONG) Release() { return 1; }
+	STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
+	{
+		if( riid == IID_ISampleGrabberCB || riid == IID_IUnknown )
+		{
+			*ppv = (void *)this;
+			return NOERROR;
+		}
+
+		return E_NOINTERFACE;
+	}
+	STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample ){
+		return 0;
+	}
+
+	STDMETHODIMP BufferCB( double dblSampleTime, BYTE * pBuffer, long lBufferSize );
+	BOOL SaveBitmap(BYTE * pBuffer, long lBufferSize );
+};
+
+extern CSampleGrabberCB mCB;
+
+#endif

+ 7 - 0
FieldTestTool/FieldTestTool/stdafx.h

@@ -54,6 +54,13 @@
 #include <map>
 #include "tinyxml2.h"
 
+// 摄像头相关头文件;
+#include <Vfw.h>
+#pragma comment(lib, "vfw32.lib")
+#include <Dshow.h>
+#include <strmif.h>
+#pragma comment(lib, "strmiids.lib")
+
 // 禁用标题栏;
 #define DISABLE_CAPTIONBAR
 // 禁用快速启动栏;