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

Commit 09474df6 authored by Glenn Kasten's avatar Glenn Kasten
Browse files

Improve underrun handling for fast tracks

Maintain more accurate accounting of type of underrun.
Automatically remove track from active list after a series of "empty" underruns.

Change-Id: If042bf80e1790dcaaf195c99dc9c0ed9b55382c1
parent d08f48c2
Loading
Loading
Loading
Loading
+51 −12
Original line number Diff line number Diff line
@@ -2760,14 +2760,20 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac

            // Determine whether the track is currently in underrun condition,
            // and whether it had a recent underrun.
            uint32_t underruns = mFastMixerDumpState.mTracks[j].mUnderruns;
            uint32_t recentUnderruns = (underruns - (track->mObservedUnderruns & ~1)) >> 1;
            FastTrackUnderruns underruns = mFastMixerDumpState.mTracks[j].mUnderruns;
            uint32_t recentFull = (underruns.mBitFields.mFull -
                    track->mObservedUnderruns.mBitFields.mFull) & UNDERRUN_MASK;
            uint32_t recentPartial = (underruns.mBitFields.mPartial -
                    track->mObservedUnderruns.mBitFields.mPartial) & UNDERRUN_MASK;
            uint32_t recentEmpty = (underruns.mBitFields.mEmpty -
                    track->mObservedUnderruns.mBitFields.mEmpty) & UNDERRUN_MASK;
            uint32_t recentUnderruns = recentPartial + recentEmpty;
            track->mObservedUnderruns = underruns;
            // don't count underruns that occur while stopping or pausing
            // or stopped which can occur when flush() is called while active
            if (!(track->isStopping() || track->isPausing() || track->isStopped())) {
                track->mUnderrunCount += recentUnderruns;
            }
            track->mObservedUnderruns = underruns;

            // This is similar to the state machine for normal tracks,
            // with a few modifications for fast tracks.
@@ -2788,10 +2794,30 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
                track->mState = TrackBase::ACTIVE;
                break;
            case TrackBase::ACTIVE:
                // no minimum frame count for fast tracks; continual underrun is allowed,
                // but later could implement automatic pause after several consecutive underruns,
                // or auto-mute yet still consider the track active and continue to service it
                if (track->sharedBuffer() == 0 || recentUnderruns == 0) {
                if (recentFull > 0 || recentPartial > 0) {
                    // track has provided at least some frames recently: reset retry count
                    track->mRetryCount = kMaxTrackRetries;
                }
                if (recentUnderruns == 0) {
                    // no recent underruns: stay active
                    break;
                }
                // there has recently been an underrun of some kind
                if (track->sharedBuffer() == 0) {
                    // were any of the recent underruns "empty" (no frames available)?
                    if (recentEmpty == 0) {
                        // no, then ignore the partial underruns as they are allowed indefinitely
                        break;
                    }
                    // there has recently been an "empty" underrun: decrement the retry counter
                    if (--(track->mRetryCount) > 0) {
                        break;
                    }
                    // indicate to client process that the track was disabled because of underrun;
                    // it will then automatically call start() when data is available
                    android_atomic_or(CBLK_DISABLED_ON, &track->mCblk->flags);
                    // remove from active list, but state remains ACTIVE [confusing but true]
                    isActive = false;
                    break;
                }
                // fall through
@@ -2862,7 +2888,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac
                }
                tracksToRemove->add(track);
                // Avoids a misleading display in dumpsys
                track->mObservedUnderruns &= ~1;
                track->mObservedUnderruns.mBitFields.mMostRecent = UNDERRUN_FULL;
            }
            continue;
        }
@@ -4107,7 +4133,6 @@ AudioFlinger::PlaybackThread::Track::Track(
    mPresentationCompleteFrames(0),
    mFlags(flags),
    mFastIndex(-1),
    mObservedUnderruns(0),
    mUnderrunCount(0),
    mCachedVolume(1.0)
{
@@ -4126,7 +4151,7 @@ AudioFlinger::PlaybackThread::Track::Track(
            //       being created.  It would be better to allocate the index dynamically.
            mFastIndex = i;
            // Read the initial underruns because this field is never cleared by the fast mixer
            mObservedUnderruns = thread->getFastTrackUnderruns(i) & ~1;
            mObservedUnderruns = thread->getFastTrackUnderruns(i);
            thread->mFastTrackAvailMask &= ~(1 << i);
        }
        // to avoid leaking a track name, do not allocate one unless there is an mCblk
@@ -4231,7 +4256,21 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
        stateChar = '?';
        break;
    }
    bool nowInUnderrun = mObservedUnderruns & 1;
    char nowInUnderrun;
    switch (mObservedUnderruns.mBitFields.mMostRecent) {
    case UNDERRUN_FULL:
        nowInUnderrun = ' ';
        break;
    case UNDERRUN_PARTIAL:
        nowInUnderrun = '<';
        break;
    case UNDERRUN_EMPTY:
        nowInUnderrun = '*';
        break;
    default:
        nowInUnderrun = '?';
        break;
    }
    snprintf(&buffer[7], size-7, " %6d %4u %3u 0x%08x %7u %6u %6u %1c %1d %1d %5u %5.2g %5.2g  "
            "0x%08x 0x%08x 0x%08x 0x%08x %#5x %9u%c\n",
            (mClient == 0) ? getpid_cached : mClient->pid(),
@@ -4253,7 +4292,7 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size)
            (int)mAuxBuffer,
            mCblk->flags,
            mUnderrunCount,
            nowInUnderrun ? '*' : ' ');
            nowInUnderrun);
}

// AudioBufferProvider interface
+7 −6
Original line number Diff line number Diff line
@@ -793,7 +793,7 @@ private:
                                            // index 0 is reserved for normal mixer's submix;
                                            // index is allocated statically at track creation time
                                            // but the slot is only used if track is active
            uint32_t            mObservedUnderruns; // Most recently observed value of
            FastTrackUnderruns  mObservedUnderruns; // Most recently observed value of
                                            // mFastMixerDumpState.mTracks[mFastIndex].mUnderruns
            uint32_t            mUnderrunCount; // Counter of total number of underruns, never reset
            volatile float      mCachedVolume;  // combined master volume and stream type volume;
@@ -1112,7 +1112,8 @@ public:
        sp<NBAIO_Sink>          mNormalSink;
    public:
        virtual     bool        hasFastMixer() const = 0;
        virtual     uint32_t    getFastTrackUnderruns(size_t fastIndex) const { return 0; }
        virtual     FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const
                                    { FastTrackUnderruns dummy; return dummy; }

    protected:
                    // accessed by both binder threads and within threadLoop(), lock on mutex needed
@@ -1167,7 +1168,7 @@ public:

    public:
        virtual     bool        hasFastMixer() const { return mFastMixer != NULL; }
        virtual     uint32_t    getFastTrackUnderruns(size_t fastIndex) const {
        virtual     FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex) const {
                                  ALOG_ASSERT(0 < fastIndex &&
                                              fastIndex < FastMixerState::kMaxFastTracks);
                                  return mFastMixerDumpState.mTracks[fastIndex].mUnderruns;
+9 −4
Original line number Diff line number Diff line
@@ -360,20 +360,25 @@ bool FastMixer::threadLoop()
                // in the overall fast mix cycle being delayed.  Should use a non-blocking FIFO.
                size_t framesReady = fastTrack->mBufferProvider->framesReady();
                FastTrackDump *ftDump = &dumpState->mTracks[i];
                uint32_t underruns = ftDump->mUnderruns;
                FastTrackUnderruns underruns = ftDump->mUnderruns;
                if (framesReady < frameCount) {
                    ATRACE_INT("underrun", i);
                    ftDump->mUnderruns = (underruns + 2) | 1;
                    if (framesReady == 0) {
                        underruns.mBitFields.mEmpty++;
                        underruns.mBitFields.mMostRecent = UNDERRUN_EMPTY;
                        mixer->disable(name);
                    } else {
                        // allow mixing partial buffer
                        underruns.mBitFields.mPartial++;
                        underruns.mBitFields.mMostRecent = UNDERRUN_PARTIAL;
                        mixer->enable(name);
                    }
                } else if (underruns & 1) {
                    ftDump->mUnderruns = underruns & ~1;
                } else {
                    underruns.mBitFields.mFull++;
                    underruns.mBitFields.mMostRecent = UNDERRUN_FULL;
                    mixer->enable(name);
                }
                ftDump->mUnderruns = underruns;
            }
            // process() is CPU-bound
            mixer->process(AudioBufferProvider::kInvalidPTS);
+30 −7
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#ifndef ANDROID_AUDIO_FAST_MIXER_H
#define ANDROID_AUDIO_FAST_MIXER_H

#include <utils/Debug.h>
#include <utils/Thread.h>
extern "C" {
#include "../private/bionic_futex.h"
@@ -42,16 +43,38 @@ private:

};  // class FastMixer

// Describes the underrun status for a single "pull" attempt
enum FastTrackUnderrunStatus {
    UNDERRUN_FULL,      // framesReady() is full frame count, no underrun
    UNDERRUN_PARTIAL,   // framesReady() is non-zero but < full frame count, partial underrun
    UNDERRUN_EMPTY,     // framesReady() is zero, total underrun
};

// Underrun counters are not reset to zero for new tracks or if track generation changes.
// This packed representation is used to keep the information atomic.
union FastTrackUnderruns {
    FastTrackUnderruns() { mAtomic = 0;
            COMPILE_TIME_ASSERT_FUNCTION_SCOPE(sizeof(FastTrackUnderruns) == sizeof(uint32_t)); }
    FastTrackUnderruns(const FastTrackUnderruns& copyFrom) : mAtomic(copyFrom.mAtomic) { }
    FastTrackUnderruns& operator=(const FastTrackUnderruns& rhs)
            { if (this != &rhs) mAtomic = rhs.mAtomic; return *this; }
    struct {
#define UNDERRUN_BITS 10
#define UNDERRUN_MASK ((1 << UNDERRUN_BITS) - 1)
        uint32_t mFull    : UNDERRUN_BITS; // framesReady() is full frame count
        uint32_t mPartial : UNDERRUN_BITS; // framesReady() is non-zero but < full frame count
        uint32_t mEmpty   : UNDERRUN_BITS; // framesReady() is zero
        FastTrackUnderrunStatus mMostRecent : 2;    // status of most recent framesReady()
    }        mBitFields;
private:
    uint32_t mAtomic;
};

// Represents the dump state of a fast track
struct FastTrackDump {
    FastTrackDump() : mUnderruns(0) { }
    FastTrackDump() { }
    /*virtual*/ ~FastTrackDump() { }
    uint32_t mUnderruns;        // Underrun status, represented as follows:
                                //   bit 0 == 0 means not currently in underrun
                                //   bit 0 == 1 means currently in underrun
                                //   bits 1 to 31 == total number of underruns
                                // Not reset to zero for new tracks or if track generation changes.
                                // This representation is used to keep the information atomic.
    FastTrackUnderruns mUnderruns;
};

// The FastMixerDumpState keeps a cache of FastMixer statistics that can be logged by dumpsys.