http://blog.csdn.net/Macapplelove/archive/2010/10/23/5961173.aspx
DirectShow的截图方案简述
用directshow可以有多种实现的方案,下面介绍三种:
第一种采用IMediaDet接口的GetBitmaps方法,速度很快,但是Video的类型有限制,对于WMF类型、RMVB等都无法正常截图,下面是例子代码:
BOOL Grabber1()
{
if(!m_pMediaDet) // IMediaDet接口,它除了用于截图还可以用于判断媒体类型
return FALSE;
BOOL bResult = FALSE;
HRESULT thr;
long width = 0, height = 0;
AM_MEDIA_TYPE mt;
m_pMediaDet->put_Filename(m_strMediaFile.AllocSysString());
thr = m_pMediaDet->get_StreamMediaType(&mt);
if (1/*mt.formattype != FORMAT_VideoInfo*/) //在此有类型限制
{
VIDEOINFOHEADER *pVih = (VIDEOINFOHEADER*)(mt.pbFormat);
width = pVih->bmiHeader.biWidth;
height = pVih->bmiHeader.biHeight;
if (height < 0)
height *= -1;
}
else
{
return VFW_E_INVALIDMEDIATYPE;
}
FreeMediaType(mt);
long size;
char * pBuffer;
int gt; // grabber time
gt = m_curpos * m_Duration / m_filelength;
thr = m_pMediaDet->GetBitmapBits(gt,&size,NULL,width,height);
if(SUCCEEDED(thr))
{
pBuffer = new char[size];
if(!pBuffer)
return E_OUTOFMEMORY;
}
try
{
thr = m_pMediaDet->GetBitmapBits(gt,0,pBuffer,width,height);
}
catch(...)
{
delete [] pBuffer;
throw;
}
if(SUCCEEDED(thr))
{
BITMAPINFOHEADER * bmih = (BITMAPINFOHEADER*)pBuffer;
HDC hdcDest = ::GetDC(0);
void * pData = pBuffer + sizeof(BITMAPINFOHEADER);
// Note: In general a BITMAPINFOHEADER can include extra color
// information at the end, so calculating the offset to the image
// data is not generally correct. However, the IMediaDet interface
// always returns an RGB-24 image with no extra color information
BITMAPINFO bmi;
ZeroMemory(&bmi, sizeof(BITMAPINFO));
CopyMemory(&(bmi.bmiHeader),bmih,sizeof(BITMAPINFOHEADER));
HBITMAP hB = CreateDIBitmap(hdcDest,
bmih,
CBM_INIT,
pData,
&bmi,
DIB_RGB_COLORS);
// write to bitmap
BITMAPFILEHEADER hdr; //Bitmap的头信息
LPBITMAPINFOHEADER lpbi; // Bitmap的文件信息(包括数据)
lpbi = (LPBITMAPINFOHEADER)pBuffer;
int nColors = 1 << lpbi->biBitCount;
if (nColors > 256)
nColors = 0;
hdr.bfType = ((WORD) ('M' << 8) | 'B'); //always is "BM"
hdr.bfSize = size + sizeof( hdr );
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
hdr.bfOffBits = (DWORD) (sizeof(BITMAPFILEHEADER) + lpbi->biSize);
// 保存图
CString bmpPath(m_grbDirectory);
bmpPath += "\\temp.bmp";
CFile bitmapFile(bmpPath, CFile::modeNoTruncate|
CFile::modeReadWrite | CFile::modeCreate | CFile::typeBinary);
bitmapFile.Write(&hdr, sizeof(BITMAPFILEHEADER));
bitmapFile.Write(pBuffer, size);
bitmapFile.Close();
bResult = TRUE;
}
bResult = FALSE;
delete [] pBuffer;
return bResult;
}
第二种方案采用的是IBasicVideo中的GetCurrentImage方法,这种方案几乎可以处理所有的视频类型,但是每一次截图都有一个短暂的暂停,也是就停顿,下面是例子:
BOOL Grabber2()
{
if(m_pGraph == NULL)
return FALSE;
else
m_pGraph->QueryInterface(IID_IBasicVideo,(void **)&m_pBasicVideo);
if(m_pBasicVideo == NULL)
return FALSE;
long bitmapSize = 0;
BOOL bResult = FALSE;
if (SUCCEEDED(m_pBasicVideo->GetCurrentImage(&bitmapSize, 0)))
{
unsigned char * buffer = new unsigned char[bitmapSize];
if (SUCCEEDED(m_pBasicVideo->GetCurrentImage(&bitmapSize, (long *)buffer)))
{
BITMAPFILEHEADER hdr;
LPBITMAPINFOHEADER lpbi;
lpbi = (LPBITMAPINFOHEADER)buffer;
int nColors = 0;
if( lpbi->biBitCount <=8)
nColors = 1 << lpbi->biBitCount;
hdr.bfType = ((WORD) ('M' << 8) | 'B'); //always is "BM"
hdr.bfSize = bitmapSize + sizeof( hdr );
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
hdr.bfOffBits = (DWORD) (sizeof(BITMAPFILEHEADER) + lpbi->biSize +
nColors * sizeof(RGBQUAD));
// write to file
CString bmpPath(m_grbDirectory);
bmpPath += "\\";// to save something
bmpPath += RandString(16);
bmpPath += ".bmp";
CFile bitmapFile(bmpPath, CFile::modeNoTruncate|
CFile::modeReadWrite | CFile::modeCreate | CFile::typeBinary);
bitmapFile.Write(&hdr, sizeof(BITMAPFILEHEADER));
bitmapFile.Write(buffer,bitmapSize);
bitmapFile.Close();
bResult = TRUE;
// MessageBox(bmpPath,"成功保存提示");
CRect rcTmp;
GetWindowRect(&rcTmp);
m_windowmove = -m_windowmove;
rcTmp.OffsetRect(CPoint(0,m_windowmove));
MoveWindow(rcTmp);
}
delete [] buffer;
bResult = FALSE;
}
if(m_pBasicVideo != NULL)
{
m_pBasicVideo->Release();
m_pBasicVideo = NULL;
}
return bResult;
}
为了寻求既快而且能处理所有类型的方案,找到了IVMRWindowlessControl9接口。它是VRM9中才有的,所以必须引入d3d9.h和vrm9.h头文件。
但是在之前用的IMediaDet接口中包含了Qedit.h,而这个文件和上述两个头文件是不相容的。所以如果同在一个.h中#include他们会出现类似这样的报错:
syntax error : identifier 'IDirect3DSurface9'
所以,我尝试着把qedit.h放置到.cpp中去,错误虽然少了,但变成了这些:
error C2061: syntax error : identifier 'LPDIRECT3D'
error C2061: syntax error : identifier 'LPDIRECT3DDEVICE'
error C2061: syntax error : identifier 'LPDIRECT3DDEVICE2'
为了解决问题,可以把qedit.h去掉,但是那样就没法用IMediaDet接口了。所以,必须建立一个小点的头文件,把包含ImediaDet那部分copy进去,然后使用它就可以了,我把用到的置于了mediadet.h中,主要包含了SampleGrabberCB和IMediaDet以及CLSID的定义。这样问题就解决了。
例子代码如下:
if(m_pVMRWC && m_fStyle == VIDEO_STYLE)
{
BYTE* lpCurrImage = NULL;
// Read the current video frame into a byte buffer. The information
// will be returned in a packed Windows DIB and will be allocated
// by the VMR.
if(m_pVMRWC->GetCurrentImage(&lpCurrImage) == S_OK)
{
BITMAPFILEHEADER hdr;
DWORD dwSize, dwWritten;
LPBITMAPINFOHEADER pdib = (LPBITMAPINFOHEADER) lpCurrImage;
// Create a new file to store the bitmap data
CString bmpPath(m_grbDirectory);
bmpPath += "\\";// to save something
bmpPath += RandString(16);
bmpPath += ".bmp";
HANDLE hFile = CreateFile(TEXT(bmpPath), GENERIC_WRITE, FILE_SHARE_READ, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
// Initialize the bitmap header
dwSize = DibSize(pdib);
hdr.bfType = BFT_BITMAP;
hdr.bfSize = dwSize + sizeof(BITMAPFILEHEADER);
hdr.bfReserved1 = 0;
hdr.bfReserved2 = 0;
hdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + pdib->biSize +
DibPaletteSize(pdib);
// Write the bitmap header and bitmap bits to the file
WriteFile(hFile, (LPCVOID) &hdr, sizeof(BITMAPFILEHEADER), &dwWritten, 0);
WriteFile(hFile, (LPCVOID) pdib, dwSize, &dwWritten, 0);
// Close the file
CloseHandle(hFile);
// The app must free the image data returned from GetCurrentImage()
CoTaskMemFree(lpCurrImage);
// Give user feedback that the write has completed
// MessageBox(bmpPath);
return TRUE;
}
}
return FALSE;
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Macapplelove/archive/2010/10/23/5961173.aspx