www.pudn.com > ImageCheck.rar > CaiDX.cpp


// CaiDX.cpp : implementation file 
// 
 
#include "stdafx.h" 
#include "ImageCheck.h" 
#include "CaiDX.h" 
 
#ifdef _DEBUG 
#define new DEBUG_NEW 
#undef THIS_FILE 
static char THIS_FILE[] = __FILE__; 
#endif 
 
 
BOOL g_bOneShot=FALSE; 
BOOL g_bCutDone=FALSE; 
DWORD g_dwGraphRegister=0;  // For running object table 
HWND g_hwnd=0; 
 
 
// Structures 
typedef struct _callbackinfo  
{ 
    double dblSampleTime; 
    long lBufferSize; 
    BYTE *pBuffer; 
    BITMAPINFOHEADER bih; 
 
} CALLBACKINFO; 
 
CALLBACKINFO cb={0}; 
 
 
// Note: this object is a SEMI-COM object, and can only be created statically. 
// We use this little semi-com object to handle the sample-grab-callback, 
// since the callback must provide a COM interface. We could have had an interface 
// where you provided a function-call callback, but that's really messy, so we 
// did it this way. You can put anything you want into this C++ object, even 
// a pointer to a CDialog. Be aware of multi-thread issues though. 
// 
class CSampleGrabberCB : public ISampleGrabberCB  
{ 
public: 
    // these will get set by the main thread below. We need to 
    // know this in order to write out the bmp 
    long lWidth; 
    long lHeight; 
    CCaiDX * pOwner; 
    BOOL bFileWritten; 
	CString m_strFilePath; 
	CRect	m_rtROI; 
 
    CSampleGrabberCB( ) 
    { 
		m_strFilePath = ""; 
        pOwner = NULL; 
        bFileWritten = FALSE; 
		cb.pBuffer = NULL; 
    }   
 
    ~CSampleGrabberCB( ) 
    { 
		if (cb.pBuffer) 
		{ 
			delete []cb.pBuffer; 
			cb.pBuffer = NULL; 
		} 
    }   
 
    // fake out any COM ref counting 
    // 
    STDMETHODIMP_(ULONG) AddRef() { return 2; } 
    STDMETHODIMP_(ULONG) Release() { return 1; } 
 
    // fake out any COM QI'ing 
    // 
    STDMETHODIMP QueryInterface(REFIID riid, void ** ppv) 
    { 
        if( riid == IID_ISampleGrabberCB || riid == IID_IUnknown )  
        { 
            *ppv = (void *) static_cast ( this ); 
            return NOERROR; 
        }     
        return E_NOINTERFACE; 
    } 
 
    // we don't implement this interface for this example 
    // 
    STDMETHODIMP SampleCB( double SampleTime, IMediaSample * pSample ) 
    { 
        return 0; 
    } 
 
    // The sample grabber is calling us back on its deliver thread. 
    // This is NOT the main app thread! 
    // 
    //           !!!!! WARNING WARNING WARNING !!!!! 
    // 
    // On Windows 9x systems, you are not allowed to call most of the  
    // Windows API functions in this callback.  Why not?  Because the 
    // video renderer might hold the global Win16 lock so that the video 
    // surface can be locked while you copy its data.  This is not an 
    // issue on Windows 2000, but is a limitation on Win95,98,98SE, and ME. 
    // Calling a 16-bit legacy function could lock the system, because  
    // it would wait forever for the Win16 lock, which would be forever 
    // held by the video renderer. 
    // 
    // As a workaround, copy the bitmap data during the callback, 
    // post a message to our app, and write the data later. 
    // 
    STDMETHODIMP BufferCB( double dblSampleTime, BYTE * pBuffer, long lBufferSize ) 
    { 
        // this flag will get set to true in order to take a picture 
        // 
        if( !g_bOneShot ) 
            return 0; 
 
        if (!pBuffer) 
            return E_POINTER; 
 
        if( cb.lBufferSize < lBufferSize ) 
        { 
            delete [] cb.pBuffer; 
            cb.pBuffer = NULL; 
            cb.lBufferSize = 0; 
        } 
 
        // Since we can't access Windows API functions in this callback, just 
        // copy the bitmap data to a global structure for later reference. 
        cb.dblSampleTime = dblSampleTime; 
 
        // If we haven't yet allocated the data buffer, do it now. 
        // Just allocate what we need to store the new bitmap. 
        if (!cb.pBuffer) 
        { 
            cb.pBuffer = new BYTE[lBufferSize]; 
            cb.lBufferSize = lBufferSize; 
        } 
 
        if( !cb.pBuffer ) 
        { 
            cb.lBufferSize = 0; 
            return E_OUTOFMEMORY; 
        } 
 
        // Copy the bitmap data into our global buffer 
        memcpy(cb.pBuffer, pBuffer, lBufferSize); 
		g_bCutDone = TRUE; 
		//DWORD dwDibWidthBytes = (((lWidth * 24 + 31) / 32) * 4); ; 
		//xImage.CreateFromArray(pBuffer,lWidth,lHeight,24,dwDibWidthBytes,FALSE); 
 
        return 0; 
    } 
}; 
 
 
////////////////////////////////////////////////////////////////// 
// 
// This semi-COM object will receive sample callbacks for us 
// 
////////////////////////////////////////////////////////////////// 
 
CSampleGrabberCB mCB; 
///////////////////////////////////////////////////////////////////////////// 
// CCaiDX 
 
CCaiDX::CCaiDX() 
{ 
	CoInitialize(NULL); 
	m_bIsPreviewing = FALSE; 
} 
 
CCaiDX::~CCaiDX() 
{ 
	StopPreview(); 
} 
 
 
BEGIN_MESSAGE_MAP(CCaiDX, CWnd) 
	//{{AFX_MSG_MAP(CCaiDX) 
	ON_WM_PAINT() 
	ON_WM_SIZE() 
	//}}AFX_MSG_MAP 
END_MESSAGE_MAP() 
 
 
///////////////////////////////////////////////////////////////////////////// 
// CCaiDX message handlers 
 
BOOL CCaiDX::StartPreview() 
{ 
	HRESULT hr; 
	if (m_bIsPreviewing) 
	{ 
		//如果已经在预览,返回 
		return TRUE; 
	} 
	 
	//创建管理器 
	if (m_pGraph!=NULL) 
	{ 
		StopPreview(); 
	} 
    m_pGraph.CoCreateInstance( CLSID_FilterGraph ); 
    if( !m_pGraph ) 
    { 
        return FALSE; 
    } 
 
	//连接默认设备 
	if(!CreateVideoFilter("",&m_pCapVideo)) 
	{ 
		return FALSE; 
	} 
	m_pGraph->AddFilter(m_pCapVideo,L"Video Capture Filter"); 
	 
    hr = m_pBuilder.CoCreateInstance( CLSID_CaptureGraphBuilder2 ); 
    if( !m_pBuilder ) 
    { 
		return FALSE; 
	} 
 
    hr = m_pBuilder->SetFiltergraph( m_pGraph ); 
    if( FAILED( hr ) ) 
    { 
        return FALSE; 
    } 
 
	//定义ISampleGrabber接口并初始化,用于采集图片 
	// 
	hr = m_pGrabber.CoCreateInstance( CLSID_SampleGrabber ); 
	if( !m_pGrabber ) 
	{ 
		return FALSE; 
	} 
	//定义Grabber Filter, 
	CComQIPtr< IBaseFilter, &IID_IBaseFilter > pGrabBase( m_pGrabber );	 
 
	//设置它的媒体类型,并将它加入Graph中 
	CMediaType VideoType; 
	VideoType.SetType(&MEDIATYPE_Video); 
	VideoType.SetSubtype(&MEDIASUBTYPE_RGB24); 
	hr = m_pGrabber->SetMediaType(&VideoType); 
	hr = m_pGraph->AddFilter(pGrabBase,L"Grabber"); 
	if( FAILED( hr ) ) 
	{ 
		return FALSE; 
	} 
 
	//定义NULL Renderer 
	CComPtr pRenderer = NULL; 
 
	// If there is a VP pin present on the video device, then put the  
	// renderer on CLSID_NullRenderer 
	CComPtr pVPPin; 
	hr = m_pBuilder->FindPin( 
						m_pCapVideo,  
						PINDIR_OUTPUT,  
						&PIN_CATEGORY_VIDEOPORT,  
						NULL,  
						FALSE,  
						0,  
						&pVPPin); 
	 
	// If there is a VP pin, put the renderer on NULL Renderer 
	if (S_OK == hr) 
	{    
		hr = pRenderer.CoCreateInstance(CLSID_NullRenderer);     
		if (S_OK != hr) 
		{ 
			return FALSE; 
		} 
		hr = m_pGraph->AddFilter(pRenderer, L"NULL renderer"); 
		if (FAILED (hr)) 
		{ 
			return FALSE; 
		} 
	} 
 
	hr = m_pBuilder->RenderStream( 
							&PIN_CATEGORY_CAPTURE, 
							&MEDIATYPE_Interleaved,  
							m_pCapVideo,  
							pGrabBase,  
							pRenderer); 
	if (FAILED (hr)) 
	{ 
		// try to render preview pin 
		hr = m_pBuilder->RenderStream(  
								&PIN_CATEGORY_CAPTURE,  
								&MEDIATYPE_Video, 
								m_pCapVideo,  
								pGrabBase,  
								pRenderer); 
 
		// try to render capture pin 
		if( FAILED( hr ) ) 
		{ 
			hr = m_pBuilder->RenderStream(  
									&PIN_CATEGORY_PREVIEW,  
									&MEDIATYPE_Video, 
									m_pCapVideo,  
									pGrabBase,  
									pRenderer); 
		} 
	} 
	 
	// ask for the connection media type so we know how big 
	// it is, so we can write out bitmaps 
	// 
	AM_MEDIA_TYPE mt; 
	hr = m_pGrabber->GetConnectedMediaType( &mt ); 
	if ( FAILED( hr) ) 
	{ 
		return FALSE; 
	} 
 
	VIDEOINFOHEADER * vih = (VIDEOINFOHEADER*) mt.pbFormat; 
	mCB.pOwner = this; 
	mCB.lWidth  = vih->bmiHeader.biWidth; 
	mCB.lHeight = vih->bmiHeader.biHeight; 
	FreeMediaType( mt ); 
 
	 
	//设置回调(CallBack),使Grabber能够通过BufferCB自动完成采集数据 
	// don't buffer the samples as they pass through 
	// 
	hr = m_pGrabber->SetBufferSamples( FALSE ); 
	 
	// only grab one at a time, stop stream after 
	// grabbing one sample 
	// 
	hr = m_pGrabber->SetOneShot( FALSE ); 
	 
	// set the callback, so we can grab the one sample 
	// 
	hr = m_pGrabber->SetCallback( &mCB, 1 );  //mCB为CSampleGrabber对象 
	if(!SetPreviewWindow()) 
	{ 
		return FALSE; 
	} 
    CComQIPtr< IMediaControl, &IID_IMediaControl > pControl = m_pGraph; 
    CComQIPtr< IVideoWindow, &IID_IVideoWindow > pWindow = m_pGraph; 
	hr = pControl->Run( ); 
	hr = pWindow->put_Visible(OATRUE); 
	if (FAILED(hr)) 
	{ 
		return FALSE; 
	} 
	m_bIsPreviewing = TRUE; 
	return TRUE; 
} 
 
void CCaiDX::StopPreview() 
{ 
    // Destroy capture graph 
    if( m_pGraph ) 
    { 
        // have to wait for the graphs to stop first 
        // 
        CComQIPtr< IMediaControl, &IID_IMediaControl > pControl = m_pGraph; 
        if( pControl )  
            pControl->Stop( ); 
 
        // make the window go away before we release graph 
        // or we'll leak memory/resources 
        //  
        CComQIPtr< IVideoWindow, &IID_IVideoWindow > pWindow = m_pGraph; 
        if( pWindow ) 
        { 
            pWindow->put_Visible( OAFALSE ); 
            pWindow->put_Owner( NULL ); 
        } 
 
#ifdef REGISTER_FILTERGRAPH 
        // Remove filter graph from the running object table    
        if (g_dwGraphRegister) 
            RemoveGraphFromRot(g_dwGraphRegister); 
#endif 
 
        m_pGraph.Release( ); 
        m_pGrabber.Release( ); 
		m_pGraph = NULL; 
		m_pGrabber = NULL; 
 
		if (m_pCapVideo) 
		{ 
			m_pCapVideo.Release(); 
		} 
		m_pCapVideo = NULL; 
    } 
 
	if (m_pBuilder) 
	{ 
		m_pBuilder.Release(); 
		m_pBuilder = NULL; 
	} 
	m_bIsPreviewing = FALSE; 
} 
 
BOOL CCaiDX::CreateVideoFilter(CString strDeviceName, IBaseFilter **pCap) 
{ 
	if (*pCap) 
	{ 
		SAFE_RELEASE((*pCap)); 
	} 
 
	//将friendlyName与所有的设备名称依次对比,如果相同,则创建Filter 
	ICreateDevEnum * enumHardware = NULL; 
	HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,NULL,CLSCTX_ALL 
		,IID_ICreateDevEnum,(void **)&enumHardware);  
	 
	if( FAILED(hr) ) 
	{ 
		return false;	 
	} 
	 
	IEnumMoniker * enumMoniker = NULL;   
	 
	hr = enumHardware->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&enumMoniker,0);  
	 
	if(enumMoniker) 
	{ 
		enumMoniker->Reset(); 
		 
		ULONG fetched = 0; 
		IMoniker * moniker = NULL; 
		char friendlyName[256]; 
		BOOL bFind = FALSE; 
		 
		while(!(*pCap) && SUCCEEDED(enumMoniker->Next(1,&moniker,&fetched)) && fetched) 
		{ 
			if(moniker) 
			{ 
				IPropertyBag * propertyBag = NULL; 
				VARIANT name; 
				friendlyName[0]=0; 
				hr=moniker->BindToStorage(0,0,IID_IPropertyBag,(void **)&propertyBag);                             
	 
				if(SUCCEEDED(hr)) 
				{ 
					name.vt=VT_BSTR; 
					hr = propertyBag->Read(L"FriendlyName",&name,NULL);	 
				} 
				else 
				{ 
					moniker->Release(); 
					break;                    
				} 
				 
				if(SUCCEEDED(hr)) 
				{             
					WideCharToMultiByte(CP_ACP,0,name.bstrVal,-1,friendlyName,256,NULL,NULL); 
					CString strTmp = friendlyName; 
					if ((!strDeviceName.IsEmpty()) && (strTmp!=strDeviceName)) 
					{ 
						moniker->Release(); 
						continue; 
					} 
					moniker->BindToObject(0,0,IID_IBaseFilter,(void **)pCap); 
					bFind = TRUE; 
				} 
				else 
				{ 
					moniker->Release(); 
					break; 
				} 
				 
				if(propertyBag)	 
				{ 
					propertyBag->Release();	 
					propertyBag=NULL;	 
				} 
				 
				moniker->Release(); 
				moniker = NULL; 
				if (bFind) 
				{ 
					break; 
				} 
			} 
		} 
		enumMoniker->Release(); 
	} 
	 
	enumHardware->Release(); 
	 
	return SUCCEEDED(hr); 
} 
 
BOOL CCaiDX::SetPreviewWindow() 
{ 
    CComQIPtr< IVideoWindow, &IID_IVideoWindow > pWindow = m_pGraph; 
    if( !pWindow ) 
    { 
        return FALSE; 
    } 
    // set up the preview window to be in our dialog 
    // instead of floating popup 
    // 
	HWND hwndPreview = GetSafeHwnd(); 
    CRect rc; 
    ::GetWindowRect( hwndPreview, &rc ); 
 
	if (rc.Height()==0 || mCB.lHeight==0) 
	{ 
		return FALSE; 
	} 
 
	LONG nWidth; 
	LONG nHeight; 
 
	DOUBLE dbRatioPic = (DOUBLE)mCB.lWidth / (DOUBLE)mCB.lHeight; 
	DOUBLE dbRatioWin = (DOUBLE)rc.Width() / (DOUBLE)rc.Height(); 
	if (dbRatioWin>dbRatioPic) 
	{ 
		nHeight = rc.Height(); 
		nWidth = (long)(nHeight * dbRatioPic); 
	} 
	else 
	{ 
		nWidth = rc.Width(); 
		nHeight = (long)(nWidth / dbRatioPic); 
	} 
 
//	nWidth = mCB.lWidth; 
//	nHeight = mCB.lHeight; 
 
	m_rtPreview.left = (rc.Width()-nWidth)/2 ; 
	m_rtPreview.top = (rc.Height() - nHeight)/2; 
	m_rtPreview.right = m_rtPreview.left + nWidth; 
	m_rtPreview.bottom = m_rtPreview.top + nHeight; 
	 
	HRESULT hr; 
    hr = pWindow->put_Owner( (OAHWND) hwndPreview ); 
    hr = pWindow->put_Left( m_rtPreview.left ); 
    hr = pWindow->put_Top( m_rtPreview.top ); 
    hr = pWindow->put_Width( nWidth ); 
    hr = pWindow->put_Height( nHeight ); 
    hr = pWindow->put_Visible( OATRUE ); 
    hr = pWindow->put_WindowStyle( WS_CHILD | WS_CLIPSIBLINGS ); 
   	return !FAILED(hr);  
} 
 
void CCaiDX::OnPaint()  
{ 
	CPaintDC dc(this); // device context for painting 
	 
	CRect rt; 
	GetClientRect(&rt); 
	dc.FillSolidRect(rt,RGB(192,192,192)); 
	 
	// Do not call CWnd::OnPaint() for painting messages 
} 
 
void CCaiDX::OnSize(UINT nType, int cx, int cy)  
{ 
	CWnd::OnSize(nType, cx, cy); 
	 
	SetPreviewWindow(); 
	 
} 
 
BOOL CCaiDX::GetImage(BYTE** pBuf,LONG& nWidth,LONG& nHeight,int& nBitCount) 
{ 
	if (!m_bIsPreviewing) 
	{ 
		return FALSE; 
	} 
	if (g_bOneShot==TRUE) 
	{ 
		return FALSE; 
	} 
	g_bOneShot = TRUE; 
	g_bCutDone = FALSE; 
	while (!g_bCutDone) 
	{ 
		//等待获取图片 
		MSG Message; 
		while (PeekMessage(&Message, NULL, 0, 0, TRUE)) 
		{ 
			TranslateMessage(&Message); 
			DispatchMessage(&Message); 
		} 
	} 
	*pBuf = cb.pBuffer; 
	nWidth = mCB.lWidth; 
	nHeight = mCB.lHeight; 
	nBitCount = 24; 
	g_bOneShot=FALSE; 
	return TRUE; 
}