当前位置: 首页 > news >正文

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

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

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

  • WebRTC源码之摄像头视频数据采集源码分析
  • 前言
    • 一、 VideoCapture
      • 1、 IKsPropertypSet
      • 1、 ComRefCount
        • ①、模板
        • ②、使用模板
        • ③、模板展开
      • 3、PIN_INFO
      • 4、Filter与FilterGraph 关系图
      • 6、如何将两个Filter连接在一起的api
        • ①、IFilterGraph::ConnectDirect方法介绍
        • ②、AM_MEDIA_TYPE类型解读
      • 3、 注意事项
      • 4、Filter的连接过程
      • 5、 Pin协商媒体的类型
      • 6、协商分配器
      • 7、 push与pull
      • 8、allocator
      • 9、allocator协商的过程
    • FilterGraph中的数据流
    • 投递采样
    • SetCameraOutput流程图
    • IAMStreamConfig
    • IAMStreamControl 控制播放
  • 总结:


WebRTC专题开嗨鸭 !!!

一、 WebRTC 线程模型

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

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

二、 WebRTC媒体协商

三、 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

相关文章:

  • stm32f4xx-PWM输出
  • 【博客474】为什么k8s控制面pod使用的ip是node ip,而非pod cidr中的ip
  • 2022 华为 Java 高级面试题及答案
  • SpringCloud集成RocketMQ
  • 计算机java毕业设计选题汇总(2022)
  • Ruby on Rails 实践:课程导读
  • OpenGL基本架构知识
  • 神奇的卡尔曼滤波,行人追踪的福音
  • 第三章 教育法律法规
  • MATLAB | 全网唯一,使用MATLAB绘制好看的韦恩图(venn)
  • 2D Transpose算子GPU实现和优化
  • 软件复杂性的来源与应对
  • 11.9 表达式求值
  • 09-排序3 Insertion or Heap Sort(浙大数据结构)
  • java-python+vue社区防疫服务管理系统网站
  • [ 一起学React系列 -- 8 ] React中的文件上传
  • Apache Zeppelin在Apache Trafodion上的可视化
  • Consul Config 使用Git做版本控制的实现
  • Git学习与使用心得(1)—— 初始化
  • input实现文字超出省略号功能
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • java正则表式的使用
  • nginx 配置多 域名 + 多 https
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • Spark RDD学习: aggregate函数
  • vue脚手架vue-cli
  • 阿里云应用高可用服务公测发布
  • 第13期 DApp 榜单 :来,吃我这波安利
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 警报:线上事故之CountDownLatch的威力
  • 使用common-codec进行md5加密
  • 使用Swoole加速Laravel(正式环境中)
  • 提醒我喝水chrome插件开发指南
  • 突破自己的技术思维
  • 异常机制详解
  • 终端用户监控:真实用户监控还是模拟监控?
  • 移动端高清、多屏适配方案
  • #android不同版本废弃api,新api。
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • #QT(串口助手-界面)
  • (10)Linux冯诺依曼结构操作系统的再次理解
  • (33)STM32——485实验笔记
  • (C语言)深入理解指针2之野指针与传值与传址与assert断言
  • (C语言)字符分类函数
  • (待修改)PyG安装步骤
  • (二十一)devops持续集成开发——使用jenkins的Docker Pipeline插件完成docker项目的pipeline流水线发布
  • (四)Android布局类型(线性布局LinearLayout)
  • (转)GCC在C语言中内嵌汇编 asm __volatile__
  • ***通过什么方式***网吧
  • *p++,*(p++),*++p,(*p)++区别?
  • .【机器学习】隐马尔可夫模型(Hidden Markov Model,HMM)
  • .NET / MSBuild 扩展编译时什么时候用 BeforeTargets / AfterTargets 什么时候用 DependsOnTargets?
  • .Net 6.0 处理跨域的方式
  • .net core 6 集成和使用 mongodb
  • .net php 通信,flash与asp/php/asp.net通信的方法