WebRtc中VoiceEngine可以完成大部分的VOIP相关人物,包括采集、自动增益、噪声消除、回声抑制、编解码、RTP传输。下边我们通过代码来解析Voe中处理流程;

创建VoiceEngine和VoEBase

[cpp]
VoiceEngine* _vePtr = VoiceEngine::Create();                               //创建VoiceEngine  
VoEBase* _veBasePtr = VoEBase::GetInterface(_vePtr);            //创建VoeBase  所有Voe相关操作通过这个共有类  
_veBasePtr->Init()                                                                                 //创建整个Voe处理线程

VoiceEngine* _vePtr = VoiceEngine::Create();                              //创建VoiceEngine
VoEBase* _veBasePtr = VoEBase::GetInterface(_vePtr);            //创建VoeBase  所有Voe相关操作通过这个共有类
_veBasePtr->Init()                                                                               //创建整个Voe处理线程
重点就在_veBasePtr->Init()  它会创建voe线程,线程负责采集、数字信号处理、编码、rtp传输。

[cpp]
int VoEBaseImpl::Init(AudioDeviceModule* external_adm,   AudioProcessing* audioproc) 

    _shared->process_thread();   //创建voe线程  
    _shared->process_thread()->Start(); 
    _shared->audio_device()->Init(); 
    
 
}

int VoEBaseImpl::Init(AudioDeviceModule* external_adm,   AudioProcessing* audioproc)
{
    _shared->process_thread();   //创建voe线程
    _shared->process_thread()->Start();
    _shared->audio_device()->Init();

}audio_device()->Init()重载了int32_t AudioDeviceWindowsWave::Init()(windowns平台),别的平台是别的函数,基本差不多,在这个Init中,创建了ThreadProcess线程,ThreadProcess线程负责所有的音频流程,从设备获取音频数据包。

[cpp]
bool AudioDeviceWindowsWave::ThreadProcess() 

   while ((nRecordedBytes = RecProc(recTime)) > 0); 
}

bool AudioDeviceWindowsWave::ThreadProcess()
{
   while ((nRecordedBytes = RecProc(recTime)) > 0);
}处理过程在RecProc

[cpp]
int32_t AudioDeviceWindowsWave::RecProc(LONGLONG& consumedTime) 

     _ptrAudioBuffer->DeliverRecordedData(); <SPAN style="FONT-FAMILY: Arial, Helvetica, sans-serif">}</SPAN>

int32_t AudioDeviceWindowsWave::RecProc(LONGLONG& consumedTime)
{
     _ptrAudioBuffer->DeliverRecordedData(); }

[cpp]
int32_t AudioDeviceBuffer::DeliverRecordedData() 

     _ptrCbAudioTransport->RecordedDataIsAvailable(); 
}

int32_t AudioDeviceBuffer::DeliverRecordedData()
{
     _ptrCbAudioTransport->RecordedDataIsAvailable();
}

RecordedDataIsAvailable是虚函数,被VoeBase重载

[cpp]
int32_t VoEBaseImpl::RecordedDataIsAvailable( 
        const void* audioSamples, 
        uint32_t nSamples, 
        uint8_t nBytesPerSample, 
        uint8_t nChannels, 
        uint32_t samplesPerSec, 
        uint32_t totalDelayMS, 
        int32_t clockDrift, 
        uint32_t currentMicLevel, 
        bool keyPressed, 
        uint32_t& newMicLevel) 

     _shared->transmit_mixer()->DemuxAndMix(); 
    _shared->transmit_mixer()->EncodeAndSend(); 
}

int32_t VoEBaseImpl::RecordedDataIsAvailable(
        const void* audioSamples,
        uint32_t nSamples,
        uint8_t nBytesPerSample,
        uint8_t nChannels,
        uint32_t samplesPerSec,
        uint32_t totalDelayMS,
        int32_t clockDrift,
        uint32_t currentMicLevel,
        bool keyPressed,
        uint32_t& newMicLevel)
{
     _shared->transmit_mixer()->DemuxAndMix();
    _shared->transmit_mixer()->EncodeAndSend();
}
DemuxAndMix() 从字面意思是分路与混合,这个函数,主要负责AudioProcess的所有过程,包括Aec,Aecm,AGC,DTMF,遍历所有channel;

[cpp]
TransmitMixer::DemuxAndMix() 

    Channel* channelPtr = sc.GetFirstChannel(iterator); 
    while (channelPtr != NULL) 
    { 
        if (channelPtr->InputIsOnHold()) 
        { 
            channelPtr->UpdateLocalTimeStamp(); 
        } else if (channelPtr->Sending()) 
        { 
            // Demultiplex makes a copy of its input.  
            channelPtr->Demultiplex(_audioFrame); 
            channelPtr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_); 
        } 
        channelPtr = sc.GetNextChannel(iterator); 
    } 
 
}

TransmitMixer::DemuxAndMix()
{
    Channel* channelPtr = sc.GetFirstChannel(iterator);
    while (channelPtr != NULL)
    {
        if (channelPtr->InputIsOnHold())
        {
            channelPtr->UpdateLocalTimeStamp();
        } else if (channelPtr->Sending())
        {
            // Demultiplex makes a copy of its input.
            channelPtr->Demultiplex(_audioFrame);
            channelPtr->PrepareEncodeAndSend(_audioFrame.sample_rate_hz_);
        }
        channelPtr = sc.GetNextChannel(iterator);
    }

}
Channel::Demutiplex(),基本上没有什么具体任务,就是把audioFrame里边的数据拷贝到channel自身, webrtc是client解决方案,对于client只认为有一个audio source,但可以有多个channel,每个channel中都有audio process,所以需要把数据copy到每个channel.

只有就是数据处理 PrepareEncodeAndSend()

[cpp]
Channel::PrepareEncodeAndSend(int mixingFrequency) 

   if (_inputFilePlaying) 
    { 
        MixOrReplaceAudioWithFile(mixingFrequency); //如果使用了voeFile::PlayFileAsMic();则从文件读取10ms数据,并覆盖audio buffer  
    } 
 
    if (_mute) 
    { 
        AudioFrameOperations::Mute(_audioFrame);//当然如果设置mutex,则memset 0   
    } 
   if (_inputExternalMedia) 
   { 
   _inputExternalMediaCallbackPtr->Process();  //所过设置了ExternalMedia,自己的audio处理过程,就是在这里调用的  
   } 
  InsertInbandDtmfTone();                     //添加DTMF音频  
 _rtpAudioProc->ProcessStream(&_audioFrame);  // 真正的GIPS牛逼代码,audio process过程: Aec Aecm AGC   
}

Channel::PrepareEncodeAndSend(int mixingFrequency)
{
   if (_inputFilePlaying)
    {
        MixOrReplaceAudioWithFile(mixingFrequency); //如果使用了voeFile::PlayFileAsMic();则从文件读取10ms数据,并覆盖audio buffer
    }

if (_mute)
    {
        AudioFrameOperations::Mute(_audioFrame);//当然如果设置mutex,则memset 0
    }
   if (_inputExternalMedia)
   {
   _inputExternalMediaCallbackPtr->Process();  //所过设置了ExternalMedia,自己的audio处理过程,就是在这里调用的
   }
  InsertInbandDtmfTone();                     //添加DTMF音频
 _rtpAudioProc->ProcessStream(&_audioFrame);  // 真正的GIPS牛逼代码,audio process过程: Aec Aecm AGC
}
int AudioProcessingImpl::ProcessStream(AudioFrame* frame) 就是上述调用的_rtpAudioProc->ProcessStream();

以上是DemuxAndMix()过程,之后就是EncodeAndSend()过程,至此整个voe数据处理流程分析结束;

关于Audio Process则是另外一个大话题;

总结一下几点:

1.  VoeBase提供大部分的对外接口

2. Channel:继承了大部分的音频功能;

WebRtc VoiceEngine代码解析的更多相关文章

  1. WebRTC VoiceEngine综合应用示例(一)——基本结构分析(转)

    把自己这两天学习VoiceEngine的成果分享出来,供大家参考,有什么问题也欢迎大家指出,一起学习一起进步. 本文将对VoiceEngine的基本结构做一个分析,分析的方法是自底向上的:看一个音频编 ...

  2. VBA常用代码解析

    031 删除工作表中的空行 如果需要删除工作表中所有的空行,可以使用下面的代码. Sub DelBlankRow() DimrRow As Long DimLRow As Long Dimi As L ...

  3. [nRF51822] 12、基础实验代码解析大全 · 实验19 - PWM

    一.PWM概述: PWM(Pulse Width Modulation):脉冲宽度调制技术,通过对一系列脉冲的宽度进行调制,来等效地获得所需要波形. PWM 的几个基本概念: 1) 占空比:占空比是指 ...

  4. [nRF51822] 11、基础实验代码解析大全 · 实验16 - 内部FLASH读写

     一.实验内容: 通过串口发送单个字符到NRF51822,NRF51822 接收到字符后将其写入到FLASH 的最后一页,之后将其读出并通过串口打印出数据. 二.nRF51822芯片内部flash知识 ...

  5. [nRF51822] 10、基础实验代码解析大全 · 实验15 - RTC

    一.实验内容: 配置NRF51822 的RTC0 的TICK 频率为8Hz,COMPARE0 匹配事件触发周期为3 秒,并使能了TICK 和COMPARE0 中断. TICK 中断中驱动指示灯D1 翻 ...

  6. [nRF51822] 9、基础实验代码解析大全 · 实验12 - ADC

    一.本实验ADC 配置 分辨率:10 位. 输入通道:5,即使用输入通道AIN5 检测电位器的电压. ADC 基准电压:1.2V. 二.NRF51822 ADC 管脚分布 NRF51822 的ADC ...

  7. java集合框架之java HashMap代码解析

     java集合框架之java HashMap代码解析 文章Java集合框架综述后,具体集合类的代码,首先以既熟悉又陌生的HashMap开始. 源自http://www.codeceo.com/arti ...

  8. Kakfa揭秘 Day8 DirectKafkaStream代码解析

    Kakfa揭秘 Day8 DirectKafkaStream代码解析 今天让我们进入SparkStreaming,看一下其中重要的Kafka模块DirectStream的具体实现. 构造Stream ...

  9. linux内存管理--slab及其代码解析

    Linux内核使用了源自于 Solaris 的一种方法,但是这种方法在嵌入式系统中已经使用了很长时间了,它是将内存作为对象按照大小进行分配,被称为slab高速缓存. 内存管理的目标是提供一种方法,为实 ...

随机推荐

  1. 浅析JavaScript引用类型之--Object、Array

    1.Object类型 对象是某个特定引用类型的实例,新对象有两种创建方式: i.使用new操作符调用构造函数来创建. var person = new Object(); person.name = ...

  2. UC编程之线程

    线程--隶属于进程,是进程中的程序流.操作系统支持多进程,每个进程内部支持多线程.多线程并行(同时执行)代码. 进程--重量级的,每个进程都需要独立的内存空间. 线程--轻量级的,线程不拥有独立的内存 ...

  3. Codeforces Round #272 (Div. 2) D. Dreamoon and Sets (思维 数学 规律)

    题目链接 题意: 1-m中,四个数凑成一组,满足任意2个数的gcd=k,求一个最小的m使得凑成n组解.并输出 分析: 直接粘一下两个很有意思的分析.. 分析1: 那我们就弄成每组数字都互质,然后全体乘 ...

  4. Android 用户界面---拖放(Drag and Drop)(二)

      拖拽事件监听器和回调方法 View对象既可以用实现View.OnDragListener接口的拖放事件监听器,也可以用View对象的onDragEvent(DragEvent)回调方法来接收拖拽事 ...

  5. 【笨嘴拙舌WINDOWS】实践检验之按键精灵【Delphi】

    通过记录键盘和鼠标位置和输入信息,然后模拟发送,就能够创建一个按键精灵! 主要代码如下: library KeyBoardHook; { Important note about DLL memory ...

  6. POJ 3225 (线段树 区间更新) Help with Intervals

    这道题搞了好久,其实坑点挺多.. 网上找了许多题解,发现思路其实都差不多,所以就不在重复了. 推荐一篇比较好的题解,请戳这. 另外,如果因为可能要更新多次,但最终查询只需要一次,所以没有写pushup ...

  7. acdream 1412 2-3Trees (组合+DP)

    题意:2-3树的每个结点(除了叶子外)有2或3个孩子(分支),假设是一个满2-3树,那么给出叶子的数量,求这样的树有多少棵.(注:有2个孩子的结点视为相同,有3个孩子的结点视为相同,比如倒数第2层有4 ...

  8. HiveQL 与 SQL的异同

    1 select 别名 (1)别名一定要加as 例:select ID as stuID from students (2) Hive QL不支持在group by, order by 中使用sele ...

  9. Oracle 中的 TO_DATE 和 TO_CHAR 函数

    Oracle 中的 TO_DATE 和 TO_CHAR 函数oracle 中 TO_DATE 函数的时间格式,以 2008-09-10 23:45:56 为例 格式 说明 显示值 备注 Year(年) ...

  10. Java中'&'与、'|'或、'^'异或、'<<'左移位、'>>'右移位

    public static void main(String[] args) { /* * &:与运算 * 全为1则为1,否则为0 */ System.out.print(1 & 0) ...