AudioTrack: Fix timestamp jitter
When HAL out_get_presentation_position returns error,
use server position adjusted for latency.
Bug: 28250436
Change-Id: I3dbcb9b7c4e56d34e7e161d1a02d8f64afd602b9
diff --git a/include/media/AudioTimestamp.h b/include/media/AudioTimestamp.h
index 969003c..4f504a4 100644
--- a/include/media/AudioTimestamp.h
+++ b/include/media/AudioTimestamp.h
@@ -36,6 +36,7 @@
struct ExtendedTimestamp {
enum Location {
+ LOCATION_INVALID = -1,
LOCATION_CLIENT, // timestamp of last read frame from client-server track buffer
LOCATION_SERVER, // timestamp of newest frame from client-server track buffer
LOCATION_KERNEL, // timestamp of newest frame in the kernel (alsa) buffer.
@@ -89,8 +90,10 @@
}
// Returns the best timestamp as judged from the closest-to-hw stage in the
- // pipeline with a valid timestamp.
- status_t getBestTimestamp(int64_t *position, int64_t *time, int timebase) const {
+ // pipeline with a valid timestamp. If the optional location parameter is non-null,
+ // it will be filled with the location where the time was obtained.
+ status_t getBestTimestamp(
+ int64_t *position, int64_t *time, int timebase, Location *location = nullptr) const {
if (position == nullptr || time == nullptr
|| timebase < 0 || timebase >= TIMEBASE_MAX) {
return BAD_VALUE;
@@ -102,18 +105,21 @@
if (mTimeNs[i] > 0) {
*position = mPosition[i];
*time = mTimeNs[i] + mTimebaseOffset[timebase];
+ if (location != nullptr) {
+ *location = (Location)i;
+ }
return OK;
}
}
return INVALID_OPERATION;
}
- status_t getBestTimestamp(AudioTimestamp *timestamp) const {
+ status_t getBestTimestamp(AudioTimestamp *timestamp, Location *location = nullptr) const {
if (timestamp == nullptr) {
return BAD_VALUE;
}
int64_t position, time;
- if (getBestTimestamp(&position, &time, TIMEBASE_MONOTONIC) == OK) {
+ if (getBestTimestamp(&position, &time, TIMEBASE_MONOTONIC, location) == OK) {
timestamp->mPosition = position;
timestamp->mTime.tv_sec = time / 1000000000;
timestamp->mTime.tv_nsec = time - timestamp->mTime.tv_sec * 1000000000LL;
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index fc7217a..3cadf6b 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -1024,6 +1024,8 @@
bool mTimestampStartupGlitchReported; // reduce log spam
bool mRetrogradeMotionReported; // reduce log spam
AudioTimestamp mPreviousTimestamp; // used to detect retrograde motion
+ ExtendedTimestamp::Location mPreviousLocation; // location used for previous timestamp
+ double mComputedLatencyMs; // latency between server and kernel
uint32_t mUnderrunCountOffset; // updated when restoring tracks
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index b9138f2..9821831 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -504,6 +504,8 @@
mPreviousTimestampValid = false;
mTimestampStartupGlitchReported = false;
mRetrogradeMotionReported = false;
+ mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID;
+ mComputedLatencyMs = 0.;
mUnderrunCountOffset = 0;
mFramesWritten = 0;
mFramesWrittenServerOffset = 0;
@@ -536,6 +538,8 @@
mPreviousTimestampValid = false;
mTimestampStartupGlitchReported = false;
mRetrogradeMotionReported = false;
+ mPreviousLocation = ExtendedTimestamp::LOCATION_INVALID;
+ mComputedLatencyMs = 0.;
// read last server side position change via timestamp.
ExtendedTimestamp ets;
@@ -2303,7 +2307,40 @@
ExtendedTimestamp ets;
status = mProxy->getTimestamp(&ets);
if (status == OK) {
- status = ets.getBestTimestamp(×tamp);
+ ExtendedTimestamp::Location location;
+ status = ets.getBestTimestamp(×tamp, &location);
+
+ if (status == OK) {
+ // It is possible that the best location has moved from the kernel to the server.
+ // In this case we adjust the position from the previous computed latency.
+ if (location == ExtendedTimestamp::LOCATION_SERVER) {
+ ALOGW_IF(mPreviousLocation == ExtendedTimestamp::LOCATION_KERNEL,
+ "getTimestamp() location moved from kernel to server");
+ const double latencyMs = mComputedLatencyMs > 0.
+ ? mComputedLatencyMs : mAfLatency;
+ const int64_t frames =
+ int64_t(latencyMs * mSampleRate * mPlaybackRate.mSpeed / 1000);
+ ALOGV("mComputedLatencyMs:%lf mAfLatency:%u frame adjustment:%lld",
+ mComputedLatencyMs, mAfLatency, (long long)frames);
+ if (frames >= ets.mPosition[location]) {
+ timestamp.mPosition = 0;
+ } else {
+ timestamp.mPosition = (uint32_t)(ets.mPosition[location] - frames);
+ }
+ } else if (location == ExtendedTimestamp::LOCATION_KERNEL) {
+ const double bufferDiffMs =
+ (double)(ets.mPosition[ExtendedTimestamp::LOCATION_SERVER]
+ - ets.mPosition[ExtendedTimestamp::LOCATION_KERNEL])
+ * 1000 / ((double)mSampleRate * mPlaybackRate.mSpeed);
+ mComputedLatencyMs = bufferDiffMs > 0. ? bufferDiffMs : 0.;
+ ALOGV("mComputedLatencyMs:%lf mAfLatency:%d",
+ mComputedLatencyMs, mAfLatency);
+ }
+ mPreviousLocation = location;
+ } else {
+ // right after AudioTrack is started, one may not find a timestamp
+ ALOGV("getBestTimestamp did not find timestamp");
+ }
}
if (status == INVALID_OPERATION) {
status = WOULD_BLOCK;