audio: Adjust default impl for non-RT audio behavior of AVD

The default implementation of the primary HAL is intended to
work with the AVD. Current implementation of virtualized audio
I/O on AVD falls behind actual hardware in terms of I/O timing.
This is not tolerated by CTS tests which are tailored for
real hardware.

Make the primary HAL implementation more resilient to irregular
ALSA read/write behavior on AVD by prolonging I/O time, or
skipping operations when falling behind expected time.

Bug: 302132812
Bug: 302587331
Test: atest CtsMediaAudioTestCases
Change-Id: Ia290d9541a8a0e22d28024f7930ef554396d63c6
This commit is contained in:
Mikhail Naganov 2023-11-03 18:33:57 -07:00
parent 69efe8e753
commit 6c419352c2
2 changed files with 48 additions and 18 deletions

View file

@ -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 {

View file

@ -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"
@ -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;
}