您的位置:首页 > 移动开发 > Android开发

[Android] AudioTrack::start

2015-09-13 00:55 573 查看
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的总体流程



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: