AudioTrack的start方法用于实现Android的音频输出,start究竟做了什么?回顾一下上一小节createTrack_l的最后部分,通过binder返回了一个Track的句柄,并以被保存了下来

  1. status_t AudioTrack::createTrack_l(...)
  2. {
  3. sp<IAudioTrack> track = audioFlinger->createTrack(...);
  4. mAudioTrack = track;
  5. }

start主要就是调用这个track的start方法实现音频输出功能的

  1. // -------------------------------------------------------------------------
  2.  
  3. status_t AudioTrack::start()
  4. {
  5. AutoMutex lock(mLock);
  6. //如果该AudioTrack已经是start状态,直接返回
  7. if (mState == STATE_ACTIVE) {
  8. return INVALID_OPERATION;
  9. }
  10.  
  11. mInUnderrun = true;
  12. //保存上一次的状态
  13. State previousState = mState;
  14. //设置当前状态
  15. if (previousState == STATE_PAUSED_STOPPING) {
  16. mState = STATE_STOPPING;
  17. } else {
  18. mState = STATE_ACTIVE;
  19. }
  20. //如果上一状态是停止状态,表明需要重新把position设置为0,从头播放
  21. if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
  22. // reset current position as seen by client to 0
  23. mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
  24. // force refresh of remaining frames by processAudioBuffer() as last
  25. // write before stop could be partial.
  26. mRefreshRemaining = true;
  27. }
  28. //当前位置
  29. mNewPosition = mProxy->getPosition() + mUpdatePeriod;
  30. //获取share buffer的flag,原子操作
  31. int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags);
  32.  
  33. //是否有回调线程,一般如果我们在apk端独立调用AudioTrack,是不会设置回调线程的,但是AudioPlayer这种系统播放器则会设置回调线程
  34. //这样做是为了设置优先级,否则Audio可能会由于得不到时间片,而卡顿
  35. //如果是AudioPlayer,会有自己定义的优先级,AudioTrack后面新创建的线程则会继承它的优先级
  36. //如果是Apk调用,优先级一般都是固定的,那么我们需要在这里设置一个ANDROID_PRIORITY_AUDIO的优先级来保证Audio的流畅输出
  37. sp<AudioTrackThread> t = mAudioTrackThread;
  38. if (t != 0) {
  39. if (previousState == STATE_STOPPING) {
  40. //中断
  41. mProxy->interrupt();
  42. } else {
  43. //恢复播放
  44. t->resume();
  45. }
  46. } else {
  47. //保存当前线程优先级,在后面停止的时候设置回来
  48. mPreviousPriority = getpriority(PRIO_PROCESS, 0);
  49. get_sched_policy(0, &mPreviousSchedulingGroup);
  50. //设置线程优先级为ANDROID_PRIORITY_AUDIO
  51. androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
  52. }
  53.  
  54. status_t status = NO_ERROR;
  55. if (!(flags & CBLK_INVALID)) {
  56. //如果share buffer可用,则调用track的start方法
  57. status = mAudioTrack->start();
  58. if (status == DEAD_OBJECT) {
  59. flags |= CBLK_INVALID;
  60. }
  61. }
  62. if (flags & CBLK_INVALID) {
  63. status = restoreTrack_l("start");
  64. }
  65.  
  66. if (status != NO_ERROR) {
  67. //start出错后的处理
  68. ALOGE("start() status %d", status);
  69. mState = previousState;
  70. if (t != 0) {
  71. if (previousState != STATE_STOPPING) {
  72. t->pause();
  73. }
  74. } else {
  75. setpriority(PRIO_PROCESS, 0, mPreviousPriority);
  76. set_sched_policy(0, mPreviousSchedulingGroup);
  77. }
  78. }
  79.  
  80. return status;
  81. }

由于mAudioTrack是binder的proxy对象,因此start会调用到BBinder对象的start方法,即

  1. status_t AudioFlinger::TrackHandle::start() {
  2. return mTrack->start();
  3. }

由于我们是在PlaybackThread下进行音频输出的,因此会进一步调用到PlaybackThread::Track:: start方法,其中最主要的是下面两个步骤:

  1. status_t AudioFlinger::PlaybackThread::Track::start(
  2. PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
  3. status = playbackThread->addTrack_l(this);
  4. }

还记得我们在getOutput的时候创建了一个MixerThread吗,而且在createTrack_l的时候把这个Thread加入了mPlaybackThreads进行管理,现在我们要把它取出来,调用它的addTrack_l方法了

  1. audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module,...)
  2. {     thread = new MixerThread(this, output, id, *pDevices);     return id;
  3. }
  4.  
  5. AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(audio_io_handle_t output) const
  6. {     return mPlaybackThreads.valueFor(output).get();
  7. }

在addTrack_l方法内,主要步骤有三个:

  • 如果该track(share buffer)是新增track,则需要调用startOutput进行初始化
  • 把该track加入mActiveTracks
  • 发送广播,通知MixerThread开始工作
  1. // addTrack_l() must be called with ThreadBase::mLock held
  2. status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
  3. {
  4. if (mActiveTracks.indexOf(track) < 0) {
  5. status = AudioSystem::startOutput(mId, track->streamType(), track->sessionId());
  6. }
  7. mActiveTracks.add(track);
  8.  
  9. broadcast_l();
  10. }

1. track初始化

在分析getOutput的时候,我们已经知道Audio接口的调用流程,即AudioSystem->AudioPolicyService->Audio_policy_hal->AudioPolicyManagerBase,现在我们来看一下AudioPolicyManagerBase:: startOutput做了什么

  1. status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
  2. AudioSystem::stream_type stream,
  3. int session)
  4. {
  5. checkAndSetVolume(stream,
  6. mStreams[stream].getVolumeIndex(newDevice),
  7. output,
  8. newDevice);
  9. }

checkAndSetVolume其实只是设置了stream volume.

2.  track加入mAudioTrack

mAudioTrack即当前MixerThread所包含的Track集合,在后面就是对这些Track集合进行混音

3. broadcast_l

  1. void AudioFlinger::PlaybackThread::broadcast_l()
  2. {
  3. // Thread could be blocked waiting for async
  4. // so signal it to handle state changes immediately
  5. // If threadLoop is currently unlocked a signal of mWaitWorkCV will
  6. // be lost so we also flag to prevent it blocking on mWaitWorkCV
  7. mSignalPending = true;
  8. mWaitWorkCV.broadcast();
  9. }

我们已经有了MixerThread,由于MixerThread继承与PlaybackThread,因此跑的是PlaybackThread::threadLoop,在threadLoop内,如果mActiveTrack为空的话,表明没有音频数据等待输出,那么threadLoop会进入睡眠,等待唤醒,这里的broadcast就是做了这个唤醒的工作

  1. bool AudioFlinger::PlaybackThread::threadLoop()
  2. {
  3. if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
  4. isSuspended())
  5.  
  6. mWaitWorkCV.wait(mLock);
  7.  
  8. }
  9. ...
  10. }

下面是start的总体流程

[Android] AudioTrack::start的更多相关文章

  1. [Android] AudioTrack实例

    AudioTrack在Android系统中是用于PCM数据的混音.播放,并不涉及到音频的解码.因此MP3这类经过编码的音频格式文件不能直接通过AudioTrack正确地播放,AudioTrack只能播 ...

  2. Android Audio Play Out Channel

    1: 7嘴8舌 扬声器, 耳机, 和听筒 就是通过: audiomanager.setmode(AudioManager.MODE_IN_COMMUNICATION)audiomanager.setS ...

  3. Android多媒体开发(3)————使用Android NKD编译havlenapetr-FFMpeg-7c27aa2

    1. 使用NDK去编译官方的FFmpeg原版的话,还得自己实现JNI层与Java层,工程量比较大.所以移植FFmpeg到Android平台时,可以移植一些已经实现JNI与JAVA层的开源项目,毕竟软件 ...

  4. 单独编译使用WebRTC的音频处理模块

    块,每块个点,(12*64=768采样)即AEC-PC仅能处理48ms的单声道16kHz延迟的数据,而 - 加载编译好的NS模块动态库 接下来只需要按照 此文 的描述在 android 的JAVA代码 ...

  5. Android音频开发之AudioTrack实时播放

    前言: 其实在Android中录音可以用MediaRecord录音,操作比较简单.但是不能对音频进行处理.考虑到项目中做的是实时语音只能选择AudioRecord进行录音.然后实时播放也只能采用Aud ...

  6. Android使用AudioTrack发送红外信号

    最近要做一个项目,利用手机的耳机口输出红外信号,从而把手机变成红外遥控器,信号处理的知识基本都还给老师了,刚开始真的挺头疼.找了不少资料研究了一下,总算有点心得,在这里做个备忘. 一.音频信号输出原理 ...

  7. Android深入浅出之 AudioTrack分析

    Android深入浅出之Audio 第一部分 AudioTrack分析 一 目的 本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如 ...

  8. 使用AudioTrack播放PCM音频数据(android)

    众所周知,Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的.MediaPl ...

  9. Android Audio System 之一:AudioTrack如何与AudioFlinger

    Android Framework的音频子系统中,每一个音频流对应着一个AudioTrack类的一个实例,每个AudioTrack会在创建时注册到 AudioFlinger中,由AudioFlinge ...

随机推荐

  1. mongoDB的基本使用----飞天博客

    Mongo的介绍:这个mongoDB官网说的好啊,MongoDB是一个开源的基于document的数据库,并且是优秀的NoSQL数据库,并且它是用C++写滴哈,非常有效率.一些什么特点呢? 全索引支持 ...

  2. Firemonkey的旁门左道[四]

    做开发,就是发现问题,解决问题,又发现问题...周而复始的循环 下面又应该是Firemonkey下的bug. 官方文档中描述: Using the FireMonkey TMenuBar's OSMe ...

  3. Reso | liunx下longeneQQ和搜狗拼音

    sogoupinyin_2.0.0.0078_amd64.deb:   http://pan.baidu.com/s/1eSDLvEU WineQQ7.8-20151109-Longene .deb: ...

  4. C/C++笔试题整理

    1. C的结构体和C++结构体的区别 (1)C的结构体内不允许有函数存在,C++允许有内部成员函数,且允许该函数是虚函数.所以C的结构体是没有构造函数.析构函数.和this指针的. (2)C的结构体对 ...

  5. Linux 基本命令(持续更新ing)

    cd -> 变换路径                        //文件一般存在/var/路径下,var为可修改存储盘 ls -> 列出所有隐藏文件与相关文件的属性   #ls -al ...

  6. Python多线程锁

    [Python之旅]第六篇(四):Python多线程锁   python lock 多线程 多线程使用方法 多线程锁 摘要:   在多线程程序执行过程中,为什么需要给一些线程加锁以及如何加锁,下面就来 ...

  7. 三星I9308(移动版)正确​Root的方法,进入正确的recovery的关键(网上很多方法是误导)

    三星I9308(移动版)正确​Root的方法,进入正确的recovery的关键(网上很多方法是误导)   1)首先在电脑上安装手机驱动:下载地址:点击这里下载  2)手机设置USB调试 方法1:设置- ...

  8. 如何在ASP.NET端获取屏幕宽度

    using System.Windows.Forms; 首先引用上面的命名空间. 然后在代码中获取屏幕信息.如下代码: System.Windows.Forms.Screen screen = Sys ...

  9. jquery $('#btn').click与$("#btn").live("click",function()有什么区别?

    live方法绑定的事件处理函数,在页面中未来添加的元素只要满足原来的选择器,仍然会导致事件触发.普通的事件绑定则没有这个效果.对于#btn这个选择器来说,如果你未来将id为btn的元素删除,然后再创建 ...

  10. Python 函数的使用 外加引入文件

    #coding=utf-8 #!user/bin/python import sys import test2 def functionsss(name,sex,age=25): print name ...