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.

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

voidOpenSlesOutput::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来实现.

open sl java audio_webrtc学习(二): audio_device之opensles相关推荐

  1. Java并发学习二:编译优化带来的有序性问题导致的并发Bug

    Java并发学习系列文章:Java并发学习-博客专栏 今天在学习极客时间专栏:<Java并发编程实战> 第一讲01 | 可见性.原子性和有序性问题:并发编程Bug的源头中提到: 编译器及解 ...

  2. java多线程学习二、安全与不安全示例:12306买票和银行取钱、java内存模型、内存可见性、线程同步块和方法

    文章目录 前言 1. 什么是块,分为几种 2. 静态块与构造块的区别 一. 举例说明:并发情况下,线程不安全 1. 示例1:unsafe12306取票 2. 示例2:unsafe银行取钱 二.线程不安 ...

  3. Java多线程学习(二)---线程创建方式

    线程创建方式 摘要: 1. 通过继承Thread类来创建并启动多线程的方式 2. 通过实现Runnable接口来创建并启动线程的方式 3. 通过实现Callable接口来创建并启动线程的方式 4. 总 ...

  4. java泛型学习二:解惑通配符

    package generic;import java.awt.Canvas; import java.util.ArrayList; import java.util.Collection; imp ...

  5. Java多线程学习二十九:AtomicInteger(原子类) 和 synchronized 的异同点?

    原子类和 synchronized 关键字都可以用来保证线程安全,在本课时中,我们首先分别用原子类和 synchronized 关键字来解决一个经典的线程安全问题,给出具体的代码对比,然后再分析它们背 ...

  6. Java多线程学习二十一:ConcurrentHashMap 在 Java7 和 8 有何不同

    在 Java 8 中,对于 ConcurrentHashMap 这个常用的工具类进行了很大的升级,对比之前 Java 7 版本在诸多方面都进行了调整和变化.不过,在 Java 7 中的 Segment ...

  7. Java多线程学习二十:HashMap 为什么是线程不安全的

    为什么 HashMap 是线程不安全的?而对于 HashMap,相信你一定并不陌生,HashMap 是我们平时工作和学习中用得非常非常多的一个容器,也是 Map 最主要的实现类之一,但是它自身并不具备 ...

  8. Java基础学习(二十七)之IO流

    1. File 1.1 File类概述和构造方法(myFile中的com.itheima_01中的FileDemo01) File:它是文件和目录路径名的抽象表示 文件和目录是可以通过File封装成对 ...

  9. Java基础学习(二十一)之接口

    1. 接口 1.1 接口概述 五孔插线面板,不同品牌都能使用,因为它们遵循同样的规范来生产的.所以说接口其实就是一种公共的规范,只要符合规范,大家都可以使用. 笔记本,USB口,鼠标,键盘,U盘 接口 ...

最新文章

  1. Graph Search图谱搜索
  2. 宇宙射线会导致路由器 bug,思科你认真的吗
  3. 关于Vmware workstation虚拟机的网络设置问题
  4. java maven junit_【JUnit】JUnit 与 maven 集成
  5. fpga 开源264编码_更好的开源安全性,学习编码,开放式家庭设计等
  6. Image合并添加文字内容
  7. python 图片 变清晰_python模糊图片过滤的方法
  8. java冻结jsp首行_收藏的 处理jsp首行 报错问题
  9. 台式计算机怎么换内存条,笔者教你如何更换电脑内存条
  10. 数学——Lipschitz连续
  11. C语言:输出奇数和偶数的个数
  12. uWSGI, Gunincorn, 啥玩意儿?
  13. 使用mac 终端登录腾讯云服务器
  14. 网关系统就该这么设计,万能通用,稳的一批!
  15. SQL Server安装总是缺少msi等文件,出现错误1612、1706等解决办法
  16. 【数据分析自学】一、系统认识数据分析
  17. 为什么键盘没反应了 键盘没反应原因分析及解决方法
  18. 苹果订阅的升级、降级、同级转换
  19. 计算机excel中行高在哪里,电脑Excel表格怎么对行高和列宽进行调整
  20. ipad发布会ipad_iPad诞生十周年

热门文章

  1. 想方便快捷的分享/收藏图片?试试免费好用的微博/b站图床
  2. 拆掉思维里的墙-摘抄
  3. WMS 系统出入库理解
  4. ONF定义的SDN架构
  5. 新番 | 万万没想到,Hulu有一天也开始推新番了
  6. IE10、IE11中WebSocket报错:SecurityError
  7. 01-android 微信实现本地视频发布到朋友圈功能
  8. 爱快固件是Linux系统吗,爱快硬路由和软路由系统有什么区别
  9. 爬虫笔记41之反爬系列四:字体反爬、JS反爬
  10. 小程序使用mp-html解析html