Direct Show: Grabbing Media Samples

来源:互联网 发布:北大青鸟编程学费 编辑:程序博客网 时间:2024/05/18 03:52
 Grabbing   Media   Samples  
  This   article   describes   how   to   retrieve   media   samples,   using   the   Sample   Grabber   filter.  
   
  The   Microsoft®   DirectShow®   architecture   shields   applications   from   having   to   manipulate   media   data   directly.   If   you   need   to   modify   media   data,   you   should   write   a   transform   filter   or   a   Microsoft®   DirectX®   Media   Object   (DMO)   for   this   purpose.   On   the   other   hand,   if   you   need   to   use   the   data   without   modification—for   example,   to   display   a   frame   from   a   video   file—you   can   use   the   Sample   Grabber   filter.  
   
  This   article   contains   the   following   sections:  
   
  Adding   the   Sample   Grabber   to   the   Filter   Graph    
  Buffers   and   One-Shot   Mode    
  Using   a   Callback   Method    
  Adding   the   Sample   Grabber   to   the   Filter   Graph  
  The   Sample   Grabber   is   a   transform   filter   that   supports   the   ISampleGrabber   interface.   It   passes   all   samples   downstream   unchanged,   so   it   can   be   inserted   into   a   filter   graph   without   altering   the   data   stream.   You   can   then   use   it   to   grab   samples   as   they   pass   through   the   filter.  
   
  By   default,   the   Sample   Grabber   has   no   preferred   media   types.   Before   you   insert   it   into   a   filter   graph,   set   the   media   type   for   the   input   pin   by   calling   the   ISampleGrabber::SetMediaType   method.   Setting   a   media   type   ensures   that   the   filter   graph   manager   inserts   the   Sample   Grabber   at   the   correct   point   in   the   graph.  
   
  The   SetMediaType   method   takes   a   pointer   to   an   AM_MEDIA_TYPE   structure   that   describes   the   media   type.   Depending   on   the   situation,   you   can   set   the   subtype   or   format   type   to   GUID_NULL,   which   indicates   "unspecified."   The   following   example   adds   the   Sample   Grabber   to   the   filter   graph   and   sets   the   media   type   to   24-bit   uncompressed   RGB   video:  
   
  #include   <dshow.h>  
  #include   <qedit.h>  
   
  IBaseFilter           *pF   =   NULL;  
  ISampleGrabber     *pGrab   =   NULL;     //   Be   sure   to   release   these   later.  
  AM_MEDIA_TYPE       mt;  
   
  CoCreateInstance(CLSID_SampleGrabber,   NULL,   CLSCTX_INPROC_SERVER,    
          IID_IBaseFilter,   (LPVOID   *)&pF);  
  pF->QueryInterface(IID_ISampleGrabber,   (void   **)&pGrab);  
   
  pGraph->AddFilter(pF,   L"Grabber");  
   
  ZeroMemory(&mt,   sizeof(AM_MEDIA_TYPE));  
  mt.majortype   =   MEDIATYPE_Video;  
  mt.subtype   =   MEDIASUBTYPE_RGB24;  
  mt.formattype   =   FORMAT_VideoInfo;    
  hr   =   pGrab->SetMediaType(&mt);  
   
  Now   you   can   build   the   rest   of   the   graph   and   connect   the   Sample   Grabber   filter.   There   are   various   ways   to   do   this,   including:  
   
  Call   IGraphBuilder::RenderFile.   The   filter   graph   manager   will   connect   the   Sample   Grabber   according   to   its   media   type.    
  Add   filters   one   at   a   time   and   connect   them   with   the   IGraphBuilder::Connect   method.    
  Use   the   ICaptureGraphBuilder2   interface.    
  For   example,   the   following   code   builds   a   playback   graph   for   the   file   Example.avi:  
   
  pGraph->RenderFile(L"C://Example.avi",   NULL);  
   
  Because   the   Sample   Grabber's   media   type   was   set   to   uncompressed   video,   the   filter   graph   manager   puts   it   between   the   video   decompressor   and   the   video   renderer,   where   it   will   receive   uncompressed   video   samples.  
   
  If   you   want   to   grab   samples   without   rendering   them,   connect   the   Sample   Grabber's   output   pin   to   the   Null   Renderer   filter,   which   discards   incoming   samples.  
   
  Buffers   and   One-Shot   Mode  
  The   Sample   Grabber   can   copy   the   samples   that   it   receives   to   an   internal   buffer.   To   enable   buffering,   call   the   ISampleGrabber::SetBufferSamples   method   with   a   value   of   TRUE.   To   retrieve   a   copy   of   the   sample,   call   the   ISampleGrabber::GetCurrentBuffer   method.  
   
  Each   sample   overwrites   the   previous   one   in   the   buffer.   To   grab   a   sample   from   a   particular   point   in   the   stream,   switch   the   Sample   Grabber   to   "one-shot"   mode.   In   one-shot   mode,   the   Sample   Grabber   stops   the   graph   as   soon   as   it   receives   one   sample.   To   enable   one-shot   mode,   call   the   ISampleGrabber::SetOneShot   method   with   a   value   of   TRUE.  
   
  Seek   to   the   desired   time,   run   the   graph,   and   wait   for   the   graph   to   stop.   The   following   code   shows   how   to   do   this:  
   
  //   Query   the   filter   graph   manager   for   these   interfaces   (not   shown).  
  IMediaControl       *pMediaControl   =   NULL;  
  IMediaSeeking       *pSeek   =   NULL;  
  IMediaEvent           *pEvent   =   NULL;  
   
  //   Set   up   one-shot   mode.  
  pGrab->SetBufferSamples(TRUE);  
  pGrab->SetOneShot(TRUE);  
   
  //   Seek   three   seconds.  
  REFERENCE_TIME   rtStart   =   3   *   10000000;  
  REFERENCE_TIME   rtStop   =   rtStart;    
   
  hr   =   pSeek->SetPositions(&rtStart,   AM_SEEKING_AbsolutePositioning,    
                                                    &rtStop,   AM_SEEKING_AbsolutePositioning);  
   
  //   Run   the   graph   and   wait   for   completion.  
  long   evCode;  
  hr   =   pMediaControl->Run();  
  hr   =   pEvent->WaitForCompletion(INFINITE,   &evCode);  
   
  When   the   WaitForCompletion   method   returns,   the   Sample   Grabber's   buffer   holds   a   copy   of   the   video   frame.   The   buffer   contains   only   the   media   data   portion   of   the   sample;   it   does   not   include   the   format   header.   To   obtain   the   format   header,   call   ISampleGrabber::GetConnectedMediaType.   This   method   returns   an   AM_MEDIA_TYPE   structure   whose   pbFormat   member   points   to   the   format   header.   Be   sure   to   release   the   buffer   memory   when   you're   done   using   it.  
   
  To   illustrate,   the   following   code   creates   a   device-independent   bitmap   (DIB)   from   a   video   stream,   using   the   format   header   and   the   sample   buffer:  
   
  AM_MEDIA_TYPE   MediaType;  
  pGrab->GetConnectedMediaType(&MediaType);    
   
  //   Get   a   pointer   to   the   video   header.  
  VIDEOINFOHEADER   *pVideoHeader   =   (VIDEOINFOHEADER*)MediaType.pbFormat;  
   
  //   The   video   header   contains   the   bitmap   information.    
  //   Copy   it   into   a   BITMAPINFO   structure.  
  BITMAPINFO   BitmapInfo;  
  ZeroMemory(&BitmapInfo,   sizeof(BitmapInfo));  
  CopyMemory(&BitmapInfo.bmiHeader,   &(pVideoHeader->bmiHeader),   sizeof(BITMAPINFOHEADER));  
   
  //   Create   a   DIB   from   the   bitmap   header,   and   get   a   pointer   to   the   buffer.  
  void   *buffer   =   NULL;  
  HBITMAP   hBitmap   =    
          CreateDIBSection(0,   &BitmapInfo,   DIB_RGB_COLORS,   &buffer,   NULL,   0);  
   
  //   Copy   the   image   into   the   buffer.  
  hr   =   pGrab->GetCurrentBuffer(NULL,   (long   *)buffer);  
   
  The   application   can   call   BitBlt   to   display   the   image.   For   more   information,   see   the   Platform   SDK.   Although   this   example   assumes   a   video   stream,   the   Sample   Grabber   can   retrieve   audio   samples   or   any   other   media   sample.   To   blit   the   image,   use   code   such   as   the   following:  
   
  long   Width   =   pVideoHeader->bmiHeader.biWidth;  
  long   Height   =   pVideoHeader->bmiHeader.biHeight;  
   
  HDC   hdcDest   =   GetDC(hwnd);  
  HDC   hdcSrc   =   CreateCompatibleDC(NULL);  
  SelectObject(hdcSrc,   hBitmap);  
  BitBlt(hdcDest,   0,   0,   Width,   Height,   hdcSrc,   0,   0,   SRCCOPY);  
   
  Using   a   Callback   Method  
  Instead   of   working   in   one-shot   mode,   the   Sample   Grabber   can   invoke   a   callback   method   for   each   sample   that   it   receives.   The   application   must   implement   the   ISampleGrabberCB   interface.   It   contains   the   following   methods:  
   
  SampleCB:   Receives   a   pointer   to   the   sample's   IMediaSample   interface.    
  BufferCB:   Receives   a   buffer   that   contains   the   media   data.    
  Typically,   you   would   implement   only   one   of   these   methods.   To   set   the   callback,   call   the   ISampleGrabber::SetCallback   method.   It   takes   an   index   value   that   specifies   which   callback   method   to   use.   If   you   specify   the   BufferCB   method,   also   call   ISampleGrabber::SetBufferSamples   to   enable   buffering,   as   described   in   the   preceding   section.    
   
  The   following   example   implements   the   callback   interface   in   a   class   named   CGrabCB.   The   class   derives   from   the   CUnknown   class,   one   of   the   base   classes   included   with   the   DirectShow   SDK.   Using   CUnknown   is   not   required,   but   it   makes   the   code   somewhat   shorter.   For   more   information   on   using   CUnknown,   see   How   to   Implement   IUnknown.  
   
  In   this   example,   the   SampleCB   method   prints   the   start   time   of   each   sample   in   the   console   window.   The   BufferCB   method   is   not   implemented   and   returns   E_NOTIMPL.  
   
  #include   <streams.h>  
  //   Link   to   Strmbase.lib  
   
  class   CGrabCB:   public   CUnknown,   public   ISampleGrabberCB  
  {  
  public:  
          DECLARE_IUNKNOWN;  
   
          STDMETHODIMP   NonDelegatingQueryInterface(REFIID   riid,   void   **ppv)  
          {  
                  if(   riid   ==   IID_ISampleGrabberCB   )  
                  {  
                          return   GetInterface((ISampleGrabberCB*)this,   ppv);  
                  }  
                  return   CUnknown::NonDelegatingQueryInterface(riid,   ppv);  
          }  
   
          //   ISampleGrabberCB   methods  
          STDMETHODIMP   SampleCB(double   SampleTime,   IMediaSample   *pSample)  
          {  
                  printf("Sample   time:   %f/n",   SampleTime);  
                  return   S_OK;  
          }  
   
          STDMETHODIMP   BufferCB(double   SampleTime,   BYTE   *pBuffer,   long   BufferLen)  
          {  
                  return   E_NOTIMPL;  
          }  
           
          //   Constructor  
          CGrabCB(   )   :   CUnknown("SGCB",   NULL)  
          {   }  
  };  
   
  To   use   the   callback,   create   a   new   instance   of   the   CGrabCB   class   and   pass   it   to   the   ISampleGrabber::SetCallback   method.   Then   run   the   filter   graph:  
   
  pGrab->SetOneShot(FALSE);  
  pGrab->SetBufferSamples(FALSE);  
   
  CGrabCB   *cb   =   new   CGrabCB();  
  pGrab->SetCallback(cb,   0);  
原创粉丝点击