WebRTC源码之摄像头视频数据采集源码分析

WebRTC源码之摄像头视频数据采集源码分析


WebRTC专题开嗨鸭 !!!

一、 WebRTC 线程模型

1、WebRTC中线程模型和常见线程模型介绍

2、WebRTC网络PhysicalSocketServer之WSAEventselect模型使用

二、 WebRTC媒体协商

1、WebRTC媒体协商之SDP中JsepSessionDescription类结构分析

三、 WebRTC 音频数据采集

1、WebRTC源码之音频设备播放流程源码分析

2、WebRTC源码之音频设备的录制流程源码分析

四、 WebRTC 音频引擎(编解码和3A算法)

五、 WebRTC 视频数据采集

1、WebRTC源码之摄像头视频数据采集源码分析

六、 WebRTC 视频引擎( 编解码)

七、 WebRTC 网络传输

1、WebRTC的ICE之STUN协议

2、WebRTC的ICE之Dtls/SSL/TLSv1.x协议详解

八、 WebRTC服务质量(Qos)

1、WebRTC中RTCP协议详解

2、WebRTC中RTP协议详解

3、WebRTC之NACK、RTX 在什么时机判断丢包发送NACK请求和RTX丢包重传

4、WebRTC源码之视频质量统计数据的数据结构分析

九、 NetEQ

十、 Simulcast与SVC

前言

WebRTC视频采集流程图

在这里插入图片描述

一、 VideoCapture

在这里插入图片描述

IPin::QueryDirection方法

HRESULT QueryDirection([out] PIN_DIRECTION* pPinDir);

参数1: 获取Pin的方向   PINDIR_INPUT/PINDIR_OUT

1、 IKsPropertypSet

  1. 提供了访问驱动程序底层扩展属性的能力
  2. 它提供了Get/Set方法获取或设置扩展属性
  3. 通过Pin的QueryInterface可以获得该接口

WebRTC中使用 IKsPropertySet::Get方法

HRESULT Get([in] REFGUID rguidPropSet/*要访问的属性集*/,
        [in] ULONG ulId /*要访问的属性集中的某一项*/, 
        [out] LPVOID pInstanceData/*实例数据*/,
        [out] ULONG ulInstanceLength/*实例数据长度*/,
        [out] LPVOID pPropertyData/*读到的属性数据*/,
        [out] ULONG ulDataLength/*存放属性数据的长度*/,
        [out] PULONG pulBytesReturened/*真正读到的属性数据长度*/)

1、 ComRefCount

①、模板
// Provides a reference count implementation for COM (IUnknown derived) classes.
// The implementation uses atomics for managing the ref count.
template <class T>
class ComRefCount : public T {
 public:
  ComRefCount() {}

  template <class P0>
  explicit ComRefCount(P0&& p0) : T(std::forward<P0>(p0)) {}

  STDMETHOD_(ULONG, AddRef)() override {
    ref_count_.IncRef();
    return 1;
  }

  STDMETHOD_(ULONG, Release)() override {
    const auto status = ref_count_.DecRef();
    if (status == rtc::RefCountReleaseStatus::kDroppedLastRef) {
      delete this;
      return 0;
    }
    return 1;
  }

 protected:
  ~ComRefCount() {}

 private:
  webrtc::webrtc_impl::RefCounter ref_count_{0};
};

②、使用模板
// Create the sink filte used for receiving Captured frames.
  sink_filter_ = new ComRefCount<CaptureSinkFilter>(this);

input_pin_ = (new ComRefCount<CaptureInputPin>(this))

③、模板展开

// 1、是一个Filter
class ComRefCount : public CaptureSinkFilter {
 public:
  ComRefCount() {}
 
  explicit ComRefCount(VideoCaptureDS&& p0) : CaptureSinkFilter(std::forward<VideoCaptureDS>(p0)) {}

  STDMETHOD_(ULONG, AddRef)() override {
    ref_count_.IncRef();
    return 1;
  }

  STDMETHOD_(ULONG, Release)() override {
    const auto status = ref_count_.DecRef();
    if (status == rtc::RefCountReleaseStatus::kDroppedLastRef) {
      delete this;
      return 0;
    }
    return 1;
  }

 protected:
  ~ComRefCount() {}

 private:
  webrtc::webrtc_impl::RefCounter ref_count_{0};
};

/
// 2. 是一个Pin
class ComRefCount : public CaptureInputPin {
 public:
  ComRefCount() {}
 
  explicit ComRefCount(CaptureSinkFilter&& p0) : CaptureInputPin(std::forward<CaptureSinkFilter>(p0)) {}

  STDMETHOD_(ULONG, AddRef)() override {
    ref_count_.IncRef();
    return 1;
  }

  STDMETHOD_(ULONG, Release)() override {
    const auto status = ref_count_.DecRef();
    if (status == rtc::RefCountReleaseStatus::kDroppedLastRef) {
      delete this;
      return 0;
    }
    return 1;
  }

 protected:
  ~ComRefCount() {}

 private:
  webrtc::webrtc_impl::RefCounter ref_count_{0};
};


3、PIN_INFO


typedef struct _PinInfo
    {
    IBaseFilter *pFilter; // Filter指针
    PIN_DIRECTION dir; // Pin的方向
    WCHAR achName[ 128 ]; // Pin的名子
    } 	PIN_INFO;

4、Filter与FilterGraph 关系图

Filter与FilterGraph 关系图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4i0NW6DK-1662190677029)(./img/filter_and_filtergraph.jpg)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kJEEYCVw-1662190677030)(./img/filter_connect.jpg)]

WebRTC中底层CapturedFilter拿到数据, 然后应用层从SinkFilter获取数据

6、如何将两个Filter连接在一起的api

①、IFilterGraph::ConnectDirect方法介绍

 IFilterGraph : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE AddFilter( 
            /* [in] */ IBaseFilter *pFilter,
            /* [string][in] */ LPCWSTR pName) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE RemoveFilter( 
            /* [in] */ IBaseFilter *pFilter) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE EnumFilters( 
            /* [annotation][out] */ 
            _Out_  IEnumFilters **ppEnum) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE FindFilterByName( 
            /* [string][in] */ LPCWSTR pName,
            /* [annotation][out] */ 
            _Out_  IBaseFilter **ppFilter) = 0;
        // 两个Filter连接在一起的api
         // TODO@chensong 20220828  将CapturePin和 sinkPin连接起来的api
	  // param1 : 前一个Filter的输出Pin
	  // param2 : 后一个Filter的输入Pin
	  // param3 : AM_MEDIA_TYPE等于DMO_MEDIA_TYPE
        virtual HRESULT STDMETHODCALLTYPE ConnectDirect( 
            /* [in] */ IPin *ppinOut,
            /* [in] */ IPin *ppinIn,
            /* [annotation][unique][in] */ 
            _In_opt_  const AM_MEDIA_TYPE *pmt) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE Reconnect( 
            /* [in] */ IPin *ppin) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE Disconnect( 
            /* [in] */ IPin *ppin) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetDefaultSyncSource( void) = 0;
        
    };

②、AM_MEDIA_TYPE类型解读
typedef struct _AMMediaType
    {
    GUID majortype;          // 流的主类型GUID
    GUID subtype;            // 流的子类型GUID
    BOOL bFixedSizeSamples;     // 采样算法是固定大小,音频为TRUE
    BOOL bTemporalCompression;   // 时域压缩
    ULONG lSampleSize;        // 以字节为单位的采样大小
    GUID formattype;         // 数据格式类型、音频为WAVEFORMATEX
    IUnknown *pUnk;          // 未使用
    ULONG cbFormat;           // 不同媒体类型格式块的大小
    /* [size_is] */ BYTE *pbFormat; // 根据cbFormat决定该字段
    } 	AM_MEDIA_TYPE;

3、 注意事项

  1. 只有在同一个FilterGraph中Filter才能进行连接
  2. Filter间的连接不能直接用Pin或Filter的相关方法
  3. 需要使用IFilterGraph的ConnectDirect方法

4、Filter的连接过程

FilterGraph调用输出Pin的Connect方法、连接输入Pin

之后调用输入Pin的ReceivConnection方法, 是否允许双方的连接

如果ReceivConnection接受连接,则两个Pin连接成功

5、 Pin协商媒体的类型

Complete Type : 指定了具体的媒体类型 : 举例子: I420格式, rgba格式 等等

Parial Media Type : 部分类型为GUID_NULL、表示任意类型

No Media Type: 传入NULL, 表示两个Pin可以接受任意媒体类型

6、协商分配器

两个Filter交换媒体数据的机制称为transport

通常两个Filter之间通过本地内存来交换数据

有两种本地内存交换数据的方式: push和pull

7、 push与pull

push模式: 源Filter使用IMeminputPin, push数据到下游Filter

pull模式: 下游Filter向源Filter请求,通过IAsyncReader获取数据

WebRTC使用的是push模式

8、allocator

负责分配内存Buffer的对象称为allocator

allocator支持IMemAllocator接口

两个Pin共享同一个allocator

无论谁提供allocator,最终由输出Pin决定使用那个allocator

输出Pin还决定allocator的属性

  1. allocator需要创建多少个buffer
  2. 每个buffer的大小
  3. 内存是否对齐

9、allocator协商的过程

输出Pin调用输入Pin的GetAllocatorRequirements获取buffer大小

输出Pin调用输入Pin的GetAllocator获取allocator

输入Pin调用NotifyAllocator通知输入Pin所做的选择

当流开始或停止时,输出Pin负责提交或者撤销allocator

FilterGraph中的数据流

有两种数据流: 媒体数据和控制数据

媒体数据向下游传播,控制数据向上游传播

音频、视频等都属于媒体数据

flush、流结束通知等都属于控制数据

投递采样

输出Pin调用输入Pin的Receive和ReciveMultiple投递采样

如果Pin可以阻塞, ReceivecanBlock能返回S_OK

但通信情况下不应该产生阻塞

SetCameraOutput流程图

六大步骤

在这里插入图片描述

IAMStreamConfig

该接口用于报告/设置设备支持的格式和能力

CaptureFilter的CapturePin和PreviewPin支持该接口

GetNumberOfCapabilities获取Pin支持的Capa数量

GetStreamCaps获取某个具体Capability

SetFormat用于设置硬件设备的输出格式

 MIDL_INTERFACE("C6E13340-30AC-11d0-A18C-00A0C9118956")
    IAMStreamConfig : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE SetFormat( 
            /* [in] */ AM_MEDIA_TYPE *pmt) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetFormat( 
            /* [annotation][out] */ 
            _Out_  AM_MEDIA_TYPE **ppmt) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetNumberOfCapabilities( 
            /* [annotation][out] */ 
            _Out_  int *piCount/*支持哪些Capabilities*/,
            /* [annotation][out] */ 
            _Out_  int *piSize/*每个Capabilities的大小*/) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetStreamCaps( 
            /* [in] */ int iIndex/*指定获取某个Index的Capability, 从0开始*/,
            /* [annotation][out] */ 
            _Out_  AM_MEDIA_TYPE **ppmt/*该方法分配且用媒体类型填充的*/,
            /* [annotation][out] */ 
            _Out_  BYTE *pSCC/*调用者分配 VIDEO_STREAM_CONFIG_CAPS*/) = 0;
        
    };

IAMStreamControl 控制播放

该接口用于控制视频采集操作, 如枚举帧率于图像方向

GetFrameRateList用于获取采集帧率列表

MIDL_INTERFACE("6a2e0670-28e4-11d0-a18c-00a0c9118956")
    IAMVideoControl : public IUnknown
    {
    public:
        virtual HRESULT STDMETHODCALLTYPE GetCaps( 
            /* [in] */ IPin *pPin,
            /* [annotation][out] */ 
            _Out_  long *pCapsFlags) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetMode( 
            /* [in] */ IPin *pPin,
            /* [in] */ long Mode) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetMode( 
            /* [in] */ IPin *pPin,
            /* [annotation][out] */ 
            _Out_  long *Mode) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetCurrentActualFrameRate( 
            /* [in] */ IPin *pPin,
            /* [annotation][out] */ 
            _Out_  LONGLONG *ActualFrameRate) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetMaxAvailableFrameRate( 
            /* [in] */ IPin *pPin,
            /* [in] */ long iIndex,
            /* [in] */ SIZE Dimensions,
            /* [annotation][out] */ 
            _Out_  LONGLONG *MaxAvailableFrameRate) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetFrameRateList( 
            /* [in] */ IPin *pPin /*查询帧率的Pin*/,
            /* [in] */ long iIndex /*查询帧率格式的索引值*/,
            /* [in] */ SIZE Dimensions /*以像素为单位图像的大小*/,
            /* [annotation][out] */ 
            _Out_  long *ListSize/*用于存放帧率列表元素个数*/,
            /* [annotation][out] */ 
            _Out_  LONGLONG **FrameRates/*存放帧率数组的地址指针*/) = 0;
        
    };

总结:

WebRTC源码分析地址:https://github.com/chensongpoixs/cwebrtc

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值