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

Commit 62b5c0c2 authored by Eric Laurent's avatar Eric Laurent Committed by Android (Google) Code Review
Browse files

Merge "audio flinger: fix offload track underrun" into nyc-dev

parents 1e3acb84 51716185
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -1819,7 +1819,8 @@ sp<AudioFlinger::PlaybackThread> AudioFlinger::openOutput_l(audio_module_handle_

        PlaybackThread *thread;
        if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
            thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady);
            thread = new OffloadThread(this, outputStream, *output, devices, mSystemReady,
                                       config->offload_info.bit_rate);
            ALOGV("openOutput_l() created offload output: ID %d thread %p", *output, thread);
        } else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT)
                || !isValidPcmSinkFormat(config->format)
+94 −15
Original line number Diff line number Diff line
@@ -108,6 +108,13 @@ static const int8_t kMaxTrackStartupRetries = 50;
// direct outputs can be a scarce resource in audio hardware and should
// be released as quickly as possible.
static const int8_t kMaxTrackRetriesDirect = 2;
// retry count before removing active track in case of underrun on offloaded thread:
// we need to make sure that AudioTrack client has enough time to send large buffers
//FIXME may be more appropriate if expressed in time units. Need to revise how underrun is handled
// for offloaded tracks
static const int8_t kMaxTrackRetriesOffload = 10;
static const int8_t kMaxTrackStartupRetriesOffload = 100;


// don't warn about blocked writes or record buffer overflows more often than this
static const nsecs_t kWarningThrottleNs = seconds(5);
@@ -136,6 +143,14 @@ static const uint32_t kMinNormalCaptureBufferSizeMs = 12;
// Offloaded output thread standby delay: allows track transition without going to standby
static const nsecs_t kOffloadStandbyDelayNs = seconds(1);

// Direct output thread minimum sleep time in idle or active(underrun) state
static const nsecs_t kDirectMinSleepTimeUs = 10000;

// Offloaded output bit rate in bits per second when unknown.
// Used for sleep time calculation, so use a high default bitrate to be conservative on sleep time.
static const uint32_t kOffloadDefaultBitRateBps = 1500000;


// Whether to use fast mixer
static const enum {
    FastMixer_Never,    // never initialize or use: for debugging only
@@ -1553,7 +1568,8 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
                                             audio_io_handle_t id,
                                             audio_devices_t device,
                                             type_t type,
                                             bool systemReady)
                                             bool systemReady,
                                             uint32_t bitRate)
    :   ThreadBase(audioFlinger, id, device, AUDIO_DEVICE_NONE, type, systemReady),
        mNormalFrameCount(0), mSinkBuffer(NULL),
        mMixerBufferEnabled(AudioFlinger::kEnableExtendedPrecision),
@@ -1616,6 +1632,13 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
        mStreamTypes[stream].volume = mAudioFlinger->streamVolume_l(stream);
        mStreamTypes[stream].mute = mAudioFlinger->streamMute_l(stream);
    }

    if (audio_has_proportional_frames(mFormat)) {
        mBufferDurationUs = (uint32_t)((mNormalFrameCount * 1000000LL) / mSampleRate);
    } else {
        bitRate = bitRate != 0 ? bitRate : kOffloadDefaultBitRateBps;
        mBufferDurationUs = (uint32_t)((mBufferSize * 8 * 1000000LL) / bitRate);
    }
}

AudioFlinger::PlaybackThread::~PlaybackThread()
@@ -2008,8 +2031,6 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
{
    status_t status = ALREADY_EXISTS;

    // set retry count for buffer fill
    track->mRetryCount = kMaxTrackStartupRetries;
    if (mActiveTracks.indexOf(track) < 0) {
        // the track is newly added, make sure it fills up all its
        // buffers before playing. This is to ensure the client will
@@ -2040,6 +2061,13 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
#endif
        }

        // set retry count for buffer fill
        if (track->isOffloaded()) {
            track->mRetryCount = kMaxTrackStartupRetriesOffload;
        } else {
            track->mRetryCount = kMaxTrackStartupRetries;
        }

        track->mFillingUpStatus = track->sharedBuffer() != 0 ? Track::FS_FILLED : Track::FS_FILLING;
        track->mResetDone = false;
        track->mPresentationCompleteFrames = 0;
@@ -2572,6 +2600,7 @@ ssize_t AudioFlinger::PlaybackThread::threadLoop_write()
        // FIXME We should have an implementation of timestamps for direct output threads.
        // They are used e.g for multichannel PCM playback over HDMI.
        bytesWritten = mOutput->write((char *)mSinkBuffer + offset, mBytesRemaining);

        if (mUseAsyncWrite &&
                ((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) {
            // do not wait for async callback in case of error of full write
@@ -3137,7 +3166,30 @@ bool AudioFlinger::PlaybackThread::threadLoop()

            } else {
                ATRACE_BEGIN("sleep");
                if ((mType == OFFLOAD) && !audio_has_proportional_frames(mFormat)) {
                    Mutex::Autolock _l(mLock);
                    if (!mSignalPending && !exitPending()) {
                        // Do not sleep more than one buffer duration since last write and not
                        // less than kDirectMinSleepTimeUs
                        // Wake up if a command is received
                        nsecs_t now = systemTime();
                        uint32_t deltaUs = (uint32_t)((now - mLastWriteTime) / 1000);
                        uint32_t timeoutUs = mSleepTimeUs;
                        if (timeoutUs + deltaUs > mBufferDurationUs) {
                            if (mBufferDurationUs > deltaUs) {
                                timeoutUs = mBufferDurationUs - deltaUs;
                                if (timeoutUs < kDirectMinSleepTimeUs) {
                                    timeoutUs = kDirectMinSleepTimeUs;
                                }
                            } else {
                                timeoutUs = kDirectMinSleepTimeUs;
                            }
                        }
                        mWaitWorkCV.waitRelative(mLock, microseconds((nsecs_t)timeoutUs));
                    }
                } else {
                    usleep(mSleepTimeUs);
                }
                ATRACE_END();
            }
        }
@@ -4583,16 +4635,17 @@ void AudioFlinger::MixerThread::cacheParameters_l()
// ----------------------------------------------------------------------------

AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
        AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device, bool systemReady)
    :   PlaybackThread(audioFlinger, output, id, device, DIRECT, systemReady)
        AudioStreamOut* output, audio_io_handle_t id, audio_devices_t device, bool systemReady,
        uint32_t bitRate)
    :   PlaybackThread(audioFlinger, output, id, device, DIRECT, systemReady, bitRate)
        // mLeftVolFloat, mRightVolFloat
{
}

AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger,
        AudioStreamOut* output, audio_io_handle_t id, uint32_t device,
        ThreadBase::type_t type, bool systemReady)
    :   PlaybackThread(audioFlinger, output, id, device, type, systemReady)
        ThreadBase::type_t type, bool systemReady, uint32_t bitRate)
    :   PlaybackThread(audioFlinger, output, id, device, type, systemReady, bitRate)
        // mLeftVolFloat, mRightVolFloat
{
}
@@ -4871,7 +4924,10 @@ void AudioFlinger::DirectOutputThread::threadLoop_mix()
        buffer.frameCount = frameCount;
        status_t status = mActiveTrack->getNextBuffer(&buffer);
        if (status != NO_ERROR || buffer.raw == NULL) {
            // no need to pad with 0 for compressed audio
            if (audio_has_proportional_frames(mFormat)) {
                memset(curBuf, 0, frameCount * mFrameSize);
            }
            break;
        }
        memcpy(curBuf, buffer.raw, buffer.frameCount * mFrameSize);
@@ -4894,7 +4950,14 @@ void AudioFlinger::DirectOutputThread::threadLoop_sleepTime()
    }
    if (mSleepTimeUs == 0) {
        if (mMixerStatus == MIXER_TRACKS_ENABLED) {
            // For compressed offload, use faster sleep time when underruning until more than an
            // entire buffer was written to the audio HAL
            if (!audio_has_proportional_frames(mFormat) &&
                    (mType == OFFLOAD) && (mBytesWritten < mBufferSize)) {
                mSleepTimeUs = kDirectMinSleepTimeUs;
            } else {
                mSleepTimeUs = mActiveSleepTimeUs;
            }
        } else {
            mSleepTimeUs = mIdleSleepTimeUs;
        }
@@ -5008,7 +5071,7 @@ uint32_t AudioFlinger::DirectOutputThread::activeSleepTimeUs() const
    if (audio_has_proportional_frames(mFormat)) {
        time = PlaybackThread::activeSleepTimeUs();
    } else {
        time = 10000;
        time = kDirectMinSleepTimeUs;
    }
    return time;
}
@@ -5019,7 +5082,7 @@ uint32_t AudioFlinger::DirectOutputThread::idleSleepTimeUs() const
    if (audio_has_proportional_frames(mFormat)) {
        time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
    } else {
        time = 10000;
        time = kDirectMinSleepTimeUs;
    }
    return time;
}
@@ -5030,7 +5093,7 @@ uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs() const
    if (audio_has_proportional_frames(mFormat)) {
        time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
    } else {
        time = 10000;
        time = kDirectMinSleepTimeUs;
    }
    return time;
}
@@ -5162,8 +5225,9 @@ void AudioFlinger::AsyncCallbackThread::resetDraining()

// ----------------------------------------------------------------------------
AudioFlinger::OffloadThread::OffloadThread(const sp<AudioFlinger>& audioFlinger,
        AudioStreamOut* output, audio_io_handle_t id, uint32_t device, bool systemReady)
    :   DirectOutputThread(audioFlinger, output, id, device, OFFLOAD, systemReady),
        AudioStreamOut* output, audio_io_handle_t id, uint32_t device, bool systemReady,
        uint32_t bitRate)
    :   DirectOutputThread(audioFlinger, output, id, device, OFFLOAD, systemReady, bitRate),
        mPausedBytesRemaining(0)
{
    //FIXME: mStandby should be set to true by ThreadBase constructor
@@ -5244,6 +5308,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr
            }
            tracksToRemove->add(track);
        } else if (track->isFlushPending()) {
            track->mRetryCount = kMaxTrackRetriesOffload;
            track->flushAck();
            if (last) {
                mFlushPending = true;
@@ -5424,6 +5489,20 @@ void AudioFlinger::OffloadThread::flushHw_l()
    }
}

uint32_t AudioFlinger::OffloadThread::activeSleepTimeUs() const
{
    uint32_t time;
    if (audio_has_proportional_frames(mFormat)) {
        time = PlaybackThread::activeSleepTimeUs();
    } else {
        // sleep time is half the duration of an audio HAL buffer.
        // Note: This can be problematic in case of underrun with variable bit rate and
        // current rate is much less than initial rate.
        time = (uint32_t)max(kDirectMinSleepTimeUs, mBufferDurationUs / 2);
    }
    return time;
}

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

AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger,
+11 −10
Original line number Diff line number Diff line
@@ -479,14 +479,9 @@ public:
        // suspend by audio policy manager is orthogonal to mixer state
    };

    // retry count before removing active track in case of underrun on offloaded thread:
    // we need to make sure that AudioTrack client has enough time to send large buffers
//FIXME may be more appropriate if expressed in time units. Need to revise how underrun is handled
    // for offloaded tracks
    static const int8_t kMaxTrackRetriesOffload = 20;

    PlaybackThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
                   audio_io_handle_t id, audio_devices_t device, type_t type, bool systemReady);
                   audio_io_handle_t id, audio_devices_t device, type_t type, bool systemReady,
                   uint32_t bitRate = 0);
    virtual             ~PlaybackThread();

                void        dump(int fd, const Vector<String16>& args);
@@ -841,6 +836,8 @@ protected:
                bool        mHwSupportsPause;
                bool        mHwPaused;
                bool        mFlushPending;
                uint32_t    mBufferDurationUs;      // estimated duration of an audio HAL buffer
                                                    // based on initial bit rate (offload only)
};

class MixerThread : public PlaybackThread {
@@ -931,7 +928,8 @@ class DirectOutputThread : public PlaybackThread {
public:

    DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
                       audio_io_handle_t id, audio_devices_t device, bool systemReady);
                       audio_io_handle_t id, audio_devices_t device, bool systemReady,
                       uint32_t bitRate = 0);
    virtual                 ~DirectOutputThread();

    // Thread virtuals
@@ -964,7 +962,7 @@ protected:

    DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
                        audio_io_handle_t id, uint32_t device, ThreadBase::type_t type,
                        bool systemReady);
                        bool systemReady, uint32_t bitRate = 0);
    void processVolume_l(Track *track, bool lastTrack);

    // prepareTracks_l() tells threadLoop_mix() the name of the single active track
@@ -980,7 +978,8 @@ class OffloadThread : public DirectOutputThread {
public:

    OffloadThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output,
                        audio_io_handle_t id, uint32_t device, bool systemReady);
                        audio_io_handle_t id, uint32_t device,
                        bool systemReady, uint32_t bitRate);
    virtual                 ~OffloadThread() {};
    virtual     void        flushHw_l();

@@ -989,6 +988,8 @@ protected:
    virtual     mixer_state prepareTracks_l(Vector< sp<Track> > *tracksToRemove);
    virtual     void        threadLoop_exit();

    virtual     uint32_t    activeSleepTimeUs() const;

    virtual     bool        waitingAsyncCallback();
    virtual     bool        waitingAsyncCallback_l();

+0 −5
Original line number Diff line number Diff line
@@ -792,11 +792,6 @@ void AudioFlinger::PlaybackThread::Track::flush()
                mState = ACTIVE;
            }

            if (mState == ACTIVE) {
                ALOGV("flush called in active state, resetting buffer time out retry count");
                mRetryCount = PlaybackThread::kMaxTrackRetriesOffload;
            }

            mFlushHwPending = true;
            mResumeToStopping = false;
        } else {
+0 −9
Original line number Diff line number Diff line
@@ -2201,15 +2201,6 @@ bool AudioPolicyManager::isOffloadSupported(const audio_offload_info_t& offloadI
        return false;
    }

    // Check if streaming is off, then only allow offload as of now.
    // This is a temporary work around until the root cause is fixed in offload
    // playback path.
    if (offloadInfo.is_streaming)
    {
        ALOGV("isOffloadSupported: is_streaming == true, returning false");
        return false;
    }

    //TODO: enable audio offloading with video when ready
    const bool allowOffloadWithVideo =
            property_get_bool("audio.offload.video", false /* default_value */);