WebRTC视频 03 - 视频采集类 VideoCaptureDS 上篇

WebRTC视频 01 - 视频采集整体架构
WebRTC视频 02 - 视频采集类 VideoCaptureModule
[WebRTC视频 03 - 视频采集类 VideoCaptureDS 上篇](本文)
WebRTC视频 04 - 视频采集类 VideoCaptureDS 中篇
WebRTC视频 05 - 视频采集类 VideoCaptureDS 下篇

一、前言:

前面两篇文章我们介绍了WebRtc的视频采集架构,并且,分析了所有关键类之间如何相互协调,一直分析到操作VideoCaptureDS这个类为止。心中有了框架,接下来我们分析具体的点,就是VideoCaptureDS再往下如何操作硬件的。

二、流程图:

其实主要干了几件事:

在这里插入图片描述

  • 连接CaptureFilter的输出Pin到SinkFilter的输入Pin,这样数据就源源不断从输出Pin到输入Pin了。
  • CaptureFilter是由DirectShow提供的,可以通过CaptureFilter来控制DirectShow完成视频采集,而SinkFilter是webrtc自己构造的。
  • 入口函数还记得吗?是VideoCaptureDS::Init()。

三、COM编程方法介绍:

CreateClassEnumerator:

CreateClassEnumerator是DirectShow API中的一个函数,它用于创建一个枚举器对象,该对象可用于枚举系统中注册的所有DirectShow滤波器的类标识符(CLSID)。

该函数的原型通常是:

HRESULT CreateClassEnumerator(
  REFCLSID   clsidDeviceClass,
  IEnumMoniker **ppEnumMoniker,
  DWORD      dwFlags
);
  • clsidDeviceClass: 指定要枚举的设备类别的 CLSID。传入 NULL 时,将枚举所有的设备类别。
  • ppEnumMoniker: 指向 IEnumMoniker 接口指针的指针。枚举器将通过该指针返回。
  • dwFlags: 可选的标志,用于指定枚举器的行为。

IEnumMoniker:

IEnumMoniker 接口是COM编程中的一个接口,用于枚举 IMoniker 接口的集合。IEnumMoniker 接口中的 Next 方法用于检索指定数量的Moniker对象。

下面是 IEnumMoniker 接口的 Next 方法的一般原型:

HRESULT Next(
  ULONG        celt,
  IMoniker     **rgelt,
  ULONG        *pceltFetched
);
  • celt: 指定要检索的Moniker对象数量。
  • rgelt: 用于输出枚举的Moniker对象的指针数组。
  • pceltFetched: 指向一个 ULONG 变量的指针,用于返回实际成功检索的Moniker对象数量。

Next 方法会尝试从枚举器的当前位置检索指定数量的Moniker对象,并将它们填充到提供的 rgelt 数组中。成功获取的Moniker对象数量将通过 pceltFetched 参数返回。如果成功检索了指定数量的Moniker对象,则返回 S_OK,否则返回 S_FALSE

BindToStorage:

IMoniker::BindToStorage 是一个用于将 Moniker 绑定到存储对象的方法。在 COM 编程中,Moniker 是用于标识和定位对象的抽象机制,而 BindToStorage 允许将 Moniker 解析为存储对象,从而可以访问该对象的数据。

具体来说,IMoniker::BindToStorage 方法的作用是将 Moniker 绑定到存储器,并返回一个指向该存储器对象的接口指针,以便可以访问存储器中所包含的数据。这个方法通常用于从 Moniker 获取实际对象的数据或属性。

下面是 IMoniker::BindToStorage 方法的一般原型:

HRESULT BindToStorage(
  IBindCtx *pbc,
  IMoniker *pmkToLeft,
  REFIID   riid,
  void     **ppvObj
);
  • pbc: 指向绑定上下文对象的指针,用于控制绑定操作的一些方面。
  • pmkToLeft: 在某些情况下可能用到,表示左侧的 Moniker 对象。
  • riid: 指定所请求接口的 IID(接口标识符)。
  • ppvObj: 用于返回存储器对象的接口指针的指针。

通过调用 IMoniker::BindToStorage 方法,可以通过 Moniker 定位并访问存储器对象中的数据。这在 COM 编程中特别有用,特别是在处理对象链接和嵌入(OLE)等场景中。

IPropertyBag:

IPropertyBag 是 COM 编程中的一个接口,用于提供一种机制,允许通过属性名称来检索和设置属性值。它通常用于在 COM 对象之间传递属性信息,并提供一种灵活的方式来访问和操作属性。

下面是 IPropertyBag 接口的一般原型:

interface IPropertyBag : IUnknown
{
    virtual HRESULT Read(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog) = 0;
    virtual HRESULT Write(LPCOLESTR pszPropName, VARIANT *pVar) = 0;
};
  • Read: 通过属性名称读取属性值,并将其存储在传入的 VARIANT 结构中。如果属性不存在或读取失败,可以使用 IErrorLog 接口来记录错误信息。
  • Write: 根据属性名称设置属性值,传入要设置的属性值的 VARIANT 结构。

通过 IPropertyBag 接口,可以实现一种通用的属性存储和检索机制,使得 COM 对象之间可以方便地传递和共享属性信息。这种机制在许多场景下非常有用,特别是在配置对象、持久化对象属性、或者在不同组件之间传递配置信息等方面。

BindToObject:

BindToObject 是 COM 编程中常用的一个方法,通常用于将 Moniker 绑定到对象,从而获取对象的接口指针。这个方法通常由 IMoniker 接口提供,可以用于实现对象的定位和访问。

下面是 IMoniker::BindToObject 方法的一般原型:

HRESULT BindToObject(
  IBindCtx *pbc,
  IMoniker *pmkToLeft,
  REFIID   riidResult,
  void     **ppvResult
);
  • pbc: 指向绑定上下文对象的指针,用于控制绑定操作的一些方面。
  • pmkToLeft: 在某些情况下可能用到,表示左侧的 Moniker 对象。
  • riidResult: 请求的接口的 IID(接口标识符)。
  • ppvResult: 用于返回绑定到的对象的接口指针的指针。

通过调用 IMoniker::BindToObject 方法,可以将 Moniker 解析为一个对象,并获取该对象的接口指针。这个方法在 COM 编程中常用于实现对象的定位和访问,特别是在处理对象链接、远程过程调用(RPC)和其他需要动态定位对象的场景中。

三、CaptureFilter:

1、作用:

CaptureFilter就是控制DirectShow完成视频采集的。

2、获取CaptureFilter:

代码入口:

int32_t VideoCaptureDS::Init(const char* deviceUniqueIdUTF8) {
   
   
  // ...
  // 构造CaptureFilter
  _captureFilter = _dsInfo.GetDeviceFilter(deviceUniqueIdUTF8);
  if (!_captureFilter) {
   
   
    RTC_LOG(LS_INFO) << "Failed to create capture filter.";
    return -1;
  }
  // ...
}

可以看出,是通过DeviceInfoDS的对象 _dsInfo 来获取CaptureFilter的。

看看如何获取CaptureFilter的:

// 获取CaptureFilter走这儿,其中productUniqueIdUTF8和productUniqueIdUTF8Length都传入的0
IBaseFilter* DeviceInfoDS::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);
  // CreateClassEnumerator 是获取视频采集设备的枚举器到_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;
  }
  // reset之后就可以从0开始遍历了
  _dsMonikerDevEnum->Reset();
  ULONG cFetched;
  IMoniker* pM;

  IBaseFilter* captureFilter = NULL;
  bool deviceFound = false;
  // 使用Moniker遍历所有视频采集设备
  while (S_OK == _dsMonikerDevEnum->Next(1, &pM, &cFetched) && !deviceFound) {
   
   
    IPropertyBag* pBag;
    // 获取对象的Bag接口,通过这个Bag接口后续获取属性
    hr = pM->BindToStorage(0, 0, IID_IPropertyBag, (void**)&pBag);
    if (S_OK == hr) {
   
   
      // Find the description or friendly name.
      // 先找设备唯一标识,找不到就去找设备描述,再找不到就去找设备名
      VARIANT varName;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值