From 8296c2522746d3500c024121577007dd5e9e685c Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 25 Sep 2019 14:59:54 -0700 Subject: [PATCH 1/4] Move PlaybackThread::Track::writeFrames to PatchRecord This code logically belongs to PatchRecord because it emulates obtaining recorded data from a client. Test: make Change-Id: Icba4e33d9d0ca57e6ad964aaf3209e7db766f284 --- services/audioflinger/PlaybackTracks.h | 2 - services/audioflinger/RecordTracks.h | 10 ++++ services/audioflinger/Tracks.cpp | 66 ++++++++++++++------------ 3 files changed, 46 insertions(+), 32 deletions(-) diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index a093893fac..d9a7cd2e0a 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -266,8 +266,6 @@ protected: private: void interceptBuffer(const AudioBufferProvider::Buffer& buffer); - /** Write the source data in the buffer provider. @return written frame count. */ - size_t writeFrames(AudioBufferProvider* dest, const void* src, size_t frameCount); template void forEachTeePatchTrack(F f) { for (auto& tp : mTeePatches) { f(tp.patchTrack); } diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index c8397cd127..c98e02f5ad 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -175,4 +175,14 @@ public: virtual status_t obtainBuffer(Proxy::Buffer *buffer, const struct timespec *timeOut = NULL); virtual void releaseBuffer(Proxy::Buffer *buffer); + + size_t writeFrames(const void* src, size_t frameCount, size_t frameSize) { + return writeFrames(this, src, frameCount, frameSize); + } + +private: + /** Write the source data into the buffer provider. @return written frame count. */ + static size_t writeFrames(AudioBufferProvider* dest, const void* src, + size_t frameCount, size_t frameSize); + }; // end of PatchRecord diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 6a999fce71..6867050fb3 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -823,16 +823,9 @@ void AudioFlinger::PlaybackThread::Track::interceptBuffer( } for (auto& teePatch : mTeePatches) { RecordThread::PatchRecord* patchRecord = teePatch.patchRecord.get(); - - size_t framesWritten = writeFrames(patchRecord, sourceBuffer.i8, frameCount); - // On buffer wrap, the buffer frame count will be less than requested, - // when this happens a second buffer needs to be used to write the leftover audio - size_t framesLeft = frameCount - framesWritten; - if (framesWritten != 0 && framesLeft != 0) { - framesWritten += - writeFrames(patchRecord, sourceBuffer.i8 + framesWritten * mFrameSize, framesLeft); - framesLeft = frameCount - framesWritten; - } + const size_t framesWritten = patchRecord->writeFrames( + sourceBuffer.i8, frameCount, mFrameSize); + const size_t framesLeft = frameCount - framesWritten; ALOGW_IF(framesLeft != 0, "%s(%d) PatchRecord %d can not provide big enough " "buffer %zu/%zu, dropping %zu frames", __func__, mId, patchRecord->mId, framesWritten, frameCount, framesLeft); @@ -844,26 +837,6 @@ void AudioFlinger::PlaybackThread::Track::interceptBuffer( spent.count(), mTeePatches.size()); } -size_t AudioFlinger::PlaybackThread::Track::writeFrames(AudioBufferProvider* dest, - const void* src, - size_t frameCount) { - AudioBufferProvider::Buffer patchBuffer; - patchBuffer.frameCount = frameCount; - auto status = dest->getNextBuffer(&patchBuffer); - if (status != NO_ERROR) { - ALOGW("%s PathRecord getNextBuffer failed with error %d: %s", - __func__, status, strerror(-status)); - return 0; - } - ALOG_ASSERT(patchBuffer.frameCount <= frameCount); - memcpy(patchBuffer.raw, src, patchBuffer.frameCount * mFrameSize); - auto framesWritten = patchBuffer.frameCount; - dest->releaseBuffer(&patchBuffer); - return framesWritten; -} - -// releaseBuffer() is not overridden - // ExtendedAudioBufferProvider interface // framesReady() may return an approximation of the number of frames if called @@ -2405,6 +2378,39 @@ AudioFlinger::RecordThread::PatchRecord::~PatchRecord() ALOGV("%s(%d)", __func__, mId); } +static size_t writeFramesHelper( + AudioBufferProvider* dest, const void* src, size_t frameCount, size_t frameSize) +{ + AudioBufferProvider::Buffer patchBuffer; + patchBuffer.frameCount = frameCount; + auto status = dest->getNextBuffer(&patchBuffer); + if (status != NO_ERROR) { + ALOGW("%s PathRecord getNextBuffer failed with error %d: %s", + __func__, status, strerror(-status)); + return 0; + } + ALOG_ASSERT(patchBuffer.frameCount <= frameCount); + memcpy(patchBuffer.raw, src, patchBuffer.frameCount * frameSize); + size_t framesWritten = patchBuffer.frameCount; + dest->releaseBuffer(&patchBuffer); + return framesWritten; +} + +// static +size_t AudioFlinger::RecordThread::PatchRecord::writeFrames( + AudioBufferProvider* dest, const void* src, size_t frameCount, size_t frameSize) +{ + size_t framesWritten = writeFramesHelper(dest, src, frameCount, frameSize); + // On buffer wrap, the buffer frame count will be less than requested, + // when this happens a second buffer needs to be used to write the leftover audio + const size_t framesLeft = frameCount - framesWritten; + if (framesWritten != 0 && framesLeft != 0) { + framesWritten += writeFramesHelper(dest, (const char*)src + framesWritten * frameSize, + framesLeft, frameSize); + } + return framesWritten; +} + // AudioBufferProvider interface status_t AudioFlinger::RecordThread::PatchRecord::getNextBuffer( AudioBufferProvider::Buffer* buffer) From 2534b388b5ab10712b9b6c24a49e3dc827ce0e00 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 25 Sep 2019 13:05:02 -0700 Subject: [PATCH 2/4] Abstract access to HAL stream via Source in RecordThread This allows to replace direct reading from HAL with obtaining audio data from another source. It should be possible to encapsulate reading from FastCapture in the same manner, but it's not required for direct inputs. Test: make Change-Id: I3f005583410cc9c5d4b07c127d95e236abb4a85f --- services/audioflinger/AudioFlinger.h | 19 ++++++++++++++++++- services/audioflinger/RecordTracks.h | 2 ++ services/audioflinger/Threads.cpp | 13 ++++++++++--- services/audioflinger/Threads.h | 1 + 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index 5e4509fb65..bbf8a293a7 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -547,6 +547,16 @@ private: bool mute; }; + // Abstraction for the Audio Source for the RecordThread (HAL or PassthruPatchRecord). + struct Source + { + virtual ~Source() = default; + // The following methods have the same signatures as in StreamHalInterface. + virtual status_t read(void *buffer, size_t bytes, size_t *read) = 0; + virtual status_t getCapturePosition(int64_t *frames, int64_t *time) = 0; + virtual status_t standby() = 0; + }; + // --- PlaybackThread --- #ifdef FLOAT_EFFECT_CHAIN #define EFFECT_BUFFER_FORMAT AUDIO_FORMAT_PCM_FLOAT @@ -749,7 +759,7 @@ using effect_buffer_t = int16_t; // For emphasis, we could also make all pointers to them be "const *", // but that would clutter the code unnecessarily. - struct AudioStreamIn { + struct AudioStreamIn : public Source { AudioHwDevice* const audioHwDev; sp stream; audio_input_flags_t flags; @@ -758,6 +768,13 @@ using effect_buffer_t = int16_t; AudioStreamIn(AudioHwDevice *dev, sp in, audio_input_flags_t flags) : audioHwDev(dev), stream(in), flags(flags) {} + status_t read(void *buffer, size_t bytes, size_t *read) override { + return stream->read(buffer, bytes, read); + } + status_t getCapturePosition(int64_t *frames, int64_t *time) override { + return stream->getCapturePosition(frames, time); + } + status_t standby() override { return stream->standby(); } }; struct TeePatch { diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index c98e02f5ad..23c515ff26 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -167,6 +167,8 @@ public: const Timeout& timeout = {}); virtual ~PatchRecord(); + virtual Source* getSource() { return nullptr; } + // AudioBufferProvider interface virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 8fddc13509..8704d16a49 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -6679,6 +6679,7 @@ AudioFlinger::RecordThread::RecordThread(const sp& audioFlinger, ) : ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD, systemReady), mInput(input), + mSource(mInput), mActiveTracks(&this->mLocalLog), mRsmpInBuffer(NULL), // mRsmpInFrames, mRsmpInFramesP2, and mRsmpInFramesOA are set by readInputParameters_l() @@ -7131,7 +7132,7 @@ reacquire_wakelock: } else { ATRACE_BEGIN("read"); size_t bytesRead; - status_t result = mInput->stream->read( + status_t result = mSource->read( (uint8_t*)mRsmpInBuffer + rear * mFrameSize, mBufferSize, &bytesRead); ATRACE_END(); if (result < 0) { @@ -7153,7 +7154,7 @@ reacquire_wakelock: int64_t position, time; if (mStandby) { mTimestampVerifier.discontinuity(); - } else if (mInput->stream->getCapturePosition(&position, &time) == NO_ERROR + } else if (mSource->getCapturePosition(&position, &time) == NO_ERROR && time > mTimestamp.mTimeNs[ExtendedTimestamp::LOCATION_KERNEL]) { mTimestampVerifier.add(position, time, mSampleRate); @@ -7434,7 +7435,7 @@ void AudioFlinger::RecordThread::inputStandBy() sq->end(false /*didModify*/); } } - status_t result = mInput->stream->standby(); + status_t result = mSource->standby(); ALOGE_IF(result != OK, "Error when putting input stream into standby: %d", result); // If going into standby, flush the pipe source. @@ -8420,11 +8421,17 @@ void AudioFlinger::RecordThread::addPatchTrack(const sp& record) { Mutex::Autolock _l(mLock); mTracks.add(record); + if (record->getSource()) { + mSource = record->getSource(); + } } void AudioFlinger::RecordThread::deletePatchTrack(const sp& record) { Mutex::Autolock _l(mLock); + if (mSource == record->getSource()) { + mSource = mInput; + } destroyTrack_l(record); } diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 6a9c0e7628..34a3f34911 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -1647,6 +1647,7 @@ private: void checkBtNrec_l(); AudioStreamIn *mInput; + Source *mSource; SortedVector < sp > mTracks; // mActiveTracks has dual roles: it indicates the current active track(s), and // is used together with mStartStopCond to indicate start()/stop() progress From caf5994f652e33336e7bded702c30e29c1d85eb6 Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Wed, 25 Sep 2019 14:05:29 -0700 Subject: [PATCH 3/4] Add PassthruPatchRecord for low latency software patches Implement a subclass of PatchRecord that uses PatchTrack's thread for reading from HAL. This eliminates the need for buffering that adds latency. The only modification needed for PatchTrack is to indicate unlimited amount of available frames to the playback thread. This is to prevent PatchTrack from being deactivated by DirectOutputThread due to lack of frames available. RecordThread believes it reads audio data on its thread, and manages timestamps as usual. The data that it "reads" and passes to PassthruPatchRecord is discarded by the latter. Bug: 117564323 Test: MS12 MSD, AOSP MSD Change-Id: I376656e3c791e91e2196331ecdf2b425697c4e18 --- services/audioflinger/PlaybackTracks.h | 2 + services/audioflinger/RecordTracks.h | 59 +++++++- services/audioflinger/TrackBase.h | 3 + services/audioflinger/Tracks.cpp | 183 +++++++++++++++++++++++++ 4 files changed, 246 insertions(+), 1 deletion(-) diff --git a/services/audioflinger/PlaybackTracks.h b/services/audioflinger/PlaybackTracks.h index d9a7cd2e0a..d0f8b170a1 100644 --- a/services/audioflinger/PlaybackTracks.h +++ b/services/audioflinger/PlaybackTracks.h @@ -385,6 +385,8 @@ public: const Timeout& timeout = {}); virtual ~PatchTrack(); + size_t framesReady() const override; + virtual status_t start(AudioSystem::sync_event_t event = AudioSystem::SYNC_EVENT_NONE, audio_session_t triggerSession = AUDIO_SESSION_NONE); diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h index 23c515ff26..d5257bdd34 100644 --- a/services/audioflinger/RecordTracks.h +++ b/services/audioflinger/RecordTracks.h @@ -182,9 +182,66 @@ public: return writeFrames(this, src, frameCount, frameSize); } -private: +protected: /** Write the source data into the buffer provider. @return written frame count. */ static size_t writeFrames(AudioBufferProvider* dest, const void* src, size_t frameCount, size_t frameSize); }; // end of PatchRecord + +class PassthruPatchRecord : public PatchRecord, public Source { +public: + PassthruPatchRecord(RecordThread *recordThread, + uint32_t sampleRate, + audio_channel_mask_t channelMask, + audio_format_t format, + size_t frameCount, + audio_input_flags_t flags); + + Source* getSource() override { return static_cast(this); } + + // Source interface + status_t read(void *buffer, size_t bytes, size_t *read) override; + status_t getCapturePosition(int64_t *frames, int64_t *time) override; + status_t standby() override; + + // AudioBufferProvider interface + // This interface is used by RecordThread to pass the data obtained + // from HAL or other source to the client. PassthruPatchRecord receives + // the data in 'obtainBuffer' so these calls are stubbed out. + status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override; + void releaseBuffer(AudioBufferProvider::Buffer* buffer) override; + + // PatchProxyBufferProvider interface + // This interface is used from DirectOutputThread to acquire data from HAL. + bool producesBufferOnDemand() const override { return true; } + status_t obtainBuffer(Proxy::Buffer *buffer, const struct timespec *timeOut = nullptr) override; + void releaseBuffer(Proxy::Buffer *buffer) override; + +private: + // This is to use with PatchRecord::writeFrames + struct PatchRecordAudioBufferProvider : public AudioBufferProvider { + explicit PatchRecordAudioBufferProvider(PassthruPatchRecord& passthru) : + mPassthru(passthru) {} + status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) override { + return mPassthru.PatchRecord::getNextBuffer(buffer); + } + void releaseBuffer(AudioBufferProvider::Buffer* buffer) override { + return mPassthru.PatchRecord::releaseBuffer(buffer); + } + private: + PassthruPatchRecord& mPassthru; + }; + + sp obtainStream(sp* thread); + + PatchRecordAudioBufferProvider mPatchRecordAudioBufferProvider; + std::unique_ptr mSinkBuffer; // frame size aligned continuous buffer + std::unique_ptr mStubBuffer; // buffer used for AudioBufferProvider + size_t mUnconsumedFrames = 0; + std::mutex mReadLock; + std::condition_variable mReadCV; + size_t mReadBytes = 0; // GUARDED_BY(mReadLock) + status_t mReadError = NO_ERROR; // GUARDED_BY(mReadLock) + int64_t mLastReadFrames = 0; // accessed on RecordThread only +}; diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h index 7cf34c1ecc..051f1e3213 100644 --- a/services/audioflinger/TrackBase.h +++ b/services/audioflinger/TrackBase.h @@ -337,6 +337,7 @@ public: virtual ~PatchProxyBufferProvider() {} + virtual bool producesBufferOnDemand() const = 0; virtual status_t obtainBuffer(Proxy::Buffer* buffer, const struct timespec *requested = NULL) = 0; virtual void releaseBuffer(Proxy::Buffer* buffer) = 0; @@ -359,6 +360,8 @@ public: mPeerProxy = nullptr; } + bool producesBufferOnDemand() const override { return false; } + protected: const sp mProxy; sp mPeerReferenceHold; // keeps mPeerProxy alive during access. diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp index 6867050fb3..8c978edd60 100644 --- a/services/audioflinger/Tracks.cpp +++ b/services/audioflinger/Tracks.cpp @@ -1786,6 +1786,15 @@ AudioFlinger::PlaybackThread::PatchTrack::~PatchTrack() ALOGV("%s(%d)", __func__, mId); } +size_t AudioFlinger::PlaybackThread::PatchTrack::framesReady() const +{ + if (mPeerProxy && mPeerProxy->producesBufferOnDemand()) { + return std::numeric_limits::max(); + } else { + return Track::framesReady(); + } +} + status_t AudioFlinger::PlaybackThread::PatchTrack::start(AudioSystem::sync_event_t event, audio_session_t triggerSession) { @@ -2455,6 +2464,180 @@ void AudioFlinger::RecordThread::PatchRecord::releaseBuffer(Proxy::Buffer* buffe mProxy->releaseBuffer(buffer); } +#undef LOG_TAG +#define LOG_TAG "AF::PthrPatchRecord" + +static std::unique_ptr allocAligned(size_t alignment, size_t size) +{ + void *ptr = nullptr; + (void)posix_memalign(&ptr, alignment, size); + return std::unique_ptr(ptr, free); +} + +AudioFlinger::RecordThread::PassthruPatchRecord::PassthruPatchRecord( + RecordThread *recordThread, + uint32_t sampleRate, + audio_channel_mask_t channelMask, + audio_format_t format, + size_t frameCount, + audio_input_flags_t flags) + : PatchRecord(recordThread, sampleRate, channelMask, format, frameCount, + nullptr /*buffer*/, 0 /*bufferSize*/, flags), + mPatchRecordAudioBufferProvider(*this), + mSinkBuffer(allocAligned(32, mFrameCount * mFrameSize)), + mStubBuffer(allocAligned(32, mFrameCount * mFrameSize)) +{ + memset(mStubBuffer.get(), 0, mFrameCount * mFrameSize); +} + +sp AudioFlinger::RecordThread::PassthruPatchRecord::obtainStream( + sp* thread) +{ + *thread = mThread.promote(); + if (!*thread) return nullptr; + RecordThread *recordThread = static_cast((*thread).get()); + Mutex::Autolock _l(recordThread->mLock); + return recordThread->mInput ? recordThread->mInput->stream : nullptr; +} + +// PatchProxyBufferProvider methods are called on DirectOutputThread +status_t AudioFlinger::RecordThread::PassthruPatchRecord::obtainBuffer( + Proxy::Buffer* buffer, const struct timespec* timeOut) +{ + if (mUnconsumedFrames) { + buffer->mFrameCount = std::min(buffer->mFrameCount, mUnconsumedFrames); + // mUnconsumedFrames is decreased in releaseBuffer to use actual frame consumption figure. + return PatchRecord::obtainBuffer(buffer, timeOut); + } + + // Otherwise, execute a read from HAL and write into the buffer. + nsecs_t startTimeNs = 0; + if (timeOut && (timeOut->tv_sec != 0 || timeOut->tv_nsec != 0) && timeOut->tv_sec != INT_MAX) { + // Will need to correct timeOut by elapsed time. + startTimeNs = systemTime(); + } + const size_t framesToRead = std::min(buffer->mFrameCount, mFrameCount); + buffer->mFrameCount = 0; + buffer->mRaw = nullptr; + sp thread; + sp stream = obtainStream(&thread); + if (!stream) return NO_INIT; // If there is no stream, RecordThread is not reading. + + status_t result = NO_ERROR; + struct timespec newTimeOut = *timeOut; + size_t bytesRead = 0; + { + ATRACE_NAME("read"); + result = stream->read(mSinkBuffer.get(), framesToRead * mFrameSize, &bytesRead); + if (result != NO_ERROR) goto stream_error; + if (bytesRead == 0) return NO_ERROR; + } + + { + std::lock_guard lock(mReadLock); + mReadBytes += bytesRead; + mReadError = NO_ERROR; + } + mReadCV.notify_one(); + // writeFrames handles wraparound and should write all the provided frames. + // If it couldn't, there is something wrong with the client/server buffer of the software patch. + buffer->mFrameCount = writeFrames( + &mPatchRecordAudioBufferProvider, + mSinkBuffer.get(), bytesRead / mFrameSize, mFrameSize); + ALOGW_IF(buffer->mFrameCount < bytesRead / mFrameSize, + "Lost %zu frames obtained from HAL", bytesRead / mFrameSize - buffer->mFrameCount); + mUnconsumedFrames = buffer->mFrameCount; + // Correct newTimeOut by elapsed time. + if (startTimeNs) { + nsecs_t newTimeOutNs = + audio_utils_ns_from_timespec(&newTimeOut) - (systemTime() - startTimeNs); + if (newTimeOutNs < 0) newTimeOutNs = 0; + newTimeOut.tv_sec = newTimeOutNs / NANOS_PER_SECOND; + newTimeOut.tv_nsec = newTimeOutNs - newTimeOut.tv_sec * NANOS_PER_SECOND; + } + return PatchRecord::obtainBuffer(buffer, &newTimeOut); + +stream_error: + stream->standby(); + { + std::lock_guard lock(mReadLock); + mReadError = result; + } + mReadCV.notify_one(); + return result; +} + +void AudioFlinger::RecordThread::PassthruPatchRecord::releaseBuffer(Proxy::Buffer* buffer) +{ + if (buffer->mFrameCount <= mUnconsumedFrames) { + mUnconsumedFrames -= buffer->mFrameCount; + } else { + ALOGW("Write side has consumed more frames than we had: %zu > %zu", + buffer->mFrameCount, mUnconsumedFrames); + mUnconsumedFrames = 0; + } + PatchRecord::releaseBuffer(buffer); +} + +// AudioBufferProvider and Source methods are called on RecordThread +// 'read' emulates actual audio data with 0's. This is OK as 'getNextBuffer' +// and 'releaseBuffer' are stubbed out and ignore their input. +// It's not possible to retrieve actual data here w/o blocking 'obtainBuffer' +// until we copy it. +status_t AudioFlinger::RecordThread::PassthruPatchRecord::read( + void* buffer, size_t bytes, size_t* read) +{ + bytes = std::min(bytes, mFrameCount * mFrameSize); + { + std::unique_lock lock(mReadLock); + mReadCV.wait(lock, [&]{ return mReadError != NO_ERROR || mReadBytes != 0; }); + if (mReadError != NO_ERROR) { + mLastReadFrames = 0; + return mReadError; + } + *read = std::min(bytes, mReadBytes); + mReadBytes -= *read; + } + mLastReadFrames = *read / mFrameSize; + memset(buffer, 0, *read); + return 0; +} + +status_t AudioFlinger::RecordThread::PassthruPatchRecord::getCapturePosition( + int64_t* frames, int64_t* time) +{ + sp thread; + sp stream = obtainStream(&thread); + return stream ? stream->getCapturePosition(frames, time) : NO_INIT; +} + +status_t AudioFlinger::RecordThread::PassthruPatchRecord::standby() +{ + // RecordThread issues 'standby' command in two major cases: + // 1. Error on read--this case is handled in 'obtainBuffer'. + // 2. Track is stopping--as PassthruPatchRecord assumes continuous + // output, this can only happen when the software patch + // is being torn down. In this case, the RecordThread + // will terminate and close the HAL stream. + return 0; +} + +// As the buffer gets filled in obtainBuffer, here we only simulate data consumption. +status_t AudioFlinger::RecordThread::PassthruPatchRecord::getNextBuffer( + AudioBufferProvider::Buffer* buffer) +{ + buffer->frameCount = mLastReadFrames; + buffer->raw = buffer->frameCount != 0 ? mStubBuffer.get() : nullptr; + return NO_ERROR; +} + +void AudioFlinger::RecordThread::PassthruPatchRecord::releaseBuffer( + AudioBufferProvider::Buffer* buffer) +{ + buffer->frameCount = 0; + buffer->raw = nullptr; +} + // ---------------------------------------------------------------------------- #undef LOG_TAG #define LOG_TAG "AF::MmapTrack" From 9515fc85a6b7de9c0520315f33c5a6d6421e738b Mon Sep 17 00:00:00 2001 From: Mikhail Naganov Date: Tue, 1 Oct 2019 16:52:14 -0700 Subject: [PATCH 4/4] Use PassthruPatchRecord for DIRECT to DIRECT connections When both input and output connected by a software patch are in 'DIRECT' mode (no framework processing), use PassthruPatchRecord as it helps to reduce the latency significantly by avoiding intermediate buffering. Remove 'std::nothrow' from tracks creation in PatchPanel for consistency with other code. Bug: 117564323 Test: MS12 MSD, AOSP MSD Change-Id: I52ec5b02a207548ebc4073c1033e396f444c041c --- services/audioflinger/PatchPanel.cpp | 40 ++++++++++++++++++---------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/services/audioflinger/PatchPanel.cpp b/services/audioflinger/PatchPanel.cpp index edb331d4ae..18cb53b1da 100644 --- a/services/audioflinger/PatchPanel.cpp +++ b/services/audioflinger/PatchPanel.cpp @@ -483,19 +483,6 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) // Fast mode is not available in this case. inputFlags = (audio_input_flags_t) (inputFlags & ~AUDIO_INPUT_FLAG_FAST); } - sp tempRecordTrack = new (std::nothrow) RecordThread::PatchRecord( - mRecord.thread().get(), - sampleRate, - inChannelMask, - format, - frameCount, - NULL, - (size_t)0 /* bufferSize */, - inputFlags); - status = mRecord.checkTrack(tempRecordTrack.get()); - if (status != NO_ERROR) { - return status; - } audio_output_flags_t outputFlags = mAudioPatch.sinks[0].config_mask & AUDIO_PORT_CONFIG_FLAGS ? mAudioPatch.sinks[0].flags.output : AUDIO_OUTPUT_FLAG_NONE; @@ -512,9 +499,34 @@ status_t AudioFlinger::PatchPanel::Patch::createConnections(PatchPanel *panel) outputFlags = (audio_output_flags_t) (outputFlags & ~AUDIO_OUTPUT_FLAG_FAST); } + sp tempRecordTrack; + if ((inputFlags & AUDIO_INPUT_FLAG_DIRECT) && (outputFlags & AUDIO_OUTPUT_FLAG_DIRECT)) { + tempRecordTrack = new RecordThread::PassthruPatchRecord( + mRecord.thread().get(), + sampleRate, + inChannelMask, + format, + frameCount, + inputFlags); + } else { + tempRecordTrack = new RecordThread::PatchRecord( + mRecord.thread().get(), + sampleRate, + inChannelMask, + format, + frameCount, + nullptr, + (size_t)0 /* bufferSize */, + inputFlags); + } + status = mRecord.checkTrack(tempRecordTrack.get()); + if (status != NO_ERROR) { + return status; + } + // create a special playback track to render to playback thread. // this track is given the same buffer as the PatchRecord buffer - sp tempPatchTrack = new (std::nothrow) PlaybackThread::PatchTrack( + sp tempPatchTrack = new PlaybackThread::PatchTrack( mPlayback.thread().get(), streamType, sampleRate,