Android Audio代码分析15 - testPlaybackHeadPositionAfterFlush

上次看到的testPlaybackHeadPositionIncrease函数中,先play了一会,然后获取position。
今天看个复杂点的,先play,然后stop,之后在flush,此时再获取position会是什么情况呢?


*****************************************源码*************************************************
    //Test case 3: getPlaybackHeadPosition() is 0 after flush();
    @LargeTest
    public void testPlaybackHeadPositionAfterFlush() throws Exception {
        // constants for test
        final String TEST_NAME = "testPlaybackHeadPositionAfterFlush";
        final int TEST_SR = 22050;
        final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
        final int TEST_MODE = AudioTrack.MODE_STREAM;
        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
        
        //-------- initialization --------------
        int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, 
                minBuffSize, TEST_MODE);
        byte data[] = new byte[minBuffSize/2];
        //--------    test        --------------
        assumeTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
        track.write(data, 0, data.length);
        track.write(data, 0, data.length);
        track.play();
        Thread.sleep(100);
        track.stop();
        track.flush();
        log(TEST_NAME, "position ="+ track.getPlaybackHeadPosition());
        assertTrue(TEST_NAME, track.getPlaybackHeadPosition() == 0);
        //-------- tear down      --------------
        track.release();
    }

**********************************************************************************************
源码路径:
frameworks\base\media\tests\mediaframeworktest\src\com\android\mediaframeworktest\functional\MediaAudioTrackTest.java


#######################说明################################
    //Test case 3: getPlaybackHeadPosition() is 0 after flush();
    @LargeTest
    public void testPlaybackHeadPositionAfterFlush() throws Exception {
        // constants for test
        final String TEST_NAME = "testPlaybackHeadPositionAfterFlush";
        final int TEST_SR = 22050;
        final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
        final int TEST_MODE = AudioTrack.MODE_STREAM;
        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
        
        //-------- initialization --------------
        int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, 
                minBuffSize, TEST_MODE);
        byte data[] = new byte[minBuffSize/2];
        //--------    test        --------------
        assumeTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
        track.write(data, 0, data.length);
        track.write(data, 0, data.length);
        track.play();
        Thread.sleep(100);
        track.stop();
// ++++++++++++++++++++++++++++stop++++++++++++++++++++++++++++++++++++
    /**
     * Stops playing the audio data.
     * @throws IllegalStateException
     */
    public void stop()
    throws IllegalStateException {
        if (mState != STATE_INITIALIZED) {
            throw(new IllegalStateException("stop() called on uninitialized AudioTrack."));
        }


        // stop playing
        synchronized(mPlayStateLock) {
            native_stop();
// ++++++++++++++++++++++++++++++android_media_AudioTrack_stop++++++++++++++++++++++++++++++++++
static void
android_media_AudioTrack_stop(JNIEnv *env, jobject thiz)
{
    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    if (lpTrack == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException",
            "Unable to retrieve AudioTrack pointer for stop()");
        return;
    }


    lpTrack->stop();
// +++++++++++++++++++++++++++++++AudioTrack::stop+++++++++++++++++++++++++++++++++
void AudioTrack::stop()
{
// mAudioTrackThread在函数AudioTrack::set中被赋值。
// mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
    sp<AudioTrackThread> t = mAudioTrackThread;


    LOGV("stop %p", this);
    if (t != 0) {
        t->mLock.lock();
    }


    if (android_atomic_and(~1, &mActive) == 1) {
        mCblk->cv.signal();
// mAudioTrack在函数AudioTrack::createTrack中被赋值,其最终指向的其实是一个TrackHandle对象
        mAudioTrack->stop();
// ++++++++++++++++++++++++++++++AudioFlinger::TrackHandle::stop++++++++++++++++++++++++++++++++++
void AudioFlinger::TrackHandle::stop() {
    mTrack->stop();
// +++++++++++++++++++++++++++++AudioFlinger::PlaybackThread::Track::stop+++++++++++++++++++++++++++++++++++
void AudioFlinger::PlaybackThread::Track::stop()
{
    LOGV("stop(%d), calling thread %d", mName, IPCThreadState::self()->getCallingPid());
// 在Track的构造函数中赋值,指向创建该Track对象的PlaybackThread
    sp<ThreadBase> thread = mThread.promote();
    if (thread != 0) {
        Mutex::Autolock _l(thread->mLock);
        int state = mState;
        if (mState > STOPPED) {
// ++++++++++++++++++++++++++++++++track_state++++++++++++++++++++++++++++++++
            enum track_state {
                IDLE,
                TERMINATED,
                STOPPED,
                RESUMING,
                ACTIVE,
                PAUSING,
                PAUSED
            };
// --------------------------------track_state--------------------------------
            mState = STOPPED;
            // If the track is not active (PAUSED and buffers full), flush buffers
            PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
            if (playbackThread->mActiveTracks.indexOf(this) < 0) {
                reset();
// ++++++++++++++++++++++++++++++AudioFlinger::PlaybackThread::Track::reset++++++++++++++++++++++++++++++++++
void AudioFlinger::PlaybackThread::Track::reset()
{
    // Do not reset twice to avoid discarding data written just after a flush and before
    // the audioflinger thread detects the track is stopped.
    if (!mResetDone) {
        TrackBase::reset();
// ++++++++++++++++++++++++++++++AudioFlinger::ThreadBase::TrackBase::reset++++++++++++++++++++++++++++++++++
void AudioFlinger::ThreadBase::TrackBase::reset() {
    audio_track_cblk_t* cblk = this->cblk();


    cblk->user = 0;
    cblk->server = 0;
    cblk->userBase = 0;
    cblk->serverBase = 0;
    mFlags &= (uint32_t)(~SYSTEM_FLAGS_MASK);
    LOGV("TrackBase::reset");
}
// ------------------------------AudioFlinger::ThreadBase::TrackBase::reset----------------------------------
        // Force underrun condition to avoid false underrun callback until first data is
        // written to buffer
        mCblk->flags |= CBLK_UNDERRUN_ON;
        mCblk->flags &= ~CBLK_FORCEREADY_MSK;
        mFillingUpStatus = FS_FILLING;
        mResetDone = true;
    }
}
// ------------------------------AudioFlinger::PlaybackThread::Track::reset----------------------------------
            }
            LOGV("(> STOPPED) => STOPPED (%d) on thread %p", mName, playbackThread);
        }
        if (!isOutputTrack() && (state == ACTIVE || state == RESUMING)) {
            thread->mLock.unlock();
            AudioSystem::stopOutput(thread->id(),
                                    (AudioSystem::stream_type)mStreamType,
                                    mSessionId);
// ++++++++++++++++++++++++++++++AudioSystem::stopOutput++++++++++++++++++++++++++++++++++
status_t AudioSystem::stopOutput(audio_io_handle_t output,
                                 AudioSystem::stream_type stream,
                                 int session)
{
    const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
    if (aps == 0) return PERMISSION_DENIED;
    return aps->stopOutput(output, stream, session);
// +++++++++++++++++++++++++++++++++AudioPolicyService::stopOutput+++++++++++++++++++++++++++++++
status_t AudioPolicyService::stopOutput(audio_io_handle_t output,
                                        AudioSystem::stream_type stream,
                                        int session)
{
    if (mpPolicyManager == NULL) {
        return NO_INIT;
    }
    LOGV("stopOutput() tid %d", gettid());
    Mutex::Autolock _l(mLock);
    return mpPolicyManager->stopOutput(output, stream, session);
// ++++++++++++++++++++++++++++++AudioPolicyManagerBase::stopOutput++++++++++++++++++++++++++++++++++
status_t AudioPolicyManagerBase::stopOutput(audio_io_handle_t output,
                                            AudioSystem::stream_type stream,
                                            int session)
{
    LOGV("stopOutput() output %d, stream %d, session %d", output, stream, session);
    ssize_t index = mOutputs.indexOfKey(output);
    if (index < 0) {
        LOGW("stopOutput() unknow output %d", output);
        return BAD_VALUE;
    }


    AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
    routing_strategy strategy = getStrategy((AudioSystem::stream_type)stream);


    // handle special case for sonification while in call
    if (isInCall()) {
        handleIncallSonification(stream, false, false);
    }


    if (outputDesc->mRefCount[stream] > 0) {
        // decrement usage count of this stream on the output
        outputDesc->changeRefCount(stream, -1);
// ++++++++++++++++++++++++++++AudioPolicyManagerBase::AudioOutputDescriptor::changeRefCount++++++++++++++++++++++++++++++++++++
void AudioPolicyManagerBase::AudioOutputDescriptor::changeRefCount(AudioSystem::stream_type stream, int delta)
{
    // forward usage count change to attached outputs
    if (isDuplicated()) {
        mOutput1->changeRefCount(stream, delta);
        mOutput2->changeRefCount(stream, delta);
    }
    if ((delta + (int)mRefCount[stream]) < 0) {
        LOGW("changeRefCount() invalid delta %d for stream %d, refCount %d", delta, stream, mRefCount[stream]);
        mRefCount[stream] = 0;
        return;
    }
// 函数AudioPolicyManagerBase::AudioOutputDescriptor::refCount会使用mRefCount
    mRefCount[stream] += delta;
// +++++++++++++++++++++++++++++AudioPolicyManagerBase::AudioOutputDescriptor::refCount+++++++++++++++++++++++++++++++++++
uint32_t AudioPolicyManagerBase::AudioOutputDescriptor::refCount()
{
    uint32_t refcount = 0;
    for (int i = 0; i < (int)AudioSystem::NUM_STREAM_TYPES; i++) {
        refcount += mRefCount[i];
    }
    return refcount;
}
// 函数AudioPolicyManagerBase::releaseOutput中有调用函数AudioPolicyManagerBase::AudioOutputDescriptor::refCount。
// +++++++++++++++++++++++++++++AudioPolicyManagerBase::releaseOutput+++++++++++++++++++++++++++++++++++
void AudioPolicyManagerBase::releaseOutput(audio_io_handle_t output)
{
    LOGV("releaseOutput() %d", output);
    ssize_t index = mOutputs.indexOfKey(output);
    if (index < 0) {
        LOGW("releaseOutput() releasing unknown output %d", output);
        return;
    }


#ifdef AUDIO_POLICY_TEST
    int testIndex = testOutputIndex(output);
    if (testIndex != 0) {
        AudioOutputDescriptor *outputDesc = mOutputs.valueAt(index);
        if (outputDesc->refCount() == 0) {
            mpClientInterface->closeOutput(output);
            delete mOutputs.valueAt(index);
            mOutputs.removeItem(output);
            mTestOutputs[testIndex] = 0;
        }
        return;
    }
#endif //AUDIO_POLICY_TEST


// 如果没定义AUDIO_POLICY_TEST,只对设置了OUTPUT_FLAG_DIRECT标志的output做delete处理
// 原因,其实在以前我们看AudioPolicyManagerBase::getOutput函数时已经看到了,
// 在get output的时候,只有需要一个direct output的时候,才会调用函数AudioPolicyService::openOutput函数来打开一个output
// 否则,只是将成员变量(在构造函数中打开的output)返回
// 当然,我们没有考虑AUDIO_POLICY_TEST时的情况
    if (mOutputs.valueAt(index)->mFlags & AudioSystem::OUTPUT_FLAG_DIRECT) {
// ++++++++++++++++++++++++++++++output_flags++++++++++++++++++++++++++++++++++
    // request to open a direct output with getOutput() (by opposition to sharing an output with other AudioTracks)
    enum output_flags {
        OUTPUT_FLAG_INDIRECT = 0x0,
        OUTPUT_FLAG_DIRECT = 0x1
    };
// ------------------------------output_flags----------------------------------
        mpClientInterface->closeOutput(output);
// +++++++++++++++++++++++++++++AudioPolicyService::closeOutput+++++++++++++++++++++++++++++++++++
status_t AudioPolicyService::closeOutput(audio_io_handle_t output)
{
    sp<IAudioFlinger> af = AudioSystem::get_audio_flinger();
    if (af == 0) return PERMISSION_DENIED;


    return af->closeOutput(output);
// +++++++++++++++++++++++++++AudioFlinger::closeOutput++++++++++++++++++++++++++++++++++++
status_t AudioFlinger::closeOutput(int output)
{
    // keep strong reference on the playback thread so that
    // it is not destroyed while exit() is executed
    sp <PlaybackThread> thread;
    {
        Mutex::Autolock _l(mLock);
        thread = checkPlaybackThread_l(output);
        if (thread == NULL) {
            return BAD_VALUE;
        }


        LOGV("closeOutput() %d", output);


// 如果thread 为mixer
// 删除所有DUPLICATING thread中包含的该thread
        if (thread->type() == PlaybackThread::MIXER) {
            for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
                if (mPlaybackThreads.valueAt(i)->type() == PlaybackThread::DUPLICATING) {
                    DuplicatingThread *dupThread = (DuplicatingThread *)mPlaybackThreads.valueAt(i).get();
                    dupThread->removeOutputTrack((MixerThread *)thread.get());
                }
            }
        }
        void *param2 = 0;
        audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2);
        mPlaybackThreads.removeItem(output);
    }
    thread->exit();
// ++++++++++++++++++++++++++++AudioFlinger::ThreadBase::exit++++++++++++++++++++++++++++++++++++
void AudioFlinger::ThreadBase::exit()
{
    // keep a strong ref on ourself so that we wont get
    // destroyed in the middle of requestExitAndWait()
    sp <ThreadBase> strongMe = this;


    LOGV("ThreadBase::exit");
    {
        AutoMutex lock(&mLock);
        mExiting = true;
        requestExit();
// +++++++++++++++++++++++++++++Thread::requestExit+++++++++++++++++++++++++++++++++++
void Thread::requestExit()
{
// threadloop函数中会判断该成员变量,以判断是否要结束线程
    mExitPending = true;
}
// -----------------------------Thread::requestExit----------------------------------
        mWaitWorkCV.signal();
    }
    requestExitAndWait();
// +++++++++++++++++++++++++++++Thread::requestExitAndWait+++++++++++++++++++++++++++++++++++
status_t Thread::requestExitAndWait()
{
    if (mThread == getThreadId()) {
        LOGW(
        "Thread (this=%p): don't call waitForExit() from this "
        "Thread object's thread. It's a guaranteed deadlock!",
        this);


        return WOULD_BLOCK;
    }
    
    requestExit();


    Mutex::Autolock _l(mLock);
    while (mRunning == true) {
        mThreadExitedCondition.wait(mLock);
    }
    mExitPending = false;


    return mStatus;
}
// -----------------------------Thread::requestExitAndWait-----------------------------------
}
// ----------------------------AudioFlinger::ThreadBase::exit------------------------------------


    if (thread->type() != PlaybackThread::DUPLICATING) {
// output是在函数AudioFlinger::openOutput中调用函数mAudioHardware->openOutputStream打开的。
        mAudioHardware->closeOutputStream(thread->getOutput());
// +++++++++++++++++++++++++++AudioHardwareALSA::closeOutputStream+++++++++++++++++++++++++++++++++++++
void
AudioHardwareALSA::closeOutputStream(AudioStreamOut* out)
{
    AutoMutex lock(mLock);
    delete out;
}
// ---------------------------AudioHardwareALSA::closeOutputStream-------------------------------------
    }
    return NO_ERROR;
}
// ----------------------------AudioFlinger::closeOutput------------------------------------
}
// -----------------------------AudioPolicyService::closeOutput-----------------------------------
        delete mOutputs.valueAt(index);
        mOutputs.removeItem(output);
    }
}
// -----------------------------AudioPolicyManagerBase::releaseOutput-----------------------------------
// -----------------------------AudioPolicyManagerBase::AudioOutputDescriptor::refCount-----------------------------------
    LOGV("changeRefCount() stream %d, count %d", stream, mRefCount[stream]);
}
// ----------------------------AudioPolicyManagerBase::AudioOutputDescriptor::changeRefCount------------------------------------
        // store time at which the last music track was stopped - see computeVolume()
        if (stream == AudioSystem::MUSIC) {
            mMusicStopTime = systemTime();
        }


        setOutputDevice(output, getNewDevice(output));


#ifdef WITH_A2DP
        if (mA2dpOutput != 0 && !a2dpUsedForSonification() &&
                strategy == STRATEGY_SONIFICATION) {
            setStrategyMute(STRATEGY_MEDIA,
                            false,
                            mA2dpOutput,
                            mOutputs.valueFor(mHardwareOutput)->mLatency*2);
        }
#endif
        if (output != mHardwareOutput) {
            setOutputDevice(mHardwareOutput, getNewDevice(mHardwareOutput), true);
        }
        return NO_ERROR;
    } else {
        LOGW("stopOutput() refcount is already 0 for output %d", output);
        return INVALID_OPERATION;
    }
}
// ------------------------------AudioPolicyManagerBase::stopOutput----------------------------------
}
// ---------------------------------AudioPolicyService::stopOutput-------------------------------
}
// ------------------------------AudioSystem::stopOutput----------------------------------
            thread->mLock.lock();
        }
    }
}
// -----------------------------AudioFlinger::PlaybackThread::Track::stop-----------------------------------
}
// ------------------------------AudioFlinger::TrackHandle::stop----------------------------------
        // Cancel loops (If we are in the middle of a loop, playback
        // would not stop until loopCount reaches 0).
        setLoop(0, 0, 0);
// +++++++++++++++++++++++++++++AudioTrack::setLoop+++++++++++++++++++++++++++++++++++
status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
    audio_track_cblk_t* cblk = mCblk;


    Mutex::Autolock _l(cblk->lock);


    if (loopCount == 0) {
        cblk->loopStart = ULLONG_MAX;
        cblk->loopEnd = ULLONG_MAX;
        cblk->loopCount = 0;
        mLoopCount = 0;
        return NO_ERROR;
    }


    if (loopStart >= loopEnd ||
        loopEnd - loopStart > cblk->frameCount) {
        LOGE("setLoop invalid value: loopStart %d, loopEnd %d, loopCount %d, framecount %d, user %lld", loopStart, loopEnd, loopCount, cblk->frameCount, cblk->user);
        return BAD_VALUE;
    }


    if ((mSharedBuffer != 0) && (loopEnd   > cblk->frameCount)) {
        LOGE("setLoop invalid value: loop markers beyond data: loopStart %d, loopEnd %d, framecount %d",
            loopStart, loopEnd, cblk->frameCount);
        return BAD_VALUE;
    }


    cblk->loopStart = loopStart;
    cblk->loopEnd = loopEnd;
    cblk->loopCount = loopCount;
    mLoopCount = loopCount;


    return NO_ERROR;
}
// -----------------------------AudioTrack::setLoop-----------------------------------
        // the playback head position will reset to 0, so if a marker is set, we need
        // to activate it again
        mMarkerReached = false;
        // Force flush if a shared buffer is used otherwise audioflinger
        // will not stop before end of buffer is reached.
        if (mSharedBuffer != 0) {
            flush();
// ++++++++++++++++++++++++++++AudioTrack::flush++++++++++++++++++++++++++++++++++++
void AudioTrack::flush()
{
    LOGV("flush");


    // clear playback marker and periodic update counter
    mMarkerPosition = 0;
    mMarkerReached = false;
    mUpdatePeriod = 0;




    if (!mActive) {
        mAudioTrack->flush();
// ++++++++++++++++++++++++++++++AudioFlinger::TrackHandle::flush++++++++++++++++++++++++++++++++++
void AudioFlinger::TrackHandle::flush() {
    mTrack->flush();
// ++++++++++++++++++++++++++++++AudioFlinger::PlaybackThread::Track::flush++++++++++++++++++++++++++++++++++
void AudioFlinger::PlaybackThread::Track::flush()
{
    LOGV("flush(%d)", mName);
    sp<ThreadBase> thread = mThread.promote();
    if (thread != 0) {
        Mutex::Autolock _l(thread->mLock);
        if (mState != STOPPED && mState != PAUSED && mState != PAUSING) {
            return;
        }
        // No point remaining in PAUSED state after a flush => go to
        // STOPPED state
        mState = STOPPED;


        mCblk->lock.lock();
        // NOTE: reset() will reset cblk->user and cblk->server with
        // the risk that at the same time, the AudioMixer is trying to read
        // data. In this case, getNextBuffer() would return a NULL pointer
        // as audio buffer => the AudioMixer code MUST always test that pointer
        // returned by getNextBuffer() is not NULL!
// reset函数在stop函数中已经看过
        reset();
        mCblk->lock.unlock();
    }
}
// ------------------------------AudioFlinger::PlaybackThread::Track::flush----------------------------------
}
// ------------------------------AudioFlinger::TrackHandle::flush----------------------------------
        // Release AudioTrack callback thread in case it was waiting for new buffers
        // in AudioTrack::obtainBuffer()
        mCblk->cv.signal();
    }
}
// ----------------------------AudioTrack::flush------------------------------------
        }
        if (t != 0) {
            t->requestExit();
        } else {
            setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL);
        }
    }


    if (t != 0) {
        t->mLock.unlock();
    }
}
// -------------------------------AudioTrack::stop---------------------------------
}
// ------------------------------android_media_AudioTrack_stop----------------------------------
            mPlayState = PLAYSTATE_STOPPED;
        }
    }
// ----------------------------stop------------------------------------
        track.flush();
// ++++++++++++++++++++++++++++flush++++++++++++++++++++++++++++++++++++
    /**
     * Flushes the audio data currently queued for playback.
     */


    public void flush() {
        if (mState == STATE_INITIALIZED) {
            // flush the data in native layer
            native_flush();
// ++++++++++++++++++++++++++++android_media_AudioTrack_flush++++++++++++++++++++++++++++++++++++
static void
android_media_AudioTrack_flush(JNIEnv *env, jobject thiz)
{
    AudioTrack *lpTrack = (AudioTrack *)env->GetIntField(
        thiz, javaAudioTrackFields.nativeTrackInJavaObj);
    if (lpTrack == NULL ) {
        jniThrowException(env, "java/lang/IllegalStateException",
            "Unable to retrieve AudioTrack pointer for flush()");
        return;
    }


    lpTrack->flush();
// +++++++++++++++++++++++++++++AudioTrack::flush+++++++++++++++++++++++++++++++++++
void AudioTrack::flush()
{
    LOGV("flush");


    // clear playback marker and periodic update counter
    mMarkerPosition = 0;
    mMarkerReached = false;
    mUpdatePeriod = 0;




    if (!mActive) {
        mAudioTrack->flush();
// ++++++++++++++++++++++++++++AudioFlinger::TrackHandle::flush++++++++++++++++++++++++++++++++++++
void AudioFlinger::TrackHandle::flush() {
// 函数AudioFlinger::PlaybackThread::Track::flush在上面已经看过
    mTrack->flush();
}
// ----------------------------AudioFlinger::TrackHandle::flush------------------------------------
        // Release AudioTrack callback thread in case it was waiting for new buffers
        // in AudioTrack::obtainBuffer()
        mCblk->cv.signal();
    }
}
// -----------------------------AudioTrack::flush-----------------------------------
}
// ----------------------------android_media_AudioTrack_flush------------------------------------
        }


    }
// ----------------------------flush------------------------------------
        log(TEST_NAME, "position ="+ track.getPlaybackHeadPosition());
        assertTrue(TEST_NAME, track.getPlaybackHeadPosition() == 0);
        //-------- tear down      --------------
        track.release();
    }

###########################################################


&&&&&&&&&&&&&&&&&&&&&&&总结&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
stop的时候:
若AudioTrack不是active的,在将其audio_track_cblk_t中的user, server等清0。
若是direct output,则将其close。


flush的时候:
会将audio_track_cblk_t中的user, server等清0。
因此,flush 之后再get position的话,肯定是0.
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值