|
DShow中实现抓图的几种方法
发表日期:2008-3-8
|
1.加入Sample Grabber Filter
当我们加入Sample Grabber Filter的时候,我们可以直接调用其接口(interface)ISampleGrabber。该接口可以获取经过该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(¤tPos); #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 Renderer和Video Mixing Renderer(VMR)有不同的实现。
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语句里面的操作时取得buffer的size。 #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 }
该方法并没有写入bitmap,具体的写入过程可以参加上面的几种方法。
|
|
上一篇:用C++品尝Vista美味:界面的毛玻璃效果
人气:3104
下一篇:在C语言中以编程的方式获取函数名
人气:2719 |
浏览全部C/C++的内容
Dreamweaver插件下载 网页广告代码 2009年新年快乐
|
|