[转]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接口应该从FilterGraphManager上获得,但真正实现在
RendererFilter上。如果我们使用的是传统的VideoRenderer,那么使用GetCurrentlmage 抓图将是不可靠的。因为如果VideoRenderer使用了DirectDraw加速,这个函数调用会失 败;而且调用这个函数,VideoRenderer必须处于暂停状态。但如果我们使用的是VMR, 则没有上述这些限制。


第2种方法比较复杂.它使用SampleGrabberFilter。它其实是一个Trans-In-Place
Filter,在SDK安装目录下的Samples\C++\DirectShow’Filters\Grabber提供了源代码。实际 上,SampleGrabber可以抓取任何类型的Sample。但在这里,我们只介绍使用它抓取视频 帧的方法。步骤如下:

(1)创建SampleGrabber,并将之加入到FilterGraph中。
//CreatetheSampleGrabber
IBaseFilter*pGrabberF=NULL;
hr=CoCreateInstanee(CLSID_SampleGrabber,NULL,CLSCTX_INPROC_SERVER,
IIDIBaseFilter,(void**)&pGrabberF);
if(FAILED(hr))
{
//Returnanerror
}
hr=pGraph->AddFilter(pGrabberF,L"SampleGrabber");
if(FAILED(hr)
{
//Returnanerror
}
ISampleGrabber*pGrabber=NULL;
pGrabberF->QueryInterface(IID_ISampleGrabber,(void**)&pGrabber);

(2)给SampleGrabber设置Pin上连接用的媒体类型。
如果我们想抓取24位的RGB图片,如下设置媒体类型:
AM_MEDIA_TYPEmt;
ZeroMemorY(&mt,sizeof(AM_MEDIA_TYPE));
mt.malOrtype=MEDIATYPEVideo;
mt.subtype=ME:DIASUBTYPERGB24;
hr=pGrabber->SetMediaType(&mt);
也可以根据当前显示器的配置来设置SampleGrabber接受的RGB类型,代码如下:
//Findthecurrentbitdepth
HDChdc=GetDC(NULL);
intiBitDepth=GetDeviceCaps(hdc,BITSPIXEL);
ReleaseDC(NULL,hdc);
//Setthemediatype
mt.maJortype=MEDIATYPEVideo;
switch(iBitDepth)
{
Case8:
mt.subtype=MEDIASUBTYPERGB8;
break;
case16:
mt.subtype=MEDIASUBTYPE_RGB555;
break;
case24:
mt.subtype=MEDIASUBTYPE_RGB24;
break;
case32:
mt.subtype=MEDIASUBTYPE_RGB32;
break;
default:
returnE_FAIL;
}
hr=pGrabber->SetMediaType(&mt);

(3)完成FilterGraph的构建。
因为SampleGrabber上已经设置了一个媒体类型,则其他Filter必须以这种媒
才能与SampleGrabber相连。我们可以使用DimctShow的“智能连接”机制,来
个FitlerGraph的创建过程,代码如下。
IBaseFiiter*pSrc;
hr=pGraph->AddSourceFilter(wszFileName,L"Source",&pSrc};
if(FAILED(hr))
{
//Returnanerrorcode
}
hr=ConnectFiiters(pGraph,pSrc,pGrabberF);
其中,ConnectFilters是我们在5.3节中介绍的自定义函数。
如果我们只是想抓图(不需要对视频预览),则SampleGrabber后面可以连接一个Null RendererFilter(它的CLSID为CLSIDNullRenderer)。如果要FilterGraph中的数据流以最快的速度传送,则FilterGraph不要使用参考时钟(调用IMediaFitter::SetSyncSource,参数为NULL)。

(4)运行FilterGraph。
SampleGrabber可以有如下两种工作模式:
缓冲模式将输入的Sample进行缓存后,再往下传送。
回调模式当有输入的Sample时,调用应用程序设置进来的回调函数。
因为回调模式会影响整个FilterGraph的效率,并且容易引起死锁,所以我们推荐使用缓冲模式。另外,我们可以设置ISampleGrabber::SetOneShot,使得SampleGrabber获取一个Sample以后,就让FilterGraph停止,代码如下:
//Setone-shotmodeandbuffering.
hr=pGrabber->SetOneShot(TRUE);
hr=pGrabber->SetBufferSamples(TRUE);
pControl->Run();//Runthegraph.
pEvent->WaitForCompletion(INFINITE,&evCode),//Waittillit’sdone.

(5)获取抓到的Sample数据
缓冲模式下,我们可以调用ISampleGrabber::GetCurrentBuffer来获取Sample数据,代码如下:
//Findtherequiredbuffersize
longcbBuffer=0;
hr=pGrabber->GetCurrentBuffer(&cbBuffer,NULL);
char*pBuffer=newchar[cbBuffer];
if(!pBuffer)
//Outofmemory.Returnanerrorcode
}
hr=pGrabber->GetCurrentBuffer(&cbBuffer,(long*)pBuffer);
我们也可以将获取的数据使用GDI函数显示出来,代码如下:
AM_MEDIA_TYPEmt;
hr=pGrabber->GetConnectedMediaType(&rot);
if(FAILED(hr))
{
//Returnerr05code
}
//Examinetheformatblock
VIDEOINFOHEADER*pVih;
if((mt.formattype==FORMAT_VideoInfo)&&
(mt.cbFormat>=sizeof(VIDEOINFOHEADER))&&
(mt.pbFormat!=NULL))
pVih={vIDEOINFOHEADER*)mt·pbFormat;
}
else
{
//Wrongformat.Freetheformatblockandreturnanerror‘
FreeMedlaType(mt);
returnVFW_E_INVALIDMEDIATYPE;
//youcanusethemediatypetoaccesstheBITMAPINFOHEAFREinformation,
//Forexample,thefollowingcodedrawsthebitmapusingGDI
SetDIBitsToDevice(
hdc,0,0,
pVih->bmiHeader.biWidth,
pVih->bmiHeader.biHeight,
O,O,
0,
pVih->bmiHeader.biHeight,
pBuffer,
(BITMAPINFO*)&pVih->bmiHeader,
DIBRGBCOLORS
),
//Freetheformatblockwhenyouaredone:
FreeMediaType(mt);

你可能感兴趣的:(show)