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

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

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

// -------------------------------------------------------------------------

status_t AudioTrack::start()
{
AutoMutex lock(mLock);
//如果该AudioTrack已经是start状态,直接返回
if (mState == STATE_ACTIVE) {
return INVALID_OPERATION;
} mInUnderrun = true;
//保存上一次的状态
State previousState = mState;
//设置当前状态
if (previousState == STATE_PAUSED_STOPPING) {
mState = STATE_STOPPING;
} else {
mState = STATE_ACTIVE;
}
//如果上一状态是停止状态,表明需要重新把position设置为0,从头播放
if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
// reset current position as seen by client to 0
mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
// force refresh of remaining frames by processAudioBuffer() as last
// write before stop could be partial.
mRefreshRemaining = true;
}
//当前位置
mNewPosition = mProxy->getPosition() + mUpdatePeriod;
//获取share buffer的flag,原子操作
int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags); //是否有回调线程,一般如果我们在apk端独立调用AudioTrack,是不会设置回调线程的,但是AudioPlayer这种系统播放器则会设置回调线程
//这样做是为了设置优先级,否则Audio可能会由于得不到时间片,而卡顿
//如果是AudioPlayer,会有自己定义的优先级,AudioTrack后面新创建的线程则会继承它的优先级
//如果是Apk调用,优先级一般都是固定的,那么我们需要在这里设置一个ANDROID_PRIORITY_AUDIO的优先级来保证Audio的流畅输出
sp<AudioTrackThread> t = mAudioTrackThread;
if (t != 0) {
if (previousState == STATE_STOPPING) {
//中断
mProxy->interrupt();
} else {
//恢复播放
t->resume();
}
} else {
//保存当前线程优先级,在后面停止的时候设置回来
mPreviousPriority = getpriority(PRIO_PROCESS, 0);
get_sched_policy(0, &mPreviousSchedulingGroup);
//设置线程优先级为ANDROID_PRIORITY_AUDIO
androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
} status_t status = NO_ERROR;
if (!(flags & CBLK_INVALID)) {
//如果share buffer可用,则调用track的start方法
status = mAudioTrack->start();
if (status == DEAD_OBJECT) {
flags |= CBLK_INVALID;
}
}
if (flags & CBLK_INVALID) {
status = restoreTrack_l("start");
} if (status != NO_ERROR) {
//start出错后的处理
ALOGE("start() status %d", status);
mState = previousState;
if (t != 0) {
if (previousState != STATE_STOPPING) {
t->pause();
}
} else {
setpriority(PRIO_PROCESS, 0, mPreviousPriority);
set_sched_policy(0, mPreviousSchedulingGroup);
}
} return status;
}

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

status_t AudioFlinger::TrackHandle::start() {
return mTrack->start();
}

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

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

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

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

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

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

1. track初始化

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

status_t AudioPolicyManagerBase::startOutput(audio_io_handle_t output,
AudioSystem::stream_type stream,
int session)
{
checkAndSetVolume(stream,
mStreams[stream].getVolumeIndex(newDevice),
output,
newDevice);
}

checkAndSetVolume其实只是设置了stream volume.

2.  track加入mAudioTrack

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

3. broadcast_l

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

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

bool AudioFlinger::PlaybackThread::threadLoop()
{
if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
isSuspended()) mWaitWorkCV.wait(mLock); }
...
}

下面是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. 如何提高Lucene构建索引的速度

    如何提高Lucene构建索引的速度 hans(汉斯) 2013-01-27 10:12 对于Lucene>=2.3:IndexWriter可以自行根据内存使用来释放缓存.调用writer.set ...

  2. Android 网络框架--Retrofit

    1.导入Jar包 compile 'com.google.code.gson:gson:2.8.0' compile 'com.squareup.retrofit2:retrofit:2.1.0' c ...

  3. php 二维数组转换成树状数组(转)

    <?php/** * @param array $list 要转换的结果集 * @param string $pid parent标记字段 * @param string $level leve ...

  4. Qt 学习之路 :线程简介

    现代的程序中,使用线程的概率应该大于进程.特别是在多核时代,随着 CPU 主频的提升,受制于发热量的限制,CPU 散热问题已经进入瓶颈,另辟蹊径地提高程序运行效率就是使用线程,充分利用多核的优势.有关 ...

  5. Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace.(转)

    1.程序运行后异常显示: 解决方案:在项目上点击右键->properties->Java Build Path, remove掉Android Dependences即可

  6. Android音频开发之——如何播放一帧音频

    本文重点关注如何在Android平台上播放一帧音频数据.阅读本文之前,建议先读一下<Android音频开发(1):基础知识>,因为音频开发过程中,经常要涉及到这些基础知识,掌握了这些重要的 ...

  7. 将sql数据库逆向生成PDM模型

    由于接手的一个项目是公司前期外包出去的,所以到手的只有繁杂的代码,和数据库文件.由于是个新手,我需要一个数据字典来帮助我完成一些东西,所以我就想到从sql数据库转换出一个pdm模型的数据字典. 第一步 ...

  8. php 在web端读出pdf 与各种文件下载

    单纯的下载功能实现 <?php // 表示调用文本类型为pdf的应用 header('Content-type: application/pdf'); // 这句可以输出下载页面进行下载 hea ...

  9. asp.net中过滤器的两种写法

    1.写在一个单独的类库中在web.config中进行配置 <httpModules> <add name="" type="类的全名称,程序集的名称&q ...

  10. java中this关键字和static关键字和super关键字的用法

    this关键字 1. this 关键字是类内部当中对自己的一个引用,可以方便类中方法访问自己的属性: 2.可以返回对象的自己这个类的引用,同时还可以在一个构造函数当中调用另一个构造函数(这里面上面有个 ...