HLS: remove no more than 10% of bandwidth history at a time

- fix a 'possible video time jump' after seek, don't update
  renderer anchor time for 0-sized audio buffers

- fix another 'possible video time jump' caused by some states
  not reset in mStreams

- bandwidth estimator changes to not remove more than 10%
  of total transfer duration at a time to avoid jumping up
  too quickly

bug: 20267388
related-to-bug: 19864613
related-to-bug: 20138395

Change-Id: I8812332cd1e26bf562acfaf086fd679a3549debc
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index f8be16a..f229452 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -621,7 +621,8 @@
             return false;
         }
 
-        if (entry->mOffset == 0) {
+        // ignore 0-sized buffer which could be EOS marker with no data
+        if (entry->mOffset == 0 && entry->mBuffer->size() > 0) {
             int64_t mediaTimeUs;
             CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
             ALOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 764ff82..ab5bb3a 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -71,8 +71,9 @@
 
 private:
     // Bandwidth estimation parameters
-    static const int32_t kMaxBandwidthHistoryItems = 20;
-    static const int64_t kMaxBandwidthHistoryWindowUs = 5000000ll; // 5 sec
+    static const int32_t kMinBandwidthHistoryItems = 20;
+    static const int64_t kMinBandwidthHistoryWindowUs = 5000000ll; // 5 sec
+    static const int64_t kMaxBandwidthHistoryWindowUs = 30000000ll; // 30 sec
 
     struct BandwidthEntry {
         int64_t mDelayUs;
@@ -109,11 +110,21 @@
     mBandwidthHistory.push_back(entry);
     mHasNewSample = true;
 
+    // Remove no more than 10% of total transfer time at a time
+    // to avoid sudden jump on bandwidth estimation. There might
+    // be long blocking reads that takes up signification time,
+    // we have to keep a longer window in that case.
+    int64_t bandwidthHistoryWindowUs = mTotalTransferTimeUs * 9 / 10;
+    if (bandwidthHistoryWindowUs < kMinBandwidthHistoryWindowUs) {
+        bandwidthHistoryWindowUs = kMinBandwidthHistoryWindowUs;
+    } else if (bandwidthHistoryWindowUs > kMaxBandwidthHistoryWindowUs) {
+        bandwidthHistoryWindowUs = kMaxBandwidthHistoryWindowUs;
+    }
     // trim old samples, keeping at least kMaxBandwidthHistoryItems samples,
     // and total transfer time at least kMaxBandwidthHistoryWindowUs.
-    while (mBandwidthHistory.size() > kMaxBandwidthHistoryItems) {
+    while (mBandwidthHistory.size() > kMinBandwidthHistoryItems) {
         List<BandwidthEntry>::iterator it = mBandwidthHistory.begin();
-        if (mTotalTransferTimeUs - it->mDelayUs < kMaxBandwidthHistoryWindowUs) {
+        if (mTotalTransferTimeUs - it->mDelayUs < bandwidthHistoryWindowUs) {
             break;
         }
         mTotalTransferTimeUs -= it->mDelayUs;
@@ -122,7 +133,8 @@
     }
 }
 
-bool LiveSession::BandwidthEstimator::estimateBandwidth(int32_t *bandwidthBps, bool *isStable) {
+bool LiveSession::BandwidthEstimator::estimateBandwidth(
+        int32_t *bandwidthBps, bool *isStable) {
     AutoMutex autoLock(mLock);
 
     if (mBandwidthHistory.size() < 2) {
@@ -159,6 +171,29 @@
     if (isStable) {
        *isStable = mIsStable;
     }
+#if 0
+    {
+        char dumpStr[1024] = {0};
+        size_t itemIdx = 0;
+        size_t histSize = mBandwidthHistory.size();
+        sprintf(dumpStr, "estimate bps=%d stable=%d history (n=%d): {",
+            *bandwidthBps, mIsStable, histSize);
+        List<BandwidthEntry>::iterator it = mBandwidthHistory.begin();
+        for (; it != mBandwidthHistory.end(); ++it) {
+            if (itemIdx > 50) {
+                sprintf(dumpStr + strlen(dumpStr),
+                        "...(%zd more items)... }", histSize - itemIdx);
+                break;
+            }
+            sprintf(dumpStr + strlen(dumpStr), "%dk/%.3fs%s",
+                it->mNumBytes / 1024,
+                (double)it->mDelayUs * 1.0e-6,
+                (it == (--mBandwidthHistory.end())) ? "}" : ", ");
+            itemIdx++;
+        }
+        ALOGE(dumpStr);
+    }
+#endif
     return true;
 }
 
@@ -338,7 +373,8 @@
                     offsetTimeUs = 0;
                 }
 
-                if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0) {
+                if (mDiscontinuityAbsStartTimesUs.indexOfKey(strm.mCurDiscontinuitySeq) >= 0
+                        && strm.mLastDequeuedTimeUs >= 0) {
                     int64_t firstTimeUs;
                     firstTimeUs = mDiscontinuityAbsStartTimesUs.valueFor(strm.mCurDiscontinuitySeq);
                     offsetTimeUs += strm.mLastDequeuedTimeUs - firstTimeUs;
@@ -1731,7 +1767,7 @@
         }
 
         for (size_t i = 0; i < kMaxStreams; ++i) {
-            mStreams[i].mCurDiscontinuitySeq = 0;
+            mStreams[i].reset();
         }
 
         mDiscontinuityOffsetTimesUs.clear();
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 56cd702..4e7ccac 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -166,10 +166,14 @@
             : StreamItem("") {}
         StreamItem(const char *type)
             : mType(type),
-              mSeekMode(kSeekModeExactPosition),
-              mCurDiscontinuitySeq(0),
-              mLastDequeuedTimeUs(0),
-              mLastSampleDurationUs(0) {}
+              mSeekMode(kSeekModeExactPosition) {
+                  reset();
+              }
+        void reset() {
+            mCurDiscontinuitySeq = 0;
+            mLastDequeuedTimeUs = -1ll;
+            mLastSampleDurationUs = 0ll;
+        }
         AString uriKey() {
             AString key(mType);
             key.append("URI");