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 &timestamp) {
+    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 &timestamp);
+
+    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 */