[转]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  阅读(4207)  评论(1编辑  收藏  举报

导航