nuplayer: support widevine sources

- handle widevine:// scheme
- add separate looper for renderer (as it can block initial buffer
  handling if all buffers are used)
- initiate secure codecs before source is started
- don't read secure buffers
- share ACodec's input buffers with Widevine source

on the decoder side

- keep track of mediabuffers released by widevine source
- keep track of dequeued input buffers (for safety)
- release mediabuffer when buffer is subsequently dequeued.  (This
  was hardcoded into OMXCodec to do this when buffer-empties message
  was handled, but MediaCodec does not support such functionality.)

Bug: 15699665
Change-Id: I4a369443294e45c644be8b0257010e52db1d7c9b
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 88c59bf..6ccd27a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -36,6 +36,7 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
@@ -221,6 +222,10 @@
                     || strstr(url, ".sdp?"))) {
         source = new RTSPSource(
                 notify, httpService, url, headers, mUIDValid, mUID, true);
+    } else if ((!strncasecmp(url, "widevine://", 11))) {
+        source = new GenericSource(notify, httpService, url, headers,
+                true /* isWidevine */, mUIDValid, mUID);
+        mSourceFlags |= Source::FLAG_SECURE;
     } else {
         source = new GenericSource(notify, httpService, url, headers);
     }
@@ -512,6 +517,17 @@
             mNumFramesDropped = 0;
             mStarted = true;
 
+            /* instantiate decoders now for secure playback */
+            if (mSourceFlags & Source::FLAG_SECURE) {
+                if (mNativeWindow != NULL) {
+                    instantiateDecoder(false, &mVideoDecoder);
+                }
+
+                if (mAudioSink != NULL) {
+                    instantiateDecoder(true, &mAudioDecoder);
+                }
+            }
+
             mSource->start();
 
             uint32_t flags = 0;
@@ -540,7 +556,10 @@
                     new AMessage(kWhatRendererNotify, id()),
                     flags);
 
-            looper()->registerHandler(mRenderer);
+            mRendererLooper = new ALooper;
+            mRendererLooper->setName("NuPlayerRenderer");
+            mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+            mRendererLooper->registerHandler(mRenderer);
 
             postScanSources();
             break;
@@ -1055,6 +1074,10 @@
 
         sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, id());
         mCCDecoder = new CCDecoder(ccNotify);
+
+        if (mSourceFlags & Source::FLAG_SECURE) {
+            format->setInt32("secure", true);
+        }
     }
 
     sp<AMessage> notify =
@@ -1073,6 +1096,28 @@
     (*decoder)->init();
     (*decoder)->configure(format);
 
+    // allocate buffers to decrypt widevine source buffers
+    if (!audio && (mSourceFlags & Source::FLAG_SECURE)) {
+        Vector<sp<ABuffer> > inputBufs;
+        CHECK_EQ((*decoder)->getInputBuffers(&inputBufs), (status_t)OK);
+
+        Vector<MediaBuffer *> mediaBufs;
+        for (size_t i = 0; i < inputBufs.size(); i++) {
+            const sp<ABuffer> &buffer = inputBufs[i];
+            MediaBuffer *mbuf = new MediaBuffer(buffer->data(), buffer->size());
+            mediaBufs.push(mbuf);
+        }
+
+        status_t err = mSource->setBuffers(audio, mediaBufs);
+        if (err != OK) {
+            for (size_t i = 0; i < mediaBufs.size(); ++i) {
+                mediaBufs[i]->release();
+            }
+            mediaBufs.clear();
+            ALOGE("Secure source didn't support secure mediaBufs.");
+            return err;
+        }
+    }
     return OK;
 }
 
@@ -1184,6 +1229,7 @@
 
         dropAccessUnit = false;
         if (!audio
+                && !(mSourceFlags & Source::FLAG_SECURE)
                 && mVideoLateByUs > 100000ll
                 && mVideoIsAVC
                 && !IsAVCReferenceFrame(accessUnit)) {
@@ -1497,6 +1543,13 @@
     ++mScanSourcesGeneration;
     mScanSourcesPending = false;
 
+    if (mRendererLooper != NULL) {
+        if (mRenderer != NULL) {
+            mRendererLooper->unregisterHandler(mRenderer->id());
+        }
+        mRendererLooper->stop();
+        mRendererLooper.clear();
+    }
     mRenderer.clear();
 
     if (mSource != NULL) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index d7c00aa..c04e277 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -125,6 +125,7 @@
     sp<Decoder> mAudioDecoder;
     sp<CCDecoder> mCCDecoder;
     sp<Renderer> mRenderer;
+    sp<ALooper> mRendererLooper;
 
     List<sp<Action> > mDeferredActions;
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index dd73cc4..1b9bafb 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -26,6 +26,7 @@
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
@@ -54,6 +55,22 @@
 NuPlayer::Decoder::~Decoder() {
 }
 
+static
+status_t PostAndAwaitResponse(
+        const sp<AMessage> &msg, sp<AMessage> *response) {
+    status_t err = msg->postAndAwaitResponse(response);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (!(*response)->findInt32("err", &err)) {
+        err = OK;
+    }
+
+    return err;
+}
+
 void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
     CHECK(mCodec == NULL);
 
@@ -72,8 +89,20 @@
     ALOGV("[%s] onConfigure (surface=%p)", mComponentName.c_str(), surface.get());
 
     mCodec = MediaCodec::CreateByType(mCodecLooper, mime.c_str(), false /* encoder */);
+    int32_t secure = 0;
+    if (format->findInt32("secure", &secure) && secure != 0) {
+        if (mCodec != NULL) {
+            mCodec->getName(&mComponentName);
+            mComponentName.append(".secure");
+            mCodec->release();
+            ALOGI("[%s] creating", mComponentName.c_str());
+            mCodec = MediaCodec::CreateByComponentName(
+                    mCodecLooper, mComponentName.c_str());
+        }
+    }
     if (mCodec == NULL) {
-        ALOGE("Failed to create %s decoder", mime.c_str());
+        ALOGE("Failed to create %s%s decoder",
+                (secure ? "secure " : ""), mime.c_str());
         handleError(UNKNOWN_ERROR);
         return;
     }
@@ -107,6 +136,7 @@
 
     // the following should work after start
     CHECK_EQ((status_t)OK, mCodec->getInputBuffers(&mInputBuffers));
+    releaseAndResetMediaBuffers();
     CHECK_EQ((status_t)OK, mCodec->getOutputBuffers(&mOutputBuffers));
     ALOGV("[%s] got %zu input and %zu output buffers",
             mComponentName.c_str(),
@@ -117,6 +147,18 @@
     mPaused = false;
 }
 
+void NuPlayer::Decoder::releaseAndResetMediaBuffers() {
+    for (size_t i = 0; i < mMediaBuffers.size(); i++) {
+        if (mMediaBuffers[i] != NULL) {
+            mMediaBuffers[i]->release();
+            mMediaBuffers.editItemAt(i) = NULL;
+        }
+    }
+    mMediaBuffers.resize(mInputBuffers.size());
+    mInputBufferIsDequeued.clear();
+    mInputBufferIsDequeued.resize(mInputBuffers.size());
+}
+
 void NuPlayer::Decoder::requestCodecNotification() {
     if (mCodec != NULL) {
         sp<AMessage> reply = new AMessage(kWhatCodecNotify, id());
@@ -141,6 +183,14 @@
     msg->post();
 }
 
+status_t NuPlayer::Decoder::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
+    sp<AMessage> msg = new AMessage(kWhatGetInputBuffers, id());
+    msg->setPointer("buffers", buffers);
+
+    sp<AMessage> response;
+    return PostAndAwaitResponse(msg, &response);
+}
+
 void NuPlayer::Decoder::handleError(int32_t err)
 {
     sp<AMessage> notify = mNotify->dup();
@@ -163,6 +213,12 @@
 
     CHECK_LT(bufferIx, mInputBuffers.size());
 
+    if (mMediaBuffers[bufferIx] != NULL) {
+        mMediaBuffers[bufferIx]->release();
+        mMediaBuffers.editItemAt(bufferIx) = NULL;
+    }
+    mInputBufferIsDequeued.editItemAt(bufferIx) = true;
+
     sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
     reply->setSize("buffer-ix", bufferIx);
     reply->setInt32("generation", mBufferGeneration);
@@ -183,6 +239,44 @@
 
     sp<ABuffer> buffer;
     bool hasBuffer = msg->findBuffer("buffer", &buffer);
+
+    // handle widevine classic source - that fills an arbitrary input buffer
+    MediaBuffer *mediaBuffer = NULL;
+    if (hasBuffer && buffer->meta()->findPointer(
+            "mediaBuffer", (void **)&mediaBuffer)) {
+        if (mediaBuffer == NULL) {
+            // received no actual buffer
+            ALOGW("[%s] received null MediaBuffer %s",
+                    mComponentName.c_str(), msg->debugString().c_str());
+            buffer = NULL;
+        } else {
+            // likely filled another buffer than we requested: adjust buffer index
+            size_t ix;
+            for (ix = 0; ix < mInputBuffers.size(); ix++) {
+                const sp<ABuffer> &buf = mInputBuffers[ix];
+                if (buf->data() == mediaBuffer->data()) {
+                    // all input buffers are dequeued on start, hence the check
+                    CHECK(mInputBufferIsDequeued[ix]);
+                    ALOGV("[%s] received MediaBuffer for #%zu instead of #%zu",
+                            mComponentName.c_str(), ix, bufferIx);
+
+                    // TRICKY: need buffer for the metadata, so instead, set
+                    // codecBuffer to the same (though incorrect) buffer to
+                    // avoid a memcpy into the codecBuffer
+                    codecBuffer = buffer;
+                    codecBuffer->setRange(
+                            mediaBuffer->range_offset(),
+                            mediaBuffer->range_length());
+                    bufferIx = ix;
+                    break;
+                }
+            }
+            CHECK(ix < mInputBuffers.size());
+        }
+    }
+
+    mInputBufferIsDequeued.editItemAt(bufferIx) = false;
+
     if (buffer == NULL /* includes !hasBuffer */) {
         int32_t streamErr = ERROR_END_OF_STREAM;
         CHECK(msg->findInt32("err", &streamErr) || !hasBuffer);
@@ -236,6 +330,11 @@
                     mComponentName.c_str(), err);
             handleError(err);
         }
+
+        if (mediaBuffer != NULL) {
+            CHECK(mMediaBuffers[bufferIx] == NULL);
+            mMediaBuffers.editItemAt(bufferIx) = mediaBuffer;
+        }
     }
 }
 
@@ -352,6 +451,8 @@
         return;
     }
 
+    releaseAndResetMediaBuffers();
+
     sp<AMessage> notify = mNotify->dup();
     notify->setInt32("what", kWhatFlushCompleted);
     notify->post();
@@ -379,6 +480,8 @@
         mComponentName = "decoder";
     }
 
+    releaseAndResetMediaBuffers();
+
     if (err != OK) {
         ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err);
         handleError(err);
@@ -403,6 +506,23 @@
             break;
         }
 
+        case kWhatGetInputBuffers:
+        {
+            uint32_t replyID;
+            CHECK(msg->senderAwaitsResponse(&replyID));
+
+            Vector<sp<ABuffer> > *dstBuffers;
+            CHECK(msg->findPointer("buffers", (void **)&dstBuffers));
+
+            dstBuffers->clear();
+            for (size_t i = 0; i < mInputBuffers.size(); i++) {
+                dstBuffers->push(mInputBuffers[i]);
+            }
+
+            (new AMessage)->postReply(replyID);
+            break;
+        }
+
         case kWhatCodecNotify:
         {
             if (!isStaleReply(msg)) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 4fa0dbd..c6fc237 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -26,6 +26,7 @@
 
 struct ABuffer;
 struct MediaCodec;
+struct MediaBuffer;
 
 struct NuPlayer::Decoder : public AHandler {
     Decoder(const sp<AMessage> &notify,
@@ -34,6 +35,7 @@
     virtual void configure(const sp<AMessage> &format);
     virtual void init();
 
+    status_t getInputBuffers(Vector<sp<ABuffer> > *dstBuffers) const;
     virtual void signalFlush();
     virtual void signalResume();
     virtual void initiateShutdown();
@@ -60,6 +62,7 @@
     enum {
         kWhatCodecNotify        = 'cdcN',
         kWhatConfigure          = 'conf',
+        kWhatGetInputBuffers    = 'gInB',
         kWhatInputBufferFilled  = 'inpF',
         kWhatRenderBuffer       = 'rndr',
         kWhatFlush              = 'flus',
@@ -77,11 +80,14 @@
 
     Vector<sp<ABuffer> > mInputBuffers;
     Vector<sp<ABuffer> > mOutputBuffers;
+    Vector<bool> mInputBufferIsDequeued;
+    Vector<MediaBuffer *> mMediaBuffers;
 
     void handleError(int32_t err);
     bool handleAnInputBuffer();
     bool handleAnOutputBuffer();
 
+    void releaseAndResetMediaBuffers();
     void requestCodecNotification();
     bool isStaleReply(const sp<AMessage> &msg);
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index f520ff7..8592ec2 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -26,6 +26,8 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MetaData.h>
 
+#include <inttypes.h>
+
 namespace android {
 
 // static
@@ -502,6 +504,7 @@
         }
     }
 
+    ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs);
     msg->post(delayUs);
 
     mDrainVideoQueuePending = true;