aaudio: improve accuracy of timestamps
Account for latency added by the AAudio service.
Fix input timestamps.
Bug: 37080396
Test: test_timestamps.cpp input_monitor.cpp
Change-Id: I1053cd21af722bb9b9371df4e5731bf4a0a57b0b
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 0f863fe..6c345cd 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -177,7 +177,10 @@
configuration.getSamplesPerFrame() != mStreamInternal->getSamplesPerFrame()) {
return false;
}
-
return true;
}
+
+aaudio_result_t AAudioServiceEndpoint::getTimestamp(int64_t *positionFrames, int64_t *timeNanos) {
+ return mStreamInternal->getTimestamp(CLOCK_MONOTONIC, positionFrames, timeNanos);
+}
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index e40a670..603d497 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -69,6 +69,8 @@
mReferenceCount = count;
}
+ aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos);
+
bool matches(const AAudioStreamConfiguration& configuration);
virtual AudioStreamInternal *getStreamInternal() = 0;
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index 6a37330..6504cc1 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -62,6 +62,9 @@
// result might be a frame count
while (mCallbackEnabled.load() && getStreamInternal()->isActive() && (result >= 0)) {
+
+ int64_t mmapFramesRead = getStreamInternal()->getFramesRead();
+
// Read audio data from stream using a blocking read.
result = getStreamInternal()->read(mDistributionBuffer, getFramesPerBurst(), timeoutNanos);
if (result == AAUDIO_ERROR_DISCONNECTED) {
@@ -74,18 +77,32 @@
}
// Distribute data to each active stream.
- { // use lock guard
+ { // brackets are for lock_guard
+
std::lock_guard <std::mutex> lock(mLockStreams);
- for (sp<AAudioServiceStreamShared> sharedStream : mRegisteredStreams) {
- if (sharedStream->isRunning()) {
- FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
+ for (sp<AAudioServiceStreamShared> clientStream : mRegisteredStreams) {
+ if (clientStream->isRunning()) {
+ FifoBuffer *fifo = clientStream->getDataFifoBuffer();
+
+ // Determine offset between framePosition in client's stream vs the underlying
+ // MMAP stream.
+ int64_t clientFramesWritten = fifo->getWriteCounter();
+ // There are two indices that refer to the same frame.
+ int64_t positionOffset = mmapFramesRead - clientFramesWritten;
+ clientStream->setTimestampPositionOffset(positionOffset);
+
if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
getFramesPerBurst()) {
underflowCount++;
} else {
fifo->write(mDistributionBuffer, getFramesPerBurst());
}
- sharedStream->markTransferTime(AudioClock::getNanoseconds());
+
+ // This timestamp represents the completion of data being written into the
+ // client buffer. It is sent to the client and used in the timing model
+ // to decide when data will be available to read.
+ Timestamp timestamp(fifo->getWriteCounter(), AudioClock::getNanoseconds());
+ clientStream->markTransferTime(timestamp);
}
}
}
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index b83b918..20cc5b8 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -73,17 +73,31 @@
while (mCallbackEnabled.load() && getStreamInternal()->isActive() && (result >= 0)) {
// Mix data from each active stream.
mMixer.clear();
- { // use lock guard
+ { // brackets are for lock_guard
int index = 0;
+ int64_t mmapFramesWritten = getStreamInternal()->getFramesWritten();
+
std::lock_guard <std::mutex> lock(mLockStreams);
- for (sp<AAudioServiceStreamShared> sharedStream : mRegisteredStreams) {
- if (sharedStream->isRunning()) {
- FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
+ for (sp<AAudioServiceStreamShared> clientStream : mRegisteredStreams) {
+ if (clientStream->isRunning()) {
+ FifoBuffer *fifo = clientStream->getDataFifoBuffer();
+ // Determine offset between framePosition in client's stream vs the underlying
+ // MMAP stream.
+ int64_t clientFramesRead = fifo->getReadCounter();
+ // These two indices refer to the same frame.
+ int64_t positionOffset = mmapFramesWritten - clientFramesRead;
+ clientStream->setTimestampPositionOffset(positionOffset);
+
float volume = 1.0; // to match legacy volume
bool underflowed = mMixer.mix(index, fifo, volume);
underflowCount += underflowed ? 1 : 0;
// TODO log underflows in each stream
- sharedStream->markTransferTime(AudioClock::getNanoseconds());
+
+ // This timestamp represents the completion of data being read out of the
+ // client buffer. It is sent to the client and used in the timing model
+ // to decide when the client has room to write more data.
+ Timestamp timestamp(fifo->getReadCounter(), AudioClock::getNanoseconds());
+ clientStream->markTransferTime(timestamp);
}
index++;
}
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index 5f7d179..3fc5957 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -37,7 +37,8 @@
AAudioServiceStreamBase::AAudioServiceStreamBase()
: mUpMessageQueue(nullptr)
- , mAAudioThread() {
+ , mAAudioThread()
+ , mAtomicTimestamp() {
mMmapClient.clientUid = -1;
mMmapClient.clientPid = -1;
mMmapClient.packageName = String16("");
@@ -211,15 +212,25 @@
aaudio_result_t AAudioServiceStreamBase::sendCurrentTimestamp() {
AAudioServiceMessage command;
+ // Send a timestamp for the clock model.
aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position,
&command.timestamp.timestamp);
if (result == AAUDIO_OK) {
- // ALOGD("sendCurrentTimestamp(): position = %lld, nanos = %lld",
- // (long long) command.timestamp.position,
- // (long long) command.timestamp.timestamp);
- command.what = AAudioServiceMessage::code::TIMESTAMP;
+ command.what = AAudioServiceMessage::code::TIMESTAMP_SERVICE;
result = writeUpMessageQueue(&command);
- } else if (result == AAUDIO_ERROR_UNAVAILABLE) {
+
+ if (result == AAUDIO_OK) {
+ // Send a hardware timestamp for presentation time.
+ result = getHardwareTimestamp(&command.timestamp.position,
+ &command.timestamp.timestamp);
+ if (result == AAUDIO_OK) {
+ command.what = AAudioServiceMessage::code::TIMESTAMP_HARDWARE;
+ result = writeUpMessageQueue(&command);
+ }
+ }
+ }
+
+ if (result == AAUDIO_ERROR_UNAVAILABLE) {
result = AAUDIO_OK; // just not available yet, try again later
}
return result;
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index cebefec..e91ea82 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -20,6 +20,7 @@
#include <assert.h>
#include <mutex>
+#include <media/AudioClient.h>
#include <utils/RefBase.h>
#include "fifo/FifoBuffer.h"
@@ -27,7 +28,7 @@
#include "binding/AudioEndpointParcelable.h"
#include "binding/AAudioServiceMessage.h"
#include "utility/AAudioUtilities.h"
-#include <media/AudioClient.h>
+#include "utility/AudioClock.h"
#include "SharedRingBuffer.h"
#include "AAudioThread.h"
@@ -170,6 +171,8 @@
*/
virtual aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) = 0;
+ virtual aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) = 0;
+
virtual aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) = 0;
aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
@@ -191,6 +194,8 @@
android::AudioClient mMmapClient;
audio_port_handle_t mClientHandle = AUDIO_PORT_HANDLE_NONE;
+ SimpleDoubleBuffer<Timestamp> mAtomicTimestamp;
+
private:
aaudio_handle_t mHandle = -1;
};
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index ff02c0f..970d734 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -37,6 +37,11 @@
#define AAUDIO_BUFFER_CAPACITY_MIN 4 * 512
#define AAUDIO_SAMPLE_RATE_DEFAULT 48000
+// This is an estimate of the time difference between the HW and the MMAP time.
+// TODO Get presentation timestamps from the HAL instead of using these estimates.
+#define OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS (3 * AAUDIO_NANOS_PER_MILLISECOND)
+#define INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS (-1 * AAUDIO_NANOS_PER_MILLISECOND)
+
/**
* Service Stream that uses an MMAP buffer.
*/
@@ -113,10 +118,14 @@
config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
? AUDIO_CHANNEL_OUT_STEREO
: audio_channel_out_mask_from_count(aaudioSamplesPerFrame);
+ mHardwareTimeOffsetNanos = OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at DAC later
+
} else if (direction == AAUDIO_DIRECTION_INPUT) {
config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
? AUDIO_CHANNEL_IN_STEREO
: audio_channel_in_mask_from_count(aaudioSamplesPerFrame);
+ mHardwareTimeOffsetNanos = INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at ADC earlier
+
} else {
ALOGE("openMmapStream - invalid direction = %d", direction);
return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
@@ -289,6 +298,7 @@
return AAudioConvert_androidToAAudioResult(mMmapStream->stop(clientHandle));
}
+// Get free-running DSP or DMA hardware position from the HAL.
aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames,
int64_t *timeNanos) {
struct audio_mmap_position position;
@@ -305,12 +315,29 @@
disconnect();
} else {
mFramesRead.update32(position.position_frames);
- *positionFrames = mFramesRead.get();
- *timeNanos = position.time_nanoseconds;
+
+ Timestamp timestamp(mFramesRead.get(), position.time_nanoseconds);
+ mAtomicTimestamp.write(timestamp);
+ *positionFrames = timestamp.getPosition();
+ *timeNanos = timestamp.getNanoseconds();
}
return result;
}
+// Get timestamp that was written by getFreeRunningPosition()
+aaudio_result_t AAudioServiceStreamMMAP::getHardwareTimestamp(int64_t *positionFrames,
+ int64_t *timeNanos) {
+ // TODO Get presentation timestamp from the HAL
+ if (mAtomicTimestamp.isValid()) {
+ Timestamp timestamp = mAtomicTimestamp.read();
+ *positionFrames = timestamp.getPosition();
+ *timeNanos = timestamp.getNanoseconds() + mHardwareTimeOffsetNanos;
+ return AAUDIO_OK;
+ } else {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+}
+
void AAudioServiceStreamMMAP::onTearDown() {
ALOGD("AAudioServiceStreamMMAP::onTearDown() called");
disconnect();
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index 533e5a8..e6f8fad 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -100,6 +100,8 @@
aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) override;
aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+ virtual aaudio_result_t getHardwareTimestamp(int64_t *positionFrames,
+ int64_t *timeNanos) override;
private:
// This proxy class was needed to prevent a crash in AudioFlinger
@@ -132,6 +134,7 @@
MonotonicCounter mFramesRead;
int32_t mPreviousFrameCounter = 0; // from HAL
int mAudioDataFileDescriptor = -1;
+ int64_t mHardwareTimeOffsetNanos = 0; // TODO get from HAL
// Interface to the AudioFlinger MMAP support.
android::sp<android::MmapStreamInterface> mMmapStream;
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 5654113..07c4faf 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -41,6 +41,7 @@
AAudioServiceStreamShared::AAudioServiceStreamShared(AAudioService &audioService)
: mAudioService(audioService)
+ , mTimestampPositionOffset(0)
{
}
@@ -307,15 +308,30 @@
return AAUDIO_OK;
}
-void AAudioServiceStreamShared::markTransferTime(int64_t nanoseconds) {
- mMarkedPosition = mAudioDataQueue->getFifoBuffer()->getReadCounter();
- mMarkedTime = nanoseconds;
+void AAudioServiceStreamShared::markTransferTime(Timestamp ×tamp) {
+ mAtomicTimestamp.write(timestamp);
}
+// Get timestamp that was written by the real-time service thread, eg. mixer.
aaudio_result_t AAudioServiceStreamShared::getFreeRunningPosition(int64_t *positionFrames,
int64_t *timeNanos) {
- // TODO get these two numbers as an atomic pair
- *positionFrames = mMarkedPosition;
- *timeNanos = mMarkedTime;
- return AAUDIO_OK;
+ if (mAtomicTimestamp.isValid()) {
+ Timestamp timestamp = mAtomicTimestamp.read();
+ *positionFrames = timestamp.getPosition();
+ *timeNanos = timestamp.getNanoseconds();
+ return AAUDIO_OK;
+ } else {
+ return AAUDIO_ERROR_UNAVAILABLE;
+ }
+}
+
+// Get timestamp from lower level service.
+aaudio_result_t AAudioServiceStreamShared::getHardwareTimestamp(int64_t *positionFrames,
+ int64_t *timeNanos) {
+
+ aaudio_result_t result = mServiceEndpoint->getTimestamp(positionFrames, timeNanos);
+ if (result == AAUDIO_OK) {
+ *positionFrames -= mTimestampPositionOffset.load(); // Offset from shared MMAP stream
+ }
+ return result;
}
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index 6b67337..8caccda 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -85,7 +85,11 @@
/* Keep a record of when a buffer transfer completed.
* This allows for a more accurate timing model.
*/
- void markTransferTime(int64_t nanoseconds);
+ void markTransferTime(Timestamp ×tamp);
+
+ void setTimestampPositionOffset(int64_t deltaFrames) {
+ mTimestampPositionOffset.store(deltaFrames);
+ }
protected:
@@ -93,6 +97,9 @@
aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+ virtual aaudio_result_t getHardwareTimestamp(int64_t *positionFrames,
+ int64_t *timeNanos) override;
+
/**
* @param requestedCapacityFrames
* @param framesPerBurst
@@ -106,8 +113,7 @@
AAudioServiceEndpoint *mServiceEndpoint = nullptr;
SharedRingBuffer *mAudioDataQueue = nullptr;
- int64_t mMarkedPosition = 0;
- int64_t mMarkedTime = 0;
+ std::atomic<int64_t> mTimestampPositionOffset;
};
} /* namespace aaudio */