webrtc学习(二): audio_device之opensles

时间:2022-12-29 00:27:43

audio_device是webrtc的音频设备模块.  封装了各个平台的音频设备相关的代码

audio device 在android下封装了两套音频代码.

1. 通过jni调用java的media进行操作.

2. 直接通过opensl es的native c接口进行操作.

native 接口自然比较高效,  但缺点在于opensl 要求 android 2.3+.

OpenSL ES (Open Sound Library for Embedded Systems) 是无授权费、跨平台、针对嵌入式系统精心优化的硬件音频加速API

opensl的资料非常少, google了一遍, 也就找到两篇有点用的文章.

OpenSL ES for Android  对于代码是ndk samples 中的native-audio.

Lock-free audio IO with OpenSL ES on Android  另一篇opensl的应用.

webrtc 的example中提供了一个opensl es的例子. opensl_loopbakc(opensldemo-debug.apk) 用于示范 opensl的使用 (回放声音).

花了一点时间分析了下全部的流程, 因为无法调试, 所以看起来很烦. 线程处理的地方加了log才看明白.

主要有这几个类:

1. AudioDeviceBuffer

缓存类, 方法RegisterAudioCallback.  通过callback来通知数据采集(record), 或者请求数据(playout).

2. OpenSlesInput

record 的实现.

3. OpenSlesOutput

playout的实现.

4. SingleRwFifo

实现了一个无锁队列.

播放的流程:

1. 创建OpenSlesOutput 并且 AttachAudioBuffer,  初始化opensl的相关信息(engine, outmix等). 初始化需要的播放缓存.

2. StartPlayout中, 创建opensl 的audio player,  注册player 缓存播放的callback.  并对所有的播放缓存Enqueue,    然后创建音频数据处理线程CbThreadImpl

说明: 音频数据Enqueue到player. 就会播放出来, 并且每次播放完成后player会回调注册的callback.

3. CbThreadImpl的 唤醒是由event_ 来控制的.  有kUnderrun 和 kNoUnderrun两种状态. kUnderrun 表示音频数据低于预计值. kNoUnderrun表示音频数据正常.

在callback(PlayerSimpleBufferQueueCallbackHandler)回调时的处理是这样的.

当fifo_中没有数据需要播放时, 以kUnderrun 唤醒CbThreadImpl.

当fifo_中有数据时, 把音频数据Enqueue 入player. 以kNoUnderrun 唤醒CbThreadImpl

void OpenSlesOutput::PlayerSimpleBufferQueueCallbackHandler(
SLAndroidSimpleBufferQueueItf sles_player_sbq_itf) {
if (fifo_->size() <= 0 || number_underruns_ > 0) {
++number_underruns_;
event_.SignalEvent(kUnderrun, number_underruns_);
return;
}
int8_t* audio = fifo_->Pop();
if (audio)
OPENSL_RETURN_ON_FAILURE(
(*sles_player_sbq_itf)->Enqueue(sles_player_sbq_itf,
audio,
buffer_size_bytes_),
VOID_RETURN);
event_.SignalEvent(kNoUnderrun, 0);
}
 

4. 当CbThreadImpl被唤醒时. 如果是kUnderrun  则player会重新启动. 并重新把所有播放缓存Enqueue.

OPENSL_RETURN_ON_FAILURE(
(*sles_player_itf_)->SetPlayState(sles_player_itf_,
SL_PLAYSTATE_STOPPED),
true);
EnqueueAllBuffers();
OPENSL_RETURN_ON_FAILURE(
(*sles_player_itf_)->SetPlayState(sles_player_itf_,
SL_PLAYSTATE_PLAYING),
true);

如果是kNoUnderrun , 则开始处理.

while (fifo_->size() < num_fifo_buffers_needed_ && playing_) {
int8_t* audio = play_buf_[active_queue_].get();
fine_buffer_->GetBufferData(audio);
fifo_->Push(audio);
active_queue_ = (active_queue_ + 1) % TotalBuffersUsed();
}

fine_buffer_ 的GetBufferData会自动处理10ms的数据. 如果数据不足, 则从audio buffer的callback –> NeedMorePlayData请求数据. 如果数据太多则存入缓存中.

fifo_ 把获取到的数据入栈. 当fifo_的大小等于num_fifo_buffers_needed_(预分配的播放缓存数量) 时, CbThreadImpl停止处理, 等待下次唤醒.

5.

webrtc中的threadWrapper::create创建的线程. start的处理代码是这样的.

result |= pthread_create(&thread_, &attr_, &StartThread, this);

StartThread的代码:

bool alive = true;
bool run = true;
while (alive) {
run = run_function_(obj_);
CriticalSectionScoped cs(crit_state_);
if (!run) {
alive_ = false;
}
alive = alive_;
}

run_function_ 就是create时, 传进去的函数.

所以opensl 的CbThreadImpl处理是不断被调用的. 这是我原先非常疑惑的一点( 没看threadwrapper的代码之前, 我并不知道CbThreadImpl会一直被调用).

录制的流程 就不赘述了. 大体没啥差别.

opensl demo中是FakeAudioDeviceBuffer继承了AudioDeviceBuffer, 在GetPlayoutData中把record的数据交付给playout. 而不是通过外部的callback来实现.

webrtc学习(二): audio_device之opensles的更多相关文章

  1. WebRTC学习之九:摄像头的捕捉和显示

    较新的WebRTC源代码中已经没有了与VoiceEngine结构相应的VidoeEngine了,取而代之的是MeidaEngine.MediaEngine包括了MediaEngineInterface ...

  2. WebRTC学习与DEMO资源一览

    一. WebRTC学习 1.1   WebRTC现状 本人最早接触WebRTC是在2011年底,那时Google已经在Android源码中加入了webrtc源码,放在/external/webrtc/ ...

  3. emberjs学习二&lpar;ember-data和localstorage&lowbar;adapter&rpar;

    emberjs学习二(ember-data和localstorage_adapter) 准备工作 首先我们加入ember-data和ember-localstorage-adapter两个依赖项,使用 ...

  4. ReactJS入门学习二

    ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...

  5. &lbrack;转&rsqb;webrtc学习&colon; 部署stun和turn服务器

    [转]webrtc学习: 部署stun和turn服务器 http://www.cnblogs.com/lingdhox/p/4209659.html webrtc的P2P穿透部分是由libjingle ...

  6. TweenMax动画库学习&lpar;二&rpar;

    目录            TweenMax动画库学习(一)            TweenMax动画库学习(二)            TweenMax动画库学习(三)            Tw ...

  7. Hbase深入学习&lpar;二&rpar; 安装hbase

    Hbase深入学习(二) 安装hbase This guidedescribes setup of a standalone hbase instance that uses the local fi ...

  8. WebRTC学习笔记&lowbar;Demo收集

    1.     WebRTC学习 1.1   WebRTC现状 本人最早接触WebRTC是在2011年底,那时Google已经在Android源代码中增加了webrtc源代码,放在/external/w ...

  9. Struts2框架学习&lpar;二&rpar; Action

    Struts2框架学习(二) Action Struts2框架中的Action类是一个单独的javabean对象.不像Struts1中还要去继承HttpServlet,耦合度减小了. 1,流程 拦截器 ...

随机推荐

  1. Qt - 读取GPS数据

    1.GPS型号为ublox(EVK-M8L),配有USB接口,Qt版本5.7 2.实现步骤: (1)实现串口通信 采用Qt5.7 内置的串口操作类QSerialPort和QSerialPortInfo ...

  2. 关于vue指令(directive)

    1.指令的注册 指令跟组件一样需要注册才能使用,同样有两种方式,一种是全局注册: Vue.directive('dirName',function(){ //定义指令 }); 另外一种是局部注册: n ...

  3. 创业小坑:内网域名 在windows下能nslookup,但ping不通,也无法访问。而在linux下正常。

    使用巴法络(BUFFALO )LS-XL 网络硬盘盒开启了FTP服务,IP是172.16.0.21 在windows和linux上,都可以访问ftp://172.16.0.21,现在想使用域名访问,便 ...

  4. ASP&period;NET MVC 5 入门教程 &lpar;2&rpar; 控制器Controller

    文章来源: Slark.NET-博客园 http://www.cnblogs.com/slark/p/mvc-5-get-started-controller.html 上一节:ASP.NET MVC ...

  5. linux设备驱动编写&lowbar;tasklet机制

    在编写设备驱动时, tasklet 机制是一种比较常见的机制,通常用于减少中断处理的时间,将本应该是在中断服务程序中完成的任务转化成软中断完成. 为了最大程度的避免中断处理时间过长而导致中断丢失,有时 ...

  6. LeetCode48 Rotate Image

    题目: You are given an n x n 2D matrix representing an image. Rotate the image by 90 degrees (clockwis ...

  7. java&lowbar;Cookie&lowbar;example(你上次访问的时间)

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, ...

  8. OrCAD Capture CIS 16&period;6 快速地编辑Part的引脚名称

    操作系统:Windows 10 x64 工具1:OrCAD Capture CIS 16.6-S062 (v16-6-112FF) 工具2:Excel 参考1:http://www.360doc.co ...

  9. me 云面试

    元祖的特点: 1.元组内的元素,不可以增加,删除,只能访问,这个是元祖的特性,比较安全.类似于字符串.但是我们可以对整个元祖进行删除.使用del内置函数 2.当元祖内只有一个元素的时候,需要加逗号消除 ...

  10. IDEA集成有道翻译插件&sol;maven帮助插件&sol;mybatis插件

    (一)IDEA集成有道翻译插件:https://www.cnblogs.com/a8457013/p/7814335.html 插件下载地址:http://plugins.jetbrains.com/ ...