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
     
  1. void OpenSlesOutput::PlayerSimpleBufferQueueCallbackHandler(
  2.     SLAndroidSimpleBufferQueueItf sles_player_sbq_itf) {
  3.   if (fifo_->size() <= 0 || number_underruns_ > 0) {
  4.     ++number_underruns_;
  5.     event_.SignalEvent(kUnderrun, number_underruns_);
  6.     return;
  7.   }
  8.   int8_t* audio = fifo_->Pop();
  9.   if (audio)
  10.   OPENSL_RETURN_ON_FAILURE(
  11.       (*sles_player_sbq_itf)->Enqueue(sles_player_sbq_itf,
  12.                                       audio,
  13.                                       buffer_size_bytes_),
  14.       VOID_RETURN);
  15.   event_.SignalEvent(kNoUnderrun, 0);
  16. }
复制代码
    4. 当CbThreadImpl被唤醒时. 如果是kUnderrun  则player会重新启动. 并重新把所有播放缓存Enqueue.
     
  1. OPENSL_RETURN_ON_FAILURE(
  2.       (*sles_player_itf_)->SetPlayState(sles_player_itf_,
  3.               SL_PLAYSTATE_STOPPED),
  4.       true);
  5.   EnqueueAllBuffers();
  6.   OPENSL_RETURN_ON_FAILURE(
  7.       (*sles_player_itf_)->SetPlayState(sles_player_itf_,
  8.               SL_PLAYSTATE_PLAYING),
  9.       true);
复制代码
    如果是kNoUnderrun , 则开始处理.
     
  1. while (fifo_->size() < num_fifo_buffers_needed_ && playing_) {
  2.     int8_t* audio = play_buf_[active_queue_].get();
  3.     fine_buffer_->GetBufferData(audio);
  4.     fifo_->Push(audio);
  5.     active_queue_ = (active_queue_ + 1) % TotalBuffersUsed();
  6.   }
复制代码
    fine_buffer_ 的GetBufferData会自动处理10ms的数据. 如果数据不足, 则从audio buffer的callback –> NeedMorePlayData请求数据. 如果数据太多则存入缓存中.
   fifo_ 把获取到的数据入栈. 当fifo_的大小等于num_fifo_buffers_needed_(预分配的播放缓存数量) 时, CbThreadImpl停止处理, 等待下次唤醒.
   5.
   webrtc中的threadWrapper::create创建的线程. start的处理代码是这样的.
     
  1. result |= pthread_create(&thread_, &attr_, &StartThread, this);
复制代码
    StartThread的代码:
     
  1. bool alive = true;
  2.   bool run = true;
  3.   while (alive) {
  4.     run = run_function_(obj_);
  5.     CriticalSectionScoped cs(crit_state_);
  6.     if (!run) {
  7.       alive_ = false;
  8.     }
  9.     alive = alive_;
  10.   }
复制代码
    run_function_ 就是create时, 传进去的函数.
   所以opensl 的CbThreadImpl处理是不断被调用的. 这是我原先非常疑惑的一点( 没看threadwrapper的代码之前, 我并不知道CbThreadImpl会一直被调用).
   录制的流程 就不赘述了. 大体没啥差别.
   opensl demo中是FakeAudioDeviceBuffer继承了AudioDeviceBuffer, 在GetPlayoutData中把record的数据交付给playout. 而不是通过外部的callback来实现.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值