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 4. 当CbThreadImpl被唤醒时. 如果是kUnderrun 则player会重新启动. 并重新把所有播放缓存Enqueue. 如果是kNoUnderrun , 则开始处理. fine_buffer_ 的GetBufferData会自动处理10ms的数据. 如果数据不足, 则从audio buffer的callback –> NeedMorePlayData请求数据. 如果数据太多则存入缓存中. fifo_ 把获取到的数据入栈. 当fifo_的大小等于num_fifo_buffers_needed_(预分配的播放缓存数量) 时, CbThreadImpl停止处理, 等待下次唤醒. 5. webrtc中的threadWrapper::create创建的线程. start的处理代码是这样的. StartThread的代码: run_function_ 就是create时, 传进去的函数. 所以opensl 的CbThreadImpl处理是不断被调用的. 这是我原先非常疑惑的一点( 没看threadwrapper的代码之前, 我并不知道CbThreadImpl会一直被调用). 录制的流程 就不赘述了. 大体没啥差别. opensl demo中是FakeAudioDeviceBuffer继承了AudioDeviceBuffer, 在GetPlayoutData中把record的数据交付给playout. 而不是通过外部的callback来实现. |