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/include/media/AudioTrack.h b/include/media/AudioTrack.h
index fe4611c..69831bc 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -320,6 +320,25 @@
             uint32_t    channelCount() const { return mChannelCount; }
             size_t      frameCount() const  { return mFrameCount; }
 
+    /* Return effective size of audio buffer that an application writes to
+     * or a negative error if the track is uninitialized.
+     */
+            ssize_t     getBufferSizeInFrames();
+
+    /* Set the effective size of audio buffer that an application writes to.
+     * This is used to determine the amount of available room in the buffer,
+     * which determines when a write will block.
+     * This allows an application to raise and lower the audio latency.
+     * The requested size may be adjusted so that it is
+     * greater or equal to the absolute minimum and
+     * less than or equal to the getBufferCapacityInFrames().
+     * It may also be adjusted slightly for internal reasons.
+     *
+     * Return the final size or a negative error if the track is unitialized
+     * or does not support variable sizes.
+     */
+            ssize_t     setBufferSizeInFrames(size_t size);
+
     /* Return the static buffer specified in constructor or set(), or 0 for streaming mode */
             sp<IMemory> sharedBuffer() const { return mSharedBuffer; }
 
@@ -817,8 +836,11 @@
     mutable uint32_t        mSampleRate;            // mutable because getSampleRate() can update it
     uint32_t                mOriginalSampleRate;
     AudioPlaybackRate       mPlaybackRate;
-    size_t                  mFrameCount;            // corresponds to current IAudioTrack, value is
-                                                    // reported back by AudioFlinger to the client
+
+    // Corresponds to current IAudioTrack, value is reported back by AudioFlinger to the client.
+    // This allocated buffer size is maintained by the proxy.
+    size_t                  mFrameCount;            // maximum size of buffer
+
     size_t                  mReqFrameCount;         // frame count to request the first or next time
                                                     // a new IAudioTrack is needed, non-decreasing
 
diff --git a/include/private/media/AudioTrackShared.h b/include/private/media/AudioTrackShared.h
index e458f3c..68d27bb 100644
--- a/include/private/media/AudioTrackShared.h
+++ b/include/private/media/AudioTrackShared.h
@@ -204,6 +204,8 @@
         size_t  mNonContig;             // number of additional non-contiguous frames available
     };
 
+    size_t frameCount() const { return mFrameCount; }
+
 protected:
     // These refer to shared memory, and are virtual addresses with respect to the current process.
     // They may have different virtual addresses within the other process.
@@ -285,7 +287,7 @@
         return mEpoch + mCblk->mServer;
     }
 
-    void             setEpoch(const Modulo<uint32_t> &epoch) {
+    void        setEpoch(const Modulo<uint32_t> &epoch) {
         mEpoch = epoch;
     }
 
@@ -305,6 +307,15 @@
         return mEpoch;
     }
 
+    size_t      getBufferSizeInFrames() const { return mBufferSizeInFrames; }
+    // See documentation for AudioTrack.setBufferSizeInFrames()
+    size_t      setBufferSizeInFrames(size_t requestedSize);
+
+protected:
+    // This is set by AudioTrack.setBufferSizeInFrames().
+    // A write will not fill the buffer above this limit.
+    size_t      mBufferSizeInFrames;      // effective size of the buffer
+
 private:
     Modulo<uint32_t> mEpoch;
 };
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()