Direcshow中視頻捕捉和參數設置報告 分類: DirectX 2014-09-29 17:40 519人閱讀 評論(0) 收藏

Direcshow中視頻捕捉和參數設置報告php

1.      關於視頻捕捉(About Video Capture in Dshow)

1視頻捕捉Graph的構建windows

一個可以捕捉音頻或者視頻的graph圖都稱之爲捕捉graph圖。捕捉graph圖比通常的文件回放graph圖要複雜許多,dshow提供了一個Capture Graph Builder COM組件使得捕捉graph圖的生成更加簡單。Capture Graph Builder提供了一個ICaptureGraphBuilder2接口,這個接口提供了一些方法用來構建和控制捕捉graph。ide

首先建立一個Capture Graph Builder對象和一個graph manger對象,而後用filter graph manager 做參數,調用ICaptureGraphBuilder2::SetFiltergraph來初始化Capture Graph Builder。看下面的代碼把函數

HRESULTInitCaptureGraphBuilder(ui

IGraphBuilder**ppGraph, // Receives the pointer.this

ICaptureGraphBuilder2**ppBuild // Receives the pointer.編碼

)spa

{指針

if (!ppGraph|| !ppBuild)code

{

returnE_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 (SUCCEEDED(hr))

  {

  // Create the Filter Graph Manager.

  hr = CoCreateInstance(CLSID_FilterGraph, 0, CLSCTX_INPROC_SERVER,

  IID_IGraphBuilder, (void**)&pGraph);

  if (SUCCEEDED(hr))

  {

  // Initialize the Capture Graph Builder.

  pBuild->SetFiltergraph(pGraph);

 // Return both interface pointers to thecaller.

  *ppBuild = pBuild;

  *ppGraph = pGraph; // The caller must releaseboth interfaces.

  return S_OK;

  }

  Else

  {

pBuild->Release();

}

}

 return hr; // Failed

}

2.      Direcshow中視頻捕捉的Filter

Pin的種類

捕捉Filter通常都有兩個或多個輸出pin,他們輸出的媒體類型都同樣,好比預覽pin和捕捉pin,所以根據媒體類型就不能很好的區別這些pin。此時就要根據pin的功能來區別每一個pin了,每一個pin都有一個GUID,稱爲pin的種類。

若是想仔細的瞭解pin的種類,請看後面的相關內容Workingwith Pin Categories。對於大多數的應用來講,ICaptureGraphBuilder2提供了一些函數能夠自動肯定pin的種類。
預覽pin和捕捉pin

視頻捕捉Filter都提供了預覽和捕捉的輸出pin,預覽pin用來將視頻流在屏幕上顯示,捕捉pin用來將視頻流寫入文件。

預覽pin和輸出pin有下面的區別:

1 爲了保證捕捉pin對視頻楨流量,預覽pin必要的時候能夠中止。

2 通過捕捉pin的視頻楨都有時間戳,可是預覽pin的視頻流沒有時間戳。

預覽pin的視頻流之因此沒有時間戳的緣由在於filter圖表管理器在視頻流里加一個很小的latency,若是捕捉時間被認爲就是render時間的話,視頻renderFilter就認爲視頻流有一個小小的延遲,若是此時render filter試圖連續播放的時候,就會丟楨。去掉時間戳就保證了視頻楨來了就能夠播放,不用等待,也不丟楨。

Video Port pin

Video Port是一個介於視頻設備(TV)和視頻卡之間的硬件設備。同過Video Port,視頻數據能夠直接發送到圖像卡上,經過硬件的覆蓋,視頻能夠直接在屏幕顯示出來。Video Port就是鏈接兩個設備的。
使用Video Port的最大好處是,不用CPU的任何工做,視頻流直接寫入內存中。若是捕捉設備使用了Video Port,捕捉Filter就用一個video port pin代替預覽pin。

預覽pin的種類GUID爲PIN_CATEGORY_PREVIEW

捕捉pin的種類GUID爲PIN_CATEGORY_CAPTURE

video port pin的種類GUID爲PIN_CATEGORY_VIDEOPORT

一個捕捉filter至少有一個Capturepin,另外,它可能有一個預覽pin 和一個videoport pin
,或者二者都沒有,也許filter有不少的capturepin,和預覽pin,每個pin都表明一種媒體類型,所以一個filter能夠有一個視頻capturepin,視頻預覽pin,音頻捕捉pin,音頻預覽pin。

3.      預覽視頻(Previewing Video)

爲了建立能夠預覽視頻的graph,能夠調用下面的代碼

ICaptureGraphBuilder2*pBuild; // Capture Graph Builder

// InitializepBuild (not shown).

IBaseFilter*pCap; // Video capture filter.

 /* Initialize pCap and add it to the filtergraph (not shown). */

hr =pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,   pCap, NULL, NULL);

4.      如何捕捉視頻流並保存到文件(Capture video to File)

1)        將視頻流保存到AVI文件

下面的圖表顯示了graph圖

圖2

AVI Mux filter接收從capture pin過來的視頻流,而後將其打包成AVI流。音頻流也能夠鏈接到AVI Mux Filter上,這樣mux filter就將視頻流和視頻流合成AVI流。File writer將AVI流寫入到文件中。

能夠像下面這樣構建graph

IBaseFilter*pMux;

hr =pBuild->SetOutputFileName(

  &MEDIASUBTYPE_Avi, // Specifies AVI forthe target file.

  L"C:\\Example.avi", // File name.

  &pMux, // Receives a pointer to the mux.

  NULL); // (Optional) Receives a pointer tothe file sink.

第一個參數代表文件的類型,這裏代表是AVI,第二個參數是制定文件的名稱。對於AVI文件,SetOutputFileName函數會建立一個AVI mux Filter 和一個 File writer Filter ,而且將兩個filter添加到graph圖中,在這個函數中,經過File Writer Filter 請求IFileSinkFilter接口,而後調用IFileSinkFilter::SetFileName方法,設置文件的名稱。而後將兩個filter鏈接起來。第三個參數返回一個指向 AVI Mux的指針,同時,它也經過第四個參數返回一個IFileSinkFilter參數,若是你不須要這個參數,你能夠將這個參數設置成NULL。

而後,你應該調用下面的函數將capture filter 和AVI Mux鏈接起來。

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指針。

當捕捉音頻的時候,媒體類型要設置爲MEDIATYPE_Audio,若是你從兩個不一樣的設備捕捉視頻和音頻,你最好將音頻設置成主流,這樣能夠防止兩個數據流間drift,由於avi mux filter爲同步音頻,會調整視頻的播放速度的。爲了設置master 流,調用IConfigAviMux::SetMasterStream方法,能夠採用以下的代碼:

IConfigAviMux*pConfigMux = NULL;

  hr =pMux->QueryInterface(IID_IConfigAviMux, (void**)&pConfigMux);

  if (SUCCEEDED(hr))

  {

  pConfigMux->SetMasterStream(1);

  pConfigMux->Release();

  }

SetMasterStream的參數指的是數據流的數目,這個是由調用RenderStream的次序決定的。例如,若是你調用RenderStream首先用於視頻流,而後是音頻,那麼視頻流就是0,音頻流就是1。

添加編碼filter

  IBaseFilter *pEncoder; /* Create the encoderfilter (not shown). */

  // Add it to the filter graph.

  pGraph->AddFilter(pEncoder,L"Encoder);

/* CallSetOutputFileName as shown previously. */

// Render thestream.

hr =pBuild->RenderStream(

&PIN_CATEGORY_CAPTURE,

 &MEDIATYPE_Video,

pCap,pEncoder, pMux);

  pEncoder->Release();

2)        將視頻流保存成wmv格式的文件

爲了將視頻流保存成並編碼成windows media video (WMV)格式的文件,將capture pin連到WM ASF Writer filter。

圖3

構建graph圖最簡單的方法就是將在ICaptureGraphBuilder2::SetOutputFileName方法中指定MEDIASUBTYPE_Asf的filter。以下

 IBaseFilter* pASFWriter = 0;

  hr = pBuild->SetOutputFileName(

  &MEDIASUBTYPE_Asf, // Create a WindowsMedia file.

  L"C:\\VidCap.wmv", // File name.

  &pASFWriter, // Receives a pointer to thefilter.

  NULL); // Receives an IFileSinkFilterinterface 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. UseMEDIATYPE_Audio for audio.

  pCap, // Pointer to the capture filter.

  0,

  pASFWriter); // Pointer to the sink filter(ASF Writer).

3)        保存成自定義的文件格式

若是你想將文件保存成本身的格式,你必須有本身的 file writer。看下面的代碼

 IBaseFilter *pMux = 0;

  IFileSinkFilter *pSink = 0;

  hr = pBuild->SetOutputFileName(

  &CLSID_MyCustomMuxFilter, //本身開發的Filter

  L"C:\\VidCap.avi", &pMux,&pSink);

4)        如何將視頻流保存進多個文件

當你將視頻流保存進一個文件後,若是你想開始保存第二個文件,這時,你應該首先將graph中止,而後經過IFileSinkFilter::SetFileName改變 File Writer 的文件名稱。注意,IFileSinkFilter指針你能夠在SetOutputFileName時經過第四個參數返回的。

看看保存多個文件的代碼吧

 IBaseFilter *pMux;同步的synchronization

  IFileSinkFilter *pSink

  hr =pBuild->SetOutputFileName(&MEDIASUBTYPE_Avi,L"C:\\YourFileName.avi",

  &pMux, &pSink);

  if (SUCCEEDED(hr))

  {

  hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE,&MEDIATYPE_Video,   pCap, NULL,pMux);

 if (SUCCEEDED(hr))

 {

  pControl->Run();

  /* Wait awhile, then stop the graph. */

  pControl->Stop();

  // Change the file name and run the graphagain.

  pSink->SetFileName(L"YourFileName02.avi",0);

  pControl->Run();

  }

 pMux->Release();

 pSink->Release();

}

5)        組合視頻的捕捉和預覽

若是想組建一個既能夠預覽視頻,又能夠將視頻保存成文件的graph,只須要兩次調用ICaptureGraphBuilder2::RenderStream便可。代碼以下:

 // Render the preview stream to the videorenderer.
  hr =pBuild->RenderStream(&PIN_CATEGORY_PREVIEW, &MEDIATYPE_Video,   pCap, NULL, NULL);

// Render thecapture stream to the mux.

hr =pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,   pCap, NULL, pMux);

在上面的代碼中,graph builder 其實隱藏了下面的細節。

1 若是capture Filter既有preview pin 也有captrue pin,那麼RenderStream僅僅將兩個pin和render filter接起來。以下圖

圖4

2若是caprture Filter只有一個capture pin,那麼Capture Graph Builder就採用一個Smart Tee Filter將視頻流分流,graph圖以下

圖5

5.      如何控制Capture Graph(ControllingCapture Graph)

Filter圖表管理器能夠經過IMediaControl接口控制整個graph的運行,中止和暫停。可是當一個graph有捕捉和預覽兩個數據流的時候,若是咱們想單獨的控制其中的一個數據流話,咱們能夠經過ICaptureGraphBuilder2::ControlStream。

下面講一下如何來單獨控制捕捉和預覽數據流。

1 控制捕捉視頻流

下面的代碼,讓捕捉數據流在graph開始運行1秒後開始,允運行4秒後結束。

// Control the video capture stream.

REFERENCE_TIME rtStart = 1000 0000, rtStop = 50000000;

const WORD wStartCookie = 1, wStopCookie = 2; //Arbitrary values.  hr = pBuild->ControlStream(

&PIN_CATEGORY_CAPTURE, // Pin category.

&MEDIATYPE_Video, // Media type.

pCap, // Capture filter.

&rtStart, &rtStop, // Start and stop times.

wStartCookie, wStopCookie // Values for the start andstop events.
  );

pControl->Run();

第一個參數代表須要控制的數據流,通常採用的是pin種類GUID,

第二個參數代表了媒體類型。

第三個參數指明瞭捕捉的filter。若是想要控制graph圖中的全部捕捉filter,第二個和第三個參數都要設置成NULL。

第四和第五個參數代表了流開始和結束的時間,這是一個相對於graph開始的時間。

只有你調用IMediaControl::Run之後,這個函數纔有做用。若是graph正在運行,這個設置當即生效。

最後的兩個參數用來設置當數據流中止,開始可以獲得的事件通知。對於任何一個運用此方法的數據流,graph當流開始的時候,會發送EC_STREAM_CONTROL_STARTED通知,在流結束的時候,要發送EC_STREAM_CONTROL_STOPPED通知。wStartCookie和wStopCookie是做爲第二個參數的。

看看事件通知處理過程吧

 while (hr = pEvent->GetEvent(&evCode,&param1, &param2, 0), SUCCEEDED(hr))

  {

  switch (evCode)

  {

  case EC_STREAM_CONTROL_STARTED:

  // param2 == wStartCookie

  break;

 case EC_STREAM_CONTROL_STOPPED:

  // param2 == wStopCookie

  break;

  }

  pEvent->FreeEventParams(evCode, param1,param2);

  }

ControlStream還定義了一些特定的值來表示開始和中止的時間。

MAXLONGLONG從不開始,只有在graph中止的時候才中止

NULL, 當即開始和中止

例如,下面的代碼當即中止捕捉流。

 pBuild->ControlStream(&PIN_CATEGORY_CAPTURE,&MEDIATYPE_Video, pCap,  0, 0, //Start and stop times.

  wStartCookie, wStopCookie);

2控制預覽視頻流

只要給ControlStream第一個參數設置成PIN_CATEGORY_PREVIEW就能夠控制預覽pin,整個函數的使用和控制捕捉流同樣,可是惟一區別是在這裏你無法設置開始和結束時間了,由於預覽的視頻流沒有時間戳,所以你必須使用NULL或者MAXLONGLONG。例子

 Use NULL to start the preview stream:

 pBuild->ControlStream(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_Video, pCap,  NULL, //Start now.

  0, // (Don't care.)

  wStartCookie, wStopCookie);

  Use MAXLONGLONG to stop the preview stream:

 pBuild->ControlStream(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_Video, pCap,  0, // (Don'tcare.)

  MAXLONGLONG, // Stop now.

  wStartCookie, wStopCookie);

3關於數據流的控制

Pin的缺省的行爲是傳遞sample,例如,若是你對PIN_CATEGORY_CAPTURE使用了ControlStream,可是對於PIN_CATEGORY_PREVIEW沒有使用該函數,所以,當你run graph的時候,preview 流會當即運行起來,而capture 流則要等到你設置的時間運行。

6.      如何配置一個視頻捕捉設備

1顯示VFW驅動的視頻設備對話框

若是視頻捕捉設備採用的仍然是VFW方式的驅動程序,則必須支持下面三個對話框,用來設置視頻設備。

1 VideoSource

用來選擇視頻輸入設備而且調整設備的設置,好比亮度和對比度。

2VideoFormat

用來設置楨的大小和位

3VideoDisplay

用來設置視頻的顯示參數

爲了顯示上面的三個對話框,你能夠do the following

1中止graph。

2向捕捉filter請求IAMVfwCaptureDialogs接口,若是成功,代表設備支持VFW驅動。

3調用IAMVfwCaptureDialogs::HasDialog來檢查驅動程序是否支持你請求的對話框,若是支持,返回S_OK,不然返回S_FALSE。注意不要用SUCCEDED宏。

4若是驅動支持該對話框,調用IAMVfwCaptureDialogs::ShowDialog顯示該對話框。

5 從新運行graph

代碼以下

 pControl->Stop(); // Stop the graph.

 // Query the capture filter for theIAMVfwCaptureDialogs interface. IAMVfwCaptureDialogs *pVfw = 0;

 hr =pCap->QueryInterface(IID_IAMVfwCaptureDialogs, (void**)&pVfw);  if (SUCCEEDED(hr))

 {

  // Check if the device supports this dialogbox.

  if (S_OK ==pVfw->HasDialog(VfwCaptureDialog_Source))

  {

  // Show the dialog box.

  hr =pVfw->ShowDialog(VfwCaptureDialog_Source, hwndParent);

  }

  }

  pControl->Run();

2 調整視頻的質量

WDM驅動的設備支持一些屬性能夠用來調整視頻的質量,好比亮度,對比度,飽和度,等要向調整視頻的質量,do the following

1 從捕捉filter上請求IAMVideoProcAmp接口

2 對於你想調整的任何一個屬性,調用IAMVideoProcAmp::GetRange能夠返回這個屬性賦值的範圍,缺省值,最小的增量值。IAMVideoProcAmp::Get返回當前正在使用的值。VideoProcAmpProperty枚舉每一個屬性定義的標誌。

3調用IAMVideoProcAmp::Set來設置這個屬性值。設置屬性的時候,不用中止graph。

看看下面的代碼是如何調整視頻的質量的

HWND hTrackbar;// Handle to the trackbar control.

// InitializehTrackbar (not shown).

// Query thecapture filter for the IAMVideoProcAmp interface.

  IAMVideoProcAmp *pProcAmp = 0;

  hr =pCap->QueryInterface(IID_IAMVideoProcAmp, (void**)&pProcAmp);

  if (FAILED(hr))

  {

  // The device does not supportIAMVideoProcAmp, so disable the control.

  EnableWindow(hTrackbar, FALSE);

  }

  Else

  {

  long Min, Max, Step, Default, Flags, Val;

  // Getthe range and default value.

  hr = m_pProcAmp->GetRange(VideoProcAmp_Brightness,&Min, &Max, &Step,

  &Default, &Flags);

  if (SUCCEEDED(hr))

  {

  // Get the current value.

  hr =m_pProcAmp->Get(VideoProcAmp_Brightness, &Val, &Flags);

  }

  if (SUCCEEDED(hr))

  {

  // Set the trackbar range and position.

  SendMessage(hTrackbar, TBM_SETRANGE, TRUE,MAKELONG(Min, Max));

  SendMessage(hTrackbar, TBM_SETPOS, TRUE,Val);

  EnableWindow(hTrackbar, TRUE);

  }

  Else

  {

  // This property is not supported, so disablethe control.

  EnableWindow(hTrackbar, FALSE);

  }

  }

3調整視頻輸出格式

咱們知道視頻流能夠有多種輸出格式,一個設備能夠支持16-bit RGB, 32-bit RGB, and YUYV,在每一種格式下,設備還能夠調整視頻楨的大小。

在WDM驅動設備上,IAMStreamConfig 接口用來報告設備輸出視頻的格式的,VFW設備,能夠採用對話框的方式來設置,參見前面的內容。

捕捉Filter的捕捉pin和預覽pin都支持IAMStreamConfig 接口,能夠經過ICaptureGraphBuilder2::FindInterface得到IAMStreamConfig接口。

 IAMStreamConfig *pConfig = NULL;

  hr = pBuild->FindInterface(

  &PIN_CATEGORY_PREVIEW, // Preview pin.

  0, // Any media type.

  pCap, // Pointer to the capture filter.

  IID_IAMStreamConfig, (void**)&pConfig);

設備還支持一系列的媒體類型,對於每個媒體類型,設備都要支持一系列的屬性,好比,楨的大小,圖像如何縮放,楨率的範圍等。

經過IAMStreamConfig::GetNumberOfCapabilities得到設備所支持的媒體類型的數量。這個方法返回兩個值,一個是媒體類型的數量,二是屬性所需結構的大小。

這個結構的大小很重要,由於這個方法是用於視頻和音頻的,視頻採用的是VIDEO_STREAM_CONFIG_CAPS結構,音頻用AUDIO_STREAM_CONFIG_CAPS結構。

經過函數IAMStreamConfig::GetStreamCaps來枚舉媒體類型,要給這個函數傳遞一個序號做爲參數,這個函數返回媒體類型和相應的屬性結構體。看代碼把

 int iCount = 0, iSize = 0;

 hr =pConfig->GetNumberOfCapabilities(&iCount, &iSize);

// Check thesize to make sure we pass in the correct structure.

  if (iSize == sizeof(VIDEO_STREAM_CONFIG_CAPS)

  {

  // Use the video capabilities structure.

  for (int iFormat = 0; iFormat < iCount;iFormat++)

  {

  VIDEO_STREAM_CONFIG_CAPS scc;

  AM_MEDIA_TYPE *pmtConfig;

  hr = pConfig->GetStreamCaps(iFormat,&pmtConfig, (BYTE*)&scc);

  if (SUCCEEDED(hr))

  {

  /* Examine the format, and possibly use it.*/

  // Delete the media type when you are done.

  hr = pConfig->SetFormat(pmtConfig);//從新設置視頻格式

  DeleteMediaType(pmtConfig);

  }

  }

你能夠調用IAMStreamConfig::SetFormat設置新的媒體類型

hr =pConfig->SetFormat(pmtConfig);

若是pin沒有鏈接,當鏈接的時候就試圖用新的格式,若是pin已經在鏈接了,它就會用的新的媒體格式從新鏈接。在任何一種狀況下,下游的filter都有可能拒絕新的媒體格式。

在SetFormat前你能夠修改VIDEO_STREAM_CONFIG_CAPS結構來從新設置媒體類型。

例如:

若是GetStreamCaps返回的是24-bit RGB format,楨的大小是320 x 240 像素,你能夠經過檢查媒體類型的major type,subtpye,和format等值

 if ((pmtConfig.majortype == MEDIATYPE_Video)&&

  (pmtConfig.subtype == MEDIASUBTYPE_RGB24)&&

  (pmtConfig.formattype == FORMAT_VideoInfo)&&

  (pmtConfig.cbFormat >= sizeof(VIDEOINFOHEADER)) &&

  (pmtConfig.pbFormat != NULL))

  {

  VIDEOINFOHEADER *pVih =(VIDEOINFOHEADER*)pmtConfig.pbFormat;

  // pVih contains the detailed formatinformation.

  LONG lWidth = pVih->bmiHeader.biWidth;

  LONG lHeight = pVih->bmiHeader.biHeight;

  }

VIDEO_STREAM_CONFIG_CAPS結構裏包含了該媒體類型的視頻長度和寬度的最大值和最小值,還有遞增的幅度值,就是每次調整視頻size的幅度,例如,設備可能返回以下的值

MinOutputSize:160 x 120

MaxOutputSize:320 x 240

OutputGranularityX:8 pixels (horizontal step size)

OutputGranularityY:8 pixels (vertical step size)

這樣你能夠在(160, 168, 176, ... 304, 312, 320) 範圍內設置寬度,在 (120, 128, 136, ... 104, 112,120).設置高度值,

圖6

若是想設置新的值,直接修改在GetStreamCaps函數中返回的值便可,

 pVih->bmiHeader.biWidth = 160;

  pVih->bmiHeader.biHeight = 120;

  pVih->bmiHeader.biSizeImage =DIBSIZE(pVih->bmiHeader);

而後將媒體類型傳遞給SetFormat函數,就可修改視頻格式了。

7.      將設備從系統中移走時的事件通知(Device remove Notify)

若是用戶將一個graph正在使用的即插即用型的設備從系統中去掉,filter圖表管理器就會發送一個EC_DEVICE_LOST事件通知,若是該設備又可使用了,filter圖表管理器就發送另外的一個EC_DEVICE_LOST通知,可是先前組建的捕捉filter graph圖就無法用了,用戶必須從新組建graph圖。

當系統中有新的設備添加時,dshow是不會發送任何通知的,因此,應用程序若是想要知道系統中什麼時候添加新的設備,應用程序能夠監控WM_DEVICECHANGE消息。

8.      從靜止圖像pin中捕捉圖片

有些照相機,攝像頭除了能夠捕獲視頻流之外還能夠捕獲單張的,靜止的圖片。一般,靜止的圖片的質量要比流的質量要高。攝像頭通常都一個按鈕來觸發,或者是支持軟件觸發。支持輸出靜態圖片的攝像頭通常都要提供一個靜態圖片pin,這個pin的種類是PIN_CATEGORY_STILL。

從設備中獲取靜態圖片,咱們通常推薦使用windows Image Acquisition (WIA) APIs。固然,你也能夠用dshow來獲取圖片。

在graph運行的時候利用IAMVideoControl::SetMode來觸發靜態的pin。代碼以下

 pControl->Run(); // Run the graph.

  IAMVideoControl *pAMVidControl = NULL;

  hr = pCap->QueryInterface(IID_IAMVideoControl,(void**)&pAMVidControl);

  if (SUCCEEDED(hr))

  {

  // Find the still pin.

  IPin *pPin = 0;

  hr = pBuild->FindPin(pCap, PINDIR_OUTPUT,&PIN_CATEGORY_STILL, 0,  FALSE, 0,&pPin);

  if (SUCCEEDED(hr))

  {

  hr = pAMVidControl->SetMode(pPin, VideoControlFlag_Trigger);

  pPin->Release();

  }

  pAMVidControl->Release();

  }

首先向capture Filter 請求IAMVideoContol,若是支持該接口,就調用ICaptureGraphBuilder2::FindPin請求指向靜止pin 的指針,而後調用pin的put_Mode方法。

根據不一樣的攝像頭,你可能靜態pin鏈接前要render 該pin。

捕捉靜態圖片經常使用的filter是Sample Grabber filter,Sample Grabber使用了一個用戶定義的回調汗水來處理圖片。關於這個filter的詳細用法,參見Using the Sample Grabber.。

下面的例子假設靜態pin傳遞的是沒有壓縮的RGB圖片。首先定義一個類,從ISampleGrabberCB繼承。

 // Class to hold the callback function for theSample Grabber filter.  classSampleGrabberCallback : public ISampleGrabberCB

  {

  // Implementation is described later.

  }

// Globalinstance of the class.

SampleGrabberCallbackg_StillCapCB;

而後將捕捉filter的靜態pin鏈接到Sample Grabber,將Sample Grabber鏈接到Null Renderer filter。Null Renderer僅僅是將她接收到的sample丟棄掉。實際的工做都是在回調函數裏進行,鏈接Null Renderer 僅僅是爲了給Sample Grabber's 輸出pin上鍊接點東西。具體見下面的代碼

  // Add the Sample Grabber filter to thegraph.

  IBaseFilter *pSG_Filter;

  hr = CoCreateInstance(CLSID_SampleGrabber,NULL, CLSCTX_INPROC_SERVER,

  IID_IBaseFilter, (void**)&pSG_Filter);

  hr = pGraph->AddFilter(pSG_Filter,L"SampleGrab");

// Add theNull Renderer filter to the graph.

IBaseFilter*pNull;

hr =CoCreateInstance(CLSID_NullRendere, NULL, CLSCTX_INPROC_SERVER,  IID_IBaseFilter, (void**)&pNull);

hr =pGraph->AddFilter(pSG_Filter, L"NullRender");

而後經過RenderStream將still pin ,sample grabber ,null Renderer鏈接起來

 hr = pBuild->RenderStream(

  &PIN_CATEGORY_STILL, // Connect this pin...

  &MEDIATYPE_Video, // with this media type...

  pCap, // on this filter ...

  pSG_Filter, // to the Sample Grabber ...

  pNull); // ... and finally to the NullRenderer.

而後調用ISampleGrabber指針,來經過這個指針能夠分配內存。

  // Configure the Sample Grabber.

 ISampleGrabber *pSG;

 hr =pSG_Filter->QueryInterface(IID_ISampleGrabber, (void**)&pSG);

 pSG->SetOneShot(FALSE);

  pSG->SetBufferSamples(TRUE);

設置你的回調對象

 pSG->SetCallback(&g_StillCapCB, 0); //0 = Use the SampleCB callback   method

獲取靜態pin和sample grabber之間鏈接所用的媒體類型

 // Store the media type for later use.

  AM_MEDIA_TYPE g_StillMediaType;

  hr =pSG->GetConnectedMediaType(&g_StillMediaType);

  pSG->Release();

媒體類型包含一個BITMAPINFOHEADER結構來定義圖片的格式,在程序退出前必定要釋放媒體類型

 // On exit, remember to release the mediatype.

  FreeMediaType(g_StillMediaType);

看看下面的回調類吧。這個類從ISampleGrabber接口派生,可是它沒有保持引用計數,由於應用程序在堆上建立這個對象,在整個graph的生存週期它都存在。

全部的工做都在BufferCB函數裏完成,當有一個新的sample到來的時候,這個函數就會被sample Grabber調用到。在下面的例子裏,bitmap被寫入到一個文件中

classSampleGrabberCallback : public ISampleGrabberCB

  {

  public:

  // Fake referance counting.

  STDMETHODIMP_(ULONG) AddRef() { return 1; }

  STDMETHODIMP_(ULONG) Release() { return 2; }

 STDMETHODIMP QueryInterface(REFIID riid, void**ppvObject)

  {

  if (NULL == ppvObject) return E_POINTER;

  if (riid == __uuidof(IUnknown))

  {

  *ppvObject =static_cast<IUnknown*>(this);

  return S_OK;

  }

  if (riid == __uuidof(ISampleGrabberCB))

  {

  *ppvObject =static_cast<ISampleGrabberCB*>(this);

  return S_OK;

  }

  return E_NOTIMPL;

  }

 STDMETHODIMP SampleCB(double Time,IMediaSample *pSample)

  {

  return E_NOTIMPL;

  }

 STDMETHODIMP BufferCB(double Time, BYTE*pBuffer, long BufferLen)

 {

  if ((g_StillMediaType.majortype !=MEDIATYPE_Video) ||

  (g_StillMediaType.formattype !=FORMAT_VideoInfo) ||

  (g_StillMediaType.cbFormat <sizeof(VIDEOINFOHEADER)) ||

  (g_StillMediaType.pbFormat == NULL))

  {

  return VFW_E_INVALIDMEDIATYPE;

 }

 HANDLE hf =CreateFile("C:\\Example.bmp", GENERIC_WRITE,

 FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0,NULL);

 if (hf == INVALID_HANDLE_VALUE)

 {

  return E_FAIL;

 }

 long cbBitmapInfoSize =g_StillMediaType.cbFormat - SIZE_PREHEADER;

VIDEOINFOHEADER*pVideoHeader = (VIDEOINFOHEADER*)g_StillMediaType.pbFormat;

 BITMAPFILEHEADER bfh;

ZeroMemory(&bfh,sizeof(bfh));

bfh.bfType ='MB'; // Little-endian for "MB".

bfh.bfSize =sizeof( bfh ) + BufferLen + cbBitmapInfoSize;

bfh.bfOffBits= sizeof( BITMAPFILEHEADER ) + cbBitmapInfoSize;

// Write thefile header.

DWORDdwWritten = 0;

WriteFile(hf, &bfh, sizeof( bfh ), &dwWritten, NULL );

WriteFile(hf,HEADER(pVideoHeader), cbBitmapInfoSize, &dwWritten, NULL);

WriteFile(hf, pBuffer, BufferLen, &dwWritten, NULL );

CloseHandle(hf );

return S_OK;

}

};

相關文章
相關標籤/搜索