<form id="dlljd"></form>
        <address id="dlljd"><address id="dlljd"><listing id="dlljd"></listing></address></address>

        <em id="dlljd"><form id="dlljd"></form></em>

          <address id="dlljd"></address>
            <noframes id="dlljd">

              聯系我們 - 廣告服務 - 聯系電話:
              您的當前位置: > 關注 > > 正文

              【熱聞】AMCAP程序詳解:AMCeGraphBuilder

              來源:CSDN 時間:2023-03-09 08:50:49

              AMCAP程序詳解

              DirectShow提供了用應用程序從適當的硬件中捕捉和預覽音/視頻的能力。數據源包括:VCR,Camera,TVTuner,Microphone,或其他的數據源。一個應用程序可以立刻顯示捕捉的數據(預覽),或是保存到一個文件中。


              (資料圖)

              在這個例子中,ICaptureGraphBuilder接口是處理捕捉工作的主要接口。你可以在你自己的捕捉程序中使用同樣的方法和接口。在這里主要討論ICaptureGraphBuilder如何執行音/視頻的捕捉。我們假設你已經熟悉了DirectShow的filtergraph的體系和一般的capturefiltergraph的結構(可以參考DirectShow基礎指南)。

              ICaptureGraphBuilder接口提供了一個filtergraph builder對象,讓你的應用程序在建立capture filtergraph時,省去處理很多單調乏味的工作,集中精力于捕捉中。它提供的方法滿足了基本的捕捉和預覽功能的要求。

              FindInterface方法,在filtergraph中查找一個與捕捉有關的詳細的接口。使得你可以訪問一個詳細接口的功能,而不需要你去列舉filtergraph中的pins和filters。 RenderStream方法,連接源過濾器和渲染過濾器,選擇添加一些中間的過濾器。ControlStream方法,獨立地精確地控制graph的開始和結束幀。

              既然是硬件捕捉,當然要和硬件打交道,接著介紹設備列舉和捕捉接口。

              通過ICreateDevEnum::CreateClassEnumerator方法列舉捕捉系統中的設備。之后,實例化一個DirectShow的filter去使用這個設備。用ICaptureGraphBuilder::FindInterface去獲得與捕捉相關的接口指針IAMDroppedFrames,IAMVideoCompression, IAMStreamConfig,以及 IAMVfwCaptureDialogs。因為設備列舉和捕捉接口比較長,放在這會打亂結構,所有專門寫了一篇(參考設備列舉和捕捉接口)。

              NOTE: 這個示例是DirectShow自帶的例子。你可以在DirectShowSDK的目錄Sample\DS\Caputre看這個例子代碼(AMCap.cpp)。這里只是他的一些片斷代碼。可以說是他的中文模塊的說明。AMCap例子中,把所有的接口指針和一些成員變量保存在一個全局結構gcap中了。

              當不在需要保存在gcap中的接口指針是,一定要釋放這些接口指針,一般是在程序的析構函數中,或是在別的同等功能函數中。如下:

              if (gcap.pBuilder) gcap.pBuilder->Release(); gcap.pBuilder = NULL; if (gcap.pSink) gcap.pSink->Release(); gcap.pSink = NULL; if (gcap.pConfigAviMux) gcap.pConfigAviMux->Release(); gcap.pConfigAviMux = NULL; if (gcap.pRender) gcap.pRender->Release(); gcap.pRender = NULL; if (gcap.pVW) gcap.pVW->Release(); gcap.pVW = NULL; if (gcap.pME) gcap.pME->Release(); gcap.pME = NULL; if (gcap.pFg) gcap.pFg->Release(); gcap.pFg = NULL;

              設置文件名

              使用普通的OpenFileDialog獲得捕捉文件的信息。通過調用AllocCaptureFile函數為捕捉文件分配空間。這一點是重要的,因為這是個巨大的空間。這樣可以提高捕捉操作的速度。ICaptureGraphBuilder::AllocCapFile執行實際的文件分配,IFileSinkFilter::SetFileName指示file writerfilter使用用戶選擇的文件名保存數據。ICaptureGraphBuilder::SetOutputFileName把filewriter filter加入filter graph(后面會介紹,它是ICaptureGraphBuilderd自代的)。

              SetCaptureFile 和 AllocCaptureFile 函數如下:

              BOOL SetCaptureFile(HWND hWnd) {if(OpenFileDialog(hWnd, gcap.szCaptureFile, _MAX_PATH))  {OFSTRUCTos;//We have a capture file nameif(OpenFile(gcap.szCaptureFile, &os, OF_EXIST) ==HFILE_ERROR)  {//Bring up dialog, and set new file size  BOOLf = AllocCaptureFile(hWnd);  if(!f)  returnFALSE;  }  }  else{returnFALSE;  }SetAppCaption();// need a new app caption//Tell the file writer to use the new file name  if(gcap.pSink) {WCHARwach[_MAX_PATH];  MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED, gcap.szCaptureFile, -1, wach, _MAX_PATH);  gcap.pSink->SetFileName(wach,NULL);  }  return TRUE; }// Preallocate the capturefile // BOOL AllocCaptureFile(HWND hWnd) {// We"ll get into an infinite loop in the dlgproc setting a value if (gcap.szCaptureFile[0] == 0)  returnFALSE;if (DoDialog(hWnd, IDD_AllocCapFileSpace, AllocCapFileProc, 0)){//Ensure repaint after dismissing dialog before  //possibly lengthy operation  UpdateWindow(ghwndApp);//User has hit OK. Alloc requested capture file space  BOOLf = MakeBuilder();  if(!f)  returnFALSE;  WCHARwach[_MAX_PATH];  MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED, gcap.szCaptureFile, -1, wach, _MAX_PATH);  if(gcap.pBuilder->AllocCapFile(wach,gcap.wCapFileSize* 1024L * 1024L) != NOERROR) {MessageBoxA(ghwndApp, "Error","Failed to pre-allocate capture filespace",MB_OK | MB_ICONEXCLAMATION);  return FALSE;  }  returnTRUE; } else {returnFALSE; } }

              建立GraphBuilder對象

              AMCap的MakeBuilder函數建立了一個capturegraph builer對象,通過調用 CoCreateInstance獲得了ICaptureGraphBuilder接口指針。AMCap把他存儲到gcap結構的pBuilder中。

              // Make a graph builderobject we can use for capture graph building // BOOL MakeBuilder() {//We have one already  if(gcap.pBuilder)  returnTRUE;HRESULThr = CoCreateInstance((REFCLSID)CLSID_CaptureGraphBuilder,  NULL,CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder,(void**)&gcap.pBuilder);  return(hr == NOERROR) ? TRUE : FALSE; }

              建立Graph的渲染部分,并告訴它寫文件(用先前決定的文件)

              這包括一個multiplexerfilter 和 file writer。DirectShow 提供了一個AVIMUX(multiplexer)filter。在這里ICaptureGraphBuilder::SetOutputFileName是一個關鍵的方法。它把multiplexer 和 file writer添加到filtergraph中,連接他們,并設置文件的名字。第一個參數MEDIASUBTYPE_Avi,指出capture graph builder對象將插入一個AVI multiplexer filter,因此,filewriter將以AVI文件格式記錄捕捉的數據。第二個參數(wach)是文件名。最后的兩個參數指出multiplexer filter(gcap.pRender) 和file writer filter(gcap.pSink),這兩個是通過SetOutputFileName函數初始化的。AMCap存儲這些指針到全局結構gcap中。capture graph builder 對象建立了一個filtergraph對象(IGraphBuilder),把這兩個filter加入到filter graph中去。他告訴filewriter使用指定的文件保存數據。下面的例子演示了如何調用SetOutputFileName。

              // // We need a rendering section that will write the capture file outin AVI // file format //WCHAR wach[_MAX_PATH]; MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gcap.szCaptureFile, -1,wach, _MAX_PATH); GUID guid = MEDIASUBTYPE_Avi; hr =gcap.pBuilder->SetOutputFileName(&guid,wach, &gcap.pRender,&gcap.pSink); if (hr != NOERROR) {ErrMsg("Error%x: Cannot set output file", hr);  gotoSetupCaptureFail; }

              獲得當前的FilterGraph

              因為在調用SetOutputFileName中,capturegraph builder 對象建立了一個filter graph,所以你必須把需要的filter加入同一個filter graph中。通過ICaptureGraphBuilder::GetFiltergraph獲得新建立的filtergraph。返回的指針是參數gcap.pFg。

              // // The graph builder created a filter graph to do that. Find outwhat it is, // and put the video capture filter in the graph too. //hr =gcap.pBuilder->GetFiltergraph(&gcap.pFg); if (hr != NOERROR) {ErrMsg("Error%x: Cannot get filtergraph", hr);  gotoSetupCaptureFail; }

              添加音/視頻過濾器到當前的Filter Graph

              hr =gcap.pFg->AddFilter(gcap.pVCap, NULL); if (hr != NOERROR) {ErrMsg("Error%x: Cannot add vidcap to filtergraph", hr);  gotoSetupPreviewFail; }hr =gcap.pFg->AddFilter(gcap.pACap, NULL); if (hr != NOERROR) {ErrMsg("Error%x: Cannot add audcap to filtergraph", hr);  gotoSetupCaptureFail; }

              渲染視頻捕捉過濾器的CapturePin和音頻捕捉的Capture Pin

              ICaptureGraphBuilder::RenderStream連接源過濾器的pin到渲染過濾器。pin的類別是可選的,capture pin (PIN_CATEGORY_CAPTURE) 或preview pin (PIN_CATEGORY_PREVIEW)。下面的例子演示了連接video capture filter(gcap.pVCap) 的capture pin到渲染gcap.pRender中。

              // // Render the video capture and preview pins - we may not havepreview, so // don"t worry if it doesn"t work //hr =gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE,NULL, gcap.pVCap, NULL, gcap.pRender); // Error checking// // Render the audio capture pin? //if (gcap.fCapAudio) {hr =gcap.pBuilder->RenderStream(&PIN_CATEGORY_CAPTURE,NULL, gcap.pACap, NULL, gcap.pRender); // Error checking

              渲染Video CaptureFilter的 Preview Pin

              再次調用ICaptureGraphBuilder::RenderStream,從capture filter的previewpin到video renderer。代碼如下:

              hr =gcap.pBuilder->RenderStream(&PIN_CATEGORY_PREVIEW,NULL, gcap.pVCap, NULL, NULL);

              獲得訪問Video PreviewWindow的接口指針

              缺省的,videopreview window是一個獨立的窗口。如果你想改變默認的行為,先調用ICaptureGraphBuilder::FindInterface獲得IVideoWindow接口。第二個參數通過gcap.pVCap指定,描述video capturefilter,第三個參數是想得到的接口(IVideoWindow),最后的是返回的接口。當你得到IVideoWindow接口后,你可以調用IVideoWindow的方法象put_Owner,put_WindowStyle, or SetWindowPosition 去獲得video previewwindow的handle,設置窗口屬性,或把他放到想要的位置。

              // This will go through apossible decoder, find the video renderer it"s // connected to, and get the IVideoWindow interface onit hr =gcap.pBuilder->FindInterface(&PIN_CATEGORY_PREVIEW,gcap.pVCap, IID_IVideoWindow, (void**)&gcap.pVW); if (hr != NOERROR) {ErrMsg("Thisgraph cannot preview"); } else {RECTrc;  gcap.pVW->put_Owner((long)ghwndApp);// We own the window now  gcap.pVW->put_WindowStyle(WS_CHILD);// you are now a child  //give the preview window all our space but where the status baris  GetClientRect(ghwndApp,&rc);  cyBorder= GetSystemMetrics(SM_CYBORDER);  cy= statusGetHeight() + cyBorder;  rc.bottom-= cy;  gcap.pVW->SetWindowPosition(0,0, rc.right, rc.bottom); // be thisbig  gcap.pVW->put_Visible(OATRUE); }

              現在你已經建立完整的capture filtergraph了,你可以預覽音頻,視頻,或捕捉數據。

              控制 Capture FilterGraph

              因為通過ICaptureGraphBuilder接口構造的capturefilter graph 只是一個簡單的專門用途的filter graph,所以,控制它就象控制其他類型的filtergraph一樣。你可以使用IMediaControl interface的 Run, Pause,和Stop方法,你也可以使用CBaseFilter::Pause的方法。另外ICaptureGraphBuilder 提供了ControlStream 方法去來控制 capture filter graph的流媒體的開始和結束時間。ControlStream調用IAMStreamControl::StartAt 和IAMStreamControl::StopAt控制filter graph的捕捉和預覽的開始和結束的位置。

              注意:不是所有的capture filter都可以,因為不是每一個capturefilter都支持IAMStreamControl。

              ICaptureGraphBuilder::ControlStream方法的第一個參數(pCategory)是一個輸出pin類的GUID。這個變量通常是PIN_CATEGORY_CAPTURE或 PIN_CATEGORY_PREVIEW。指定為NULL則控制所有的capture filter。第二個參數在(pFilter)指出那個filter控制。NULL說明為控制所有的filtergraph。如果只是預覽(防止捕捉)的話,可以調用ICaptureGraphBuilder::ControlStream,參數用capturepin類型,MAX_TIME作為開始時間(第三個參數,pstart)。再次調用該方法,參數用previewpin類型,NULL作為開始時間則立即開始預覽。第四參數指出結束的時間(pstop),含義和第三個參數一樣(NULL意味著立刻)。MAX_TIME在DirectShow中定義為最大的參考時間。在這里意味著忽略或取消指定的操作。最后的參數,wStartCookie和wStopCookie分別是開始和結束的cookies(不知道該怎么翻譯,因為我也不理解這個參數的含義)。下面的代碼設置立刻開始預覽,但是忽略捕捉。

              // Let the preview sectionrun, but not the capture section // (There might not be a capture section) REFERENCE_TIME start = MAX_TIME, stop = MAX_TIME; // show us a preview first? but don"t capturequite yet... hr =gcap.pBuilder->ControlStream(&PIN_CATEGORY_PREVIEW,NULL, gcap.fWantPreview ? NULL : &start, gcap.fWantPreview ? &stop : NULL, 0, 0); if (SUCCEEDED(hr))  hr=gcap.pBuilder->ControlStream(&PIN_CATEGORY_CAPTURE,NULL, &start, NULL, 0, 0);

              同樣的,如果你只想要捕捉而不要預覽,設置捕捉的開始時間為NULL,設置捕捉的結束時間為MAX_TIME。設置預覽的開始時間為MAX_TIME,NULL為結束時間。下面的例子告訴filtergraph開始預覽(第三個參數:開始時間為NULL)。結束時間指定為MAX_TIME意味著忽視停止時間(永遠放下去)。 gcap.pBuilder->ControlStream(&PIN_CATEGORY_PREVIEW,NULL, NULL, MAX_TIME, 0, 0);

              調用IMediaControl::Run 運行 graph

              // Run the graph IMediaControl *pMC = NULL; HRESULT hr =gcap.pFg->QueryInterface(IID_IMediaControl, (void**)&pMC); if (SUCCEEDED(hr)) {hr= pMC->Run();  if(FAILED(hr)) {//Stop parts that ran  pMC->Stop();  }  pMC->Release(); } if (FAILED(hr)) {ErrMsg("Error%x: Cannot run preview graph", hr);  returnFALSE;

              如果graph已經運行,通過調用ICaptureGraphBuilder::ControlStream立刻開始捕捉。例如下面的代碼,控制整個的filtergraph(第二個參數為NULL),立刻開始(第三個參數是NULL),并且永不停止(第四個參數是MAX_TIME)。

              // NOW! gcap.pBuilder->ControlStream(&PIN_CATEGORY_CAPTURE,NULL, MAX_TIME, &stop, 0, 0);

              停止預覽或捕捉操作,調用IMediaControl::Stop,就同你調用IMediaControl::Run一樣。

              // Stop the graph IMediaControl *pMC = NULL; HRESULT hr =gcap.pFg->QueryInterface(IID_IMediaControl, (void**)&pMC); if (SUCCEEDED(hr)) {hr= pMC->Stop();  pMC->Release(); }

              獲得捕捉的信息

              通過IAMDroppedFrames接口獲得。測試丟失幀的數量(IAMDroppedFrames::GetNumDropped),捕捉的數量(IAMDroppedFrames::GetNumNotDropped)。IAMDroppedFrames::GetAverageFrameSize方法提供了捕捉幀的平均尺寸(單位:byte)。使用這些信息可以知道總的捕捉字節和每秒的幀數(速率)。

              保存文件

              最初分配的捕捉文件只是臨時的保存數據,所有你可以盡可能快的捕捉。當你想把捕捉的數據保存到硬盤中時,調用ICaptureGraphBuilder::CopyCaptureFile。這個方法從先前得到的捕捉文件輸出數據到你選擇的另一個文件中。這個新的儲存文件的大小是和實際捕捉的數據匹配的,而不是和先前的文件大小匹配。ICaptureGraphBuilder::CopyCaptureFile方法的第一個參數是復制源,第二個是目標文件。第三個參數設為TRUE指出用戶允許用ESC鍵中斷復制操作。最后參數是可選的。允許你提供一個進程指示器。如果想要的化,通過執行IAMCopyCaptureFileProgress 接口。下面示例了如何調用CopyCaptureFile。

              hr =pBuilder->CopyCaptureFile(wachSrcFile,wachDstFile,TRUE,NULL);

              通過普通的OpenFile Dialog得到新的文件名。用 MultiByteToWideChar函數把文件名轉成寬字符(widestring),使用ICaptureGraphBuilder::CopyCaptureFile把捕捉的數據保存到指定的文件中。

              BOOL SaveCaptureFile(HWND hWnd) {HRESULT hr; char achDstFile[_MAX_PATH]; WCHAR wachDstFile[_MAX_PATH]; WCHAR wachSrcFile[_MAX_PATH];if (gcap.pBuilder == NULL)  returnFALSE;if (OpenFileDialog(hWnd, achDstFile, _MAX_PATH)){// We have a capture filename MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, gcap.szCaptureFile, -1,wachSrcFile, _MAX_PATH); MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, achDstFile, -1,wachDstFile, _MAX_PATH); statusUpdateStatus(ghwndStatus, "Saving capture file - pleasewait...");// We need our own graphbuilder because the main one might not exist ICaptureGraphBuilder *pBuilder; hr = CoCreateInstance((REFCLSID)CLSID_CaptureGraphBuilder, NULL, CLSCTX_INPROC, (REFIID)IID_ICaptureGraphBuilder, (void **)&pBuilder); if (hr == NOERROR) {// Allow the user to press ESC to abort...don"t ask for progress hr = pBuilder->CopyCaptureFile(wachSrcFile,wachDstFile,TRUE,NULL); pBuilder->Release(); } if (hr == S_OK) statusUpdateStatus(ghwndStatus, "Capture file saved"); else if (hr == S_FALSE) statusUpdateStatus(ghwndStatus, "Capture file save aborted"); else statusUpdateStatus(ghwndStatus, "Capture file save ERROR"); return (hr == NOERROR ? TRUE : FALSE);} else {return TRUE; // They canceled orsomething } }

              關于捕捉媒體文件和獲得捕捉信息的詳細內容,可以參考AMCap例子的Amcap.cpp 和Status.cpp 。

              責任編輯:

              標簽:

              相關推薦:

              精彩放送:

              新聞聚焦
              Top 中文字幕在线观看亚洲日韩