[转]DirectShow:图片的抓取
本文转自:http://blog.chinaunix.net/u2/63021/showart_492136.html
原文如下:
在播放媒体文件的过程中,有一个很有用的功能,就是在当前播放的位置抓取图,实现这种图片抓取功能的方法很多,我们这里只介绍常用的两种。
第1种方法最简单,它使用1BasicVideo::GetCurrentImage接口方法,代码如下。
heel SnapshotBitmap(IBasicVideo*pBa8icVideo, const char*OutFile)
if (pBasicVldeo)
{
long bitmapSize=0;
//首先获得图像大小
if(SUCCEEDED(pEasicVidee->GetcurrentImage(&bitmapSize,0)))
{
bool pass=false;
//分配图像帧内存
unsigned char*buffer=new unsigned char[bitmapSize];
//获取图像帧数据
if(SUCCEEDED(pBasicVideo->GetCurrentImage(&bitmapSize,(long*)buffer)))
{
BITMAPFILEHEADER hdr;
LPBITMAPINFOHEADER ipbi;
ipbi=(LPBITMAPINFOHEADER)buffer;
int nColors=1<<ipbi->biBitCount;
if(nColors>256)
//always is”BM”
hdr.bfType =((WORD)(‘M’<<8)|’B’);
hdr.bfSize =bitmapSize+sizeof(hdr);
hdr.bfReservedl =0;
hdr.bfReserved2 =0;
hdr.bfOffBits =(DWORD)
(sizeof(BITMAPFILEHEADER)+lpbi->biSize+nColors*sizeof(RGBQUAD));
CFile bitmapFile(outFile,CFile::modeReadWrite |
CFile::modeCreate | CFile::typeBinary);
//写入位图文件头
bitmapFile.Write{&hdr,sizeof【BITMApFILEHEADER));
//写入图像帧数据(包括BITMAPINFOHEADER信息)
bitmapFile.Write(buffer,bitmapSize);
bitmapFile.Close();
pas8=true;
}
delete[]burfer;
return Pass;
}
return false ;
值得注意的是,IBasieVideo接口应该从Filter Graph Manager上获得,但真正实现在
Renderer Filter上。如果我们使用的是传统的Video Renderer,那么使用GetCurrentlmage 抓图将是不可靠的。因为如果Video Renderer使用了DirectDraw加速,这个函数调用会失 败;而且调用这个函数,Video Renderer必须处于暂停状态。但如果我们使用的是VMR, 则没有上述这些限制。
第2种方法比较复杂.它使用Sample Grabber Filter。它其实是一个Trans-In-Place
Filter,在SDK安装目录下的Samples\C++\DirectShow’Filters\Grabber提供了源代码。实际 上,Sample Grabber可以抓取任何类型的Sample。但在这里,我们只介绍使用它抓取视频 帧的方法。步骤如下:
(1)创建Sample Grabber,并将之加入到Filter Graph中。
//Create the Sample Grabber
IBaseFilter*pGrabberF=NULL;
hr=CoCreateInstanee(CLSID_SampleGrabber,NULL,CLSCTX_INPROC_SERVER,
IID IBaseFilter, (void**)&pGrabberF);
if(FAILED(hr))
{
//Return an error
}
hr=pGraph->AddFilter(pGrabberF,L"Sample Grabber");
if(FAILED(hr)
{
//Return an error
}
ISampleGrabber*pGrabber=NULL;
pGrabberF->QueryInterface(IID_ISampleGrabber,(void**)&pGrabber);
(2)给SampleGrabber设置Pin上连接用的媒体类型。
如果我们想抓取24位的RGB图片,如下设置媒体类型:
AM_MEDIA_TYPE mt;
ZeroMemorY(&mt,sizeof(AM_MEDIA_TYPE));
mt.malOrtype=MEDIATYPE Video;
mt.subtype=ME:DIASUBTYPE RGB24;
hr=pGrabber->SetMediaType(&mt);
也可以根据当前显示器的配置来设置Sample Grabber接受的RGB类型,代码如下:
//Find the current bit depth
HDC hdc=GetDC(NULL);
int iBitDepth=GetDeviceCaps(hdc, BITSPIXEL);
ReleaseDC(NULL,hdc);
//Set the media type
mt.maJortype=MEDIATYPE Video;
switch(iBitDepth)
{
Case 8:
mt.subtype=MEDIASUBTYPE RGB8 ;
break;
case 1 6:
mt.subtype=MEDIASUBTYPE_RGB555;
break;
case 24:
mt.subtype=MEDIASUBTYPE_RGB24;
break;
case 32:
mt.subtype=MEDIASUBTYPE_RGB32;
break;
default:
return E_FAIL;
}
hr=pGrabber->SetMediaType(&mt);
(3)完成FilterGraph的构建。
因为Sample Grabber上已经设置了一个媒体类型,则其他Filter必须以这种媒
才能与Sample Grabber相连。我们可以使用DimctShow的“智能连接”机制,来
个Fitler Graph的创建过程,代码如下。
IBaseFiiter*pSrc;
hr=pGraph->AddSourceFilter(wszFileName, L"Source", &pSrc};
if(FAILED(hr))
{
//Return an error code
}
hr=ConnectFiiters(pGraph,pSrc,pGrabberF);
其中,ConnectFilters是我们在5.3节中介绍的自定义函数。
如果我们只是想抓图(不需要对视频预览),则Sample Grabber后面可以连接一个Null Renderer Filter(它的CLSID为CLSID NullRenderer)。如果要Filter Graph中的数据流以最快的速度传送,则Filter Graph不要使用参考时钟(调用IMediaFitter::SetSyncSource,参数为NULL)。
(4)运行FilterGraph。
Sample Grabber可以有如下两种工作模式:
缓冲模式将输入的Sample进行缓存后,再往下传送。
回调模式当有输入的Sample时,调用应用程序设置进来的回调函数。
因为回调模式会影响整个Filter Graph的效率,并且容易引起死锁,所以我们推荐使用缓冲模式。另外,我们可以设置ISampleGrabber::SetOneShot,使得Sample Grabber获取一个Sample以后,就让FilterGraph停止,代码如下:
//Set one-shot mode and buffering.
hr=pGrabber->SetOneShot(TRUE);
hr=pGrabber->SetBufferSamples(TRUE);
pControl->Run();//Run the graph.
pEvent->WaitForCompletion(INFINITE,&evCode),//Wait till it’s done.
(5)获取抓到的Sample数据。
缓冲模式下,我们可以调用ISampleGrabber::GetCurrentBuffer来获取Sample数据,代码如下:
//Find the required buffer size
long cbBuffer=0;
hr=pGrabber->GetCurrentBuffer(&cbBuffer,NULL);
char*pBuffer=new char[cbBuffer];
if(!pBuffer)
//Out of memory.Return an error code
}
hr=pGrabber->GetCurrentBuffer(&cbBuffer,(long*)pBuffer);
我们也可以将获取的数据使用GDI函数显示出来,代码如下:
AM_MEDIA_TYPE mt;
hr=pGrabber->GetConnectedMediaType(&rot);
if(FAILED(hr))
{
//Return err05 code
}
//Examine the format block
VIDEOINFOHEADER*pVih;
if((mt.formattype==FORMAT_VideoInfo)&&
(mt.cbFormat>=sizeof(VIDEOINFOHEADER))&&
(mt.pbFormat!=NULL))
pVih={vIDEOINFOHEADER*)mt·pbFormat;
}
else
{
//Wrong format.Free the format block and return an error‘
FreeMedlaType(mt);
return VFW_E_INVALIDMEDIATYPE;
//youcan use the media type to access the BITMAPINFOHEAFRE information,
//For example,the following code draws the bitmap using GDI
SetDIBitsToDevice(
hdc,0,0,
pVih->bmiHeader.biWidth,
pVih->bmiHeader.biHeight,
O, O,
0,
pVih->bmiHeader.biHeight,
pBuffer,
(BITMAPINFO*)&pVih->bmiHeader,
DIB RGB COLORS
),
//Free the format block when you are done:
FreeMediaType(mt);
posted on 2009-01-16 09:44 freeliver54 阅读(4224) 评论(1) 编辑 收藏 举报