第四篇 DirectShow 采集调用结构关系

第一篇: DirectShow视频采集_会头痛的可达鸭的博客-CSDN博客

一、GraphBuilder

1、IFilterGraph2、IGraphBuilder、ICaptureGraphBuiler2

(1)、CLSID

IFilterGraph           CLSID_FilterGraph

IFilterGraph2          CLSID_CaptureGraphBuilder

IGraphBuilder          CLSID_CaptureGraphBuilder

ICaptureGraphBuiler2   CLSID_CaptureGraphBuilder2

(2)、IFilterGraph2还有一个IFilterGraph,IFilterGraph3.

继承关系:

IFilterGraph——>IGraphBuilder——>IFilterGraph2——>IFilterGraph3

(3)、如果仅仅只需要预览 USB Video, 可以只使用IFilterGraph2

通过

pFilterGraph2->AddSourceFilterForMoniker()

用Moniker指针将filter添加到graph,则不必创建IcaptureGraphBuiler2这个接口。

如果需要需要视频流,则需要使用IFilterGraph

ICaptureGraphBuilder2->SetFiltergraph(IFilterGraph)

(4)、ICaptureGraphBuiler2是一个专门用来进行视频,音频捕捉的增强型接口;如果要特别的编解码,用它就很方便

创建了IcaptureGraphBuiler2,仍然需要创建IGraphBuilder ​ 因为在预览视频时处理播放,暂停,停止这些动作,需要用到IGraphBuilder,而这些功能IcaptureGraphBuiler2 ​ 是没有的,所以需要而这协调处理。 ​ IcaptureGraphBuiler2创建后,要将IGraphBuilder的指针与他关联:

pCaptureGraphBuiler2->captureGraphBuiler2(pGraphBuilder).

2、绑定并获取基础过滤器

// 写法1: 将视频设备绑定到基础过滤器上
IBaseFilter* GetDeviceFilter(const char* deviceUniqueIdUTF8,
                             char* productUniqueIdUTF8,
                             uint32_t productUniqueIdUTF8Length) 
{
  const int32_t deviceUniqueIdUTF8Length = (int32_t)strlen(
      (char*)deviceUniqueIdUTF8);  // UTF8 is also NULL terminated
  if (deviceUniqueIdUTF8Length >= kVideoCaptureUniqueNameLength) {
    RTC_LOG(LS_INFO) << "Device name too long";
    return NULL;
  }
​
  // enumerate all video capture devices
  RELEASE_AND_CLEAR(_dsMonikerDevEnum);
  HRESULT hr = _dsDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
                                                 &_dsMonikerDevEnum, 0);
  if (hr != NOERROR) 
  {
    RTC_LOG(LS_INFO) << "Failed to enumerate CLSID_SystemDeviceEnum, error 0x"
                     << rtc::ToHex(hr) << ". No webcam exist?";
    return 0;
  }
  _dsMonikerDevEnum->Reset();
  ULONG cFetched;
  IMoniker* pM;
​
  IBaseFilter* captureFilter = NULL;
  bool deviceFound = false;
  while (S_OK == _dsMonikerDevEnum->Next(1, &pM, &cFetched) && !deviceFound) 
  {
    IPropertyBag* pBag;
    hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag);
    if (S_OK == hr) 
    {
      // Find the description or friendly name.
      VARIANT varName;
      VariantInit(&varName);
      if (deviceUniqueIdUTF8Length > 0) 
      {
        hr = pBag->Read(L"DevicePath", &varName, 0);
        if (FAILED(hr)) {
          hr = pBag->Read(L"Description", &varName, 0);
          if (FAILED(hr)) {
            hr = pBag->Read(L"FriendlyName", &varName, 0);
          }
        }
        if (SUCCEEDED(hr)) 
        {
          char tempDevicePathUTF8[256];
          tempDevicePathUTF8[0] = 0;
          WideCharToMultiByte(CP_UTF8, 0, varName.bstrVal, -1,
                              tempDevicePathUTF8, sizeof(tempDevicePathUTF8),
                              NULL, NULL);
          if (strncmp(tempDevicePathUTF8, (const char*)deviceUniqueIdUTF8,
                      deviceUniqueIdUTF8Length) == 0) 
          {
            // We have found the requested device
            deviceFound = true;
            hr = pM->BindToObject(0, 0, IID_IBaseFilter, (void**)&captureFilter);
            if(FAILED(hr))
            {
                RTC_LOG(LS_ERROR) << "Failed to bind to the selected "
                                     "capture device " << hr;
            }
​
            if (productUniqueIdUTF8 &&
                productUniqueIdUTF8Length > 0)  // Get the device name
            {
              GetProductId(deviceUniqueIdUTF8, productUniqueIdUTF8,
                           productUniqueIdUTF8Length);
            }
          }
        }
      }
      VariantClear(&varName);
      pBag->Release();
    }
    pM->Release();
  }
  return captureFilter;
}
​
//写法2:枚举设备并绑定设备
BOOL BindToVideoDev(int deviceId, IBaseFilter **pFilter)
{
  if (deviceId < 0)
  {
    return FALSE;
  }
  
  CComPtr pCreateDevEnum;
  HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC_SERVER,
                  IID_ICreateDevEnum, (void**)&pCreateDevEnum);
  if (hr != NOERROR)
  {
    //ERR_DEBUG("Instance DeviceEnum Failed");
    return FALSE;
  }
  CComPtr pEm;
  hr = pCreateDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,&pEm, 0);
  if (hr != NOERROR)
  {
    //ERR_DEBUG("Enum VideoInputDeviceCategory Failed");
    return FALSE;
  }
  pEm->Reset();
  ULONG cFetched;
  IMoniker *pM=NULL;
​
  int index = 0;
  while((( pEm->Next(1, &pM, &cFetched))==S_OK)&&( index <= deviceId))
  {
    IPropertyBag *pBag=NULL;
    if (pM==NULL)
    {
      break;
    }
    hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void **)&pBag);
    if(pBag!=NULL) 
    {
      VARIANT var;
      var.vt = VT_BSTR;
      hr = pBag->Read(L"FriendlyName", &var, NULL);
      if (hr == NOERROR) 
      {
        if (index == deviceId)
        {
          //将视频设备绑定到基础过滤器上
          pM->BindToObject(0, 0, IID_IBaseFilter, (void**)pFilter);
        }
        SysFreeString(var.bstrVal);
        pBag->Release();
      }
    }
    pM->Release();
    index++;
  }
  return TRUE;
}
// 添加到过滤器
// AddFilter调用过滤器的IBaseFilter::JoinFilterGraph方法来通知过滤器它已被添加
// AddFilter必须在尝试使用IGraphBuilder::Connect、IFilterGraph::ConnectDirect或IGraphBuilder::Render
// 方法连接或渲染属于添加的过滤器的引脚之前调用
IFilterGraph->AddFilter(IBaseFilter, "name");
// 获取InputPin
IPin* GetInputPin(IBaseFilter* filter)
{
  IPin* pin = NULL;
  IEnumPins* pPinEnum = NULL;
  filter->EnumPins(&pPinEnum);
  if (pPinEnum == NULL) 
  {
    return NULL;
  }
  // get first unconnected pin
  pPinEnum->Reset();  // set to first pin
​
  while (S_OK == pPinEnum->Next(1, &pin, NULL)) {
    PIN_DIRECTION pPinDir;
    pin->QueryDirection(&pPinDir);
    if (PINDIR_INPUT == pPinDir)  // This is an input pin
    {
      IPin* tempPin = NULL;
      if (S_OK != pin->ConnectedTo(&tempPin))  // The pint is not connected
      {
        pPinEnum->Release();
        return pin;
      }
    }
    pin->Release();
  }
  pPinEnum->Release();
  return NULL;
}

3、设置采集的分辨率

HRESULT SetVideoSize(int nPreview,CString strRGBBytes,int nFrameRate,int iWidth , int iHeight)
{
  HRESULT hr=E_FAIL;
  if(m_pCaptureGraphBulid==NULL)
    return hr;
​
  IAMStreamConfig *pAMStreamConfig=NULL;
  if(nPreview==0)
  {
    hr = m_pCaptureGraphBulid->FindInterface(&PIN_CATEGORY_PREVIEW,&MEDIATYPE_Video,
        m_pBaseFilter,IID_IAMStreamConfig,(void **)&pAMStreamConfig);
  }
  else
  {
    hr = m_pCaptureGraphBulid->FindInterface(&PIN_CATEGORY_CAPTURE,&MEDIATYPE_Video,
        m_pBaseFilter,IID_IAMStreamConfig,(void **)&pAMStreamConfig);
  }
​
  if(FAILED( hr ))
  {
    SAFE_RELEASE(pAMStreamConfig);
    return hr;
  }
    //得到视频格式大小
  AM_MEDIA_TYPE *pmt;
  pAMStreamConfig->GetFormat(&pmt);
​
  //设置视频格式
  pmt->majortype = MEDIATYPE_Video;
  pmt->subtype  = GetMediaTypeGuid(strRGBBytes);
​
​
  VIDEOINFOHEADER *pvih = reinterpret_cast(pmt->pbFormat);
  //设置回去
  int nDefualWidth = pvih->bmiHeader.biWidth;
  int nDefualHeight = pvih->bmiHeader.biHeight;
​
  pvih->bmiHeader.biWidth = iWidth; 
  pvih->bmiHeader.biHeight = iHeight;
  pvih->bmiHeader.biSizeImage = pmt->lSampleSize = iWidth*iHeight*pvih->bmiHeader.biPlanes*pvih->bmiHeader.biBitCount/8;
  pvih->AvgTimePerFrame = (LONGLONG)(10000000/nFrameRate);
​
  hr = pAMStreamConfig->SetFormat(pmt);
  if(FAILED(hr))
  {
    //如果设置失败可以选用默认的,但运用之后,小屏幕初始化时会出现闪动的情况   
    pvih->bmiHeader.biWidth = nDefualWidth; 
    pvih->bmiHeader.biHeight = nDefualHeight;
    pvih->bmiHeader.biSizeImage = pmt->lSampleSize = nDefualWidth*nDefualHeight*pvih->bmiHeader.biPlanes*pvih->bmiHeader.biBitCount/8;
    hr = pAMStreamConfig->SetFormat(pmt);
    
    if(FAILED(hr))
    {
      SAFE_RELEASE(pAMStreamConfig);
      FreeMediaType(*pmt);
      //ERR_DEBUG("初始化设置视频格式失败");
      return hr;
    }
  }
  SAFE_RELEASE(pAMStreamConfig);
  FreeMediaType(*pmt);//
  return hr;
}

4、流程

(1)、创建CLSID_FilterGraph---- CoCreateInstance--- IFilterGraph

(2)、创建IID_IMediaControl --- IFilterGraph->QueryInterface --- IMediaControl

(3)、获取并连接:IBaseFilter---GetDeviceFilter---BindToObject

IFilterGraph->AddFilter(IBaseFilter, L"VideoCaptureFilter")

(4)、创建自定义的IBaseFilter(成员有IPin)---CaptureSinkFilter

IFilterGraph->AddFilter(CaptureSinkFilter)

(5)、获取InputPin,通过自定义的CaptureSinkFilter,并连接CaptureInputPin::ConnectedTo

(6)、连接OutputPin和InputPin(直接连接两个引脚, 无需中间滤波器)

IFilterGraph->ConnectDirect(OutputPin, InputPin, NULL)

三、VideoCaptureDS

1、初始化

// 基类: VideoCaptureImpl
VideoCaptureDS :public VideoCaptureImpl
// 初始化并且创建:
// IBaseFilter          DirectShow主要过滤器     
// IGraphBuilder        DirectShow图形生成器
// IMediaControl        DirectShow媒体控制器
// CaptureSinkFilter
// IPin
VideoCaptureDS::Init  

2、CaptureSinkFilter

// 基类
// 创建 CaptureInputPin
CaptureSinkFilter : public IBaseFilter 

3、CaptureInputPin

// Input Pin--->camera input
// 负责绑定: receive_pin 和 媒体类型
// 接受视频数据发送给observer
class CaptureInputPin : public IMemInputPin, public IPin 

4、DeviceInfoDS

// 主要提供设备能力、获取设备信息、获取Filter
DeviceInfoDS : public DeviceInfoImpl

你可能感兴趣的:(视频采集模块,音视频,DirectShow)