DirectShow:图片的抓取 zz在播放媒体文件的过程中,有一个很有用的功能,就是在当前播放的位置抓取图,实现这种图片抓取功能的方法很多,我们这里只介绍常用的两种。
第1种方法最简单,它使用1BasicVideo::GetCurrentImage接口方法,代码如下。
boolSnapshotBitmap(IBasicVideo*pBa8icVideo,ê? constchar*OutFile) if (pBasicVldeo){long bitmapSize=0;ê?//首先获得图像大小if(SUCCEEDED(pEasicVidee->GetcurrentImage(&bitmapSize,ê?0))){bool pass=false;ê?//分¤?配?图ª?像?帧?内¨²存ä?unsignedchar*buffer=newunsignedchar[bitmapSize];//获?取¨?图ª?像?帧?数ºy据Yif(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));CFilebitmapFile(outFile,ê?CFile:êo:êomodeReadWrite |CFile:êo:êomodeCreate | CFile:êo:êotypeBinary);ê?//写¡ä入¨?位?图ª?文?件t头ª¡¤bitmapFile.ê?Write{&hdr,ê?sizeof【?BITMApFILEHEADER));//写¡ä入¨?图ª?像?帧?数ºy据Y(包㨹括¤¡§BITMAPINFOHEADER信?息¡é)bitmapFile.ê?Write(buffer,ê?bitmapSize);ê?bitmapFile.ê?Close();ê?pas8=true;}delete[]burfer;ê?return Pass;}returnfalse ;值得注意的是,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 GrabberIBaseFilter*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 depthHDC hdc=GetDC(NULL);int iBitDepth=GetDeviceCaps(hdc, BITSPIXEL);ReleaseDC(NULL,hdc);//Set the media typemt.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 sizelong 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 blockVIDEOINFOHEADER*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 GDISetDIBitsToDevice(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);从摄像头流中捕捉一张图片zzAtomictry(天影))关键字:视频捕获拍照 ISampleGrabber函数说明HRESULT StartDisplay(HWND hwnd);HRESULT BuilderGraph()HRESULT SnapStill()BOOL SetFormat(ICaptureGraphBuilder2* pBuilder, IBaseFilter* pCap, long lWidth, long lHeight, unsigned short iColorBit, __int64 iRate )HRESULT StartDisplay(HWND hwnd){HRESULT hr = S_OK;hr = BuilderGraph();if(hr==S_FALSE) return S_FALSE;//Create Display WindowspSendWindow->put_Owner((OAHWND)hwnd);pSendWindow->put_WindowStyle(WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);pSendWindow->SetWindowPosition(0,0,250,250); pSendWindow->put_Visible(OATRUE);hr = pGrabber->SetOneShot(FALSE);hr = pGrabber->SetBufferSamples(TRUE);//Begin displayhr=pSendControl->Run();Sleep(500);SnapStill();return S_OK;}HRESULT BuilderGraph(){HRESULT hr=S_OK;pSendGraph=NULL;pCaputerFilter=NULL;pCaputerBuilder=NULL;pSendWindow=NULL;pSendControl=NULL;pSendEvent=NULL;pGrabberSample = NULL;//1.Builder Filter Graphhr=CoCreateInstance((REFCLSID)CLSID_FilterGraph,NULL, CLSCTX_INPROC_SERVER,(REFIID)IID_IGraphBuilder,(void**)&pSendGraph);if(FAILED(hr)) return S_FALSE;//2.Builder Caputer Filterhr=CoCreateInstance(CLSID_CaptureGraphBuilder2,NULL, CLSCTX_INPROC,IID_ICaptureGraphBuilder2,(void**)&pCaputerBuilder);if(FAILED(hr)) return S_FALSE;//3.Builder SampleGrabber Filterhr = CoCreateInstance(CLSID_SampleGrabber, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)&pGrabberSample);if(FAILED(hr)) return S_FALSE;hr=pCaputerBuilder->SetFiltergraph(pSendGraph);if(FAILED(hr))return S_FALSE;//Find Caputer Filterhr=FindCaputerDevice();if(FAILED(hr)) {SAFE_RELEASE(pCaputerBuilder);SAFE_RELEASE(pSendGraph);return S_FALSE;}hr =pSendGraph->AddFilter(pNetSend,NULL);hr =pSendGraph->AddFilter(pCaputerFilter,L"Caputer Filter");hr =pSendGraph->AddFilter(pCompressor,L"Compressor Filter");hr = pSendGraph->AddFilter(pGrabberSample, L"Sample Grabber");hr=pSendGraph->QueryInterface(IID_IMediaControl,(void**)&pSendControl );if(FAILED(hr)) return S_FALSE;hr=pSendGraph->QueryInterface(IID_IVideoWindow,(void**)&pSendWindow); if(FAILED(hr)) return S_FALSE;hr=pSendGraph->QueryInterface(IID_IMediaEvent,(void**)&pSendEvent);if(FAILED(hr)) return S_FALSE;hr=pGrabberSample->QueryInterface(IID_ISampleGrabber,(void**)&pGrabber);if(FAILED(hr)) return S_FALSE;// 修改分辨率SetFormat(pCaputerBuilder, pCaputerFilter, 640, 480, 24, 30);//Display local videohr=pCaputerBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,NULL,pCaputerF ilter,pGrabberSample,NULL);if(FAILED(hr)) return S_FALSE;return S_OK;}HRESULT SnapStill(){HRESULT hr;long cbBuffer = 0;hr = pGrabber->GetCurrentBuffer(&cbBuffer, NULL);if(FAILED(hr)) return E_FAIL;char *pBuffer = new char[cbBuffer];if (!pBuffer) {// Deal Out of memory. Return an error code.}hr = pGrabber->GetCurrentBuffer(&cbBuffer, (long*)pBuffer);if(FAILED(hr)) return E_FAIL;//生成BitmapAM_MEDIA_TYPE mt;hr = pGrabber->GetConnectedMediaType(&mt);if (FAILED(hr)) return E_FAIL;VIDEOINFOHEADER *pVideoHeader = (VIDEOINFOHEADER*)mt.pbFormat;if(pVideoHeader==NULL) return E_FAIL;BITMAPINFO BitmapInfo;ZeroMemory(&BitmapInfo, sizeof(BitmapInfo));CopyMemory(&BitmapInfo.bmiHeader, &(pVideoHeader->bmiHeader),sizeof(BITMAPINFOHEADER));HBITMAP hBitmap;hBitmap = ::CreateDIBitmap(::GetDC(NULL), &(pVideoHeader->bmiHeader), CBM_INIT, pBuffer, &BitmapInfo, DIB_RGB_COLORS);if(hBitmap==NULL) return E_FAIL;CStringstrSaveFileName="";CFileDialogfiledlg(FALSE,_T("bmp"),_T(""),OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,_ T("BMP(*.bmp)"));if(filedlg.DoModal()==IDOK){strSaveFileName=filedlg.GetPathName();SaveBitmapToFile(hBitmap,strSaveFileName.GetBuffer(0));}return hr;BOOL SetFormat(ICaptureGraphBuilder2* pBuilder, IBaseFilter* pCap, long lWidth, long lHeight, unsigned short iColorBit, __int64 iRate ){VIDEOINFOHEADER* phead;IAMStreamConfig* iconfig;HRESULT hr;hr = pBuilder ->FindInterface( &PIN_CATEGORY_CAPTURE,&MEDIATYPE_Interleaved,pCap, IID_IAMStreamConfig, (void **)&iconfig );if ( hr != NOERROR ){hr = pBuilder ->FindInterface( &PIN_CATEGORY_CAPTURE,&MEDIATYPE_Video, pCap,IID_IAMStreamConfig, (void **)&iconfig);}if ( FAILED( hr ) )return FALSE;AM_MEDIA_TYPE* pmt;if ( iconfig ->GetFormat( &pmt ) != S_OK )return FALSE;if ( pmt ->formattype == FORMAT_VideoInfo){phead = ( VIDEOINFOHEADER* )pmt ->pbFormat;phead ->bmiHeader.biBitCount = iColorBit;phead ->bmiHeader.biWidth = lWidth;phead ->bmiHeader.biHeight = lHeight;phead ->bmiHeader.biSizeImage = lWidth * lHeight * iColorBit / 8; phead ->AvgTimePerFrame = iRate;if ( ( hr = iconfig ->SetFormat( pmt ) ) != S_OK )return FALSE;}iconfig -> Release();iconfig = NULL;FreeMediaType( *pmt );return TRUE;}从摄像头流中捕捉一张图片zz视频问题,从摄像头流中捕捉一张图片。