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

Commit e8972b0a authored by Phil Burk's avatar Phil Burk
Browse files

AudioTrack: fix stall if setBufferSizeInFrames() called before play()



The server was waiting for a full buffer.
But the buffer was only getting partly filled.
So the stream was not starting.

The fix involves having the server look at the adjustable threshold.

Bug: 27505889
Change-Id: I5dbf686413e670dacbbecc9e0f838744e465f44f
Signed-off-by: default avatarPhil Burk <philburk@google.com>
parent f56a02a5
Loading
Loading
Loading
Loading
+15 −8
Original line number Diff line number Diff line
@@ -177,6 +177,10 @@ private:
                // server write-only, client read
                ExtendedTimestampQueue::Shared mExtendedTimestampQueue;

                // This is set by AudioTrack.setBufferSizeInFrames().
                // A write will not fill the buffer above this limit.
    volatile    uint32_t   mBufferSizeInFrames;  // effective size of the buffer

public:

    volatile    int32_t     mFlags;         // combinations of CBLK_*
@@ -312,9 +316,9 @@ public:
        return mEpoch;
    }

    size_t      getBufferSizeInFrames() const { return mBufferSizeInFrames; }
    // See documentation for AudioTrack.setBufferSizeInFrames()
    size_t      setBufferSizeInFrames(size_t requestedSize);
    uint32_t      getBufferSizeInFrames() const { return mBufferSizeInFrames; }
    // See documentation for AudioTrack::setBufferSizeInFrames()
    uint32_t      setBufferSizeInFrames(uint32_t requestedSize);

    status_t    getTimestamp(ExtendedTimestamp *timestamp) {
        if (timestamp == nullptr) {
@@ -329,12 +333,10 @@ public:
        mTimestamp.clear();
    }

protected:
    // This is set by AudioTrack.setBufferSizeInFrames().
    // A write will not fill the buffer above this limit.
    size_t      mBufferSizeInFrames;      // effective size of the buffer

private:
    // This is a copy of mCblk->mBufferSizeInFrames
    uint32_t   mBufferSizeInFrames;  // effective size of the buffer

    Modulo<uint32_t> mEpoch;

    // The shared buffer contents referred to by the timestamp observer
@@ -518,6 +520,11 @@ public:
        mTimestampMutator.push(timestamp);
    }

    // Get dynamic buffer size from the shared control block.
    uint32_t            getBufferSizeInFrames() const {
        return android_atomic_acquire_load((int32_t *)&mCblk->mBufferSizeInFrames);
    }

protected:
    size_t      mAvailToClient; // estimated frames available to client prior to releaseBuffer()
    int32_t     mFlush;         // our copy of cblk->u.mStreaming.mFlush, for streaming output only
+2 −5
Original line number Diff line number Diff line
@@ -860,7 +860,7 @@ ssize_t AudioTrack::getBufferSizeInFrames()
    if (mOutput == AUDIO_IO_HANDLE_NONE || mProxy.get() == 0) {
        return NO_INIT;
    }
    return mProxy->getBufferSizeInFrames();
    return (ssize_t) mProxy->getBufferSizeInFrames();
}

ssize_t AudioTrack::setBufferSizeInFrames(size_t bufferSizeInFrames)
@@ -873,10 +873,7 @@ ssize_t AudioTrack::setBufferSizeInFrames(size_t bufferSizeInFrames)
    if (!audio_is_linear_pcm(mFormat)) {
        return INVALID_OPERATION;
    }
    // TODO also need to inform the server side (through mAudioTrack) that
    // the buffer count is reduced, otherwise the track may never start
    // because the server thinks it is never filled.
    return mProxy->setBufferSizeInFrames(bufferSizeInFrames);
    return (ssize_t) mProxy->setBufferSizeInFrames((uint32_t) bufferSizeInFrames);
}

status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
+27 −18
Original line number Diff line number Diff line
@@ -46,8 +46,10 @@ static uint32_t incrementSequence(uint32_t self, uint32_t other) {
}

audio_track_cblk_t::audio_track_cblk_t()
    : mServer(0), mFutex(0), mMinimum(0),
    mVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY), mSampleRate(0), mSendLevel(0), mFlags(0)
    : mServer(0), mFutex(0), mMinimum(0)
    , mVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY), mSampleRate(0), mSendLevel(0)
    , mBufferSizeInFrames(0)
    , mFlags(0)
{
    memset(&u, 0, sizeof(u));
}
@@ -67,10 +69,10 @@ Proxy::Proxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount, size_t
ClientProxy::ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
        size_t frameSize, bool isOut, bool clientInServer)
    : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer)
    , mBufferSizeInFrames(frameCount)
    , mEpoch(0)
    , mTimestampObserver(&cblk->mExtendedTimestampQueue)
{
    setBufferSizeInFrames(frameCount);
}

const struct timespec ClientProxy::kForever = {INT_MAX /*tv_sec*/, 0 /*tv_nsec*/};
@@ -84,6 +86,26 @@ const struct timespec ClientProxy::kNonBlocking = {0 /*tv_sec*/, 0 /*tv_nsec*/};
// order of minutes.
#define MAX_SEC    5

uint32_t ClientProxy::setBufferSizeInFrames(uint32_t size)
{
    // TODO set minimum to 2X the fast mixer buffer size.
    // The minimum should be  greater than zero and less than the size
    // at which underruns will occur.
    const uint32_t minimum = 128 * 2; // arbitrary
    const uint32_t maximum = frameCount();
    uint32_t clippedSize = size;
    if (clippedSize < minimum) {
        clippedSize = minimum;
    } else if (clippedSize > maximum) {
        clippedSize = maximum;
    }
    // for server to read
    android_atomic_release_store(clippedSize, (int32_t *)&mCblk->mBufferSizeInFrames);
    // for client to read
    mBufferSizeInFrames = clippedSize;
    return clippedSize;
}

status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,
        struct timespec *elapsed)
{
@@ -174,7 +196,7 @@ status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *reques
        // The calculation for avail can go negative if the buffer size
        // is suddenly dropped below the amount already in the buffer.
        // So use a signed calculation to prevent a numeric overflow abort.
        ssize_t adjustableSize = (ssize_t) mBufferSizeInFrames;
        ssize_t adjustableSize = (ssize_t) getBufferSizeInFrames();
        ssize_t avail =  (mIsOut) ? adjustableSize - filled : filled;
        if (avail < 0) {
            avail = 0;
@@ -357,20 +379,6 @@ size_t ClientProxy::getMisalignment()
            (mFrameCountP2 - 1);
}

size_t ClientProxy::setBufferSizeInFrames(size_t size)
{
    // TODO set minimum to 2X the fast mixer buffer size.
    size_t minimum = 128 * 2; // arbitrary
    size_t maximum = frameCount();
    if (size < minimum) {
        size = minimum;
    } else if (size > maximum) {
        size = maximum;
    }
    mBufferSizeInFrames = size;
    return size;
}

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

void AudioTrackClientProxy::flush()
@@ -601,6 +609,7 @@ ServerProxy::ServerProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCo
      mAvailToClient(0), mFlush(0), mReleased(0)
    , mTimestampMutator(&cblk->mExtendedTimestampQueue)
{
    cblk->mBufferSizeInFrames = frameCount;
}

status_t ServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
+1 −1
Original line number Diff line number Diff line
@@ -617,7 +617,7 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const {
        return true;
    }

    if (framesReady() >= mFrameCount ||
    if (framesReady() >= mServerProxy->getBufferSizeInFrames() ||
            (mCblk->mFlags & CBLK_FORCEREADY)) {
        mFillingUpStatus = FS_FILLED;
        android_atomic_and(~CBLK_FORCEREADY, &mCblk->mFlags);