Merge "Bug 3229711"
diff --git a/drm/drmserver/DrmManager.cpp b/drm/drmserver/DrmManager.cpp
index 13c7454..ef7d274 100644
--- a/drm/drmserver/DrmManager.cpp
+++ b/drm/drmserver/DrmManager.cpp
@@ -117,7 +117,11 @@
 status_t DrmManager::setDrmServiceListener(
             int uniqueId, const sp<IDrmServiceListener>& drmServiceListener) {
     Mutex::Autolock _l(mLock);
-    mServiceListeners.add(uniqueId, drmServiceListener);
+    if (NULL != drmServiceListener.get()) {
+        mServiceListeners.add(uniqueId, drmServiceListener);
+    } else {
+        mServiceListeners.removeItem(uniqueId);
+    }
     return DRM_NO_ERROR;
 }
 
diff --git a/drm/libdrmframework/DrmManagerClient.cpp b/drm/libdrmframework/DrmManagerClient.cpp
index 578e135..7b51822 100644
--- a/drm/libdrmframework/DrmManagerClient.cpp
+++ b/drm/libdrmframework/DrmManagerClient.cpp
@@ -31,6 +31,7 @@
 DrmManagerClient::~DrmManagerClient() {
     DrmManagerClientImpl::remove(mUniqueId);
     mDrmManagerClientImpl->removeClient(mUniqueId);
+    mDrmManagerClientImpl->setOnInfoListener(mUniqueId, NULL);
     delete mDrmManagerClientImpl; mDrmManagerClientImpl = NULL;
 }
 
diff --git a/drm/libdrmframework/DrmManagerClientImpl.cpp b/drm/libdrmframework/DrmManagerClientImpl.cpp
index f39131d..d20de92 100644
--- a/drm/libdrmframework/DrmManagerClientImpl.cpp
+++ b/drm/libdrmframework/DrmManagerClientImpl.cpp
@@ -81,7 +81,8 @@
             int uniqueId, const sp<DrmManagerClient::OnInfoListener>& infoListener) {
     Mutex::Autolock _l(mLock);
     mOnInfoListener = infoListener;
-    return getDrmManagerService()->setDrmServiceListener(uniqueId, this);
+    return getDrmManagerService()->setDrmServiceListener(uniqueId,
+            (NULL != infoListener.get()) ? this : NULL);
 }
 
 status_t DrmManagerClientImpl::installDrmEngine(int uniqueId, const String8& drmEngineFile) {
diff --git a/include/drm/DrmManagerClient.h b/include/drm/DrmManagerClient.h
index e6ba3c4..085ebf1 100644
--- a/include/drm/DrmManagerClient.h
+++ b/include/drm/DrmManagerClient.h
@@ -49,6 +49,9 @@
     class OnInfoListener: virtual public RefBase {
 
     public:
+        virtual ~OnInfoListener() {}
+
+    public:
         virtual void onInfo(const DrmInfoEvent& event) = 0;
     };
 
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index a098d69..87c8fe4 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -607,7 +607,9 @@
     case MEDIA_INFO:
         // ext1: Media framework error code.
         // ext2: Implementation dependant error code.
-        LOGW("info/warning (%d, %d)", ext1, ext2);
+        if (ext1 != MEDIA_INFO_VIDEO_TRACK_LAGGING) {
+            LOGW("info/warning (%d, %d)", ext1, ext2);
+        }
         break;
     case MEDIA_SEEK_COMPLETE:
         LOGV("Received seek complete");
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 914e409..4cfe28e 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -179,6 +179,8 @@
     mStreamDoneEventPending = false;
     mBufferingEvent = new AwesomeEvent(this, &AwesomePlayer::onBufferingUpdate);
     mBufferingEventPending = false;
+    mVideoLagEvent = new AwesomeEvent(this, &AwesomePlayer::onVideoLagUpdate);
+    mVideoEventPending = false;
 
     mCheckAudioStatusEvent = new AwesomeEvent(
             this, &AwesomePlayer::onCheckAudioStatus);
@@ -205,6 +207,8 @@
     mStreamDoneEventPending = false;
     mQueue.cancelEvent(mCheckAudioStatusEvent->eventID());
     mAudioStatusEventPending = false;
+    mQueue.cancelEvent(mVideoLagEvent->eventID());
+    mVideoLagEventPending = false;
 
     if (!keepBufferingGoing) {
         mQueue.cancelEvent(mBufferingEvent->eventID());
@@ -530,6 +534,28 @@
     }
 }
 
+void AwesomePlayer::onVideoLagUpdate() {
+    Mutex::Autolock autoLock(mLock);
+    if (!mVideoLagEventPending) {
+        return;
+    }
+    mVideoLagEventPending = false;
+
+    int64_t audioTimeUs = mAudioPlayer->getMediaTimeUs();
+    int64_t videoLateByUs = audioTimeUs - mVideoTimeUs;
+
+    if (videoLateByUs > 300000ll) {
+        LOGV("video late by %lld ms.", videoLateByUs / 1000ll);
+
+        notifyListener_l(
+                MEDIA_INFO,
+                MEDIA_INFO_VIDEO_TRACK_LAGGING,
+                videoLateByUs / 1000ll);
+    }
+
+    postVideoLagEvent_l();
+}
+
 void AwesomePlayer::onBufferingUpdate() {
     Mutex::Autolock autoLock(mLock);
     if (!mBufferingEventPending) {
@@ -788,6 +814,10 @@
     if (mVideoSource != NULL) {
         // Kick off video playback
         postVideoEvent_l();
+
+        if (mAudioSource != NULL && mVideoSource != NULL) {
+            postVideoLagEvent_l();
+        }
     }
 
     if (deferredAudioSeek) {
@@ -1347,6 +1377,14 @@
     mQueue.postEventWithDelay(mBufferingEvent, 1000000ll);
 }
 
+void AwesomePlayer::postVideoLagEvent_l() {
+    if (mVideoLagEventPending) {
+        return;
+    }
+    mVideoLagEventPending = true;
+    mQueue.postEventWithDelay(mVideoLagEvent, 1000000ll);
+}
+
 void AwesomePlayer::postCheckAudioStatusEvent_l() {
     if (mAudioStatusEventPending) {
         return;
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 110fb03..cdf4270 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -348,7 +348,7 @@
 ssize_t NuCachedSource2::readAt(off64_t offset, void *data, size_t size) {
     Mutex::Autolock autoSerializer(mSerializer);
 
-    LOGV("readAt offset %ld, size %d", offset, size);
+    LOGV("readAt offset %lld, size %d", offset, size);
 
     Mutex::Autolock autoLock(mLock);
 
@@ -408,7 +408,7 @@
 }
 
 ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) {
-    LOGV("readInternal offset %ld size %d", offset, size);
+    LOGV("readInternal offset %lld size %d", offset, size);
 
     Mutex::Autolock autoLock(mLock);
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 7750a9d..e516cb4 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -3923,6 +3923,8 @@
 
             if (!mIsEncoder) {
                 OMX_CONFIG_RECTTYPE rect;
+                InitOMXParams(&rect);
+                rect.nPortIndex = kPortIndexOutput;
                 status_t err =
                         mOMX->getConfig(
                             mNode, OMX_IndexConfigCommonOutputCrop,
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 17b83c1..130ad82 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -173,6 +173,8 @@
     bool mBufferingEventPending;
     sp<TimedEventQueue::Event> mCheckAudioStatusEvent;
     bool mAudioStatusEventPending;
+    sp<TimedEventQueue::Event> mVideoLagEvent;
+    bool mVideoLagEventPending;
 
     sp<TimedEventQueue::Event> mAsyncPrepareEvent;
     Condition mPreparedCondition;
@@ -184,6 +186,7 @@
     void postBufferingEvent_l();
     void postStreamDoneEvent_l(status_t status);
     void postCheckAudioStatusEvent_l();
+    void postVideoLagEvent_l();
     status_t play_l();
 
     MediaBuffer *mVideoBuffer;
@@ -233,6 +236,7 @@
     void onPrepareAsyncEvent();
     void abortPrepare(status_t err);
     void finishAsyncPrepare_l();
+    void onVideoLagUpdate();
 
     bool getCachedDuration_l(int64_t *durationUs, bool *eos);
 
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index 78719c1..aa320fc 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -55,8 +55,8 @@
 
     enum {
         kPageSize            = 65536,
-        kHighWaterThreshold  = 5 * 1024 * 1024,
-        kLowWaterThreshold   = 1024 * 1024,
+        kHighWaterThreshold  = 20 * 1024 * 1024,
+        kLowWaterThreshold   = 4 * 1024 * 1024,
 
         // Read data after a 15 sec timeout whether we're actively
         // fetching or not.
diff --git a/media/libstagefright/matroska/MatroskaExtractor.cpp b/media/libstagefright/matroska/MatroskaExtractor.cpp
index e0ac49f..eca9ed6 100644
--- a/media/libstagefright/matroska/MatroskaExtractor.cpp
+++ b/media/libstagefright/matroska/MatroskaExtractor.cpp
@@ -118,6 +118,9 @@
     virtual status_t read(
             MediaBuffer **buffer, const ReadOptions *options);
 
+protected:
+    virtual ~MatroskaSource();
+
 private:
     enum Type {
         AVC,
@@ -131,8 +134,13 @@
     BlockIterator mBlockIter;
     size_t mNALSizeLen;  // for type AVC
 
+    List<MediaBuffer *> mPendingFrames;
+
     status_t advance();
 
+    status_t readBlock();
+    void clearPendingFrames();
+
     MatroskaSource(const MatroskaSource &);
     MatroskaSource &operator=(const MatroskaSource &);
 };
@@ -168,6 +176,10 @@
     }
 }
 
+MatroskaSource::~MatroskaSource() {
+    clearPendingFrames();
+}
+
 status_t MatroskaSource::start(MetaData *params) {
     mBlockIter.reset();
 
@@ -175,6 +187,8 @@
 }
 
 status_t MatroskaSource::stop() {
+    clearPendingFrames();
+
     return OK;
 }
 
@@ -256,6 +270,214 @@
     return ptr[0] << 16 | ptr[1] << 8 | ptr[2];
 }
 
+static size_t clz(uint8_t x) {
+    size_t numLeadingZeroes = 0;
+
+    while (!(x & 0x80)) {
+        ++numLeadingZeroes;
+        x = x << 1;
+    }
+
+    return numLeadingZeroes;
+}
+
+void MatroskaSource::clearPendingFrames() {
+    while (!mPendingFrames.empty()) {
+        MediaBuffer *frame = *mPendingFrames.begin();
+        mPendingFrames.erase(mPendingFrames.begin());
+
+        frame->release();
+        frame = NULL;
+    }
+}
+
+#define BAIL(err) \
+    do {                        \
+        if (bigbuf) {           \
+            bigbuf->release();  \
+            bigbuf = NULL;      \
+        }                       \
+                                \
+        return err;             \
+    } while (0)
+
+status_t MatroskaSource::readBlock() {
+    CHECK(mPendingFrames.empty());
+
+    if (mBlockIter.eos()) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    const mkvparser::Block *block = mBlockIter.block();
+
+    size_t size = block->GetSize();
+    int64_t timeUs = mBlockIter.blockTimeUs();
+    int32_t isSync = block->IsKey();
+
+    MediaBuffer *bigbuf = new MediaBuffer(size);
+
+    long res = block->Read(
+            mExtractor->mReader, (unsigned char *)bigbuf->data());
+
+    if (res != 0) {
+        bigbuf->release();
+        bigbuf = NULL;
+
+        return ERROR_END_OF_STREAM;
+    }
+
+    mBlockIter.advance();
+
+    bigbuf->meta_data()->setInt64(kKeyTime, timeUs);
+    bigbuf->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
+
+    unsigned lacing = (block->Flags() >> 1) & 3;
+
+    if (lacing == 0) {
+        mPendingFrames.push_back(bigbuf);
+        return OK;
+    }
+
+    LOGV("lacing = %u, size = %d", lacing, size);
+
+    const uint8_t *data = (const uint8_t *)bigbuf->data();
+    // hexdump(data, size);
+
+    if (size == 0) {
+        BAIL(ERROR_MALFORMED);
+    }
+
+    unsigned numFrames = (unsigned)data[0] + 1;
+    ++data;
+    --size;
+
+    Vector<uint64_t> frameSizes;
+
+    switch (lacing) {
+        case 1:  // Xiph
+        {
+            for (size_t i = 0; i < numFrames - 1; ++i) {
+                size_t frameSize = 0;
+                uint8_t byte;
+                do {
+                    if (size == 0) {
+                        BAIL(ERROR_MALFORMED);
+                    }
+                    byte = data[0];
+                    ++data;
+                    --size;
+
+                    frameSize += byte;
+                } while (byte == 0xff);
+
+                frameSizes.push(frameSize);
+            }
+
+            break;
+        }
+
+        case 2:  // fixed-size
+        {
+            if ((size % numFrames) != 0) {
+                BAIL(ERROR_MALFORMED);
+            }
+
+            size_t frameSize = size / numFrames;
+            for (size_t i = 0; i < numFrames - 1; ++i) {
+                frameSizes.push(frameSize);
+            }
+
+            break;
+        }
+
+        case 3:  // EBML
+        {
+            uint64_t lastFrameSize = 0;
+            for (size_t i = 0; i < numFrames - 1; ++i) {
+                uint8_t byte;
+
+                if (size == 0) {
+                    BAIL(ERROR_MALFORMED);
+                }
+                byte = data[0];
+                ++data;
+                --size;
+
+                size_t numLeadingZeroes = clz(byte);
+
+                uint64_t frameSize = byte & ~(0x80 >> numLeadingZeroes);
+                for (size_t j = 0; j < numLeadingZeroes; ++j) {
+                    if (size == 0) {
+                        BAIL(ERROR_MALFORMED);
+                    }
+
+                    frameSize = frameSize << 8;
+                    frameSize |= data[0];
+                    ++data;
+                    --size;
+                }
+
+                if (i == 0) {
+                    frameSizes.push(frameSize);
+                } else {
+                    size_t shift =
+                        7 - numLeadingZeroes + 8 * numLeadingZeroes;
+
+                    int64_t delta =
+                        (int64_t)frameSize - (1ll << (shift - 1)) + 1;
+
+                    frameSize = lastFrameSize + delta;
+
+                    frameSizes.push(frameSize);
+                }
+
+                lastFrameSize = frameSize;
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+
+#if 0
+    AString out;
+    for (size_t i = 0; i < frameSizes.size(); ++i) {
+        if (i > 0) {
+            out.append(", ");
+        }
+        out.append(StringPrintf("%llu", frameSizes.itemAt(i)));
+    }
+    LOGV("sizes = [%s]", out.c_str());
+#endif
+
+    for (size_t i = 0; i < frameSizes.size(); ++i) {
+        uint64_t frameSize = frameSizes.itemAt(i);
+
+        if (size < frameSize) {
+            BAIL(ERROR_MALFORMED);
+        }
+
+        MediaBuffer *mbuf = new MediaBuffer(frameSize);
+        mbuf->meta_data()->setInt64(kKeyTime, timeUs);
+        mbuf->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
+        memcpy(mbuf->data(), data, frameSize);
+        mPendingFrames.push_back(mbuf);
+
+        data += frameSize;
+        size -= frameSize;
+    }
+
+    size_t offset = bigbuf->range_length() - size;
+    bigbuf->set_range(offset, size);
+
+    mPendingFrames.push_back(bigbuf);
+
+    return OK;
+}
+
+#undef BAIL
+
 status_t MatroskaSource::read(
         MediaBuffer **out, const ReadOptions *options) {
     *out = NULL;
@@ -263,17 +485,38 @@
     int64_t seekTimeUs;
     ReadOptions::SeekMode mode;
     if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+        clearPendingFrames();
         mBlockIter.seek(seekTimeUs);
     }
 
 again:
-    if (mBlockIter.eos()) {
-        return ERROR_END_OF_STREAM;
+    while (mPendingFrames.empty()) {
+        status_t err = readBlock();
+
+        if (err != OK) {
+            clearPendingFrames();
+
+            return err;
+        }
     }
 
-    const mkvparser::Block *block = mBlockIter.block();
-    size_t size = block->GetSize();
-    int64_t timeUs = mBlockIter.blockTimeUs();
+    MediaBuffer *frame = *mPendingFrames.begin();
+    mPendingFrames.erase(mPendingFrames.begin());
+
+    size_t size = frame->range_length();
+
+    if (mType != AVC) {
+        *out = frame;
+
+        return OK;
+    }
+
+    if (size < mNALSizeLen) {
+        frame->release();
+        frame = NULL;
+
+        return ERROR_MALFORMED;
+    }
 
     // In the case of AVC content, each NAL unit is prefixed by
     // mNALSizeLen bytes of length. We want to prefix the data with
@@ -283,73 +526,54 @@
     static const size_t kPadding = 3;
 
     MediaBuffer *buffer = new MediaBuffer(size + kPadding);
+
+    int64_t timeUs;
+    CHECK(frame->meta_data()->findInt64(kKeyTime, &timeUs));
+    int32_t isSync;
+    CHECK(frame->meta_data()->findInt32(kKeyIsSyncFrame, &isSync));
+
     buffer->meta_data()->setInt64(kKeyTime, timeUs);
-    buffer->meta_data()->setInt32(kKeyIsSyncFrame, block->IsKey());
+    buffer->meta_data()->setInt32(kKeyIsSyncFrame, isSync);
 
-    long res = block->Read(
-            mExtractor->mReader, (unsigned char *)buffer->data() + kPadding);
-
-    if (res != 0) {
-        return ERROR_END_OF_STREAM;
-    }
+    memcpy((uint8_t *)buffer->data() + kPadding,
+           (const uint8_t *)frame->data() + frame->range_offset(),
+           size);
 
     buffer->set_range(kPadding, size);
 
-    if (mType == AVC) {
-        CHECK_GE(size, mNALSizeLen);
+    frame->release();
+    frame = NULL;
 
-        uint8_t *data = (uint8_t *)buffer->data();
+    uint8_t *data = (uint8_t *)buffer->data();
 
-        size_t NALsize;
-        switch (mNALSizeLen) {
-            case 1: NALsize = data[kPadding]; break;
-            case 2: NALsize = U16_AT(&data[kPadding]); break;
-            case 3: NALsize = U24_AT(&data[kPadding]); break;
-            case 4: NALsize = U32_AT(&data[kPadding]); break;
-            default:
-                TRESPASS();
-        }
-
-        CHECK_GE(size, NALsize + mNALSizeLen);
-        if (size > NALsize + mNALSizeLen) {
-            LOGW("discarding %d bytes of data.", size - NALsize - mNALSizeLen);
-        }
-
-        // actual data starts at &data[kPadding + mNALSizeLen]
-
-        memcpy(&data[mNALSizeLen - 1], "\x00\x00\x00\x01", 4);
-        buffer->set_range(mNALSizeLen - 1, NALsize + 4);
-    } else if (mType == AAC) {
-        // There's strange junk at the beginning...
-
-        const uint8_t *data = (const uint8_t *)buffer->data() + kPadding;
-
-        // hexdump(data, size);
-
-        size_t offset = 0;
-        while (offset < size && data[offset] != 0x21) {
-            ++offset;
-        }
-
-        if (size == offset) {
-            buffer->release();
-
-            mBlockIter.advance();
-            goto again;
-        }
-
-        buffer->set_range(kPadding + offset, size - offset);
+    size_t NALsize;
+    switch (mNALSizeLen) {
+        case 1: NALsize = data[kPadding]; break;
+        case 2: NALsize = U16_AT(&data[kPadding]); break;
+        case 3: NALsize = U24_AT(&data[kPadding]); break;
+        case 4: NALsize = U32_AT(&data[kPadding]); break;
+        default:
+            TRESPASS();
     }
 
+    if (size < NALsize + mNALSizeLen) {
+        buffer->release();
+        buffer = NULL;
+
+        return ERROR_MALFORMED;
+    }
+
+    if (size > NALsize + mNALSizeLen) {
+        LOGW("discarding %d bytes of data.", size - NALsize - mNALSizeLen);
+    }
+
+    // actual data starts at &data[kPadding + mNALSizeLen]
+
+    memcpy(&data[mNALSizeLen - 1], "\x00\x00\x00\x01", 4);
+    buffer->set_range(mNALSizeLen - 1, NALsize + 4);
+
     *out = buffer;
 
-#if 0
-    hexdump((const uint8_t *)buffer->data() + buffer->range_offset(),
-            buffer->range_length());
-#endif
-
-    mBlockIter.advance();
-
     return OK;
 }
 
diff --git a/media/libstagefright/matroska/mkvparser.cpp b/media/libstagefright/matroska/mkvparser.cpp
index 455b1d6..7448d96 100644
--- a/media/libstagefright/matroska/mkvparser.cpp
+++ b/media/libstagefright/matroska/mkvparser.cpp
@@ -4474,6 +4474,9 @@
     return ((m_flags & static_cast<unsigned char>(1 << 7)) != 0);

 }

 

+unsigned char Block::Flags() const {

+    return m_flags;

+}

 

 void Block::SetKey(bool bKey)

 {

diff --git a/media/libstagefright/matroska/mkvparser.hpp b/media/libstagefright/matroska/mkvparser.hpp
index c46d349..f7d8948 100644
--- a/media/libstagefright/matroska/mkvparser.hpp
+++ b/media/libstagefright/matroska/mkvparser.hpp
@@ -80,6 +80,8 @@
     bool IsKey() const;

     void SetKey(bool);

 

+    unsigned char Flags() const;

+

     long long GetOffset() const;

     long GetSize() const;

     long Read(IMkvReader*, unsigned char*) const;