Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1a270d1 authored by Richard Fitzgerald's avatar Richard Fitzgerald Committed by Eric Laurent
Browse files

libmedia: offloaded playback support



- start() returns a status so that upper layers can
recreate a non offloaded track in case of error.

- Added states to handle offloaded tracks specific:
  - waiting for stream end (drain) notification by
audio flinger
  - allow pause while waiting for stream end notification

- getPosition() queries the render position directly from
audio HAL.

- disable APIs not applicable to offloaded tracks

- Modified track restoring behavior for invalidated
offloaded tracks: just send the callback and wait for
upper layers to create a new track.

- Added wait for stream end management in audio track client
proxy. Similar to obtainBuffer and should be factored in.

Change-Id: I0fc48117946364cb255afd653195498891f622bd
Signed-off-by: default avatarEric Laurent <elaurent@google.com>
parent bfb1b832
Loading
Loading
Loading
Loading
+8 −4
Original line number Diff line number Diff line
@@ -277,7 +277,7 @@ public:
     * make it active. If set, the callback will start being called.
     * If the track was previously paused, volume is ramped up over the first mix buffer.
     */
            void        start();
            status_t        start();

    /* Stop a track.
     * In static buffer mode, the track is stopped immediately.
@@ -635,11 +635,12 @@ protected:
            void setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount);
            audio_io_handle_t getOutput_l();

            status_t getPosition_l(uint32_t *position);

            // FIXME enum is faster than strcmp() for parameter 'from'
            status_t restoreTrack_l(const char *from);

            bool     isOffloaded() const
                { return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; }

    // may be changed if IAudioTrack is re-created
    sp<IAudioTrack>         mAudioTrack;
    sp<IMemory>             mCblkMemory;
@@ -676,7 +677,9 @@ protected:
        STATE_ACTIVE,
        STATE_STOPPED,
        STATE_PAUSED,
        STATE_PAUSED_STOPPING,
        STATE_FLUSHED,
        STATE_STOPPING,
    }                       mState;

    callback_t              mCbf;                   // callback handler for events, or NULL
@@ -694,7 +697,7 @@ protected:
    // These are private to processAudioBuffer(), and are not protected by a lock
    uint32_t                mRemainingFrames;       // number of frames to request in obtainBuffer()
    bool                    mRetryOnPartialBuffer;  // sleep and retry after partial obtainBuffer()
    int                     mObservedSequence;      // last observed value of mSequence
    uint32_t                mObservedSequence;      // last observed value of mSequence

    sp<IMemory>             mSharedBuffer;
    uint32_t                mLoopPeriod;            // in frames, zero means looping is disabled
@@ -736,6 +739,7 @@ private:

    sp<DeathNotifier>       mDeathNotifier;
    uint32_t                mSequence;              // incremented for each new IAudioTrack attempt
    audio_io_handle_t       mOutput;                // cached output io handle
};

class TimedAudioTrack : public AudioTrack
+2 −1
Original line number Diff line number Diff line
@@ -296,6 +296,7 @@ public:

    bool        getStreamEndDone() const;

    status_t    waitStreamEndDone(const struct timespec *requested);
};

class StaticAudioTrackClientProxy : public AudioTrackClientProxy {
@@ -379,8 +380,8 @@ public:

protected:
    size_t      mAvailToClient; // estimated frames available to client prior to releaseBuffer()
private:
    int32_t     mFlush;         // our copy of cblk->u.mStreaming.mFlush, for streaming output only
private:
    bool        mDeferWake;     // whether another releaseBuffer() is expected soon
};

+187 −38
Original line number Diff line number Diff line
@@ -28,6 +28,8 @@
#include <media/IAudioFlinger.h>

#define WAIT_PERIOD_MS                  10
#define WAIT_STREAM_END_TIMEOUT_SEC     120


namespace android {
// ---------------------------------------------------------------------------
@@ -141,6 +143,7 @@ AudioTrack::~AudioTrack()
        // Otherwise the callback thread will never exit.
        stop();
        if (mAudioTrackThread != 0) {
            mProxy->interrupt();
            mAudioTrackThread->requestExit();   // see comment in AudioTrack.h
            mAudioTrackThread->requestExitAndWait();
            mAudioTrackThread.clear();
@@ -224,6 +227,8 @@ status_t AudioTrack::set(
        return INVALID_OPERATION;
    }

    mOutput = 0;

    // handle default values first.
    if (streamType == AUDIO_STREAM_DEFAULT) {
        streamType = AUDIO_STREAM_MUSIC;
@@ -259,7 +264,12 @@ status_t AudioTrack::set(
    }

    // force direct flag if format is not linear PCM
    if (!audio_is_linear_pcm(format)) {
    // or offload was requested
    if ((flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
            || !audio_is_linear_pcm(format)) {
        ALOGV( (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD)
                    ? "Offload request, forcing to Direct Output"
                    : "Not linear PCM, forcing to Direct Output");
        flags = (audio_output_flags_t)
                // FIXME why can't we allow direct AND fast?
                ((flags | AUDIO_OUTPUT_FLAG_DIRECT) & ~AUDIO_OUTPUT_FLAG_FAST);
@@ -325,9 +335,14 @@ status_t AudioTrack::set(

    if (status != NO_ERROR) {
        if (mAudioTrackThread != 0) {
            mAudioTrackThread->requestExit();
            mAudioTrackThread->requestExit();   // see comment in AudioTrack.h
            mAudioTrackThread->requestExitAndWait();
            mAudioTrackThread.clear();
        }
        //Use of direct and offloaded output streams is ref counted by audio policy manager.
        // As getOutput was called above and resulted in an output stream to be opened,
        // we need to release it.
        AudioSystem::releaseOutput(output);
        return status;
    }

@@ -346,23 +361,29 @@ status_t AudioTrack::set(
    mSequence = 1;
    mObservedSequence = mSequence;
    mInUnderrun = false;
    mOutput = output;

    return NO_ERROR;
}

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

void AudioTrack::start()
status_t AudioTrack::start()
{
    AutoMutex lock(mLock);

    if (mState == STATE_ACTIVE) {
        return;
        return INVALID_OPERATION;
    }

    mInUnderrun = true;

    State previousState = mState;
    if (previousState == STATE_PAUSED_STOPPING) {
        mState = STATE_STOPPING;
    } else {
        mState = STATE_ACTIVE;
    }
    if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
        // reset current position as seen by client to 0
        mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
@@ -372,7 +393,11 @@ void AudioTrack::start()

    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);
@@ -394,14 +419,16 @@ void AudioTrack::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);
        }
    }

    // FIXME discarding status
    return status;
}

void AudioTrack::stop()
@@ -412,7 +439,12 @@ void AudioTrack::stop()
        return;
    }

    if (isOffloaded()) {
        mState = STATE_STOPPING;
    } else {
        mState = STATE_STOPPED;
    }

    mProxy->interrupt();
    mAudioTrack->stop();
    // the playback head position will reset to 0, so if a marker is set, we need
@@ -426,9 +458,12 @@ void AudioTrack::stop()
        flush_l();
    }
#endif

    sp<AudioTrackThread> t = mAudioTrackThread;
    if (t != 0) {
        if (!isOffloaded()) {
            t->pause();
        }
    } else {
        setpriority(PRIO_PROCESS, 0, mPreviousPriority);
        set_sched_policy(0, mPreviousSchedulingGroup);
@@ -461,8 +496,12 @@ void AudioTrack::flush_l()
    mMarkerPosition = 0;
    mMarkerReached = false;
    mUpdatePeriod = 0;
    mRefreshRemaining = true;

    mState = STATE_FLUSHED;
    if (isOffloaded()) {
        mProxy->interrupt();
    }
    mProxy->flush();
    mAudioTrack->flush();
}
@@ -470,10 +509,13 @@ void AudioTrack::flush_l()
void AudioTrack::pause()
{
    AutoMutex lock(mLock);
    if (mState != STATE_ACTIVE) {
    if (mState == STATE_ACTIVE) {
        mState = STATE_PAUSED;
    } else if (mState == STATE_STOPPING) {
        mState = STATE_PAUSED_STOPPING;
    } else {
        return;
    }
    mState = STATE_PAUSED;
    mProxy->interrupt();
    mAudioTrack->pause();
}
@@ -520,7 +562,7 @@ void AudioTrack::getAuxEffectSendLevel(float* level) const

status_t AudioTrack::setSampleRate(uint32_t rate)
{
    if (mIsTimed) {
    if (mIsTimed || isOffloaded()) {
        return INVALID_OPERATION;
    }

@@ -552,7 +594,7 @@ uint32_t AudioTrack::getSampleRate() const

status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
{
    if (mSharedBuffer == 0 || mIsTimed) {
    if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) {
        return INVALID_OPERATION;
    }

@@ -586,7 +628,7 @@ void AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCount)
status_t AudioTrack::setMarkerPosition(uint32_t marker)
{
    // The only purpose of setting marker position is to get a callback
    if (mCbf == NULL) {
    if (mCbf == NULL || isOffloaded()) {
        return INVALID_OPERATION;
    }

@@ -599,6 +641,9 @@ status_t AudioTrack::setMarkerPosition(uint32_t marker)

status_t AudioTrack::getMarkerPosition(uint32_t *marker) const
{
    if (isOffloaded()) {
        return INVALID_OPERATION;
    }
    if (marker == NULL) {
        return BAD_VALUE;
    }
@@ -612,19 +657,21 @@ status_t AudioTrack::getMarkerPosition(uint32_t *marker) const
status_t AudioTrack::setPositionUpdatePeriod(uint32_t updatePeriod)
{
    // The only purpose of setting position update period is to get a callback
    if (mCbf == NULL) {
    if (mCbf == NULL || isOffloaded()) {
        return INVALID_OPERATION;
    }

    AutoMutex lock(mLock);
    mNewPosition = mProxy->getPosition() + updatePeriod;
    mUpdatePeriod = updatePeriod;

    return NO_ERROR;
}

status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const
{
    if (isOffloaded()) {
        return INVALID_OPERATION;
    }
    if (updatePeriod == NULL) {
        return BAD_VALUE;
    }
@@ -637,7 +684,7 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const

status_t AudioTrack::setPosition(uint32_t position)
{
    if (mSharedBuffer == 0 || mIsTimed) {
    if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) {
        return INVALID_OPERATION;
    }
    if (position > mFrameCount) {
@@ -670,10 +717,19 @@ status_t AudioTrack::getPosition(uint32_t *position) const
    }

    AutoMutex lock(mLock);
    if (isOffloaded()) {
        uint32_t dspFrames = 0;

        if (mOutput != 0) {
            uint32_t halFrames;
            AudioSystem::getRenderPosition(mOutput, &halFrames, &dspFrames);
        }
        *position = dspFrames;
    } else {
        // IAudioTrack::stop() isn't synchronous; we don't know when presentation completes
        *position = (mState == STATE_STOPPED || mState == STATE_FLUSHED) ? 0 :
                mProxy->getPosition();

    }
    return NO_ERROR;
}

@@ -693,7 +749,7 @@ status_t AudioTrack::getBufferPosition(size_t *position)

status_t AudioTrack::reload()
{
    if (mSharedBuffer == 0 || mIsTimed) {
    if (mSharedBuffer == 0 || mIsTimed || isOffloaded()) {
        return INVALID_OPERATION;
    }

@@ -713,15 +769,19 @@ status_t AudioTrack::reload()
audio_io_handle_t AudioTrack::getOutput()
{
    AutoMutex lock(mLock);
    return getOutput_l();
    return mOutput;
}

// must be called with mLock held
audio_io_handle_t AudioTrack::getOutput_l()
{
    if (mOutput) {
        return mOutput;
    } else {
        return AudioSystem::getOutput(mStreamType,
                                      mSampleRate, mFormat, mChannelMask, mFlags);
    }
}

status_t AudioTrack::attachAuxEffect(int effectId)
{
@@ -791,7 +851,9 @@ status_t AudioTrack::createTrack_l(
            }
            frameCount = afFrameCount;
        }

        if (mNotificationFramesAct != frameCount) {
            mNotificationFramesAct = frameCount;
        }
    } else if (sharedBuffer != 0) {

        // Ensure that buffer alignment matches channel count
@@ -875,6 +937,10 @@ status_t AudioTrack::createTrack_l(
        }
    }

    if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
        trackFlags |= IAudioFlinger::TRACK_OFFLOAD;
    }

    sp<IAudioTrack> track = audioFlinger->createTrack(streamType,
                                                      sampleRate,
                                                      // AudioFlinger only sees 16-bit PCM
@@ -937,6 +1003,17 @@ status_t AudioTrack::createTrack_l(
            }
        }
    }
    if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
        if (trackFlags & IAudioFlinger::TRACK_OFFLOAD) {
            ALOGV("AUDIO_OUTPUT_FLAG_OFFLOAD successful");
        } else {
            ALOGW("AUDIO_OUTPUT_FLAG_OFFLOAD denied by server");
            flags = (audio_output_flags_t) (flags & ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD);
            mFlags = flags;
            return NO_INIT;
        }
    }

    mRefreshRemaining = true;

    // Starting address of buffers in shared memory.  If there is a shared buffer, buffers
@@ -1040,6 +1117,9 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *re
                if (newSequence == oldSequence) {
                    status = restoreTrack_l("obtainBuffer");
                    if (status != NO_ERROR) {
                        buffer.mFrameCount = 0;
                        buffer.mRaw = NULL;
                        buffer.mNonContig = 0;
                        break;
                    }
                }
@@ -1050,6 +1130,14 @@ status_t AudioTrack::obtainBuffer(Buffer* audioBuffer, const struct timespec *re
            proxy = mProxy;
            iMem = mCblkMemory;

            if (mState == STATE_STOPPING) {
                status = -EINTR;
                buffer.mFrameCount = 0;
                buffer.mRaw = NULL;
                buffer.mNonContig = 0;
                break;
            }

            // Non-blocking if track is stopped or paused
            if (mState != STATE_ACTIVE) {
                requested = &ClientProxy::kNonBlocking;
@@ -1255,12 +1343,18 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)

    // Check for track invalidation
    if (flags & CBLK_INVALID) {
        (void) restoreTrack_l("processAudioBuffer");
        // for offloaded tracks restoreTrack_l() will just update the sequence and clear
        // AudioSystem cache. We should not exit here but after calling the callback so
        // that the upper layers can recreate the track
        if (!isOffloaded() || (mSequence == mObservedSequence)) {
            status_t status = restoreTrack_l("processAudioBuffer");
            mLock.unlock();
            // Run again immediately, but with a new IAudioTrack
            return 0;
        }
    }

    bool waitStreamEnd = mState == STATE_STOPPING;
    bool active = mState == STATE_ACTIVE;

    // Manage underrun callback, must be done under lock to avoid race with releaseBuffer()
@@ -1314,7 +1408,7 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
        mRetryOnPartialBuffer = false;
    }
    size_t misalignment = mProxy->getMisalignment();
    int32_t sequence = mSequence;
    uint32_t sequence = mSequence;

    // These fields don't need to be cached, because they are assigned only by set():
    //     mTransfer, mCbf, mUserData, mFormat, mFrameSize, mFrameSizeAF, mFlags
@@ -1322,6 +1416,38 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)

    mLock.unlock();

    if (waitStreamEnd) {
        AutoMutex lock(mLock);

        sp<AudioTrackClientProxy> proxy = mProxy;
        sp<IMemory> iMem = mCblkMemory;

        struct timespec timeout;
        timeout.tv_sec = WAIT_STREAM_END_TIMEOUT_SEC;
        timeout.tv_nsec = 0;

        mLock.unlock();
        status_t status = mProxy->waitStreamEndDone(&timeout);
        mLock.lock();
        switch (status) {
        case NO_ERROR:
        case DEAD_OBJECT:
        case TIMED_OUT:
            mLock.unlock();
            mCbf(EVENT_STREAM_END, mUserData, NULL);
            mLock.lock();
            if (mState == STATE_STOPPING) {
                mState = STATE_STOPPED;
                if (status != DEAD_OBJECT) {
                   return NS_INACTIVE;
                }
            }
            return 0;
        default:
            return 0;
        }
    }

    // perform callbacks while unlocked
    if (newUnderrun) {
        mCbf(EVENT_UNDERRUN, mUserData, NULL);
@@ -1343,9 +1469,14 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
        newPosition += updatePeriod;
        newPosCount--;
    }

    if (mObservedSequence != sequence) {
        mObservedSequence = sequence;
        mCbf(EVENT_NEW_IAUDIOTRACK, mUserData, NULL);
        // for offloaded tracks, just wait for the upper layers to recreate the track
        if (isOffloaded()) {
            return NS_INACTIVE;
        }
    }

    // if inactive, then don't run me again until re-started
@@ -1404,10 +1535,11 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
                "obtainBuffer() err=%d frameCount=%u", err, audioBuffer.frameCount);
        requested = &ClientProxy::kNonBlocking;
        size_t avail = audioBuffer.frameCount + nonContig;
        ALOGV("obtainBuffer(%u) returned %u = %u + %u",
                mRemainingFrames, avail, audioBuffer.frameCount, nonContig);
        ALOGV("obtainBuffer(%u) returned %u = %u + %u err %d",
                mRemainingFrames, avail, audioBuffer.frameCount, nonContig, err);
        if (err != NO_ERROR) {
            if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR) {
            if (err == TIMED_OUT || err == WOULD_BLOCK || err == -EINTR ||
                    (isOffloaded() && (err == DEAD_OBJECT))) {
                return 0;
            }
            ALOGE("Error %d obtaining an audio buffer, giving up.", err);
@@ -1500,7 +1632,8 @@ nsecs_t AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)

status_t AudioTrack::restoreTrack_l(const char *from)
{
    ALOGW("dead IAudioTrack, creating a new one from %s()", from);
    ALOGW("dead IAudioTrack, %s, creating a new one from %s()",
          isOffloaded() ? "Offloaded" : "PCM", from);
    ++mSequence;
    status_t result;

@@ -1508,6 +1641,14 @@ status_t AudioTrack::restoreTrack_l(const char *from)
    // output parameters in getOutput_l() and createTrack_l()
    AudioSystem::clearAudioConfigCache();

    if (isOffloaded()) {
        return DEAD_OBJECT;
    }

    // force new output query from audio policy manager;
    mOutput = 0;
    audio_io_handle_t output = getOutput_l();

    // if the new IAudioTrack is created, createTrack_l() will modify the
    // following member variables: mAudioTrack, mCblkMemory and mCblk.
    // It will also delete the strong references on previous IAudioTrack and IMemory
@@ -1520,7 +1661,7 @@ status_t AudioTrack::restoreTrack_l(const char *from)
                           mReqFrameCount,  // so that frame count never goes down
                           mFlags,
                           mSharedBuffer,
                           getOutput_l(),
                           output,
                           position /*epoch*/);

    if (result == NO_ERROR) {
@@ -1549,6 +1690,10 @@ status_t AudioTrack::restoreTrack_l(const char *from)
        }
    }
    if (result != NO_ERROR) {
        //Use of direct and offloaded output streams is ref counted by audio policy manager.
        // As getOutput was called above and resulted in an output stream to be opened,
        // we need to release it.
        AudioSystem::releaseOutput(output);
        ALOGW("restoreTrack_l() failed status %d", result);
        mState = STATE_STOPPED;
    }
@@ -1568,8 +1713,12 @@ status_t AudioTrack::setParameters(const String8& keyValuePairs)

String8 AudioTrack::getParameters(const String8& keys)
{
    if (mOutput) {
        return AudioSystem::getParameters(mOutput, keys);
    } else {
        return String8::empty();
    }
}

status_t AudioTrack::dump(int fd, const Vector<String16>& args) const
{
+126 −7
Original line number Diff line number Diff line
@@ -200,7 +200,7 @@ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *reques
            ts = &remaining;
            break;
        default:
            LOG_FATAL("%s timeout=%d", timeout);
            LOG_FATAL("obtainBuffer() timeout=%d", timeout);
            ts = NULL;
            break;
        }
@@ -259,7 +259,8 @@ end:
        requested = &kNonBlocking;
    }
    if (measure) {
        ALOGV("requested %d.%03d elapsed %d.%03d", requested->tv_sec, requested->tv_nsec / 1000000,
        ALOGV("requested %ld.%03ld elapsed %ld.%03ld",
              requested->tv_sec, requested->tv_nsec / 1000000,
              total.tv_sec, total.tv_nsec / 1000000);
    }
    return status;
@@ -323,13 +324,120 @@ void AudioTrackClientProxy::flush()
}

bool AudioTrackClientProxy::clearStreamEndDone() {
    return android_atomic_and(~CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE;
    return (android_atomic_and(~CBLK_STREAM_END_DONE, &mCblk->flags) & CBLK_STREAM_END_DONE) != 0;
}

bool AudioTrackClientProxy::getStreamEndDone() const {
    return (mCblk->flags & CBLK_STREAM_END_DONE) != 0;
}

status_t AudioTrackClientProxy::waitStreamEndDone(const struct timespec *requested)
{
    struct timespec total;          // total elapsed time spent waiting
    total.tv_sec = 0;
    total.tv_nsec = 0;
    audio_track_cblk_t* cblk = mCblk;
    status_t status;
    enum {
        TIMEOUT_ZERO,       // requested == NULL || *requested == 0
        TIMEOUT_INFINITE,   // *requested == infinity
        TIMEOUT_FINITE,     // 0 < *requested < infinity
        TIMEOUT_CONTINUE,   // additional chances after TIMEOUT_FINITE
    } timeout;
    if (requested == NULL) {
        timeout = TIMEOUT_ZERO;
    } else if (requested->tv_sec == 0 && requested->tv_nsec == 0) {
        timeout = TIMEOUT_ZERO;
    } else if (requested->tv_sec == INT_MAX) {
        timeout = TIMEOUT_INFINITE;
    } else {
        timeout = TIMEOUT_FINITE;
    }
    for (;;) {
        int32_t flags = android_atomic_and(~(CBLK_INTERRUPT|CBLK_STREAM_END_DONE), &cblk->flags);
        // check for track invalidation by server, or server death detection
        if (flags & CBLK_INVALID) {
            ALOGV("Track invalidated");
            status = DEAD_OBJECT;
            goto end;
        }
        if (flags & CBLK_STREAM_END_DONE) {
            ALOGV("stream end received");
            status = NO_ERROR;
            goto end;
        }
        // check for obtainBuffer interrupted by client
        // check for obtainBuffer interrupted by client
        if (flags & CBLK_INTERRUPT) {
            ALOGV("waitStreamEndDone() interrupted by client");
            status = -EINTR;
            goto end;
        }
        struct timespec remaining;
        const struct timespec *ts;
        switch (timeout) {
        case TIMEOUT_ZERO:
            status = WOULD_BLOCK;
            goto end;
        case TIMEOUT_INFINITE:
            ts = NULL;
            break;
        case TIMEOUT_FINITE:
            timeout = TIMEOUT_CONTINUE;
            if (MAX_SEC == 0) {
                ts = requested;
                break;
            }
            // fall through
        case TIMEOUT_CONTINUE:
            // FIXME we do not retry if requested < 10ms? needs documentation on this state machine
            if (requested->tv_sec < total.tv_sec ||
                    (requested->tv_sec == total.tv_sec && requested->tv_nsec <= total.tv_nsec)) {
                status = TIMED_OUT;
                goto end;
            }
            remaining.tv_sec = requested->tv_sec - total.tv_sec;
            if ((remaining.tv_nsec = requested->tv_nsec - total.tv_nsec) < 0) {
                remaining.tv_nsec += 1000000000;
                remaining.tv_sec++;
            }
            if (0 < MAX_SEC && MAX_SEC < remaining.tv_sec) {
                remaining.tv_sec = MAX_SEC;
                remaining.tv_nsec = 0;
            }
            ts = &remaining;
            break;
        default:
            LOG_FATAL("waitStreamEndDone() timeout=%d", timeout);
            ts = NULL;
            break;
        }
        int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
        if (!(old & CBLK_FUTEX_WAKE)) {
            int rc;
            int ret = __futex_syscall4(&cblk->mFutex,
                    mClientInServer ? FUTEX_WAIT_PRIVATE : FUTEX_WAIT, old & ~CBLK_FUTEX_WAKE, ts);
            switch (ret) {
            case 0:             // normal wakeup by server, or by binderDied()
            case -EWOULDBLOCK:  // benign race condition with server
            case -EINTR:        // wait was interrupted by signal or other spurious wakeup
            case -ETIMEDOUT:    // time-out expired
                break;
            default:
                ALOGE("%s unexpected error %d", __func__, ret);
                status = -ret;
                goto end;
            }
        }
    }

end:
    if (requested == NULL) {
        requested = &kNonBlocking;
    }
    return status;
}

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

StaticAudioTrackClientProxy::StaticAudioTrackClientProxy(audio_track_cblk_t* cblk, void *buffers,
@@ -393,13 +501,19 @@ status_t ServerProxy::obtainBuffer(Buffer* buffer)
    if (mIsOut) {
        int32_t flush = cblk->u.mStreaming.mFlush;
        rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
        front = cblk->u.mStreaming.mFront;
        if (flush != mFlush) {
            front = rear;
            mFlush = flush;
            // effectively obtain then release whatever is in the buffer
            android_atomic_release_store(rear, &cblk->u.mStreaming.mFront);
        } else {
            front = cblk->u.mStreaming.mFront;
            if (front != rear) {
                int32_t old = android_atomic_or(CBLK_FUTEX_WAKE, &cblk->mFutex);
                if (!(old & CBLK_FUTEX_WAKE)) {
                    (void) __futex_syscall3(&cblk->mFutex,
                            mClientInServer ? FUTEX_WAKE_PRIVATE : FUTEX_WAKE, 1);
                }
            }
            front = rear;
        }
    } else {
        front = android_atomic_acquire_load(&cblk->u.mStreaming.mFront);
@@ -517,6 +631,11 @@ size_t AudioTrackServerProxy::framesReady()
        return 0;
    }
    audio_track_cblk_t* cblk = mCblk;

    int32_t flush = cblk->u.mStreaming.mFlush;
    if (flush != mFlush) {
        return mFrameCount;
    }
    // the acquire might not be necessary since not doing a subsequent read
    int32_t rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
    ssize_t filled = rear - cblk->u.mStreaming.mFront;
+13 −2

File changed.

Preview size limit exceeded, changes collapsed.

Loading