AudioTrack: add setBufferSizeInFrames()

Also add getBufferCapacityInFrames().
These can be used to dynamically raise or lower latency.

Bug: 21019153
Signed-off-by: Phil Burk <philburk@google.com>
Change-Id: I02ca7f6f5cc4e089fcd81cc8a2b6ff234e0381a8
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index 5e14940..caf685e 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -855,6 +855,31 @@
     return mPlaybackRate;
 }
 
+ssize_t AudioTrack::getBufferSizeInFrames()
+{
+    AutoMutex lock(mLock);
+    if (mOutput == AUDIO_IO_HANDLE_NONE || mProxy.get() == 0) {
+        return NO_INIT;
+    }
+    return mProxy->getBufferSizeInFrames();
+}
+
+ssize_t AudioTrack::setBufferSizeInFrames(size_t bufferSizeInFrames)
+{
+    AutoMutex lock(mLock);
+    if (mOutput == AUDIO_IO_HANDLE_NONE || mProxy.get() == 0) {
+        return NO_INIT;
+    }
+    // Reject if timed track or compressed audio.
+    if (mIsTimed || !audio_is_linear_pcm(mFormat)) {
+        return INVALID_OPERATION;
+    }
+    // TODO also need to inform the server side (through mAudioTrack) that
+    // the buffer count is reduced, otherwise the track may never start
+    // because the server thinks it is never filled.
+    return mProxy->setBufferSizeInFrames(bufferSizeInFrames);
+}
+
 status_t AudioTrack::setLoop(uint32_t loopStart, uint32_t loopEnd, int loopCount)
 {
     if (mSharedBuffer == 0 || mIsTimed || isOffloadedOrDirect()) {
diff --git a/media/libmedia/AudioTrackShared.cpp b/media/libmedia/AudioTrackShared.cpp
index 9fad500..0d43da8 100644
--- a/media/libmedia/AudioTrackShared.cpp
+++ b/media/libmedia/AudioTrackShared.cpp
@@ -66,7 +66,9 @@
 
 ClientProxy::ClientProxy(audio_track_cblk_t* cblk, void *buffers, size_t frameCount,
         size_t frameSize, bool isOut, bool clientInServer)
-    : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer), mEpoch(0)
+    : Proxy(cblk, buffers, frameCount, frameSize, isOut, clientInServer)
+    , mBufferSizeInFrames(frameCount)
+    , mEpoch(0)
 {
 }
 
@@ -151,6 +153,7 @@
             rear = android_atomic_acquire_load(&cblk->u.mStreaming.mRear);
             front = cblk->u.mStreaming.mFront;
         }
+        // write to rear, read from front
         ssize_t filled = rear - front;
         // pipe should not be overfull
         if (!(0 <= filled && (size_t) filled <= mFrameCount)) {
@@ -166,11 +169,17 @@
             cblk->u.mStreaming.mFront = rear;
             (void) android_atomic_or(CBLK_OVERRUN, &cblk->mFlags);
         }
-        // don't allow filling pipe beyond the nominal size
-        size_t avail = mIsOut ? mFrameCount - filled : filled;
-        if (avail > 0) {
+        // Don't allow filling pipe beyond the user settable size.
+        // The calculation for avail can go negative if the buffer size
+        // is suddenly dropped below the amount already in the buffer.
+        // So use a signed calculation to prevent a numeric overflow abort.
+        ssize_t adjustableSize = (ssize_t) mBufferSizeInFrames;
+        ssize_t avail =  (mIsOut) ? adjustableSize - filled : filled;
+        if (avail < 0) {
+            avail = 0;
+        } else if (avail > 0) {
             // 'avail' may be non-contiguous, so return only the first contiguous chunk
-            size_t part1;
+            ssize_t part1;
             if (mIsOut) {
                 rear &= mFrameCountP2 - 1;
                 part1 = mFrameCountP2 - rear;
@@ -181,10 +190,10 @@
             if (part1 > avail) {
                 part1 = avail;
             }
-            if (part1 > buffer->mFrameCount) {
+            if (part1 > (ssize_t) buffer->mFrameCount) {
                 part1 = buffer->mFrameCount;
             }
-            buffer->mFrameCount = part1;
+            buffer->mFrameCount = (size_t) part1;
             buffer->mRaw = part1 > 0 ?
                     &((char *) mBuffers)[(mIsOut ? rear : front) * mFrameSize] : NULL;
             buffer->mNonContig = avail - part1;
@@ -347,6 +356,20 @@
             (mFrameCountP2 - 1);
 }
 
+size_t ClientProxy::setBufferSizeInFrames(size_t size)
+{
+    // TODO set minimum to 2X the fast mixer buffer size.
+    size_t minimum = 128 * 2; // arbitrary
+    size_t maximum = frameCount();
+    if (size < minimum) {
+        size = minimum;
+    } else if (size > maximum) {
+        size = maximum;
+    }
+    mBufferSizeInFrames = size;
+    return size;
+}
+
 // ---------------------------------------------------------------------------
 
 void AudioTrackClientProxy::flush()