aaudio: lower latency using MMAP capture

MMAP can be enabled by setting system properties.

Bug: 38267780
Test: input_monitor.cpp
Change-Id: I5e86fd1d9baef4fe59837ccbca7971acbb54d8b5
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 57dc58b..e7d9e0d 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -44,43 +44,23 @@
 // This is the maximum size in frames. The effective size can be tuned smaller at runtime.
 #define DEFAULT_BUFFER_CAPACITY   (48 * 8)
 
-// The mStreamInternal will use a service interface that does not go through Binder.
-AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)
-        : mStreamInternal(audioService, true)
-        {
-}
-
-AAudioServiceEndpoint::~AAudioServiceEndpoint() {
-}
-
 // Set up an EXCLUSIVE MMAP stream that will be shared.
-aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId, aaudio_direction_t direction) {
+aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId) {
+    mStreamInternal = getStreamInternal();
+
     AudioStreamBuilder builder;
     builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
     // Don't fall back to SHARED because that would cause recursion.
     builder.setSharingModeMatchRequired(true);
     builder.setDeviceId(deviceId);
-    builder.setDirection(direction);
+    builder.setDirection(getDirection());
     builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
 
-    aaudio_result_t result = mStreamInternal.open(builder);
-    if (result == AAUDIO_OK) {
-        mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
-
-        int32_t burstsPerBuffer = AAudioProperty_getMixerBursts();
-        if (burstsPerBuffer == 0) {
-            mLatencyTuningEnabled = true;
-            burstsPerBuffer = 2;
-        }
-        ALOGD("AAudioServiceEndpoint(): burstsPerBuffer = %d", burstsPerBuffer);
-        int32_t desiredBufferSize = burstsPerBuffer * mStreamInternal.getFramesPerBurst();
-        mStreamInternal.setBufferSize(desiredBufferSize);
-    }
-    return result;
+    return getStreamInternal()->open(builder);
 }
 
 aaudio_result_t AAudioServiceEndpoint::close() {
-    return mStreamInternal.close();
+    return getStreamInternal()->close();
 }
 
 // TODO, maybe use an interface to reduce exposure
@@ -102,96 +82,51 @@
     std::lock_guard<std::mutex> lock(mLockStreams);
     mRunningStreams.push_back(sharedStream);
     if (mRunningStreams.size() == 1) {
-        startMixer_l();
+        startSharingThread_l();
     }
     return AAUDIO_OK;
 }
 
 aaudio_result_t AAudioServiceEndpoint::stopStream(AAudioServiceStreamShared *sharedStream) {
-    std::lock_guard<std::mutex> lock(mLockStreams);
-    mRunningStreams.erase(std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
-              mRunningStreams.end());
-    if (mRunningStreams.size() == 0) {
-        stopMixer_l();
+    int numRunningStreams = 0;
+    {
+        std::lock_guard<std::mutex> lock(mLockStreams);
+        mRunningStreams.erase(
+                std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
+                mRunningStreams.end());
+        numRunningStreams = mRunningStreams.size();
+    }
+    if (numRunningStreams == 0) {
+        // Don't call this under a lock because the callbackLoop also uses the lock.
+        stopSharingThread();
     }
     return AAUDIO_OK;
 }
 
-static void *aaudio_mixer_thread_proc(void *context) {
-    AAudioServiceEndpoint *stream = (AAudioServiceEndpoint *) context;
-    if (stream != NULL) {
-        return stream->callbackLoop();
+static void *aaudio_endpoint_thread_proc(void *context) {
+    AAudioServiceEndpoint *endpoint = (AAudioServiceEndpoint *) context;
+    if (endpoint != NULL) {
+        return endpoint->callbackLoop();
     } else {
         return NULL;
     }
 }
 
-// Render audio in the application callback and then write the data to the stream.
-void *AAudioServiceEndpoint::callbackLoop() {
-    ALOGD("AAudioServiceEndpoint(): callbackLoop() entering");
-    int32_t underflowCount = 0;
-
-    aaudio_result_t result = mStreamInternal.requestStart();
-
-    // result might be a frame count
-    while (mCallbackEnabled.load() && mStreamInternal.isPlaying() && (result >= 0)) {
-        // Mix data from each active stream.
-        {
-            mMixer.clear();
-            std::lock_guard<std::mutex> lock(mLockStreams);
-            for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
-                FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
-                const float volume = 1.0f; // to match the perceived volume from AudioTrack
-                bool underflowed = mMixer.mix(fifo, volume);
-                underflowCount += underflowed ? 1 : 0;
-                // TODO log underflows in each stream
-                sharedStream->markTransferTime(AudioClock::getNanoseconds());
-            }
-        }
-
-        // Write audio data to stream using a blocking write.
-        int64_t timeoutNanos = calculateReasonableTimeout(mStreamInternal.getFramesPerBurst());
-        result = mStreamInternal.write(mMixer.getOutputBuffer(), getFramesPerBurst(), timeoutNanos);
-        if (result == AAUDIO_ERROR_DISCONNECTED) {
-            disconnectRegisteredStreams();
-            break;
-        } else if (result != getFramesPerBurst()) {
-            ALOGW("AAudioServiceEndpoint(): callbackLoop() wrote %d / %d",
-                  result, getFramesPerBurst());
-            break;
-        }
-    }
-
-    result = mStreamInternal.requestStop();
-
-    ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, %d underflows", underflowCount);
-    return NULL; // TODO review
-}
-
-aaudio_result_t AAudioServiceEndpoint::startMixer_l() {
+aaudio_result_t AAudioServiceEndpoint::startSharingThread_l() {
     // Launch the callback loop thread.
-    int64_t periodNanos = mStreamInternal.getFramesPerBurst()
+    int64_t periodNanos = getStreamInternal()->getFramesPerBurst()
                           * AAUDIO_NANOS_PER_SECOND
                           / getSampleRate();
     mCallbackEnabled.store(true);
-    return mStreamInternal.createThread(periodNanos, aaudio_mixer_thread_proc, this);
+    return getStreamInternal()->createThread(periodNanos, aaudio_endpoint_thread_proc, this);
 }
 
-aaudio_result_t AAudioServiceEndpoint::stopMixer_l() {
+aaudio_result_t AAudioServiceEndpoint::stopSharingThread() {
+    ALOGD("AAudioServiceEndpoint(): call joinThread()");
     mCallbackEnabled.store(false);
-    return mStreamInternal.joinThread(NULL, calculateReasonableTimeout(mStreamInternal.getFramesPerBurst()));
-}
-
-// TODO Call method in AudioStreamInternal when that callback CL is merged.
-int64_t AAudioServiceEndpoint::calculateReasonableTimeout(int32_t framesPerOperation) {
-
-    // Wait for at least a second or some number of callbacks to join the thread.
-    int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS * framesPerOperation * AAUDIO_NANOS_PER_SECOND)
-                                 / getSampleRate();
-    if (timeoutNanoseconds < MIN_TIMEOUT_NANOS) { // arbitrary number of seconds
-        timeoutNanoseconds = MIN_TIMEOUT_NANOS;
-    }
-    return timeoutNanoseconds;
+    aaudio_result_t result = getStreamInternal()->joinThread(NULL);
+    ALOGD("AAudioServiceEndpoint(): joinThread() returned %d", result);
+    return result;
 }
 
 void AAudioServiceEndpoint::disconnectRegisteredStreams() {