Implementation of stagefright-enabled MediaPlayerService::decode functionality

related-to-bug: 2359268
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 8e61011..55b06f4 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1597,9 +1597,12 @@
     AudioOutput *me = (AudioOutput *)cookie;
     AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
 
-    (*me->mCallback)(
+    size_t actualSize = (*me->mCallback)(
             me, buffer->raw, buffer->size, me->mCallbackCookie);
-    me->snoopWrite(buffer->raw, buffer->size);
+
+    if (actualSize > 0) {
+        me->snoopWrite(buffer->raw, actualSize);
+    }
 }
 
 #undef LOG_TAG
@@ -1629,14 +1632,75 @@
     return NO_ERROR;
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
+struct CallbackThread : public Thread {
+    CallbackThread(const wp<MediaPlayerBase::AudioSink> &sink,
+                   MediaPlayerBase::AudioSink::AudioCallback cb,
+                   void *cookie);
+
+protected:
+    virtual ~CallbackThread();
+
+    virtual bool threadLoop();
+
+private:
+    wp<MediaPlayerBase::AudioSink> mSink;
+    MediaPlayerBase::AudioSink::AudioCallback mCallback;
+    void *mCookie;
+    void *mBuffer;
+    size_t mBufferSize;
+
+    CallbackThread(const CallbackThread &);
+    CallbackThread &operator=(const CallbackThread &);
+};
+
+CallbackThread::CallbackThread(
+        const wp<MediaPlayerBase::AudioSink> &sink,
+        MediaPlayerBase::AudioSink::AudioCallback cb,
+        void *cookie)
+    : mSink(sink),
+      mCallback(cb),
+      mCookie(cookie),
+      mBuffer(NULL),
+      mBufferSize(0) {
+}
+
+CallbackThread::~CallbackThread() {
+    if (mBuffer) {
+        free(mBuffer);
+        mBuffer = NULL;
+    }
+}
+
+bool CallbackThread::threadLoop() {
+    sp<MediaPlayerBase::AudioSink> sink = mSink.promote();
+    if (sink == NULL) {
+        return false;
+    }
+
+    if (mBuffer == NULL) {
+        mBufferSize = sink->bufferSize();
+        mBuffer = malloc(mBufferSize);
+    }
+
+    size_t actualSize =
+        (*mCallback)(sink.get(), mBuffer, mBufferSize, mCookie);
+
+    if (actualSize > 0) {
+        sink->write(mBuffer, actualSize);
+    }
+
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
 status_t MediaPlayerService::AudioCache::open(
         uint32_t sampleRate, int channelCount, int format, int bufferCount,
         AudioCallback cb, void *cookie)
 {
     LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount);
-    if (cb != NULL) {
-        return UNKNOWN_ERROR;  // TODO: implement this.
-    }
     if (mHeap->getHeapID() < 0) {
         return NO_INIT;
     }
@@ -1645,9 +1709,25 @@
     mChannelCount = (uint16_t)channelCount;
     mFormat = (uint16_t)format;
     mMsecsPerFrame = 1.e3 / (float) sampleRate;
+
+    if (cb != NULL) {
+        mCallbackThread = new CallbackThread(this, cb, cookie);
+    }
     return NO_ERROR;
 }
 
+void MediaPlayerService::AudioCache::start() {
+    if (mCallbackThread != NULL) {
+        mCallbackThread->run("AudioCache callback");
+    }
+}
+
+void MediaPlayerService::AudioCache::stop() {
+    if (mCallbackThread != NULL) {
+        mCallbackThread->requestExitAndWait();
+    }
+}
+
 ssize_t MediaPlayerService::AudioCache::write(const void* buffer, size_t size)
 {
     LOGV("write(%p, %u)", buffer, size);