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

WebRTC、audio、AEC

Posted by chensong on 2022-08-14 23::50::33

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

WebRTC专题开嗨鸭 !!!

一、 WebRTC 线程模型

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

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

二、 WebRTC媒体协商

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

三、 WebRTC 音频数据采集

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

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

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

五、 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音频引擎启动基本流程图

webrtc_audio_init

ADM

一、 ADM的作用即创建

1、Audio Device Module 类关系图

Audio Device Module 类关系图

2、创建ADM的时机

3、创建ADM的过程

二、 WIndows Audio Core 基本概念

1、从Window Vista开始提供了 Core Audio API

2、 其运行在用户空间的

3、Core Audio 是DirectSound和WaveXxx的基础

4、Win7 对Core Audio API做了增强的

5、Core Audio 的特性

①、低延迟

②、提高了可靠性,API从内核层移到了用户层

③、安全性更高

等等…

6、Core Audio关系图

MMDevice API

WAS API

DeviceTopology API

EndpointVolume API

Core Audio 有两种模式: 同享模式和独享模式

7、音频设备种类

  1. 音频终端设备,指外设
  2. 音频适配器设备,一般集成在主板上,接供PCI/PNP插口

8、音频终端设备

  1. 扬声器
  2. 麦克风
  3. 辅助输入设备(耳机)

9、音频适配设备

  1. 不行输出设备(数模转换器)
  2. 输出控制设备(音量和静音控件)
  3. 波输入设备(模数转换器)
  4. 输入控制设备(输入音量控制器,多数复用器)

三、 Audio Core API

分下面四种API

1、MMDevice API,最常用到的接口

①、MMDevice API 的作用

  1. 获得音频终端设备得Client
  2. 决定音频终端设备的能力
  3. 能为音频终端设备创建驱动实例

②、MMDevice API的组成

  1. IMMDevice,代表一个音频设备
  2. IMMDeviceCollection,音频设备集
  3. IMMDeviceEnumerator,用于枚举音频设备
  4. IMMEndpoint,代表一个音频终端设备

③、IMMDeviceEnumerator接口

  1. 它是IMMDevice API种最关键的接口
  2. 通过它可以获得IMMDevice接口
  3. 通过它可以获取IMMDeviceCollection接口

④、创建IMMDeviceEnumerator接口

⑤、COM组件

  1. 它是一个小的,可单独运行的程序;使用时类似一个API
  2. 应用程序可以通过多个COM组件的构成
  3. COM组件有可复用、动态更新、灵活等优点
要记住 1. 两个COM对象能实现相同的接口 通过CLSID找到组件 通过IID找到COM组件种的接口 2. 每个COM组件对象可以实现多个接口

⑥、IMMDeviceEnumerator支持的方法

  1. GateDefaultAudioEndpoint获取默认设备
  2. EnumAudioEndpoint枚举音频设备

等等

2、Windows Audio Session API (WASAPI)

3、Device Topology API(更高级的API)

4、EndpointVolume API( 修改主声音或独享方式使用)

四、ADM的初始化与设备管理

五、AudioState 的作用

六、音频播放设备的初始化

七、音频播放设备任何工作

八、音频采集设备的初始化

九、音频采集设备任何工作

十、采集后的数据交给谁?

十一、播放设置从哪儿获取数据?

InitPlay基础知识

一、几个主要的状态值

状态 默认值 作用
_playing false 是否处于播放状态
_playIsinitialized false 是否对播放参数进行了设置
_buildInAecEnabled false 是否启用了硬件AEC功能
_recIsInitiaized false 是否对录录制参数进行了设备
_recording false 是否处理录制状态
_speakerIsInitialized false 是否对扬声器进行了初始化
_microphoneIsIntialized false 是否对microphone进行了初始化

二、音频的数据结构

/* general extended waveform format structure
   Use this for all NON PCM formats
   (information common to all formats)
*/
#ifndef _WAVEFORMATEX_
#define _WAVEFORMATEX_

typedef struct tWAVEFORMATEX
{
    WORD    wFormatTag;        /* 设置声音格式类型 PCM  format type  */
    WORD    nChannels;         /* 设置通道数 number of channels (i.e. mono, stereo...)   */
    DWORD   nSamplesPerSec;    /* 设置采样率  sample rate */
    DWORD   nAvgBytesPerSec;   /* 设置每秒平均字节数 for buffer estimation */
    WORD    nBlockAlign;       /* 设置对齐字节数 block size of data */
    WORD    wBitsPerSample;    /*设置每个采样的大小(位深) Number of bits per sample of mono data */
    WORD    cbSize;            /* 额外消息大小 The count in bytes of the size of
                                    extra information (after cbSize) */

} WAVEFORMATEX;
typedef WAVEFORMATEX       *PWAVEFORMATEX;
typedef WAVEFORMATEX NEAR *NPWAVEFORMATEX;
typedef WAVEFORMATEX FAR  *LPWAVEFORMATEX;
#endif /* _WAVEFORMATEX_ */

#ifdef GUID_DEFINED

三、 音频接口 IAudioClient 接口

  1. 该接口属于WASAPI
  2. 该接口能够再音频应用程序和音频引擎之间采集音频流
  3. 也能再应用程序和设备的硬件缓冲区之间创建音频流
音频设备客户端分为两种分别: 1. 采集客户端 2. 扬声器客户端

 MIDL_INTERFACE("1CB9AD4C-DBFA-4c32-B178-C2F568A703B2")
    IAudioClient : public IUnknown
    {
    public:
         ////////////////////////////////////////////////////
          //        TODO@chensong 2022-07-24 初始化音频流
          ///////////////////////////////////////////////////
          /**
          * @param AUDCLNT_SHAREMODE : 共享模式/独占模式
          * @param DWORD             : 控制流创建的flag
          * @param REFERENCE_TIME    : 请求缓冲区的大小
          * @param REFERENCE_TIME    : 周期时间
          * @param WAVEFORMATEX      : 数据格式
          * @param LPCGUID           : Audio Session 的 GUID       
          * @return
          */
          
          /////////////////////////////////////////////////////////////////
          // StreamFlags 参数类型解释:
          // AUDCLNT_STREAMFLAGS_LOOPBACK		: 
          // AUDCLNT_STREAMFLAGS_EVENTCALLBACK	:
          // ...
        virtual HRESULT STDMETHODCALLTYPE Initialize( 
            /* [annotation][in] */ 
            _In_  AUDCLNT_SHAREMODE ShareMode,
            /* [annotation][in] */ 
            _In_  DWORD StreamFlags,
            /* [annotation][in] */ 
            _In_  REFERENCE_TIME hnsBufferDuration,
            /* [annotation][in] */ 
            _In_  REFERENCE_TIME hnsPeriodicity,
            /* [annotation][in] */ 
            _In_  const WAVEFORMATEX *pFormat,
            /* [annotation][in] */ 
            _In_opt_  LPCGUID AudioSessionGuid) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetBufferSize( 
            /* [annotation][out] */ 
            _Out_  UINT32 *pNumBufferFrames) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetStreamLatency( 
            /* [annotation][out] */ 
            _Out_  REFERENCE_TIME *phnsLatency) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetCurrentPadding( 
            /* [annotation][out] */ 
            _Out_  UINT32 *pNumPaddingFrames) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE IsFormatSupported( 
            /* [annotation][in] */ 
            _In_  AUDCLNT_SHAREMODE ShareMode,
            /* [annotation][in] */ 
            _In_  const WAVEFORMATEX *pFormat,
            /* [unique][annotation][out] */ 
            _Out_opt_  WAVEFORMATEX **ppClosestMatch) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetMixFormat( 
            /* [annotation][out] */ 
            _Out_  WAVEFORMATEX **ppDeviceFormat) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE GetDevicePeriod( 
            /* [annotation][out] */ 
            _Out_opt_  REFERENCE_TIME *phnsDefaultDevicePeriod,
            /* [annotation][out] */ 
            _Out_opt_  REFERENCE_TIME *phnsMinimumDevicePeriod) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE Start( void) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE Stop( void) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
        
        virtual HRESULT STDMETHODCALLTYPE SetEventHandle( 
            /* [in] */ HANDLE eventHandle) = 0;
        
        /////////////////////////////////////////////////////////
          //   TODO@chensong 2022-07-24 GetService 方法解释
          ////////////////////////////////////////////////////////
          /** 
          * 
          *   @param rrid    : 接口ID
          *   @param ppv     : 输出的接口对象
          *   return STDMETHODCALLTYPE 
          */
        virtual HRESULT STDMETHODCALLTYPE GetService( 
            /* [annotation][in] */ 
            _In_  REFIID riid,
            /* [annotation][iid_is][out] */ 
            _Out_  void **ppv) = 0;
        
    };
    

音频流的访问方式

1、IAuidoClient的几个重要方法

  1. Initialize (模式[共享,独享] …)
  2. GetBufferSize
  3. SetEventHandle
  4. GetService

①、初始化音频流

WebRTC源码中音频流初始化在 modules/audio_device/win/audio_device_core_win.cc文件中InitPlayout方法中调用的音频流初始化方法Initialize

...

////////////////////////////////////////////////////
  //        TODO@chensong 2022-07-24 初始化音频流
  ///////////////////////////////////////////////////
  /**
  * @param AUDCLNT_SHAREMODE : 共享模式/独占模式
  * @param DWORD             : 控制流创建的flag
  * @param REFERENCE_TIME    : 请求缓冲区的大小 [是与时间有关系的哈]
  * @param REFERENCE_TIME    : 周期时间 [10ms采集一次、 20ms采集一次]
  * @param WAVEFORMATEX      : 数据格式
  * @param LPCGUID           : Audio Session 的 GUID       
  * @return
  */

 /////////////////////////////////////////////////////////////////
  // StreamFlags 参数类型解释:
  // AUDCLNT_STREAMFLAGS_LOOPBACK		: 
  // AUDCLNT_STREAMFLAGS_EVENTCALLBACK	:
  // ...
  hr = _ptrClientOut->Initialize(
      AUDCLNT_SHAREMODE_SHARED,  // share Audio Engine with other applications
      AUDCLNT_STREAMFLAGS_EVENTCALLBACK,  // processing of the audio buffer by
                                          // the client will be event driven
      hnsBufferDuration,  // requested buffer capacity as a time value (in
                          // 100-nanosecond units)
      0,                  // periodicity
      &Wfx,               // selected wave format
      NULL);              // session GUID

  if (FAILED(hr)) {
    RTC_LOG(LS_ERROR) << "IAudioClient::Initialize() failed:";
  }
...

②、GetService方法

  1. 它是IAudioClient中的方法
  2. 用于获取IAudioRenderClient接口
  3. 用于下写数据到渲染终端的buffer

WebRTC源码中音频流初始化在 modules/audio_device/win/audio_device_core_win.cc文件中InitPlayout方法中调用的音频流初始化方法GetService


...
    
/////////////////////////////////////////////////////////
  //   TODO@chensong 2022-07-24 GetService 方法解释
  ////////////////////////////////////////////////////////
  /** 
  * 
  *   @param rrid    : 接口ID
  *   @param ppv     : 输出的接口对象
  *   return STDMETHODCALLTYPE 
  */
  // Get an IAudioRenderClient interface.
  SAFE_RELEASE(_ptrRenderClient);
  hr = _ptrClientOut->GetService(__uuidof(IAudioRenderClient),
                                 (void**)&_ptrRenderClient);
  EXIT_ON_ERROR(hr);

...

总结:

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