Loading services/audioflinger/AudioFlinger.h +1 −0 Original line number Diff line number Diff line Loading @@ -86,6 +86,7 @@ #include <timing/MonotonicFrameCounter.h> #include <timing/SyncEvent.h> #include <timing/SynchronizedRecordState.h> #include "FastCapture.h" #include "FastMixer.h" Loading services/audioflinger/RecordTracks.h +2 −4 Original line number Diff line number Diff line Loading @@ -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; Loading services/audioflinger/Threads.cpp +9 −36 Original line number Diff line number Diff line Loading @@ -8222,7 +8222,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 Loading @@ -8232,28 +8236,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; } Loading Loading @@ -8586,20 +8569,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)); } { Loading services/audioflinger/Tracks.cpp +13 −15 Original line number Diff line number Diff line Loading @@ -1615,6 +1615,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 { Loading Loading @@ -1865,6 +1866,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(); Loading Loading @@ -2399,7 +2402,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), Loading Loading @@ -2601,28 +2603,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( Loading services/audioflinger/timing/SynchronizedRecordState.h 0 → 100644 +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
services/audioflinger/AudioFlinger.h +1 −0 Original line number Diff line number Diff line Loading @@ -86,6 +86,7 @@ #include <timing/MonotonicFrameCounter.h> #include <timing/SyncEvent.h> #include <timing/SynchronizedRecordState.h> #include "FastCapture.h" #include "FastMixer.h" Loading
services/audioflinger/RecordTracks.h +2 −4 Original line number Diff line number Diff line Loading @@ -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; Loading
services/audioflinger/Threads.cpp +9 −36 Original line number Diff line number Diff line Loading @@ -8222,7 +8222,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 Loading @@ -8232,28 +8236,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; } Loading Loading @@ -8586,20 +8569,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)); } { Loading
services/audioflinger/Tracks.cpp +13 −15 Original line number Diff line number Diff line Loading @@ -1615,6 +1615,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 { Loading Loading @@ -1865,6 +1866,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(); Loading Loading @@ -2399,7 +2402,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), Loading Loading @@ -2601,28 +2603,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( Loading
services/audioflinger/timing/SynchronizedRecordState.h 0 → 100644 +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")