NuPlayer now properly sends MEDIA_SET_VIDEOSIZE notifications.

Change-Id: I99b4223ad6ecfd8839a3c0e737fef3165565d76d
related-to-bug: 3336496
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index b3815c4..4599d70 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -101,6 +101,8 @@
 
     List<sp<AMessage> > mDeferredQueue;
 
+    bool mSentFormat;
+
     status_t allocateBuffersOnPort(OMX_U32 portIndex);
     status_t freeBuffersOnPort(OMX_U32 portIndex);
     status_t freeBuffer(OMX_U32 portIndex, size_t i);
@@ -145,6 +147,8 @@
     void deferMessage(const sp<AMessage> &msg);
     void processDeferredMessages();
 
+    void sendFormatChange();
+
     DISALLOW_EVIL_CONSTRUCTORS(ACodec);
 };
 
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 91ec60c..72dc730 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -52,6 +52,10 @@
     void setObject(const char *name, const sp<RefBase> &obj);
     void setMessage(const char *name, const sp<AMessage> &obj);
 
+    void setRect(
+            const char *name,
+            int32_t left, int32_t top, int32_t right, int32_t bottom);
+
     bool findInt32(const char *name, int32_t *value) const;
     bool findInt64(const char *name, int64_t *value) const;
     bool findSize(const char *name, size_t *value) const;
@@ -62,6 +66,10 @@
     bool findObject(const char *name, sp<RefBase> *obj) const;
     bool findMessage(const char *name, sp<AMessage> *obj) const;
 
+    bool findRect(
+            const char *name,
+            int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const;
+
     void post(int64_t delayUs = 0);
 
     // Performs a deep-copy of "this", contained messages are in turn "dup'ed".
@@ -85,11 +93,16 @@
         kTypeString,
         kTypeObject,
         kTypeMessage,
+        kTypeRect,
     };
 
     uint32_t mWhat;
     ALooper::handler_id mTarget;
 
+    struct Rect {
+        int32_t mLeft, mTop, mRight, mBottom;
+    };
+
     struct Item {
         union {
             int32_t int32Value;
@@ -100,6 +113,7 @@
             void *ptrValue;
             RefBase *refValue;
             AString *stringValue;
+            Rect rectValue;
         } u;
         const char *mName;
         Type mType;
diff --git a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
index 4d4cd8d..802d1fb 100644
--- a/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
+++ b/media/libmediaplayerservice/nuplayer/DecoderWrapper.cpp
@@ -309,6 +309,18 @@
 
         realNotify->setInt32("width", width);
         realNotify->setInt32("height", height);
+
+        int32_t cropLeft, cropTop, cropRight, cropBottom;
+        if (!meta->findRect(
+                    kKeyCropRect,
+                    &cropLeft, &cropTop, &cropRight, &cropBottom)) {
+            cropLeft = 0;
+            cropTop = 0;
+            cropRight = width - 1;
+            cropBottom = height - 1;
+        }
+
+        realNotify->setRect("crop", cropLeft, cropTop, cropRight, cropBottom);
     }
 
     notify->post();
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index e1b371e..7f534c0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -271,22 +271,43 @@
 
                 finishFlushIfPossible();
             } else if (what == ACodec::kWhatOutputFormatChanged) {
-                CHECK(audio);
+                if (audio) {
+                    int32_t numChannels;
+                    CHECK(codecRequest->findInt32("channel-count", &numChannels));
 
-                int32_t numChannels;
-                CHECK(codecRequest->findInt32("channel-count", &numChannels));
+                    int32_t sampleRate;
+                    CHECK(codecRequest->findInt32("sample-rate", &sampleRate));
 
-                int32_t sampleRate;
-                CHECK(codecRequest->findInt32("sample-rate", &sampleRate));
+                    LOGV("Audio output format changed to %d Hz, %d channels",
+                         sampleRate, numChannels);
 
-                LOGV("Audio output format changed to %d Hz, %d channels",
-                     sampleRate, numChannels);
+                    mAudioSink->close();
+                    CHECK_EQ(mAudioSink->open(sampleRate, numChannels), (status_t)OK);
+                    mAudioSink->start();
 
-                mAudioSink->close();
-                CHECK_EQ(mAudioSink->open(sampleRate, numChannels), (status_t)OK);
-                mAudioSink->start();
+                    mRenderer->signalAudioSinkChanged();
+                } else {
+                    // video
 
-                mRenderer->signalAudioSinkChanged();
+                    int32_t width, height;
+                    CHECK(codecRequest->findInt32("width", &width));
+                    CHECK(codecRequest->findInt32("height", &height));
+
+                    int32_t cropLeft, cropTop, cropRight, cropBottom;
+                    CHECK(codecRequest->findRect(
+                                "crop",
+                                &cropLeft, &cropTop, &cropRight, &cropBottom));
+
+                    LOGV("Video output format changed to %d x %d "
+                         "(crop: %d, %d, %d, %d)",
+                         width, height,
+                         cropLeft, cropTop, cropRight, cropBottom);
+
+                    notifyListener(
+                            MEDIA_SET_VIDEO_SIZE,
+                            cropRight - cropLeft + 1,
+                            cropBottom - cropTop + 1);
+                }
             } else if (what == ACodec::kWhatShutdownCompleted) {
                 LOGV("%s shutdown completed", audio ? "audio" : "video");
                 if (audio) {
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 39e0c51..dfb4e00 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -299,7 +299,8 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 ACodec::ACodec()
-    : mNode(NULL) {
+    : mNode(NULL),
+      mSentFormat(false) {
     mUninitializedState = new UninitializedState(this);
     mLoadedToIdleState = new LoadedToIdleState(this);
     mIdleToExecutingState = new IdleToExecutingState(this);
@@ -980,6 +981,103 @@
     }
 }
 
+void ACodec::sendFormatChange() {
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatOutputFormatChanged);
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    InitOMXParams(&def);
+    def.nPortIndex = kPortIndexOutput;
+
+    CHECK_EQ(mOMX->getParameter(
+                mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)),
+             (status_t)OK);
+
+    CHECK_EQ((int)def.eDir, (int)OMX_DirOutput);
+
+    switch (def.eDomain) {
+        case OMX_PortDomainVideo:
+        {
+            OMX_VIDEO_PORTDEFINITIONTYPE *videoDef = &def.format.video;
+
+            notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW);
+            notify->setInt32("width", videoDef->nFrameWidth);
+            notify->setInt32("height", videoDef->nFrameHeight);
+
+            OMX_CONFIG_RECTTYPE rect;
+            InitOMXParams(&rect);
+            rect.nPortIndex = kPortIndexOutput;
+
+            if (mOMX->getConfig(
+                        mNode, OMX_IndexConfigCommonOutputCrop,
+                        &rect, sizeof(rect)) != OK) {
+                rect.nLeft = 0;
+                rect.nTop = 0;
+                rect.nWidth = videoDef->nFrameWidth;
+                rect.nHeight = videoDef->nFrameHeight;
+            }
+
+            CHECK_GE(rect.nLeft, 0);
+            CHECK_GE(rect.nTop, 0);
+            CHECK_GE(rect.nWidth, 0u);
+            CHECK_GE(rect.nHeight, 0u);
+            CHECK_LE(rect.nLeft + rect.nWidth - 1, videoDef->nFrameWidth);
+            CHECK_LE(rect.nTop + rect.nHeight - 1, videoDef->nFrameHeight);
+
+            notify->setRect(
+                    "crop",
+                    rect.nLeft,
+                    rect.nTop,
+                    rect.nLeft + rect.nWidth - 1,
+                    rect.nTop + rect.nHeight - 1);
+
+            if (mNativeWindow != NULL) {
+                android_native_rect_t crop;
+                crop.left = rect.nLeft;
+                crop.top = rect.nTop;
+                crop.right = rect.nLeft + rect.nWidth - 1;
+                crop.bottom = rect.nTop + rect.nHeight - 1;
+
+                CHECK_EQ(0, native_window_set_crop(
+                            mNativeWindow.get(), &crop));
+            }
+            break;
+        }
+
+        case OMX_PortDomainAudio:
+        {
+            OMX_AUDIO_PORTDEFINITIONTYPE *audioDef = &def.format.audio;
+            CHECK_EQ((int)audioDef->eEncoding, (int)OMX_AUDIO_CodingPCM);
+
+            OMX_AUDIO_PARAM_PCMMODETYPE params;
+            InitOMXParams(&params);
+            params.nPortIndex = kPortIndexOutput;
+
+            CHECK_EQ(mOMX->getParameter(
+                        mNode, OMX_IndexParamAudioPcm,
+                        &params, sizeof(params)),
+                     (status_t)OK);
+
+            CHECK(params.nChannels == 1 || params.bInterleaved);
+            CHECK_EQ(params.nBitPerSample, 16u);
+            CHECK_EQ((int)params.eNumData, (int)OMX_NumericalDataSigned);
+            CHECK_EQ((int)params.ePCMMode, (int)OMX_AUDIO_PCMModeLinear);
+
+            notify->setString("mime", MEDIA_MIMETYPE_AUDIO_RAW);
+            notify->setInt32("channel-count", params.nChannels);
+            notify->setInt32("sample-rate", params.nSamplingRate);
+            break;
+        }
+
+        default:
+            TRESPASS();
+    }
+
+    notify->post();
+
+    mSentFormat = true;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 ACodec::BaseState::BaseState(ACodec *codec, const sp<AState> &parentState)
@@ -1305,6 +1403,10 @@
                     info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
                 }
             } else {
+                if (!mCodec->mSentFormat) {
+                    mCodec->sendFormatChange();
+                }
+
                 if (mCodec->mNativeWindow == NULL) {
                     info->mData->setRange(rangeOffset, rangeLength);
                 }
@@ -1717,7 +1819,7 @@
         {
             CHECK_EQ(data1, (OMX_U32)kPortIndexOutput);
 
-            if (data2 == OMX_IndexParamPortDefinition) {
+            if (data2 == 0 || data2 == OMX_IndexParamPortDefinition) {
                 CHECK_EQ(mCodec->mOMX->sendCommand(
                             mCodec->mNode,
                             OMX_CommandPortDisable, kPortIndexOutput),
@@ -1729,6 +1831,8 @@
                 }
 
                 mCodec->changeState(mCodec->mOutputPortSettingsChangedState);
+            } else if (data2 == OMX_IndexConfigCommonOutputCrop) {
+                mCodec->mSentFormat = false;
             } else {
                 LOGV("[%s] OMX_EventPortSettingsChanged 0x%08lx",
                      mCodec->mComponentName.c_str(), data2);
@@ -1816,6 +1920,8 @@
             } else if (data1 == (OMX_U32)OMX_CommandPortEnable) {
                 CHECK_EQ(data2, (OMX_U32)kPortIndexOutput);
 
+                mCodec->mSentFormat = false;
+
                 LOGV("[%s] Output port now reenabled.",
                         mCodec->mComponentName.c_str());
 
@@ -1869,6 +1975,8 @@
 
 void ACodec::ExecutingToIdleState::stateEntered() {
     LOGV("[%s] Now Executing->Idle", mCodec->mComponentName.c_str());
+
+    mCodec->mSentFormat = false;
 }
 
 bool ACodec::ExecutingToIdleState::onOMXEvent(
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 0e40acc..b592c3f 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -171,6 +171,18 @@
     item->u.refValue = obj.get();
 }
 
+void AMessage::setRect(
+        const char *name,
+        int32_t left, int32_t top, int32_t right, int32_t bottom) {
+    Item *item = allocateItem(name);
+    item->mType = kTypeRect;
+
+    item->u.rectValue.mLeft = left;
+    item->u.rectValue.mTop = top;
+    item->u.rectValue.mRight = right;
+    item->u.rectValue.mBottom = bottom;
+}
+
 bool AMessage::findString(const char *name, AString *value) const {
     const Item *item = findItem(name, kTypeString);
     if (item) {
@@ -198,6 +210,22 @@
     return false;
 }
 
+bool AMessage::findRect(
+        const char *name,
+        int32_t *left, int32_t *top, int32_t *right, int32_t *bottom) const {
+    const Item *item = findItem(name, kTypeRect);
+    if (item == NULL) {
+        return false;
+    }
+
+    *left = item->u.rectValue.mLeft;
+    *top = item->u.rectValue.mTop;
+    *right = item->u.rectValue.mRight;
+    *bottom = item->u.rectValue.mBottom;
+
+    return true;
+}
+
 void AMessage::post(int64_t delayUs) {
     extern ALooperRoster gLooperRoster;