zoukankan      html  css  js  c++  java
  • Direcshow中视频捕捉和參数设置报告

    Direcshow中视频捕捉和參数设置报告

    1.      关于视频捕捉(About Video Capture in Dshow)

    1视频捕捉Graph的构建

    一个能够捕捉音频或者视频的graph图都称之为捕捉graph图。捕捉graph图比一般的文件回放graph图要复杂很多,dshow提供了一个Capture Graph Builder COM组件使得捕捉graph图的生成更加简单。Capture Graph Builder提供了一个ICaptureGraphBuilder2接口。这个接口提供了一些方法用来构建和控制捕捉graph。

    首先创建一个Capture Graph Builder对象和一个graph manger对象。然后用filter graph manager 作參数,调用ICaptureGraphBuilder2::SetFiltergraph来初始化Capture Graph Builder。看以下的代码把

    HRESULTInitCaptureGraphBuilder(

    IGraphBuilder**ppGraph, // Receives the pointer.

    ICaptureGraphBuilder2**ppBuild // Receives the pointer.

    )

    {

    if (!ppGraph|| !ppBuild)

    {

    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中捕捉图片

    有些照相机,摄像头除了能够捕获视频流以外还能够捕获单张的。精巧的图片。通常,精巧的图片的质量要比流的质量要高。摄像头一般都一个button来触发,或者是支持软件触发。支持输出静态图片的摄像头一般都要提供一个静态图片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;

    }

    };


  • 相关阅读:
    Ubuntu 14.04 卸载通过源码安装的库
    Ubuntu 14.04 indigo 相关依赖
    Ubuntu 14.04 indigo 安装 cartographer 1.0.0
    Ubuntu 14.04 改变文件或者文件夹的拥有者
    安装cartographer遇到Unrecognized syntax identifier "proto3". This parser only recognizes "proto2"问题
    Unrecognized syntax identifier "proto3". This parser only recognizes "proto2". ”问题解决方法
    查看所有用户组,用户名
    1卸载ROS
    Ubuntu14.04 软件安装卸载
    Ubuntu14.04系统显示器不自动休眠修改
  • 原文地址:https://www.cnblogs.com/yfceshi/p/6776965.html
Copyright © 2011-2022 走看看