diff --git a/audio/aidl/default/include/core-impl/StreamPrimary.h b/audio/aidl/default/include/core-impl/StreamPrimary.h index abc119c3f1..145c3c44cf 100644 --- a/audio/aidl/default/include/core-impl/StreamPrimary.h +++ b/audio/aidl/default/include/core-impl/StreamPrimary.h @@ -27,13 +27,18 @@ class StreamPrimary : public StreamAlsa { public: StreamPrimary(StreamContext* context, const Metadata& metadata); + ::android::status_t start() override; ::android::status_t transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, int32_t* latencyMs) override; + ::android::status_t refinePosition(StreamDescriptor::Position* position) override; protected: std::vector getDeviceProfiles() override; const bool mIsAsynchronous; + long mStartTimeNs = 0; + long mFramesSinceStart = 0; + bool mSkipNextTransfer = false; }; class StreamInPrimary final : public StreamIn, public StreamSwitcher, public StreamInHwGainHelper { diff --git a/audio/aidl/default/primary/StreamPrimary.cpp b/audio/aidl/default/primary/StreamPrimary.cpp index 7e3bdd468b..457fbb226c 100644 --- a/audio/aidl/default/primary/StreamPrimary.cpp +++ b/audio/aidl/default/primary/StreamPrimary.cpp @@ -14,12 +14,11 @@ * limitations under the License. */ -#include - #define LOG_TAG "AHAL_StreamPrimary" #include #include #include +#include #include #include "PrimaryMixer.h" @@ -43,26 +42,52 @@ StreamPrimary::StreamPrimary(StreamContext* context, const Metadata& metadata) context->startStreamDataProcessor(); } +::android::status_t StreamPrimary::start() { + RETURN_STATUS_IF_ERROR(StreamAlsa::start()); + mStartTimeNs = ::android::uptimeNanos(); + mFramesSinceStart = 0; + mSkipNextTransfer = false; + return ::android::OK; +} + ::android::status_t StreamPrimary::transfer(void* buffer, size_t frameCount, size_t* actualFrameCount, int32_t* latencyMs) { - auto start = std::chrono::steady_clock::now(); - if (auto status = StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs); - status != ::android::OK) { - return status; - } // This is a workaround for the emulator implementation which has a host-side buffer - // and this can result in reading faster than real time. - if (mIsInput && !mIsAsynchronous) { - auto recordDurationUs = std::chrono::duration_cast( - std::chrono::steady_clock::now() - start); - const long projectedVsObservedOffsetUs = - *actualFrameCount * MICROS_PER_SECOND / mContext.getSampleRate() - - recordDurationUs.count(); - if (projectedVsObservedOffsetUs > 0) { - LOG(VERBOSE) << __func__ << ": sleeping for " << projectedVsObservedOffsetUs << " us"; - usleep(projectedVsObservedOffsetUs); - } + // and is not being able to achieve real-time behavior similar to ADSPs (b/302587331). + if (!mSkipNextTransfer) { + RETURN_STATUS_IF_ERROR( + StreamAlsa::transfer(buffer, frameCount, actualFrameCount, latencyMs)); + } else { + LOG(DEBUG) << __func__ << ": skipping transfer (" << frameCount << " frames)"; + *actualFrameCount = frameCount; + if (mIsInput) memset(buffer, 0, frameCount); + mSkipNextTransfer = false; } + if (!mIsAsynchronous) { + const long bufferDurationUs = + (*actualFrameCount) * MICROS_PER_SECOND / mContext.getSampleRate(); + const auto totalDurationUs = + (::android::uptimeNanos() - mStartTimeNs) / NANOS_PER_MICROSECOND; + mFramesSinceStart += *actualFrameCount; + const long totalOffsetUs = + mFramesSinceStart * MICROS_PER_SECOND / mContext.getSampleRate() - totalDurationUs; + LOG(VERBOSE) << __func__ << ": totalOffsetUs " << totalOffsetUs; + if (totalOffsetUs > 0) { + const long sleepTimeUs = std::min(totalOffsetUs, bufferDurationUs); + LOG(VERBOSE) << __func__ << ": sleeping for " << sleepTimeUs << " us"; + usleep(sleepTimeUs); + } else { + mSkipNextTransfer = true; + } + } else { + LOG(VERBOSE) << __func__ << ": asynchronous transfer"; + } + return ::android::OK; +} + +::android::status_t StreamPrimary::refinePosition(StreamDescriptor::Position*) { + // Since not all data is actually sent to the HAL, use the position maintained by Stream class + // which accounts for all frames passed from / to the client. return ::android::OK; }