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;
}