Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09524837 authored by Lajos Molnar's avatar Lajos Molnar
Browse files

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
parent cc227036
Loading
Loading
Loading
Loading
+54 −1
Original line number Diff line number Diff line
@@ -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 @@ void NuPlayer::setDataSourceAsync(
                    || 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 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
            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 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
                    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 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) {

        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 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) {
    (*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 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {

        dropAccessUnit = false;
        if (!audio
                && !(mSourceFlags & Source::FLAG_SECURE)
                && mVideoLateByUs > 100000ll
                && mVideoIsAVC
                && !IsAVCReferenceFrame(accessUnit)) {
@@ -1497,6 +1543,13 @@ void NuPlayer::performReset() {
    ++mScanSourcesGeneration;
    mScanSourcesPending = false;

    if (mRendererLooper != NULL) {
        if (mRenderer != NULL) {
            mRendererLooper->unregisterHandler(mRenderer->id());
        }
        mRendererLooper->stop();
        mRendererLooper.clear();
    }
    mRenderer.clear();

    if (mSource != NULL) {
+1 −0
Original line number Diff line number Diff line
@@ -125,6 +125,7 @@ private:
    sp<Decoder> mAudioDecoder;
    sp<CCDecoder> mCCDecoder;
    sp<Renderer> mRenderer;
    sp<ALooper> mRendererLooper;

    List<sp<Action> > mDeferredActions;

+121 −1
Original line number Diff line number Diff line
@@ -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(
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 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
    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 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {

    // 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 @@ void NuPlayer::Decoder::onConfigure(const sp<AMessage> &format) {
    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 @@ void NuPlayer::Decoder::configure(const sp<AMessage> &format) {
    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 @@ bool NuPlayer::Decoder::handleAnInputBuffer() {

    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 @@ void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) {

    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 @@ void android::NuPlayer::Decoder::onInputBufferFilled(const sp<AMessage> &msg) {
                    mComponentName.c_str(), err);
            handleError(err);
        }

        if (mediaBuffer != NULL) {
            CHECK(mMediaBuffers[bufferIx] == NULL);
            mMediaBuffers.editItemAt(bufferIx) = mediaBuffer;
        }
    }
}

@@ -352,6 +451,8 @@ void NuPlayer::Decoder::onFlush() {
        return;
    }

    releaseAndResetMediaBuffers();

    sp<AMessage> notify = mNotify->dup();
    notify->setInt32("what", kWhatFlushCompleted);
    notify->post();
@@ -379,6 +480,8 @@ void NuPlayer::Decoder::onShutdown() {
        mComponentName = "decoder";
    }

    releaseAndResetMediaBuffers();

    if (err != OK) {
        ALOGE("failed to release %s (err=%d)", mComponentName.c_str(), err);
        handleError(err);
@@ -403,6 +506,23 @@ void NuPlayer::Decoder::onMessageReceived(const sp<AMessage> &msg) {
            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)) {
+6 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ namespace android {

struct ABuffer;
struct MediaCodec;
struct MediaBuffer;

struct NuPlayer::Decoder : public AHandler {
    Decoder(const sp<AMessage> &notify,
@@ -34,6 +35,7 @@ struct NuPlayer::Decoder : public AHandler {
    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 @@ private:
    enum {
        kWhatCodecNotify        = 'cdcN',
        kWhatConfigure          = 'conf',
        kWhatGetInputBuffers    = 'gInB',
        kWhatInputBufferFilled  = 'inpF',
        kWhatRenderBuffer       = 'rndr',
        kWhatFlush              = 'flus',
@@ -77,11 +80,14 @@ private:

    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);

+3 −0
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>

#include <inttypes.h>

namespace android {

// static
@@ -502,6 +504,7 @@ void NuPlayer::Renderer::postDrainVideoQueue() {
        }
    }

    ALOGW_IF(delayUs > 500000, "unusually high delayUs: %" PRId64, delayUs);
    msg->post(delayUs);

    mDrainVideoQueuePending = true;