编写C++程序使DirectShow进行视频捕捉
视频捕捉Graph的构建 HRESULT InitCaptureGraphBuilder(IGraphBuilder **ppGraph,//Receives the pointer ICaptureGraphBuilder2 **ppBuilder) //Receives the pointer { if(!ppGraph || !ppBuilder) { return E_POINTER; } IGraphBuilder *pGraph = NULL; ICaptureGraphBuilder2 *pBuild = NULL; //Create the Capture Graph Builder HRESULT hr = CoCreateInstance(CLSID_CaptureGraphBuilder2,NULL,CLSCTX_INPROC_SERVER,IID_ICaptureGraphBuilder2,(void**)&pGraph); if(SECCEEDED(hr)) { //Create the Filter Graph Manager hr = CoCreateInstance(CLSID_FilterGraph,IID_IGraphBuilder,(void**)&pGraph); if(SECCEEDED(hr)) { //Initialize the Capture Graph Builder pBuild->SetFiltergraph(pGraph); //Return both interface pointers to the caller *ppBuild = pBuild; *ppGraph = pGraph; //The caller must release both interface return S_OK; } else { pBuild->Release(); } } return hr; //Failed } Direcshow中视频捕捉的Filter Pin的种类 捕捉Filter一般都有两个或多个输出pin,他们输出的媒体类型都一样,比如预览pin和捕捉pin,因此根据媒体类型就不能很好的区别这些pin。此时就要根据pin的功能来区别每个pin了,每个pin都有一个GUID,称为pin的种类。 视频捕捉Filter都提供了预览和捕捉的输出pin,预览pin用来将视频流在屏幕上显示,捕捉pin用来将视频流写入文件。 预览pin和输出pin有下面的区别: 预览pin的视频流之所以没有时间戳的原因在于filter图表管理器在视频流里加一个很小的latency,如果捕捉时间被认为就是render时间的话,视频renderFilter就认为视频流有一个小小的延迟,如果此时render filter试图连续播放的时候,就会丢桢。去掉时间戳就保证了视频桢来了就可以播放,不用等待,也不丢桢。
Video Port pin video port pin的种类GUID为PIN_CATEGORY_VIDEOPORT 一个捕捉filter至少有一个Capture pin,另外,它可能有一个预览pin 和一个video port pin,或者两者都没有,也许filter有很多的capture pin,和预览pin,每一个pin都代表一种媒体类型,因此一个filter可以有一个视频capture pin,视频预览pin,音频捕捉pin,音频预览pin。 Upstream WDM Filters
尽管这些都是一些独立的filter,但是他们可能代表的是同一个硬件设备,每个filter都控制设备的不同函数,这些filter通过pin连接起来,但是在pin中没有数据流动。因此,这些pin 的连接和媒体类型无关。他们使用一个GUID值来定义一个给定设备的minidriver,例如:TV tuner Filter 和video capture filter都支持同一种medium。 在实际应用中,如果你使用ICaptureGraphBuilder2来创建你的capture graphs,这些filters就会自动被添加到你的graph中。更多的详细资料,可以参考WDM Class Driver Filters。 选择一个视频捕捉设备(Select capture device) 如何选择一个视频捕捉设备,可以采用系统设备枚举,详细资料参见Using the System Device Enumerator 。enumerator可以根据filter的种类返回一个设备的monikers。Moniker是一个com对象,可以参见IMoniker的SDK。 对于捕捉设备,下面两种类是相关的。
下面的代码演示了如何枚举一个视频捕捉设备 ICreateDevEnum *pDevEnum = NULL; IEnumMoniker *pEnum = NULL; //Create the system device enumerator HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum,CLSCT_INPROC_SERVER,IID_ICreateDevEnum,reinterpret_cast<void**>(&pDevEnum)); if(SUCCEEDED(hr)) { //创建一个枚举器,枚举视频设备 hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEnum,0); } 1 FriendlyName 是设备的名字 下面的代码演示了如何显示遍历设备的名称 ,接上面的代码 HWND hList; //Handle to the list box IMoniker *pMoniker = NULL; while(pEnum->Next(1,&pMoniker,NULL) == S_OK) { IPropertyBag *pPropBag; hr = pMoniker->BindToStorage(0,IID_IPropertyBag,(void**)(&pPropBag)); if(FAILED(hr)) { pMoniker->Release(); continue; //Skip this one,maybe the next one will work } VARIANT varName; hr = pPropBag->Read(L"Description",&varName,0); if(FAILED(hr)) { hr = pPropBag->Read(L"FriendlyName",0); } if(SECCEEDED(hr)) { //Add it to the application's list box USES_CONVERSION; (long)SendMessage(hList,LB_ADDSTRING,(LPARAM)OLE2T(varName.bstrVal)); VariantClear(&varName); } pPropBag->Release(); pMoniker->Release(); } IBaseFilter *pCap = NULL; hr = pMoniker->BindToObject(0,IID_IBaseFilter,(void**)&pCap); if(SECCEEDED(hr)) { hr = m_pGraph->AddFilter(pCap,L"Capture Filter");
ICaptureGraphBuilder2 *pBuild; //Capture Graph Builder //Initialize pBuild(not shown) ... IBaseFilter *pCap; //Video capture filter hr = pBuild->RenderStream(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_Video,pCap,NULL); } 如何捕捉视频流并保存到文件(Capture video to File) 1 将视频流保存到AVI文件 AVI Mux filter接收从capture pin过来的视频流,然后将其打包成AVI流。音频流也可以连接到AVI Mux Filter上,这样mux filter就将视频流和视频流合成AVI流。File writer将AVI流写入到文件中。 IBaseFilter *pMux; hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi,//Specifies AVI for the target file L"C:Example.avi",//File name &pMux,//Receives a pointer to the mux NULL); //(Optional)Receives a pointer to the file sink hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE,//Pin category &MEDIATYPE_Video,//Media type pCap,//Capture filter NULL,//Intermediate filter(optional) pMux); //Mux or file sink filter //Release the mux filter pMux->Release(); 第5个参数就是使用的上面函数返回的pMux指针。 IConfigAviMux *pConfigMux = NULL; hr = pMux->QueryInterface(IID_IConfigAviMux,(void**)&pConfigMux); if(SUCCEEDED(hr)) { pConfigMux->SetMasterStream(1); pConfigMux->Release(); } SetMasterStream的参数指的是数据流的数目,这个是由调用RenderStream的次序决定的。例如,如果你调用RenderStream首先用于视频流,然后是音频,那么视频流就是0,音频流就是1。 IBaseFilter *pEncoder; //Add it to the filter graph pGraph->AddFilter(pEncoder,L"Encode"); //Render the stream hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE,pEncoder,pMux); pEncoder->Release(); 2 将视频流保存成wmv格式的文件 为了将视频流保存成并编码成windows media video (WMV)格式的文件,将capture pin连到WM ASF Writer filter。 构建graph图最简单的方法就是将在ICaptureGraphBuilder2::SetOutputFileName方法中指定MEDIASUBTYPE_Asf的filter。如下 IBaseFilter *pASFWriter = 0; hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Asf,//Create a windows media file L"C:VidCap.wmv",//File name &pASFWriter,//Receives a pointer to the filter NULL); //Receives an IFileSinkFilter interface pointer(optional) 参数MEDIASUBTYPE_Asf 告诉graph builder,要使用wm asf writer作为文件接收器,于是,pbuild 就创建这个filter,将其添加到graph图中,然后调用IFileSinkFilter::SetFileName来设置输出文件的名字。第三个参数用来返回一个ASF writer指针,第四个参数用来返回文件的指针。 在将任何pin连接到WM ASF Writer之前,一定要对WM ASF Writer进行一下设置,你可以同过WM ASF Writer的IConfigAsfWriter接口指针来进行设置。 IConfigAsfWriter *pConfig = 0; hr = pASFWriter->QueryInterface(IID_IConfigAsfWriter,(void**)&pConfig); if(SUCCEEDED(hr)) { //Configure the ASF Writer filter pConfig->Release(); }然后调用ICaptureGraphBuilder2::RenderStream将capture Filter 和 ASF writer连接起来: hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE,//Capture pin &MEDIATYPE_Video,//Video. Use MEDIATYPE_Audio for audio pCap,//Pointer to the capture filter 0,pASFWriter); //Pointer to the sink filter(ASF Filter) IBaseFilter *pMux = 0; IFileSinkFilter *pSink = 0; hr = pBuild->SetOutputFileName(&CLSID_MyCustomMuxFilter,//开发自己的Filter L"C:VidCap.avi",&pMux,&pSink); 4如何将视频流保存进多个文件 IBaseFilter *pMux = 0; IFileSinkFilter *pSink = 0; hr = pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi,L"C:YourFileName.avi",&pSink); if(SUCCEEDED(hr)) { hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE,pMux); if(SUCCEEDED(hr)) { pControl->Run(); pControl->Stop(); //Change the file name and run the graph again pSink->SetFileName(L"YourFileName02.avi",0); pControl->Run(); } pMux->Release(); pSink->Release(); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |