zoukankan      html  css  js  c++  java
  • WebRTC VoiceEngine综合应用示例(一)——基本结构分析(转)

    把自己这两天学习VoiceEngine的成果分享出来,供大家参考,有什么问题也欢迎大家指出,一起学习一起进步。

    本文将对VoiceEngine的基本结构做一个分析,分析的方法是自底向上的:看一个音频编码器是如何一层层被封装到VoiceEngine中的。

    首先我们来看一下VoiceEngine的核心API,基本上就在webrtcvoiceengineinclude的几个头文件中了。
    具体来说,
    voe_base
    -支持G.711编码的、RTP传输的全双工VoIP应用,若要支持其他编码器,则需要VoECodec的支持
    -初始化和销毁VoiceEngine实例
    -通过文本或回调函数记录trace信息
    -支持多channel(mixing或发送至多目标地址)

    voe_codec
    -支持其他编码器
    -Voice Activity检测
    -Possibility to specify how to map received payload types to codecs.

    voe_dtmf
    -Telephone event transmission.
    -DTMF tone generation.

    voe_errors
    -错误信息

    voe_external_media
    -注册额外的音频处理功能

    voe_file
    -文件的播放、录制、转换

    voe_hardware
    -音频设备的操作
    -设备信息
    -CPU负载监控

    voe_neteq_stats
    -获取网络信息和音频解码信息

    voe_network
    -额外协议的支持
    -Packet timeout notification.
    -Dead-or-Alive connection observations.

    voe_rtp_rtcp
    - Callbacks for RTP and RTCP events such as modified SSRC or CSRC.
    - SSRC handling.
    - Transmission of RTCP sender reports.
    - Obtaining RTCP data from incoming RTCP sender reports.
    - RTP and RTCP statistics (jitter, packet loss, RTT etc.).
    - Redundant Coding (RED)
    - Writing RTP and RTCP packets to binary files for off-line analysis of the call quality.

    voe_video_sync
    -RTP header modification (time stamp and sequence number fields).
    -Playout delay tuning to synchronize the voice with video.
    -Playout delay monitoring.

    voe_volume_control
    -扬声器、麦克风音量控制
    -静音

    voe_audio_processing
    -噪声抑制Noise Suppression)
    -自动增益控制AGC
    -回声消除EC
    -接收端的VAD、NS、AGC
    -语音、噪声、回音level的测量
    -audio processing调试信息的生成与记录
    -检测键盘动作

    而各类音频编码器都在webrtcmodules下的各个项目中,包括了g711,g722,ilbc,isac,red(redundant audio coding),pcm16b,用于噪音生成的CNG,以及位于third_party目录下的opus。以G722音频编码器为例,在webrtcmodulesaudio_codingcodecsg722g722_enc_dec.h文件中定义了编解码过程中的两个关键结构体G722EncoderState和G722DecoderState,如下

    typedef struct
    {
        /*! TRUE if the operating in the special ITU test mode, with the band split filters
        disabled. */
        int itu_test_mode;
        /*! TRUE if the G.722 data is packed */
        int packed;
        /*! TRUE if encode from 8k samples/second */
        int eight_k;
        /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */
        int bits_per_sample;
    
        /*! Signal history for the QMF */
        int x[24];
    
        struct
        {
            int s;
            int sp;
            int sz;
            int r[3];
            int a[3];
            int ap[3];
            int p[3];
            int d[7];
            int b[7];
            int bp[7];
            int sg[7];
            int nb;
            int det;
        } band[2];
    
        unsigned int in_buffer;
        int in_bits;
        unsigned int out_buffer;
        int out_bits;
    } G722EncoderState;
    
    typedef struct
    {
        /*! TRUE if the operating in the special ITU test mode, with the band split filters
        disabled. */
        int itu_test_mode;
        /*! TRUE if the G.722 data is packed */
        int packed;
        /*! TRUE if decode to 8k samples/second */
        int eight_k;
        /*! 6 for 48000kbps, 7 for 56000kbps, or 8 for 64000kbps. */
        int bits_per_sample;
    
        /*! Signal history for the QMF */
        int x[24];
    
        struct
        {
            int s;
            int sp;
            int sz;
            int r[3];
            int a[3];
            int ap[3];
            int p[3];
            int d[7];
            int b[7];
            int bp[7];
            int sg[7];
            int nb;
            int det;
        } band[2];
    
        unsigned int in_buffer;
        int in_bits;
        unsigned int out_buffer;
        int out_bits;
    } G722DecoderState;

    对应的G722编解码流程图如下

    图中左侧的WebRtcG722xxx函数都定义于g722_interface.h中,而右边的WebRtc_g722xx函数都定义于g722_enc_dec.h中,真正的编解码功能都在WebRtc_g722_encodedecode中实现。图中的G722EncInstDecInst其实就是前面说到的G722EncoderState和G722DecoderState。

    像上面这样的一个G722音频编码器被封装到了AudioEncoderG722类中,此类继承了AudioEncoder。类似的还有G711编码器被封装到了AudioEncoderPcm类中,此类同样继承了AudioEncoder;iLBC被封装到了AudioEncoderIlbc类中,此类同样继承了AudioEncoder。AudioEncoder的定义位于webrtcmodulesaudio_encoder_interface,包含了一个音频编码器的基本参数和接口。AudioEncoder进一步被AudioCoding类调用,此类位于webrtcmodulesaudio_coding_module,顾名思义,AudioCoding是一个专门负责音频编码的类,具体来说,是在如下两个接口之中调用了AudioEncoder类

    virtual bool RegisterSendCodec(AudioEncoder* send_codec) = 0;
    virtual const AudioEncoder* GetSenderInfo() const = 0;

    这两个接口的具体实现则在AudioCodingImpl类中,这个类同样继承了AudioCoding,但是当我们去看它们的具体实现时,却发现

    bool AudioCodingImpl::RegisterSendCodec(AudioEncoder* send_codec)
    {
        FATAL() << "Not implemented yet.";
        return false;
    }
    const AudioEncoder* AudioCodingImpl::GetSenderInfo() const
    {
        FATAL() << "Not implemented yet.";
        return reinterpret_cast<const AudioEncoder*>(NULL);
    }

     转而实现的是

    bool AudioCodingImpl::RegisterSendCodec(int encoder_type,
        uint8_t payload_type,
        int frame_size_samples)
    {
        std::string codec_name;
        int sample_rate_hz;
        int channels;
        if (!MapCodecTypeToParameters(
            encoder_type, &codec_name, &sample_rate_hz, &channels))
        {
            return false;
        }
        webrtc::CodecInst codec;
        AudioCodingModule::Codec(
            codec_name.c_str(), &codec, sample_rate_hz, channels);
        codec.pltype = payload_type;
        if (frame_size_samples > 0)
        {
            codec.pacsize = frame_size_samples;
        }
        return acm_old_->RegisterSendCodec(codec) == 0;
    }

    const CodecInst* AudioCodingImpl::GetSenderCodecInst()
    {
        if (acm_old_->SendCodec(&current_send_codec_) != 0)
        {
            return NULL;
        }
        return &current_send_codec_;
    }

    调用的是acm_old_中的一些方法,而这个acm_old_的定义也在AudioCodingImpl类中,如下

    // TODO(henrik.lundin): All members below this line are temporary and should
    // be removed after refactoring is completed.
    rtc::scoped_ptr<acm2::AudioCodingModuleImpl> acm_old_;
    CodecInst current_send_codec_;

    可以看到,都是一些将来可能会被取消掉的类,但是我们现在还是要看一下它们的内容,前面看到acm_old_是一个AudioCodingModuleImpl类的对象,继承的是AudioCodingModule类,这个类和AudioCoding的功能类似,只不过没有使用AudioEncoder来表示各个编码器,而是使用了CodecInst结构体。这都是在老版的webrtc中就有的东西。

    // Each codec supported can be described by this structure.
    struct CodecInst
    {
        int pltype;
        char plname[RTP_PAYLOAD_NAME_SIZE];
        int plfreq;
        int pacsize;
        int channels;
        int rate;  // bits/sec unlike {start,min,max}Bitrate elsewhere in this file!
    
        bool operator==(const CodecInst& other) const
        {
            return pltype == other.pltype &&
                (STR_CASE_CMP(plname, other.plname) == 0) &&
                plfreq == other.plfreq &&
                pacsize == other.pacsize &&
                channels == other.channels &&
                rate == other.rate;
        }
    
        bool operator!=(const CodecInst& other) const
        {
            return !(*this == other);
        }
    };

    再接着来看AudioCodingModuleImpl中RegisterSendCodec和SendCodec的实现

    // Can be called multiple times for Codec, CNG, RED.
    int AudioCodingModuleImpl::RegisterSendCodec(const CodecInst& send_codec)
    {
        CriticalSectionScoped lock(acm_crit_sect_);
        return codec_manager_.RegisterEncoder(send_codec);
    }
    // Get current send codec.
    int AudioCodingModuleImpl::SendCodec(CodecInst* current_codec) const
    {
        CriticalSectionScoped lock(acm_crit_sect_);
        return codec_manager_.GetCodecInst(current_codec);
    }

          可以看到调用的是codec_manager_中的对应方法,它是一个CodecManager类的对象,定义同样位于webrtcmodulesaudio_coding_module中。总结来说,新版webrtc中提出了利用AudioCoding对编码器进行管理的新思路,但是具体实现还没有完成,目前还是使用老办法。
          具体来看看codec_manager_.RegisterEncoder(send_codec),除了一些基本的检查之外,可以看到主要调用的是codec_owner_.SetEncoders()方法。
          codec_owner_是一个CodecOwner类的对象,位于webrtcmodulesaudio_coding_module中。codec_owner_.SetEncoders()中调用的是CreateSpeechEncoder方法。目前来看的话,就是把具体的编码器的指针做一个赋值操作,不像ffmpeg那样维护一个编码器链表。

          回过头来,再看看AudioCodingModule类都被谁调用了,我们的目标就是一路找到VoiceEngine中对应的调用。
          比较引人注目的是两个类对它的调用,一是voe命名空间中的Channel类,这一个信道方面的类我们以后再看,另一个就是VoECodecImpl类中对其的调用,这个类继承了VoECodec。VoECodecImpl类被VoiceEngineImpl类直接继承,VoECodec则是VoiceEngine的核心成员之一。

         

    以上分析对应于下图,图中的红线代表继承关系、绿线代表调用关系

          以上都是编码器的部分,中间还注意到一些解码器的部分。例如与webrtcmodulesaudio_encoder_interface对应的webrtcmodulesaudio_decoder_interface,其中的AudioDecoder类,也同样被很多编码器对应的解码器类所集成,例如AudioDecoderG722类等等,这些都在webrtcmodule eteq目录中,我们暂且按下不表,接着来说音频相关内容中的发送端。

          来看一下诸如回音消除AEC、增益控制AGC、高通滤波、噪声抑制DeNoise(Noise Suppression)、静音检测VAD、等等的预处理步骤。
          这里以回音消除技术为例进行说明,webrtc中的回音消除有针对移动端和非移动端的两个实现,分别在webrtcmodulesaudio_processing的aec和aecm两个目录中,除此之外,对于支持SSE的x86平台,还有对应的SSE实现,位于webrtcmodulesaudio_processing_sse2目录下。
          具体的回音消除代码被封装到了EchoCancellationImpl类中,除了具体的实现之外,EchoCancellationImpl类中还包含一些handle操作的内容,这部分则继承自ProcessingComponent类。而EchoCancellationImpl的父类则是EchoCancellation,这一父类的对象直接在VoEAudioProcessingImpl类中被调用,其定义位于webrtcvoice_engine下。VoEAudioProcessingImpl的父类就是VoEAudioProcessing,同样是VoiceEngine的核心成员之一。其中的关系已经非常明了了。
          这些预处理相关的内容相对于编码器的部分来说更加简单了。

          下面再来看看音频解码的部分。前面说到具体的解码器类都在webrtcmodule eteq目录中,实际上解码端的其他一些内容,诸如抖动缓冲区等,都在这个目录下。而解码端的另一项重要功能,即混音功能,则在webrtcmodulesaudio_conference_mixer目录下。
          neteq模块即当年GIPS的核心技术,将解码、自适应抖动缓冲和丢包隐藏结合在一起,实现优秀的语音质量和低延时。
          还是以我最熟悉的、单纯的解码模块来入手,前面提到的AudioDecoder类和AudioEncoder类相似,也在audio_coding_module、codec_manager、codec_owner中得到了调用,除此之外,在neteq的一干涉及到解码的内容之中都能看到他的身影。
          neteq作为音频接收端的一个子模块,接下来在AcmReceiver类中得到了调用,而这个类是AudioCodingModuleImpl的成员之一,由此,即可追溯得到它与VoiceEngine的关系。

          以上完成了VoiceEngine中音频编解码、预处理、neteq内容的分析总结。

          下面再看看音频设备部分,这部分代码位于webrtcmodulesaudio_devices,相关的类是AudioDeviceModule,其中支持了多个平台的音频设备,也包括了很多功能,例如麦克风和扬声器的音量控制、静音控制、采样率选择、立体声、混音等等。具体的实现我们暂且不看,只看它与VoiceEngine的接口关系。
          需要注意的是,尽管VoiceEngine里面有一个VoEHardware类,但是对音频设备的初始化却是在VoEBase类中实现的。

    virtual int Init(AudioDeviceModule* external_adm = NULL, AudioProcessing* audioproc = NULL) = 0;

    混音在AudioConferenceMixer类中,受VoiceEngine的OutputMixer类调用
    其他一些辅助的类:AcmDump用于输出调试信息;MediaFile类用于音频文件的输入输出,同样受VoiceEngine的OutputMixer类的调用(中间经过了FileRecorder和FilePlayer)

    rtp tcp中包含了RTP、RTCP传输中的全部内容,例如网络信道数据的报告、重传请求、视频关键帧的request、码率控制等等。暂且按下不表,后续再进行详细分析。跟它配套的还有remote_bitrate_estimatorpaced_senderitrate_controler。
    而RtpRtcp这个类也很自然地被VoiceEngine的Channel类调用了。

    至此,webrtcmodule下与音频传输的模块基本都分析了一遍,也看了一下它们是怎么与VoiceEngine联系到一起的,

    在下一篇文章中,将使用VoiceEngine完成一个语音通话示例。

     

    原文转自 http://blog.csdn.net/nonmarking/article/details/50577860

  • 相关阅读:
    如何删除Windows的服务
    在使用ORACLE时常用到的命令和脚本
    windows 查看端口使用情况
    jQuery获取及设置单选框,多选框,文本框内容
    disabled="disabled" readonly="readonly" type="hidden"提交表单的区别
    @Column标记持久化详细说明
    jQuery核心及其工具
    Hibernate JPA注解说明
    php要点
    jQuery中的动画与效果
  • 原文地址:https://www.cnblogs.com/happykoukou/p/5765157.html
Copyright © 2011-2022 走看看