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

Commit 4618df33 authored by Andy Hung's avatar Andy Hung Committed by Automerger Merge Worker
Browse files

Merge "MediaSyncEvent: Fix thread safety" am: d84c55fb am: 3c99f759 am: 41e1bb49

parents 8d62670e 41e1bb49
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@
#include <sounddose/SoundDoseManager.h>
#include <timing/MonotonicFrameCounter.h>
#include <timing/SyncEvent.h>
#include <timing/SynchronizedRecordState.h>

#include "FastCapture.h"
#include "FastMixer.h"
+2 −4
Original line number Diff line number Diff line
@@ -109,10 +109,8 @@ private:
            // be dropped and therefore not read by the application.
            sp<audioflinger::SyncEvent>        mSyncStartEvent;

            // number of captured frames to drop after the start sync event has been received.
            // when < 0, maximum frames to drop before starting capture even if sync event is
            // not received
            ssize_t                             mFramesToDrop;
            audioflinger::SynchronizedRecordState
                    mSynchronizedRecordState{mSampleRate}; // sampleRate defined in base

            // used by resampler to find source frames
            ResamplerBufferProvider            *mResamplerBufferProvider;
+9 −36
Original line number Diff line number Diff line
@@ -8470,7 +8470,11 @@ reacquire_wakelock:
                    overrun = OVERRUN_FALSE;
                }

                if (activeTrack->mFramesToDrop == 0) {
                // MediaSyncEvent handling: Synchronize AudioRecord to AudioTrack completion.
                const ssize_t framesToDrop =
                        activeTrack->mSynchronizedRecordState.updateRecordFrames(framesOut);
                if (framesToDrop == 0) {
                    // no sync event, process normally, otherwise ignore.
                    if (framesOut > 0) {
                        activeTrack->mSink.frameCount = framesOut;
                        // Sanitize before releasing if the track has no access to the source data
@@ -8480,28 +8484,7 @@ reacquire_wakelock:
                        }
                        activeTrack->releaseBuffer(&activeTrack->mSink);
                    }
                } else {
                    // FIXME could do a partial drop of framesOut
                    if (activeTrack->mFramesToDrop > 0) {
                        activeTrack->mFramesToDrop -= (ssize_t)framesOut;
                        if (activeTrack->mFramesToDrop <= 0) {
                            activeTrack->clearSyncStartEvent();
                        }
                    } else {
                        activeTrack->mFramesToDrop += framesOut;
                        if (activeTrack->mFramesToDrop >= 0 || activeTrack->mSyncStartEvent == 0 ||
                                activeTrack->mSyncStartEvent->isCancelled()) {
                            ALOGW("Synced record %s, session %d, trigger session %d",
                                  (activeTrack->mFramesToDrop >= 0) ? "timed out" : "cancelled",
                                  activeTrack->sessionId(),
                                  (activeTrack->mSyncStartEvent != 0) ?
                                          activeTrack->mSyncStartEvent->triggerSession() :
                                          AUDIO_SESSION_NONE);
                            activeTrack->clearSyncStartEvent();
                        }
                    }
                }

                if (framesOut == 0) {
                    break;
                }
@@ -8834,20 +8817,10 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac
    if (event == AudioSystem::SYNC_EVENT_NONE) {
        recordTrack->clearSyncStartEvent();
    } else if (event != AudioSystem::SYNC_EVENT_SAME) {
        recordTrack->mSyncStartEvent = mAudioFlinger->createSyncEvent(event,
                                       triggerSession,
                                       recordTrack->sessionId(),
                                       syncStartEventCallback,
                                       recordTrack);
        // Sync event can be cancelled by the trigger session if the track is not in a
        // compatible state in which case we start record immediately
        if (recordTrack->mSyncStartEvent->isCancelled()) {
            recordTrack->clearSyncStartEvent();
        } else {
            // do not wait for the event for more than AudioSystem::kSyncRecordStartTimeOutMs
            recordTrack->mFramesToDrop = -(ssize_t)
                    ((AudioSystem::kSyncRecordStartTimeOutMs * recordTrack->mSampleRate) / 1000);
        }
        recordTrack->mSynchronizedRecordState.startRecording(
                mAudioFlinger->createSyncEvent(
                        event, triggerSession,
                        recordTrack->sessionId(), syncStartEventCallback, recordTrack));
    }

    {
+13 −15
Original line number Diff line number Diff line
@@ -1681,6 +1681,7 @@ void AudioFlinger::PlaybackThread::Track::triggerEvents(AudioSystem::sync_event_
{
    for (auto it = mSyncEvents.begin(); it != mSyncEvents.end();) {
        if ((*it)->type() == type) {
            ALOGV("%s: triggering SyncEvent type %d", __func__, type);
            (*it)->trigger();
            it = mSyncEvents.erase(it);
        } else {
@@ -1931,6 +1932,8 @@ void AudioFlinger::PlaybackThread::Track::updateTrackFrameInfo(
        }
    }

    ALOGV("%s: trackFramesReleased:%lld  sinkFramesWritten:%lld  setDrained: %d",
        __func__, (long long)trackFramesReleased, (long long)sinkFramesWritten, drained);
    mAudioTrackServerProxy->setDrained(drained);
    // Set correction for flushed frames that are not accounted for in released.
    local.mFlushed = mAudioTrackServerProxy->framesFlushed();
@@ -2505,7 +2508,6 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
                  type, portId,
                  std::string(AMEDIAMETRICS_KEY_PREFIX_AUDIO_RECORD) + std::to_string(portId)),
        mOverflow(false),
        mFramesToDrop(0),
        mResamplerBufferProvider(NULL), // initialize in case of early constructor exit
        mRecordBufferConverter(NULL),
        mFlags(flags),
@@ -2707,28 +2709,24 @@ void AudioFlinger::RecordThread::RecordTrack::appendDump(String8& result, bool a
    result.append("\n");
}

// This is invoked by SyncEvent callback.
void AudioFlinger::RecordThread::RecordTrack::handleSyncStartEvent(
        const sp<audioflinger::SyncEvent>& event)
{
    if (event == mSyncStartEvent) {
        ssize_t framesToDrop = 0;
    size_t framesToDrop = 0;
    sp<ThreadBase> threadBase = mThread.promote();
    if (threadBase != 0) {
        // TODO: use actual buffer filling status instead of 2 buffers when info is available
        // from audio HAL
        framesToDrop = threadBase->mFrameCount * 2;
    }
        mFramesToDrop = framesToDrop;
    }

    mSynchronizedRecordState.onPlaybackFinished(event, framesToDrop);
}

void AudioFlinger::RecordThread::RecordTrack::clearSyncStartEvent()
{
    if (mSyncStartEvent != 0) {
        mSyncStartEvent->cancel();
        mSyncStartEvent.clear();
    }
    mFramesToDrop = 0;
    mSynchronizedRecordState.clear();
}

void AudioFlinger::RecordThread::RecordTrack::updateTrackFrameInfo(
+112 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#pragma once

#include "SyncEvent.h"

#pragma push_macro("LOG_TAG")
#undef LOG_TAG
#define LOG_TAG "SynchronizedRecordState"

namespace android::audioflinger {

class SynchronizedRecordState {
public:
    explicit SynchronizedRecordState(uint32_t sampleRate)
        : mSampleRate(sampleRate)
        {}

    void clear() {
        std::lock_guard lg(mLock);
        clear_l();
    }

    // Called by the RecordThread when recording is starting.
    void startRecording(const sp<SyncEvent>& event) {
        std::lock_guard lg(mLock);
        mSyncStartEvent = event;
        // Sync event can be cancelled by the trigger session if the track is not in a
        // compatible state in which case we start record immediately
        if (mSyncStartEvent->isCancelled()) {
            clear_l();
        } else {
            mFramesToDrop = -(ssize_t)
                ((AudioSystem::kSyncRecordStartTimeOutMs * mSampleRate) / 1000);
        }
    }

    // Invoked by SyncEvent callback.
    void onPlaybackFinished(const sp<SyncEvent>& event, size_t framesToDrop = 1) {
        std::lock_guard lg(mLock);
        if (event == mSyncStartEvent) {
            mFramesToDrop = framesToDrop;  // compute this
            ALOGV("%s: framesToDrop:%zd", __func__, mFramesToDrop);
        }
    }

    // Returns the current FramesToDrop counter
    //
    //   if <0 waiting (drop the frames)
    //   if >0 draining (drop the frames)
    //    else if ==0 proceed to record.
    ssize_t updateRecordFrames(size_t frames) {
        std::lock_guard lg(mLock);
        if (mFramesToDrop > 0) {
            // we've been triggered, we count down for start delay
            ALOGV("%s: trigger countdown %zd by %zu frames", __func__, mFramesToDrop, frames);
            mFramesToDrop -= (ssize_t)frames;
            if (mFramesToDrop <= 0) clear_l();
        } else if (mFramesToDrop < 0) {
            // we're waiting to be triggered.
            // ALOGD("%s: timeout countup %zd with %zu frames", __func__, mFramesToDrop, frames);
            mFramesToDrop += (ssize_t)frames;
            if (mFramesToDrop >= 0 || !mSyncStartEvent || mSyncStartEvent->isCancelled()) {
                ALOGW("Synced record %s, trigger session %d",
                        (mFramesToDrop >= 0) ? "timed out" : "cancelled",
                        (mSyncStartEvent) ? mSyncStartEvent->triggerSession()
                                          : AUDIO_SESSION_NONE);
                 clear_l();
            }
        }
        return mFramesToDrop;
    }

private:
    const uint32_t mSampleRate;

    std::mutex mLock;
    // number of captured frames to drop after the start sync event has been received.
    // when < 0, maximum frames to drop before starting capture even if sync event is
    // not received
    ssize_t mFramesToDrop GUARDED_BY(mLock) = 0;

    // sync event triggering actual audio capture. Frames read before this event will
    // be dropped and therefore not read by the application.
    sp<SyncEvent> mSyncStartEvent GUARDED_BY(mLock);

    void clear_l() REQUIRES(mLock) {
        if (mSyncStartEvent) {
            mSyncStartEvent->cancel();
            mSyncStartEvent.clear();
        }
        mFramesToDrop = 0;
    }
};

} // namespace android::audioflinger

#pragma pop_macro("LOG_TAG")
Loading