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

Commit 9cbf9ff8 authored by Eric Laurent's avatar Eric Laurent Committed by Automerger Merge Worker
Browse files

Merge "AudioFlinger: synchronize OutputTracks start on duplicating threads"...

Merge "AudioFlinger: synchronize OutputTracks start on duplicating threads" into udc-dev am: 4ac986d8

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/av/+/22749304



Change-Id: Ibfea6da47aa17c33b7613597320731a8bc607ef3
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents fae8f9c5 4ac986d8
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -426,6 +426,7 @@ public:
private:
    status_t            obtainBuffer(AudioBufferProvider::Buffer* buffer,
                                     uint32_t waitTimeMs);
    void                queueBuffer(Buffer& inBuffer);
    void                clearBufferQueue();

    void                restartIfDisabled();
+12 −4
Original line number Diff line number Diff line
@@ -4023,7 +4023,7 @@ NO_THREAD_SAFETY_ANALYSIS // manual locking of AudioFlinger
                        LOG_AUDIO_STATE();
                        mThreadMetrics.logEndInterval();
                        mThreadSnapshot.onEnd();
                        mStandby = true;
                        setStandby_l();
                    }
                    sendStatistics(false /* force */);
                }
@@ -4103,6 +4103,14 @@ NO_THREAD_SAFETY_ANALYSIS // manual locking of AudioFlinger
            activeTracks.insert(activeTracks.end(), mActiveTracks.begin(), mActiveTracks.end());

            setHalLatencyMode_l();

            // signal actual start of output stream when the render position reported by the kernel
            // starts moving.
            if (!mStandby && !mHalStarted && mKernelPositionOnStandby !=
                    mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL]) {
                mHalStarted = true;
                mWaitHalStartCV.broadcast();
            }
        } // mLock scope ends

        if (mBytesRemaining == 0) {
@@ -4488,7 +4496,7 @@ NO_THREAD_SAFETY_ANALYSIS // manual locking of AudioFlinger

    if (!mStandby) {
        threadLoop_standby();
        mStandby = true;
        setStandby();
    }

    releaseWakeLock();
@@ -6239,7 +6247,7 @@ bool AudioFlinger::MixerThread::checkForNewParameter_l(const String8& keyValuePa
            if (!mStandby) {
                mThreadMetrics.logEndInterval();
                mThreadSnapshot.onEnd();
                mStandby = true;
                setStandby_l();
            }
            mBytesWritten = 0;
            status = mOutput->stream->setParameters(keyValuePair);
@@ -6890,7 +6898,7 @@ bool AudioFlinger::DirectOutputThread::checkForNewParameter_l(const String8& key
            if (!mStandby) {
                mThreadMetrics.logEndInterval();
                mThreadSnapshot.onEnd();
                mStandby = true;
                setStandby_l();
            }
            mBytesWritten = 0;
            status = mOutput->stream->setParameters(keyValuePair);
+34 −0
Original line number Diff line number Diff line
@@ -1116,6 +1116,32 @@ public:
                void startMelComputation_l(const sp<audio_utils::MelProcessor>& processor) override;
                void stopMelComputation_l() override;

                void setStandby() {
                    Mutex::Autolock _l(mLock);
                    setStandby_l();
                }

                void setStandby_l() {
                    mStandby = true;
                    mHalStarted = false;
                    mKernelPositionOnStandby =
                        mTimestamp.mPosition[ExtendedTimestamp::LOCATION_KERNEL];
                }

                bool waitForHalStart() {
                    Mutex::Autolock _l(mLock);
                    static const nsecs_t kWaitHalTimeoutNs = seconds(2);
                    nsecs_t endWaitTimetNs = systemTime() + kWaitHalTimeoutNs;
                    while (!mHalStarted) {
                        nsecs_t timeNs = systemTime();
                        if (timeNs >= endWaitTimetNs) {
                            break;
                        }
                        nsecs_t waitTimeLeftNs = endWaitTimetNs - timeNs;
                        mWaitHalStartCV.waitRelative(mLock, waitTimeLeftNs);
                    }
                    return mHalStarted;
                }
protected:
    // updated by readOutputParameters_l()
    size_t                          mNormalFrameCount;  // normal mixer and effects
@@ -1415,6 +1441,14 @@ private:
    // Downstream patch latency, available if mDownstreamLatencyStatMs.getN() > 0.
    audio_utils::Statistics<double> mDownstreamLatencyStatMs{0.999};

    // output stream start detection based on render position returned by the kernel
    // condition signalled when the output stream has started
    Condition                mWaitHalStartCV;
    // true when the output stream render position has moved, reset to false in standby
    bool                     mHalStarted = false;
    // last kernel render position saved when entering standby
    int64_t                  mKernelPositionOnStandby = 0;

public:
    virtual     bool        hasFastMixer() const = 0;
    virtual     FastTrackUnderruns getFastTrackUnderruns(size_t fastIndex __unused) const
+63 −25
Original line number Diff line number Diff line
@@ -2056,17 +2056,50 @@ void AudioFlinger::PlaybackThread::OutputTrack::stop()

ssize_t AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t frames)
{
    Buffer *pInBuffer;
    Buffer inBuffer;
    inBuffer.frameCount = frames;
    inBuffer.raw = data;
    if (!mActive && frames != 0) {
        sp<ThreadBase> thread = mThread.promote();
        if (thread != nullptr && thread->standby()) {
            // preload one silent buffer to trigger mixer on start()
            ClientProxy::Buffer buf { .mFrameCount = mClientProxy->getStartThresholdInFrames() };
            status_t status = mClientProxy->obtainBuffer(&buf);
            if (status != NO_ERROR && status != NOT_ENOUGH_DATA && status != WOULD_BLOCK) {
                ALOGE("%s(%d): could not obtain buffer on start", __func__, mId);
                return 0;
            }
            memset(buf.mRaw, 0, buf.mFrameCount * mFrameSize);
            mClientProxy->releaseBuffer(&buf);

    uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs();
            (void) start();

    if (!mActive && frames != 0) {
            // wait for HAL stream to start before sending actual audio. Doing this on each
            // OutputTrack makes that playback start on all output streams is synchronized.
            // If another OutputTrack has already started it can underrun but this is OK
            // as only silence has been played so far and the retry count is very high on
            // OutputTrack.
            auto pt = static_cast<PlaybackThread *>(thread.get());
            if (!pt->waitForHalStart()) {
                ALOGW("%s(%d): timeout waiting for thread to exit standby", __func__, mId);
                stop();
                return 0;
            }

            // enqueue the first buffer and exit so that other OutputTracks will also start before
            // write() is called again and this buffer actually consumed.
            Buffer firstBuffer;
            firstBuffer.frameCount = frames;
            firstBuffer.raw = data;
            queueBuffer(firstBuffer);
            return frames;
        } else {
            (void) start();
        }
    }

    Buffer *pInBuffer;
    Buffer inBuffer;
    inBuffer.frameCount = frames;
    inBuffer.raw = data;
    uint32_t waitTimeLeftMs = mSourceThread->waitTimeMs();
    while (waitTimeLeftMs) {
        // First write pending buffers, then new data
        if (mBufferQueue.size()) {
@@ -2134,8 +2167,23 @@ ssize_t AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t fr
    if (inBuffer.frameCount) {
        sp<ThreadBase> thread = mThread.promote();
        if (thread != 0 && !thread->standby()) {
            queueBuffer(inBuffer);
        }
    }

    // Calling write() with a 0 length buffer means that no more data will be written:
    // We rely on stop() to set the appropriate flags to allow the remaining frames to play out.
    if (frames == 0 && mBufferQueue.size() == 0 && mActive) {
        stop();
    }

    return frames - inBuffer.frameCount;  // number of frames consumed.
}

void AudioFlinger::PlaybackThread::OutputTrack::queueBuffer(Buffer& inBuffer) {

    if (mBufferQueue.size() < kMaxOverFlowBuffers) {
                pInBuffer = new Buffer;
        Buffer *pInBuffer = new Buffer;
        const size_t bufferSize = inBuffer.frameCount * mFrameSize;
        pInBuffer->mBuffer = malloc(bufferSize);
        LOG_ALWAYS_FATAL_IF(pInBuffer->mBuffer == nullptr,
@@ -2154,16 +2202,6 @@ ssize_t AudioFlinger::PlaybackThread::OutputTrack::write(void* data, uint32_t fr
        // TODO: return error for this.
    }
}
    }

    // Calling write() with a 0 length buffer means that no more data will be written:
    // We rely on stop() to set the appropriate flags to allow the remaining frames to play out.
    if (frames == 0 && mBufferQueue.size() == 0 && mActive) {
        stop();
    }

    return frames - inBuffer.frameCount;  // number of frames consumed.
}

void AudioFlinger::PlaybackThread::OutputTrack::copyMetadataTo(MetadataInserter& backInserter) const
{