DShow中实现抓图的几种方法

1.加入Sample Grabber Filter

当我们加入Sample Grabber Filter的时候,我们可以直接调用其接口(interfaceISampleGrabber。该接口可以获取经过该Filter的单独的Media Samples。详情请参见DXSDK

1.1 派生出自己的Sample Grabber

ISampleGrabberCB中派生出自己的类,然后实现其虚函数,详情请参见SDK中的示例程序(DXSDK ROOT/Samples/C++/DirectShow/Editing/GrabBitmaps)

1.2 直接调用Sample Grabber Filter的接口

如果我们在播放的过程中动态的加入Filter的话,操作和效率都不乐观。所以我采用下面的方法:

该方法传递的是时间,不是在播放的时候动态加入Filter然后截图,而是另外打开源文件进行操作。

A)。申明以下接口:

#001 IGraphBuilder   *pGraph       = NULL;

//for graph builder

 

#002 IMediaControl   *pControl     = NULL;

//media control

 

#003 IMediaSeeking   *pSeeking     = NULL;

//media seeking

 

#004 IMediaEventEx   *pEvent       = NULL;

//media envent

 

#005 IBaseFilter      *pNullFilter =NULL;

//for holding the Sample grabber Filter

 

B)。初始化接口:

#001 JIF(CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC,

#002                  IID_IGraphBuilder, (void **)&pGraph));

#003

#004 JIF(CoCreateInstance(CLSID_NullRenderer, NULL, CLSCTX_INPROC,

#005                 IID_IBaseFilter, (void **)&pNullFilter));

#006 JIF(pGraph->QueryInterface(IID_IMediaControl,(void                                     **)&pControl));

#007 JIF(pGraph->QueryInterface(IID_IMediaSeeking, (void                                **)&pSeeking));

#008 JIF(pGraph->QueryInterface(IID_IMediaEvent, (void                                  **)&pEvent));

C)。创建Sample Grabber

#001

// Create the Sample Grabber.

 

#002 IBaseFilter *pGrabberF = NULL;

#003 JIF(CoCreateInstance(CLSID_SampleGrabber,NULL, CLSCTX_INPROC_SERVER,

#004                             IID_IBaseFilter, (void**)&pGrabberF));

#005

#006 JIF(pGraph->AddFilter(pGrabberF, L"Sample Grabber"));

#007 JIF(pGraph->AddFilter(pNullFilter, L"Null Render Filter"));

#008

#009 ISampleGrabber *pGrabber;

#010 JIF(pGrabberF->QueryInterface(IID_ISampleGrabber, (void**)&pGrabber));

设置Sample Grabber的媒体格式:调用SetMediaType,该函数接受一个AM_MEDIA_TYPE的结构,主要是设置该结构中的majortype,和subtype域。

D)。添加Source Filter

#001 IBaseFilter *pSrc;

#002 JIF(pGraph->AddSourceFilter(T2W(m_szFile), L"Source", &pSrc));

E)。连接Grabber NullRender两个Filter

#001 IPin  *pOutPin;

#002 hr = GetPin(pGrabberF, PINDIR_OUTPUT, &pOutPin);

#003

#004 IPin  *pInPin;

#005 hr = GetPin(pNullFilter, PINDIR_INPUT, &pInPin);

#006

#007 pGraph->Connect(pOutPin, pInPin);

F)。取得当前所连接媒体的类型

#001 AM_MEDIA_TYPE mt;

#002 hr = pGrabber->GetConnectedMediaType(&mt);

#003

// Examine the format block.

 

#004 VIDEOINFOHEADER *pVih;

#005 if ((mt.formattype == FORMAT_VideoInfo) &&

#006       (mt.cbFormat >= sizeof(VIDEOINFOHEADER)) &&

#007       (mt.pbFormat != NULL) )

#008 {

#009       pVih = (VIDEOINFOHEADER*)mt.pbFormat;

#010 }

#011

else

 

#012 {

#013      

// Wrong format. Free the format block and return an error.

 

#014       FreeMediaType(mt);

#015       return VFW_E_INVALIDMEDIATYPE;

#016 }

#017

#018

// Do buffer the samples as they pass through

 

#019

//

 

#020 hr = pGrabber->SetBufferSamples(TRUE);

#021

#022

// Only grab one at a time, stop stream after

 

#023

// grabbing one sample

 

#024

//

 

#025 hr = pGrabber->SetOneShot( TRUE );

G)。Seeking文件,使其到达要截图的时间帧

#001 pSeeking->SetPositions(pCurrentPos,

#002                     AM_SEEKING_AbsolutePositioning

#003                      NULL, AM_SEEKING_NoPositioning );

#004

#005 pControl->Run();

#006

#007 long EvCode = 0;

#008

#009 hr = pEvent->WaitForCompletion( INFINITE, &EvCode );

H)。取得当前的buffer数据

#001

// Find the required buffer size.

 

#002 long cbBuffer = 0;

#003 hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);

#004 LONGLONG currentPos;

#005 pSeeking->GetCurrentPosition(&currentPos);

#006 BYTE *pBuffer = new BYTE[cbBuffer];

#007 if (!pBuffer)

#008 {

#009  

// Out of memory. Return an error code.

 

#010   Msg("Out of Memory");

#011 }

#012 hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);

I)。写入文件

#001

// Create a file to hold the bitmap

 

#002 HANDLE hf = CreateFile(szFilename, GENERIC_WRITE, FILE_SHARE_READ,

#003                                NULL, CREATE_ALWAYS, NULL, NULL );

#004

#005 if( hf == INVALID_HANDLE_VALUE )

#006 {

#007      

// Failed to create file

 

#008       return 0;

#009 }

#010  

#011

// Write out the file header

 

#012

//

 

#013 BITMAPFILEHEADER bfh;

#014 memset( &bfh, 0, sizeof( bfh ) );

#015 bfh.bfType = 'MB';

#016 bfh.bfSize = sizeof( bfh ) + cbBuffer + sizeof( BITMAPINFOHEADER );

#017 bfh.bfOffBits =                                 sizeof(BITMAPINFOHEADER)+sizeof( BITMAPFILEHEADER );

#018  

#019 DWORD Written = 0;

#020 WriteFile( hf, &bfh, sizeof( bfh ), &Written, NULL );

#021  

#022

// Write the bitmap format

 

#023

//

 

#024 BITMAPINFOHEADER bih;

#025 memset( &bih, 0, sizeof( bih ) );

#026 bih.biSize = sizeof( bih );

#027 bih.biWidth = pVih->bmiHeader.biWidth;

#028 bih.biHeight = pVih->bmiHeader.biHeight;

#029 bih.biPlanes = pVih->bmiHeader.biPlanes;

#030 bih.biBitCount = pVih->bmiHeader.biBitCount;

#031

#032 Written = 0;

#033

#034 WriteFile( hf, &bih, sizeof( bih ), &Written, NULL );

#035

#036

// Write the bitmap bits

 

#037

//

 

#038 Written = 0;

#039 WriteFile( hf, pBuffer, cbBuffer, &Written, NULL );

#040 FreeMediaType(mt);

#041 CloseHandle(hf);

J)。释放资源

#001 pControl->Stop();  

#002 SAFE_RELEASE(pControl);

#003 SAFE_RELEASE(pSeeking);

#004 SAFE_RELEASE(pEvent);

#005 SAFE_RELEASE(pSrc);

#006 SAFE_RELEASE(pNullFilter);

#007 SAFE_RELEASE(pGrabber);

#008 SAFE_RELEASE(pGrabberF);

#009 SAFE_RELEASE(pGraph);

K)。其实我们可以不用NullRender,而是用IVideoWindow接口来实现。如果是那样的话,首先申明IVideoWindow *pVideo = NULL;pVideo加入到Filter Graph

#001 JIF(pGraph->QueryInterface(IID_IVideoWindow,(void**)&pVideo));

#002 hr = pGraph->Render(pOutPin);

#003 if (pVideo)

#004 {

#005         hr = pVideo->put_AutoShow(OAFALSE);

#006 }

通过IBasicVideo::GetCurrentImage接口

对于该接口,Video RendererVideo Mixing RendererVMR)有不同的实现。

A)。Video Renderer

如果该Renderer使用了DDraw加速的话,该调用会失败。在调用该接口的时候,必须首先暂停Renderer(可以通过IMediaControl::Pause()暂停,如果不能确信该操作是否成功,应该调用IMediaControl::GetState()判断状态)。

B)。Video Mixing Renderer

对于VMR,该方法都会成功(不管是否运用了DDraw加速,也不管是否是暂停状态),此时对于它所有的状态(running stopped or paused)都适用。

函数Grabber代码如下(调用该函数的时候应该先将媒体文件暂停,原因上面已经说了):

#001 bool Grabber(IBasicVideo mBasicVideo, TCHAR *szFilename)

#002 {

#003   if (mBasicVideo)

#004   {

#005       long bitmapSize = 0;

#006   if(SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize, 0)))

#007       {

#008          //if语句里面的操作时取得buffersize

 

#009          //当我们在布确定image buffer的大小的情况下,我们给

 

#010       //GetCurrentImage的第二个参数传递0或者NULL,取得buffer

 

#011          //大小供以后使用。

 

#012          bool pass = false;

#013          unsigned char * buffer = new unsigned char[bitmapSize];

#014 if(SUCCEEDED(mBasicVideo->GetCurrentImage(&bitmapSize,(long*)buffer)))

#015          {

#016              //此时已经用到刚才所取得的大小(分配空间)

 

#017              BITMAPFILEHEADER  hdr;    //Bitmap的头信息

 

#018              LPBITMAPINFOHEADER   lpbi; // Bitmap的文件信息(包括数据)

 

#019

#020              lpbi = (LPBITMAPINFOHEADER)buffer;

#021

#022              int nColors = 1 << lpbi->biBitCount;

#023              if (nColors > 256)

#024                 nColors = 0;

#025

#026              hdr.bfType    = ((WORD) ('M' << 8) | 'B');   

//always is "BM"

 

#027              hdr.bfSize    = bitmapSize + sizeof( hdr );

#028              hdr.bfReserved1   = 0;

#029              hdr.bfReserved2   = 0;

#030              hdr.bfOffBits     = (DWORD) (sizeof(BITMAPFILEHEADER) + lpbi->biSize

#031              CFile bitmapFile(outFile, CFile::modeReadWrite | CFile::modeCreate | CFile::typeBinary);

#032              bitmapFile.Write(&hdr, sizeof(BITMAPFILEHEADER));

#033              bitmapFile.Write(buffer, bitmapSize);

#034              bitmapFile.Close();

#035              pass = true;

#036          }

#037          delete [] buffer; //数据用过之后记得要释放空间

 

#038          return true;

#039       }

#040   }

#041  

#042   return false;

#043 }

IMediaDet接口

IMediaDet接口可以取得媒体文件的信息。SDK里面的示例代码(没有写入文件):

#001 long size;

#002 //取得图像帧的大小,给GetBitmapBits的第三个参数传递

0 or NULL

 

 

#003 hr = pDet->GetBitmapBits(0, &size, 0, width, height);

#004 if(SUCCEEDED(hr))

#005 {

#006   char *pBuffer = new char[size];

#007   if(!pBuffer)

#008   {

#009       return E_OUTOFMEMORY;

#010   }

#011  

#012  

try

 

#013   {

#014       hr = pDet->GetbitmapsBits(0, 0, pBuffer, width, height);

#015   }

#016   catch(...)

#017   {

#018       delete [] pBuffer;

#019       throw;

#020   }

#021  

#022   if(SUCCEEDED(hr))

#023   {

#024       BITMAPINFOHEADER *bmih = (BITMAPINFOHEADER*)pBuffer;

#025       HDC    hdcDest = GetDC(0);

#026      

#027      

//Find the address of the start of the image data

 

#028       void *pData = pBuffer + sizeof(BITMAPINFOHEADER);

#029      

#030      

//Note: In general a BITMAPINFOHEADER can include extra color

 

#031      

//information at the end, so calculating the offset to the image

 

#032      

//data i snot generally correct. However, the IMediaDet interface

 

#033      

//always returns an RGB-24 image with no extra color information

 

#034         

#035       BITMAPINFO bmi;

#036       ZeroMemory(&bmi, sizeof(BITMAPINFO));

#037       CopyMemory(&(bmi.bmiHeader), bmih, sizeof(BITMAPINFOHEADER));

#038       HBITMAP hBitmap = CreateDIBitmap(hdcDect, bmih, CBM_INIT,

#039                     pData, &bmi, DIB_RGB_COLORS);

#040   }

#041  

#042   delete [] pBuffer;

#043 }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值