Merge "Cache audio attributes when player not available" into lmp-dev
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index f257ef3..8e1987a 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -32,6 +32,7 @@
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
 #include "../../libstagefright/include/DRMExtractor.h"
 #include "../../libstagefright/include/NuCachedSource2.h"
 #include "../../libstagefright/include/WVMExtractor.h"
@@ -318,7 +319,14 @@
     }
 
     if (mVideoTrack.mSource != NULL) {
-        notifyVideoSizeChanged(getFormat(false /* audio */));
+        sp<MetaData> meta = doGetFormatMeta(false /* audio */);
+        sp<AMessage> msg = new AMessage;
+        err = convertMetaDataToMessage(meta, &msg);
+        if(err != OK) {
+            notifyPreparedAndCleanup(err);
+            return;
+        }
+        notifyVideoSizeChanged(msg);
     }
 
     notifyFlagsChanged(
@@ -422,7 +430,7 @@
         mAudioTrack.mPackets =
             new AnotherPacketSource(mAudioTrack.mSource->getFormat());
 
-        readBuffer(MEDIA_TRACK_TYPE_AUDIO);
+        postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
     }
 
     if (mVideoTrack.mSource != NULL) {
@@ -430,7 +438,7 @@
         mVideoTrack.mPackets =
             new AnotherPacketSource(mVideoTrack.mSource->getFormat());
 
-        readBuffer(MEDIA_TRACK_TYPE_VIDEO);
+        postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
     }
 
     setDrmPlaybackStatusIfNeeded(Playback::START, getLastReadPosition() / 1000);
@@ -459,6 +467,8 @@
     if (mDecryptHandle != NULL) {
         mDrmManagerClient->setPlaybackStatus(mDecryptHandle, playbackStatus, position);
     }
+    mSubtitleTrack.mPackets = new AnotherPacketSource(NULL);
+    mTimedTextTrack.mPackets = new AnotherPacketSource(NULL);
 }
 
 status_t NuPlayer::GenericSource::feedMoreTSData() {
@@ -615,6 +625,37 @@
           }
           break;
       }
+
+      case kWhatGetFormat:
+      {
+          onGetFormatMeta(msg);
+          break;
+      }
+
+      case kWhatGetSelectedTrack:
+      {
+          onGetSelectedTrack(msg);
+          break;
+      }
+
+      case kWhatSelectTrack:
+      {
+          onSelectTrack(msg);
+          break;
+      }
+
+      case kWhatSeek:
+      {
+          onSeek(msg);
+          break;
+      }
+
+      case kWhatReadBuffer:
+      {
+          onReadBuffer(msg);
+          break;
+      }
+
       default:
           Source::onMessageReceived(msg);
           break;
@@ -690,6 +731,34 @@
 }
 
 sp<MetaData> NuPlayer::GenericSource::getFormatMeta(bool audio) {
+    sp<AMessage> msg = new AMessage(kWhatGetFormat, id());
+    msg->setInt32("audio", audio);
+
+    sp<AMessage> response;
+    void *format;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findPointer("format", &format));
+        return (MetaData *)format;
+    } else {
+        return NULL;
+    }
+}
+
+void NuPlayer::GenericSource::onGetFormatMeta(sp<AMessage> msg) const {
+    int32_t audio;
+    CHECK(msg->findInt32("audio", &audio));
+
+    sp<AMessage> response = new AMessage;
+    sp<MetaData> format = doGetFormatMeta(audio);
+    response->setPointer("format", format.get());
+
+    uint32_t replyID;
+    CHECK(msg->senderAwaitsResponse(&replyID));
+    response->postReply(replyID);
+}
+
+sp<MetaData> NuPlayer::GenericSource::doGetFormatMeta(bool audio) const {
     sp<MediaSource> source = audio ? mAudioTrack.mSource : mVideoTrack.mSource;
 
     if (source == NULL) {
@@ -709,7 +778,7 @@
 
     if (mIsWidevine && !audio) {
         // try to read a buffer as we may not have been able to the last time
-        readBuffer(MEDIA_TRACK_TYPE_VIDEO, -1ll);
+        postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
     }
 
     status_t finalResult;
@@ -720,18 +789,7 @@
     status_t result = track->mPackets->dequeueAccessUnit(accessUnit);
 
     if (!track->mPackets->hasBufferAvailable(&finalResult)) {
-        readBuffer(audio? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO, -1ll);
-    }
-
-    if (mSubtitleTrack.mSource == NULL && mTimedTextTrack.mSource == NULL) {
-        return result;
-    }
-
-    if (mSubtitleTrack.mSource != NULL) {
-        CHECK(mSubtitleTrack.mPackets != NULL);
-    }
-    if (mTimedTextTrack.mSource != NULL) {
-        CHECK(mTimedTextTrack.mPackets != NULL);
+        postReadBuffer(audio? MEDIA_TRACK_TYPE_AUDIO : MEDIA_TRACK_TYPE_VIDEO);
     }
 
     if (result != OK) {
@@ -825,6 +883,35 @@
 }
 
 ssize_t NuPlayer::GenericSource::getSelectedTrack(media_track_type type) const {
+    sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id());
+    msg->setInt32("type", type);
+
+    sp<AMessage> response;
+    int32_t index;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("index", &index));
+        return index;
+    } else {
+        return -1;
+    }
+}
+
+void NuPlayer::GenericSource::onGetSelectedTrack(sp<AMessage> msg) const {
+    int32_t tmpType;
+    CHECK(msg->findInt32("type", &tmpType));
+    media_track_type type = (media_track_type)tmpType;
+
+    sp<AMessage> response = new AMessage;
+    ssize_t index = doGetSelectedTrack(type);
+    response->setInt32("index", index);
+
+    uint32_t replyID;
+    CHECK(msg->senderAwaitsResponse(&replyID));
+    response->postReply(replyID);
+}
+
+ssize_t NuPlayer::GenericSource::doGetSelectedTrack(media_track_type type) const {
     const Track *track = NULL;
     switch (type) {
     case MEDIA_TRACK_TYPE_VIDEO:
@@ -852,6 +939,34 @@
 
 status_t NuPlayer::GenericSource::selectTrack(size_t trackIndex, bool select) {
     ALOGV("%s track: %zu", select ? "select" : "deselect", trackIndex);
+    sp<AMessage> msg = new AMessage(kWhatSelectTrack, id());
+    msg->setInt32("trackIndex", trackIndex);
+    msg->setInt32("select", trackIndex);
+
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("err", &err));
+    }
+
+    return err;
+}
+
+void NuPlayer::GenericSource::onSelectTrack(sp<AMessage> msg) {
+    int32_t trackIndex, select;
+    CHECK(msg->findInt32("trackIndex", &trackIndex));
+    CHECK(msg->findInt32("select", &select));
+
+    sp<AMessage> response = new AMessage;
+    status_t err = doSelectTrack(trackIndex, select);
+    response->setInt32("err", err);
+
+    uint32_t replyID;
+    CHECK(msg->senderAwaitsResponse(&replyID));
+    response->postReply(replyID);
+}
+
+status_t NuPlayer::GenericSource::doSelectTrack(size_t trackIndex, bool select) {
     if (trackIndex >= mSources.size()) {
         return BAD_INDEX;
     }
@@ -922,6 +1037,32 @@
 }
 
 status_t NuPlayer::GenericSource::seekTo(int64_t seekTimeUs) {
+    sp<AMessage> msg = new AMessage(kWhatSeek, id());
+    msg->setInt64("seekTimeUs", seekTimeUs);
+
+    sp<AMessage> response;
+    status_t err = msg->postAndAwaitResponse(&response);
+    if (err == OK && response != NULL) {
+        CHECK(response->findInt32("err", &err));
+    }
+
+    return err;
+}
+
+void NuPlayer::GenericSource::onSeek(sp<AMessage> msg) {
+    int64_t seekTimeUs;
+    CHECK(msg->findInt64("seekTimeUs", &seekTimeUs));
+
+    sp<AMessage> response = new AMessage;
+    status_t err = doSeek(seekTimeUs);
+    response->setInt32("err", err);
+
+    uint32_t replyID;
+    CHECK(msg->senderAwaitsResponse(&replyID));
+    response->postReply(replyID);
+}
+
+status_t NuPlayer::GenericSource::doSeek(int64_t seekTimeUs) {
     if (mVideoTrack.mSource != NULL) {
         int64_t actualTimeUs;
         readBuffer(MEDIA_TRACK_TYPE_VIDEO, seekTimeUs, &actualTimeUs);
@@ -1006,6 +1147,19 @@
     return ab;
 }
 
+void NuPlayer::GenericSource::postReadBuffer(media_track_type trackType) {
+    sp<AMessage> msg = new AMessage(kWhatReadBuffer, id());
+    msg->setInt32("trackType", trackType);
+    msg->post();
+}
+
+void NuPlayer::GenericSource::onReadBuffer(sp<AMessage> msg) {
+    int32_t tmpType;
+    CHECK(msg->findInt32("trackType", &tmpType));
+    media_track_type trackType = (media_track_type)tmpType;
+    readBuffer(trackType);
+}
+
 void NuPlayer::GenericSource::readBuffer(
         media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) {
     Track *track;
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 1f13120..50ff98a 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -84,6 +84,11 @@
         kWhatSendTimedTextData,
         kWhatChangeAVSource,
         kWhatPollBuffering,
+        kWhatGetFormat,
+        kWhatGetSelectedTrack,
+        kWhatSelectTrack,
+        kWhatSeek,
+        kWhatReadBuffer,
     };
 
     Vector<sp<MediaSource> > mSources;
@@ -140,6 +145,18 @@
 
     void notifyPreparedAndCleanup(status_t err);
 
+    void onGetFormatMeta(sp<AMessage> msg) const;
+    sp<MetaData> doGetFormatMeta(bool audio) const;
+
+    void onGetSelectedTrack(sp<AMessage> msg) const;
+    ssize_t doGetSelectedTrack(media_track_type type) const;
+
+    void onSelectTrack(sp<AMessage> msg);
+    status_t doSelectTrack(size_t trackIndex, bool select);
+
+    void onSeek(sp<AMessage> msg);
+    status_t doSeek(int64_t seekTimeUs);
+
     void onPrepareAsync();
 
     void fetchTextData(
@@ -155,6 +172,8 @@
             media_track_type trackType,
             int64_t *actualTimeUs = NULL);
 
+    void postReadBuffer(media_track_type trackType);
+    void onReadBuffer(sp<AMessage> msg);
     void readBuffer(
             media_track_type trackType,
             int64_t seekTimeUs = -1ll, int64_t *actualTimeUs = NULL, bool formatChange = false);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index ae22123..4a5d18a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -541,7 +541,13 @@
                         static_cast<NativeWindowWrapper *>(obj.get())));
 
             if (obj != NULL) {
-                mDeferredActions.push_back(new SeekAction(mCurrentPositionUs));
+                if (mStarted && mVideoDecoder != NULL) {
+                    // Issue a seek to refresh the video screen only if started otherwise
+                    // the extractor may not yet be started and will assert.
+                    // If the video decoder is not set (perhaps audio only in this case)
+                    // do not perform a seek as it is not needed.
+                    mDeferredActions.push_back(new SeekAction(mCurrentPositionUs));
+                }
 
                 // If there is a new surface texture, instantiate decoders
                 // again if possible.
@@ -1540,6 +1546,10 @@
         ALOGE_IF(mFlushingVideo != NONE,
                 "video flushDecoder() is called in state %d", mFlushingVideo);
         mFlushingVideo = newStatus;
+
+        if (mCCDecoder != NULL) {
+            mCCDecoder->flush();
+        }
     }
 }
 
@@ -1665,6 +1675,14 @@
           seekTimeUs,
           seekTimeUs / 1E6);
 
+    if (mSource == NULL) {
+        // This happens when reset occurs right before the loop mode
+        // asynchronously seeks to the start of the stream.
+        LOG_ALWAYS_FATAL_IF(mAudioDecoder != NULL || mVideoDecoder != NULL,
+                "mSource is NULL and decoders not NULL audio(%p) video(%p)",
+                mAudioDecoder.get(), mVideoDecoder.get());
+        return;
+    }
     mSource->seekTo(seekTimeUs);
     ++mTimedTextGeneration;
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 5aaf48c..8ce7baf 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -716,72 +716,28 @@
     return seamless;
 }
 
-struct NuPlayer::CCDecoder::CCData {
+struct CCData {
     CCData(uint8_t type, uint8_t data1, uint8_t data2)
         : mType(type), mData1(data1), mData2(data2) {
     }
+    bool getChannel(size_t *channel) const {
+        if (mData1 >= 0x10 && mData1 <= 0x1f) {
+            *channel = (mData1 >= 0x18 ? 1 : 0) + (mType ? 2 : 0);
+            return true;
+        }
+        return false;
+    }
 
     uint8_t mType;
     uint8_t mData1;
     uint8_t mData2;
 };
 
-NuPlayer::CCDecoder::CCDecoder(const sp<AMessage> &notify)
-    : mNotify(notify),
-      mTrackCount(0),
-      mSelectedTrack(-1) {
-}
-
-size_t NuPlayer::CCDecoder::getTrackCount() const {
-    return mTrackCount;
-}
-
-sp<AMessage> NuPlayer::CCDecoder::getTrackInfo(size_t index) const {
-    CHECK(index == 0);
-
-    sp<AMessage> format = new AMessage();
-
-    format->setInt32("type", MEDIA_TRACK_TYPE_SUBTITLE);
-    format->setString("language", "und");
-    format->setString("mime", MEDIA_MIMETYPE_TEXT_CEA_608);
-    format->setInt32("auto", 1);
-    format->setInt32("default", 1);
-    format->setInt32("forced", 0);
-
-    return format;
-}
-
-status_t NuPlayer::CCDecoder::selectTrack(size_t index, bool select) {
-    CHECK(index < mTrackCount);
-
-    if (select) {
-        if (mSelectedTrack == (ssize_t)index) {
-            ALOGE("track %zu already selected", index);
-            return BAD_VALUE;
-        }
-        ALOGV("selected track %zu", index);
-        mSelectedTrack = index;
-    } else {
-        if (mSelectedTrack != (ssize_t)index) {
-            ALOGE("track %zu is not selected", index);
-            return BAD_VALUE;
-        }
-        ALOGV("unselected track %zu", index);
-        mSelectedTrack = -1;
-    }
-
-    return OK;
-}
-
-bool NuPlayer::CCDecoder::isSelected() const {
-    return mSelectedTrack >= 0 && mSelectedTrack < (int32_t)mTrackCount;
-}
-
-bool NuPlayer::CCDecoder::isNullPad(CCData *cc) const {
+static bool isNullPad(CCData *cc) {
     return cc->mData1 < 0x10 && cc->mData2 < 0x10;
 }
 
-void NuPlayer::CCDecoder::dumpBytePair(const sp<ABuffer> &ccBuf) const {
+static void dumpBytePair(const sp<ABuffer> &ccBuf) {
     size_t offset = 0;
     AString out;
 
@@ -843,6 +799,78 @@
     ALOGI("%s", out.c_str());
 }
 
+NuPlayer::CCDecoder::CCDecoder(const sp<AMessage> &notify)
+    : mNotify(notify),
+      mCurrentChannel(0),
+      mSelectedTrack(-1) {
+      for (size_t i = 0; i < sizeof(mTrackIndices)/sizeof(mTrackIndices[0]); ++i) {
+          mTrackIndices[i] = -1;
+      }
+}
+
+size_t NuPlayer::CCDecoder::getTrackCount() const {
+    return mFoundChannels.size();
+}
+
+sp<AMessage> NuPlayer::CCDecoder::getTrackInfo(size_t index) const {
+    if (!isTrackValid(index)) {
+        return NULL;
+    }
+
+    sp<AMessage> format = new AMessage();
+
+    format->setInt32("type", MEDIA_TRACK_TYPE_SUBTITLE);
+    format->setString("language", "und");
+    format->setString("mime", MEDIA_MIMETYPE_TEXT_CEA_608);
+    //CC1, field 0 channel 0
+    bool isDefaultAuto = (mFoundChannels[index] == 0);
+    format->setInt32("auto", isDefaultAuto);
+    format->setInt32("default", isDefaultAuto);
+    format->setInt32("forced", 0);
+
+    return format;
+}
+
+status_t NuPlayer::CCDecoder::selectTrack(size_t index, bool select) {
+    if (!isTrackValid(index)) {
+        return BAD_VALUE;
+    }
+
+    if (select) {
+        if (mSelectedTrack == (ssize_t)index) {
+            ALOGE("track %zu already selected", index);
+            return BAD_VALUE;
+        }
+        ALOGV("selected track %zu", index);
+        mSelectedTrack = index;
+    } else {
+        if (mSelectedTrack != (ssize_t)index) {
+            ALOGE("track %zu is not selected", index);
+            return BAD_VALUE;
+        }
+        ALOGV("unselected track %zu", index);
+        mSelectedTrack = -1;
+    }
+
+    return OK;
+}
+
+bool NuPlayer::CCDecoder::isSelected() const {
+    return mSelectedTrack >= 0 && mSelectedTrack < (int32_t) getTrackCount();
+}
+
+bool NuPlayer::CCDecoder::isTrackValid(size_t index) const {
+    return index < getTrackCount();
+}
+
+int32_t NuPlayer::CCDecoder::getTrackIndex(size_t channel) const {
+    if (channel < sizeof(mTrackIndices)/sizeof(mTrackIndices[0])) {
+        return mTrackIndices[channel];
+    }
+    return -1;
+}
+
+// returns true if a new CC track is found
 bool NuPlayer::CCDecoder::extractFromSEI(const sp<ABuffer> &accessUnit) {
     int64_t timeUs;
     CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
@@ -852,7 +880,7 @@
         return false;
     }
 
-    bool hasCC = false;
+    bool trackAdded = false;
 
     NALBitReader br(sei->data() + 1, sei->size() - 1);
     // sei_message()
@@ -887,8 +915,6 @@
                     && itu_t_t35_provider_code == 0x0031
                     && user_identifier == 'GA94'
                     && user_data_type_code == 0x3) {
-                hasCC = true;
-
                 // MPEG_cc_data()
                 // ATSC A/53 Part 4: 6.2.3.1
                 br.skipBits(1); //process_em_data_flag
@@ -918,6 +944,12 @@
                                 && (cc_type == 0 || cc_type == 1)) {
                             CCData cc(cc_type, cc_data_1, cc_data_2);
                             if (!isNullPad(&cc)) {
+                                size_t channel;
+                                if (cc.getChannel(&channel) && getTrackIndex(channel) < 0) {
+                                    mTrackIndices[channel] = mFoundChannels.size();
+                                    mFoundChannels.push_back(channel);
+                                    trackAdded = true;
+                                }
                                 memcpy(ccBuf->data() + ccBuf->size(),
                                         (void *)&cc, sizeof(cc));
                                 ccBuf->setRange(0, ccBuf->size() + sizeof(CCData));
@@ -940,13 +972,33 @@
         br.skipBits(payload_size * 8);
     }
 
-    return hasCC;
+    return trackAdded;
+}
+
+sp<ABuffer> NuPlayer::CCDecoder::filterCCBuf(
+        const sp<ABuffer> &ccBuf, size_t index) {
+    sp<ABuffer> filteredCCBuf = new ABuffer(ccBuf->size());
+    filteredCCBuf->setRange(0, 0);
+
+    size_t cc_count = ccBuf->size() / sizeof(CCData);
+    const CCData* cc_data = (const CCData*)ccBuf->data();
+    for (size_t i = 0; i < cc_count; ++i) {
+        size_t channel;
+        if (cc_data[i].getChannel(&channel)) {
+            mCurrentChannel = channel;
+        }
+        if (mCurrentChannel == mFoundChannels[index]) {
+            memcpy(filteredCCBuf->data() + filteredCCBuf->size(),
+                    (void *)&cc_data[i], sizeof(CCData));
+            filteredCCBuf->setRange(0, filteredCCBuf->size() + sizeof(CCData));
+        }
+    }
+
+    return filteredCCBuf;
 }
 
 void NuPlayer::CCDecoder::decode(const sp<ABuffer> &accessUnit) {
-    if (extractFromSEI(accessUnit) && mTrackCount == 0) {
-        mTrackCount++;
-
+    if (extractFromSEI(accessUnit)) {
         ALOGI("Found CEA-608 track");
         sp<AMessage> msg = mNotify->dup();
         msg->setInt32("what", kWhatTrackAdded);
@@ -956,13 +1008,18 @@
 }
 
 void NuPlayer::CCDecoder::display(int64_t timeUs) {
+    if (!isTrackValid(mSelectedTrack)) {
+        ALOGE("Could not find current track(index=%d)", mSelectedTrack);
+        return;
+    }
+
     ssize_t index = mCCMap.indexOfKey(timeUs);
     if (index < 0) {
         ALOGV("cc for timestamp %" PRId64 " not found", timeUs);
         return;
     }
 
-    sp<ABuffer> &ccBuf = mCCMap.editValueAt(index);
+    sp<ABuffer> ccBuf = filterCCBuf(mCCMap.valueAt(index), mSelectedTrack);
 
     if (ccBuf->size() > 0) {
 #if 0
@@ -983,5 +1040,9 @@
     mCCMap.removeItemsAt(0, index + 1);
 }
 
+void NuPlayer::CCDecoder::flush() {
+    mCCMap.clear();
+}
+
 }  // namespace android
 
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
index 67bddb8..cc1bdff 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.h
@@ -126,18 +126,20 @@
     bool isSelected() const;
     void decode(const sp<ABuffer> &accessUnit);
     void display(int64_t timeUs);
+    void flush();
 
 private:
-    struct CCData;
-
     sp<AMessage> mNotify;
     KeyedVector<int64_t, sp<ABuffer> > mCCMap;
-    size_t mTrackCount;
+    size_t mCurrentChannel;
     int32_t mSelectedTrack;
+    int32_t mTrackIndices[4];
+    Vector<size_t> mFoundChannels;
 
-    bool isNullPad(CCData *cc) const;
-    void dumpBytePair(const sp<ABuffer> &ccBuf) const;
+    bool isTrackValid(size_t index) const;
+    int32_t getTrackIndex(size_t channel) const;
     bool extractFromSEI(const sp<ABuffer> &accessUnit);
+    sp<ABuffer> filterCCBuf(const sp<ABuffer> &ccBuf, size_t index);
 
     DISALLOW_EVIL_CONSTRUCTORS(CCDecoder);
 };
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 2423f5f..09324ae 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -626,7 +626,7 @@
     switch (msg) {
         case MEDIA_PLAYBACK_COMPLETE:
         {
-            if (mLooping) {
+            if (mLooping && mState != STATE_RESET_IN_PROGRESS) {
                 mLock.unlock();
                 mPlayer->seekToAsync(0);
                 mLock.lock();
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 19a5908..9b03b71 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -2313,7 +2313,6 @@
         return 0;
     }
     OMX_U32 ret = frameRate * iFramesInterval;
-    CHECK(ret > 1);
     return ret;
 }
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 78758da..a8806c8 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -994,7 +994,6 @@
         return 0;
     }
     OMX_U32 ret = frameRate * iFramesInterval - 1;
-    CHECK(ret > 1);
     return ret;
 }
 
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
index cb9aca6..9849f4d 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
@@ -445,11 +445,18 @@
     if (mNewAEState) {
         if (!mAeInPrecapture) {
             // Waiting to see PRECAPTURE state
-            if (mAETriggerId == mTriggerId &&
-                    mAEState == ANDROID_CONTROL_AE_STATE_PRECAPTURE) {
-                ALOGV("%s: Got precapture start", __FUNCTION__);
-                mAeInPrecapture = true;
-                mTimeoutCount = kMaxTimeoutsForPrecaptureEnd;
+            if (mAETriggerId == mTriggerId) {
+                if (mAEState == ANDROID_CONTROL_AE_STATE_PRECAPTURE) {
+                    ALOGV("%s: Got precapture start", __FUNCTION__);
+                    mAeInPrecapture = true;
+                    mTimeoutCount = kMaxTimeoutsForPrecaptureEnd;
+                } else if (mAEState == ANDROID_CONTROL_AE_STATE_CONVERGED ||
+                        mAEState == ANDROID_CONTROL_AE_STATE_FLASH_REQUIRED) {
+                    // It is legal to transit to CONVERGED or FLASH_REQUIRED
+                    // directly after a trigger.
+                    ALOGV("%s: AE is already in good state, start capture", __FUNCTION__);
+                    return STANDARD_CAPTURE;
+                }
             }
         } else {
             // Waiting to see PRECAPTURE state end