audiotrack的play流程(mode_stream)

简述

AudioTrack已经创建出来了,那第二步就是执行它的play方法,最后才是写数据,当然,你也可以先写一点点数据,然后在执行play的。因为AudioTrack创建结束就已经代表了你已经完成了对空间的申请了,并且在output thread中也有track的对象存在,只是如果没有执行play的话,那这个track是不属于active track,output thread在执行的过程中,并不会去看这个track的数据是否已经填充满了。并且正常情况下,我们填充数据也是阻塞的形式的,所以如果是先执行writeplay的话,你需要注意一下填充的量,不要让它进入阻塞,就需要赶紧执行play了。

当然你也可以执行完play之后不去执行write,但是经过一定的时间之后,这个track就会被移出active track行列。

我们直接来看code吧,这部分其实也会跟AudioPolicy打交道,但是我们还是先做绕过吧,那部分后面我们再做讲解。

正文

还是从java层开始往下看,代码太多,最后也会弄一张图来梳理流程,这样看起来会清晰一些,代码的部分更注重扣细节吧。

play函数解析

public void play()
throws IllegalStateException {
    if (mState != STATE_INITIALIZED) {
        throw new IllegalStateException("play() called on uninitialized AudioTrack.");
    }
    // 这个私密模式是干嘛?
    if (isRestricted()) {
        setVolume(0);
    }
    synchronized(mPlayStateLock) {
        native_start();
        // 进行play之后将mPlayState设置为PLAYSTATE_PLAYING
        mPlayState = PLAYSTATE_PLAYING;
    }
}

jni的代码只是简单的调用了audiotrack的start函数,所以我们直接看native的audiotrack的start函数

status_t AudioTrack::start()
{
    // 上锁。
    AutoMutex lock(mLock);
    // 如果当前的audiotrack已经处在激活状态了,那就直接返回非法操作
    if (mState == STATE_ACTIVE) {
        return INVALID_OPERATION;
    }

    mInUnderrun = true;

    State previousState = mState;
    // 状态STATE_PAUSED_STOPPING是对于offload,一开始已经stop了,然后又调用了pause。
    // 对于offload的话,这个时候的start会将它引入stopping的状态。就是说需要调用两次start才会变成
    // active
    if (previousState == STATE_PAUSED_STOPPING) {
        mState = STATE_STOPPING;
    } else {
        // 其他情况则进入active
        mState = STATE_ACTIVE;
    }
    // 更新mPosition和mServer,mPosition为新server的位置 减去 旧server的位置 加上 原来的值
    (void) updateAndGetPosition_l();
    if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
        // reset current position as seen by client to 0
        // 如果之前的状态是stopped或者是flush的话,那需要将mPosition设置为0
        mPosition = 0;
        mPreviousTimestampValid = false;
        mTimestampStartupGlitchReported = false;
        mRetrogradeMotionReported = false;

        // For offloaded tracks, we don't know if the hardware counters are really zero here,
        // since the flush is asynchronous and stop may not fully drain.
        // We save the time when the track is started to later verify whether
        // the counters are realistic (i.e. start from zero after this time).
        mStartUs = getNowUs();

        // force refresh of remaining frames by processAudioBuffer() as last
        // write before stop could be partial.
        // 这个会刷新mRemainingFrames的值为notificationFrames,因为stop之前的那次写可能还没写完
        mRefreshRemaining = true;
    }
    // 记录下mNewPosition
    mNewPosition = mPosition + mUpdatePeriod;
    int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags);

    sp<AudioTrackThread> t = mAudioTrackThread;
    if (t != 0) {
        // 还是offload的场景
        if (previousState == STATE_STOPPING) {
            // 这个可以帮助发出FUTEX_WAKE
            mProxy->interrupt();
        } else {
            // 如果不是stopping,那就是别的状态,则会触发thread的resume
            // 会去唤醒线程,并且设置mPaused、mPausedInt为false,mIgnoreNextPausedInt为true
            t->resume();
        }
    } else {
        // 如果不存在该线程,则获取当前进程的优先级,以及优先级组。然后设置当前线程的优先级为audio。
        mPreviousPriority = getpriority(PRIO_PROCESS, 0);
        get_sched_policy(0, &mPreviousSchedulingGroup);
        androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
    }

    status_t status = NO_ERROR;
    // 判断当前状态是否异常
    if (!(flags & CBLK_INVALID)) {
        // 没有异常 就直接start操作
        status = mAudioTrack->start();
        // 如果start操作失败了,返回DEAD_OBJECT则将flag添加上CBLK_INVALID
        if (status == DEAD_OBJECT) {
            flags |= CBLK_INVALID;
        }
    }
    // 含有CBLK_INVALID标志,会触发restoreTrack_l的操作。
    // restore会将mSequence计数+1, 然后重新调用createTrack_l,最后调用start操作
    if (flags & CBLK_INVALID) {
        status = restoreTrack_l("start");
    }
    // 如果依然有异常
    if (status != NO_ERROR) {
        // 把当前状态恢复到调用该函数之前
        mState = previousState;
        if (t != 0) {
            // 如果线程存在,并且状态不是stopping,让线程进入pause的状态。因为能到这边的话,状态必然
            // 是非active的。所以这边只要排除是非offload的状态即可。如果是offload的话就不会进去。
            if (previousState != STATE_STOPPING) {
                t->pause();
            }
        } else {
            // 如果线程不存在,则将权限设置回去保存下来的状态。
            setpriority(PRIO_PROCESS, 0, mPreviousPriority);
            set_sched_policy(0, mPreviousSchedulingGroup);
        }
    }

    return status;
}

由于start函数的调用,并不是只调用一次,它可能在你调用了pause或者stop之后还会再次调用。所以它的逻辑里面涵盖了各种状态下面调用start函数的处理。

该函数主要做了:

  1. 进行状态的转换,一些成员变量的更新,包括位置、标志、状态等
  2. mAudioTrackThread线程的调度
  3. IAudioTrack的start
  4. 失败之后状态的回置

接下来我们要继续分析的是这句mAudioTrack->start().

status_t AudioFlinger::PlaybackThread::Track::start(AudioSystem::sync_event_t event __unused,
                                                    int triggerSession __unused)
{
    status_t status = NO_ERROR;
    // 从Track内部获取到该track所在的output thread。
    sp<ThreadBase> thread = mThread.promote();
    if (thread != 0) {
        // 该函数只是简单的判断下trackFlags是否含有OFFLOAD的标志
        if (isOffloaded()) {
            Mutex::Autolock _laf(thread->mAudioFlinger->mLock);
            Mutex::Autolock _lth(thread->mLock);
            sp<EffectChain> ec = thread->getEffectChain_l(mSessionId);
            // 看下效果链里面的效果有没有不支持offload effect的,如果有则判定为异常的track
            if (thread->mAudioFlinger->isNonOffloadableGlobalEffectEnabled_l() ||
                    (ec != 0 && ec->isNonOffloadableEnabled())) {
                invalidate();
                return PERMISSION_DENIED;
            }
        }
        Mutex::Autolock _lth(thread->mLock);
        track_state state = mState;
        // here the track could be either new, or restarted
        // in both cases "unstop" the track

        // initial state-stopping. next state-pausing.
        // What if resume is called ?
        // 如果之前的状态为pause的话,根据情况设置state。
        if (state == PAUSED || state == PAUSING) {
            // mResumeToStopping这个变量是针对offload的。stop后pause会将这个开关打开。
            if (mResumeToStopping) {
                // happened we need to resume to STOPPING_1
                mState = TrackBase::STOPPING_1;
            } else {
                mState = TrackBase::RESUMING;
            }
        } else {
            // 如果非pause的话,则设置为active
            mState = TrackBase::ACTIVE;
        }

        PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
        if (isFastTrack()) {
            // 在start函数中刷新下fast track的underruns因为这个变量在fast mixer中不会被清除。
            // 此外,这个track还有可能被回收,例如 start -> stop这种场景。
            mObservedUnderruns = playbackThread->getFastTrackUnderruns(mFastIndex);
        }
        // 至关重要的一句话,将该track添加到对应的thread中。
        status = playbackThread->addTrack_l(this);
        // 判断是否添加成功。
        if (status == INVALID_OPERATION || status == PERMISSION_DENIED) {
            triggerEvents(AudioSystem::SYNC_EVENT_PRESENTATION_COMPLETE);
            //  restore previous state if start was rejected by policy manager
            if (status == PERMISSION_DENIED) {
                mState = state;
            }
        }
        // status为ALREADY_EXISTS 表示已经存在了,不算失败。
        if (status == ALREADY_EXISTS) {
            status = NO_ERROR;
        } else {
            // Acknowledge any pending flush(), so that subsequent new data isn't discarded.
            // 在一个binder线程里面访问server proxy是不安全的。但是这边是个例外,我们知道mixer thread
            // 还没不知道该track的存在,我们任然持有mixer thread的锁,还有对于fast track,它也还没被放
            // 到fast mixer thread的激活队列中。
            ServerProxy::Buffer buffer;
            buffer.mFrameCount = 1;
            (void) mAudioTrackServerProxy->obtainBuffer(&buffer, true /*ackFlush*/);
        }
    } else {
        status = BAD_VALUE;
    }
    return status;
}

这个函数跟audiotrack中的start很相似,也是更新下state,然后将该track添加到thread的active list中。
最后还会调用mAudioTrackServerProxy->obtainBuffer

  1. 判断该track是否是符合要求的
  2. 进行track的状态转换
  3. 添加track到thread中
  4. 失败的状态回置
  5. 成功的话,server proxy则进行1帧的obtainBuffer操作,带ackFlush的。

现在我们先看下函数AudioFlinger::PlaybackThread::addTrack_l()的流程。

status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{
    status_t status = ALREADY_EXISTS;

    // 缓冲区填充等待多少次循环,默认50次
    track->mRetryCount = kMaxTrackStartupRetries;
    // 判断这个track是否已经在active track队列中
    if (mActiveTracks.indexOf(track) < 0) {
        // 这个track是新添加的,确保它在先把自己的缓冲区都填满了之后再开始播放。
        // This is to ensure the client will effectively get the latency it requested.
        // 不是patch和output类型的track
        if (track->isExternalTrack()) {
            TrackBase::track_state state = track->mState;
            mLock.unlock();
            // 这个lock在上面的函数中被持有了,所以这边要释放锁,因为接下来将进入新的通信。
            status = AudioSystem::startOutput(mId, track->streamType(),
                                              (audio_session_t)track->sessionId());
            // 重新获取该锁
            mLock.lock();
            // 终止该track,如果在我们执行startoutput的过程中,该track被stop或者pause了。
            if (state != track->mState) {
                if (status == NO_ERROR) {
                    mLock.unlock();
                    AudioSystem::stopOutput(mId, track->streamType(),
                                            (audio_session_t)track->sessionId());
                    mLock.lock();
                }
                return INVALID_OPERATION;
            }
            // 如果被AudioPolicy给拒绝了,也返回失败
            if (status != NO_ERROR) {
                return PERMISSION_DENIED;
            }
#ifdef ADD_BATTERY_DATA
            // 用来跟踪speaker的使用情况
            addBatteryData(IMediaPlayerService::kBatteryDataAudioFlingerStart);
#endif
        }
        // 如果是共享内存的话,则将状态设置Track::FS_FILLED,否则设置为Track::FS_FILLING
        // 这个状态是用来保证,在播放之前数据已经将缓冲区全部填满,在还没有进入FS_FILLED之前,
        // thread是不会去获取buffer里面的数据的。这个只针对一开始的写。
        track->mFillingUpStatus = track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
        track->mResetDone = false;
        // 这个变量主要是用在pause和stop之后,要将缓冲区的东西排干用的。
        track->mPresentationCompleteFrames = 0;
        // 一旦已经startoutput之后,就将这个track添加到mActiveTracks中来。
        mActiveTracks.add(track);
        mWakeLockUids.add(track->uid());
        mActiveTracksGeneration++;
        mLatestActiveTrack = track;
        sp<EffectChain> chain = getEffectChain_l(track->sessionId());
        if (chain != 0) {
            // 如果效果链不为0,则为该效果链进行计数累加
            chain->incActiveTrackCnt();
        }
        status = NO_ERROR;
    }
    // 如果子类有实现该方法,会调用到子类的该方法,然后调用PlaybackThread的该方法,所以有时候我们可以在
    // 子类的这个函数上做一些实现,比如音量、效果等的一些设置。
    onAddNewTrack_l();
    return status;
}

函数逻辑:

  1. 如果这个track不属于active track列表里面的,则继续执行,否则直接返回已经存在的标志
  2. 会通过AudioSystem去进行startouput的流程,这个就是打通路由通路
  3. 如果打开失败的话,则进行stopoutput流程
  4. 如果成功,则进行track的一些状态更新,添加到active track队列来,效果链的引用计数更新。

startoutputstopoutput我们这边暂且不进行分析了,这个牵扯的东西非常多,现在我们只要明确,这是用来打开路由通路即可。

那接着我们回到mAudioTrackServerProxy->obtainBuffer来继续看

status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
{
    LOG_ALWAYS_FATAL_IF(buffer == NULL || buffer->mFrameCount == 0);
    if (mIsShutdown) {
        goto no_init;
    }
    {
    // 获取公共的控制块
    audio_track_cblk_t* cblk = mCblk;
    // compute number of frames available to write (AudioTrack) or read (AudioRecord),
    // or use previous cached value from framesReady(), with added barrier if it omits.
    int32_t front;
    int32_t rear;
    // See notes on barriers at ClientProxy::obtainBuffer()
    if (mIsOut) {
        // 这个flush的值是又rear的头+flush的尾+二个p2构成的
        int32_t flush = cblk->u.mStreaming.mFlush;
        rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
        front = cblk->u.mStreaming.mFront;
        // 两个flush的值不一致,说明之前已经start过一次了,并且有执行过flush的操作
        if (flush != mFlush) {
            // effectively obtain then release whatever is in the buffer
            const size_t overflowBit = mFrameCountP2 << 1;
            const size_t mask = overflowBit - 1;
            // front是0~n*mFrameCountP2,在二进制是很好表示的,取front的头,结合
            // flush的部分,flush取的是尾部的。
            int32_t newFront = (front & ~mask) | (flush & mask);
            ssize_t filled = rear - newFront;
            if (filled >= (ssize_t)overflowBit) {
                // front and rear offsets span the overflow bit of the p2 mask
                // so rebasing newFront on the front offset is off by the overflow bit.
                // adjust newFront to match rear offset.
                ALOGV("flush wrap: filled %zx >= overflowBit %zx", filled, overflowBit);
                newFront += overflowBit;
                filled -= overflowBit;
            }
            // Rather than shutting down on a corrupt flush, just treat it as a full flush
            // 对于一个数据污染的flush,我们不是采用停止,而是给一个full flush操作。
            if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
                newFront = rear;
            }
            mFlush = flush;
            // 更新front的位置,其实就是将flush到之前front这段数据给清掉了。
            android_atomic_release_store(newFront, &cblk->u.mStreaming.mFront);
            if (true /*front != newFront*/) {
                // 有必要的话,去唤醒client进行数据填充。
                int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
                if (!(old & CBLK_FUTEX_WAKE)) {
                    (void) syscall(__NR_futex, &cblk->mFutex,
                            mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
                }
            }
            front = newFront;
        }
    } else {
        front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
        rear = cblk->u.mStreaming.mRear;
    }
    // 正常获取空间的部分,先省略,后面会write的时候会分析。
    ...  

    // After flush(), allow releaseBuffer() on a previously obtained buffer;
    // see "Acknowledge any pending flush()" in audioflinger/Tracks.cpp.
    // 执行完flush之后,允许调用releaseBuffer来释放之前获取的buffer。
    if (!ackFlush) {
        mUnreleased = part1;
    }
    return part1 > 0 ? NO_ERROR : WOULD_BLOCK;
    }
no_init:
    buffer->mFrameCount = 0;
    buffer->mRaw = NULL;
    buffer->mNonContig = 0;
    mUnreleased = 0;
    return NO_INIT;
}

从上面的代码逻辑上看,在start函数中调用获取buffer操作,看似没啥用,其实是为了处理,某个track之前有被stop或者pause,并且被flush的场景。

从上面的代码上看,flush是将清掉flush到front这区间的数据。不过这个需要取决于flush值是如何计算的,因为如果计算出来filled的空间不在framecount的预期内的话,会将front设置为rear,也就是整个buffer都清空。

总结

我们来大致梳理下,这个play都干嘛了。

  1. 状态的设置
  2. 路由通路打开
  3. 将该track添加到active track中
  4. 对缓冲区进行flush操作

流程图示:

状态机更新:

audiotrack:

AudioFlinger端的track的状态机: