Loading audio/aidl/default/include/core-impl/StreamPrimary.h +5 −0 Original line number Diff line number Diff line Loading @@ -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<alsa::DeviceProfile> getDeviceProfiles() override; const bool mIsAsynchronous; long mStartTimeNs = 0; long mFramesSinceStart = 0; bool mSkipNextTransfer = false; }; class StreamInPrimary final : public StreamIn, public StreamSwitcher, public StreamInHwGainHelper { Loading audio/aidl/default/primary/StreamPrimary.cpp +42 −17 Original line number Diff line number Diff line Loading @@ -14,12 +14,11 @@ * limitations under the License. */ #include <chrono> #define LOG_TAG "AHAL_StreamPrimary" #include <android-base/logging.h> #include <android-base/properties.h> #include <audio_utils/clock.h> #include <error/Result.h> #include <error/expected_utils.h> #include "PrimaryMixer.h" Loading @@ -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::microseconds>( 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; } Loading Loading
audio/aidl/default/include/core-impl/StreamPrimary.h +5 −0 Original line number Diff line number Diff line Loading @@ -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<alsa::DeviceProfile> getDeviceProfiles() override; const bool mIsAsynchronous; long mStartTimeNs = 0; long mFramesSinceStart = 0; bool mSkipNextTransfer = false; }; class StreamInPrimary final : public StreamIn, public StreamSwitcher, public StreamInHwGainHelper { Loading
audio/aidl/default/primary/StreamPrimary.cpp +42 −17 Original line number Diff line number Diff line Loading @@ -14,12 +14,11 @@ * limitations under the License. */ #include <chrono> #define LOG_TAG "AHAL_StreamPrimary" #include <android-base/logging.h> #include <android-base/properties.h> #include <audio_utils/clock.h> #include <error/Result.h> #include <error/expected_utils.h> #include "PrimaryMixer.h" Loading @@ -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::microseconds>( 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; } Loading