webrtc学习(二): audio_device之opensles
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的更多相关文章
- WebRTC学习之九:摄像头的捕捉和显示
较新的WebRTC源代码中已经没有了与VoiceEngine结构相应的VidoeEngine了,取而代之的是MeidaEngine.MediaEngine包括了MediaEngineInterface ...
- WebRTC学习与DEMO资源一览
一. WebRTC学习 1.1 WebRTC现状 本人最早接触WebRTC是在2011年底,那时Google已经在Android源码中加入了webrtc源码,放在/external/webrtc/ ...
- emberjs学习二(ember-data和localstorage_adapter)
emberjs学习二(ember-data和localstorage_adapter) 准备工作 首先我们加入ember-data和ember-localstorage-adapter两个依赖项,使用 ...
- ReactJS入门学习二
ReactJS入门学习二 阅读目录 React的背景和基本原理 理解React.render() 什么是JSX? 为什么要使用JSX? JSX的语法 如何在JSX中如何使用事件 如何在JSX中如何使用 ...
- [转]webrtc学习: 部署stun和turn服务器
[转]webrtc学习: 部署stun和turn服务器 http://www.cnblogs.com/lingdhox/p/4209659.html webrtc的P2P穿透部分是由libjingle ...
- TweenMax动画库学习(二)
目录 TweenMax动画库学习(一) TweenMax动画库学习(二) TweenMax动画库学习(三) Tw ...
- Hbase深入学习(二) 安装hbase
Hbase深入学习(二) 安装hbase This guidedescribes setup of a standalone hbase instance that uses the local fi ...
- WebRTC学习笔记_Demo收集
1. WebRTC学习 1.1 WebRTC现状 本人最早接触WebRTC是在2011年底,那时Google已经在Android源代码中增加了webrtc源代码,放在/external/w ...
- Struts2框架学习(二) Action
Struts2框架学习(二) Action Struts2框架中的Action类是一个单独的javabean对象.不像Struts1中还要去继承HttpServlet,耦合度减小了. 1,流程 拦截器 ...
随机推荐
- mysql基础知识(5)--视图
视图 单词:view 什么是视图: 视图可以看作是一个“临时存储的数据所构成的表”(非真实表),其实本质上只是一个select语句.只是将该select语句(通常比较复杂)进行一个“包装”,并设定了一 ...
- 第二百一十八天 how can I 坚持
真的是自以为是吗?或许是我想太多. 今天下雪了,2015年入冬以来的第一场雪,好冷. 又是一周. 睡觉吧,明天老贾生日. 没啥了,中午有点肚子疼,冬天了要注意.
- 在WP8上搭建cocos2d-x开发环境
在WP8上搭建cocos2d-x开发环境 转自:https://github.com/koowolf/cocos-docs/blob/master/manual/framework/native/in ...
- Dagger学习笔记
@Inject 提供依赖的构造函数,或者需要依赖的成员变量 @Module 提供依赖,实例化的地方( 使用module实例化,方便测试的时候替换成其他对象,而这也是和构造方法注入的区别,如果用构造方法 ...
- datagridview里面有combox避免双击两次的写法
双击两次变成单击一次的写法: void dataGridView_CellEnter(object sender, DataGridViewCellEventArgs e) { //实现单击一次显示下 ...
- HDU 4497 GCD and LCM (数学,质数分解)
题意:给定G,L,分别是三个数最大公因数和最小公倍数,问你能找出多少对. 析:数学题,当时就想错了,就没找出规律,思路是这样的. 首先G和L有公因数,就是G,所以就可以用L除以G,然后只要找从1-(n ...
- JDBC 是什么
JDBC is a Java database connectivity technology (Java Standard Edition platform) from Oracle Corpora ...
- HDU 1392 Surround the Trees 构造凸包
又是一道模板题 #include <iostream> #include <cstring> #include <cstdlib> #include <cst ...
- PL/pgSQL学习笔记之九
http://www.postgresql.org/docs/9.1/static/plpgsql-declarations.html 39.3.2. ALIAS newname ALIAS FOR ...
- QML学习笔记之一
摘自<Qt Quick中文手册> Qt Quick提供了一套高动态,丰富的QML元素来定制用户界面的说明性框架. Qt Quick包含了QtDeclarative C++模块.QML,并且 ...