Send estimated bandwidth value as informational event when cache fetcher pauses

o Application can make informed decision about the available network bandwidth
  when cache fetcher pauses.

o Application can also adjust how frequently the bandwidth is estimated within
  a range from one second to one minute.

Change-Id: I90068001343e79da1886de03c565537787e1580b
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 50a378f..ea5a9d3 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -100,6 +100,9 @@
     MEDIA_INFO_BUFFERING_START = 701,
     // MediaPlayer is resuming playback after filling buffers.
     MEDIA_INFO_BUFFERING_END = 702,
+    // Bandwidth in recent past
+    MEDIA_INFO_NETWORK_BANDWIDTH = 703,
+
     // 8xx
     // Bad interleaving means that a media has been improperly interleaved or not
     // interleaved at all, e.g has all the video samples first then all the audio
@@ -128,6 +131,9 @@
 enum media_set_parameter_keys {
     KEY_PARAMETER_TIMED_TEXT_TRACK_INDEX = 1000,
     KEY_PARAMETER_TIMED_TEXT_ADD_OUT_OF_BAND_SOURCE = 1001,
+
+    // Streaming/buffering parameters
+    KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS = 1100,
 };
 // ----------------------------------------------------------------------------
 // ref-counted object for callbacks
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 3d270f8..07a47e5 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -634,6 +634,7 @@
                     mFlags |= CACHE_UNDERRUN;
                     pause_l();
                     ensureCacheIsFetching_l();
+                    sendCacheStats();
                     notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
                 } else if (eos || cachedDataRemaining > kHighWaterMarkBytes) {
                     if (mFlags & CACHE_UNDERRUN) {
@@ -692,6 +693,7 @@
             mFlags |= CACHE_UNDERRUN;
             pause_l();
             ensureCacheIsFetching_l();
+            sendCacheStats();
             notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
         } else if (eos || cachedDurationUs > highWaterMarkUs) {
             if (mFlags & CACHE_UNDERRUN) {
@@ -711,6 +713,18 @@
     postBufferingEvent_l();
 }
 
+void AwesomePlayer::sendCacheStats() {
+    sp<MediaPlayerBase> listener = mListener.promote();
+    if (listener != NULL) {
+        int32_t kbps = 0;
+        status_t err = mCachedSource->getEstimatedBandwidthKbps(&kbps);
+        if (err == OK) {
+            listener->sendEvent(
+                MEDIA_INFO, MEDIA_INFO_NETWORK_BANDWIDTH, kbps);
+        }
+    }
+}
+
 void AwesomePlayer::onStreamDone() {
     // Posted whenever any stream finishes playing.
 
@@ -2083,6 +2097,10 @@
 
             return mTextPlayer->setParameter(key, request);
         }
+        case KEY_PARAMETER_CACHE_STAT_COLLECT_FREQ_MS:
+        {
+            return setCacheStatCollectFreq(request);
+        }
         default:
         {
             return ERROR_UNSUPPORTED;
@@ -2090,6 +2108,16 @@
     }
 }
 
+status_t AwesomePlayer::setCacheStatCollectFreq(const Parcel &request) {
+    if (mCachedSource != NULL) {
+        int32_t freqMs = request.readInt32();
+        LOGD("Request to keep cache stats in the past %d ms",
+            freqMs);
+        return mCachedSource->setCacheStatCollectFreq(freqMs);
+    }
+    return ERROR_UNSUPPORTED;
+}
+
 status_t AwesomePlayer::getParameter(int key, Parcel *reply) {
     return OK;
 }
diff --git a/media/libstagefright/HTTPBase.cpp b/media/libstagefright/HTTPBase.cpp
index 58b17a7..c0ae29d 100644
--- a/media/libstagefright/HTTPBase.cpp
+++ b/media/libstagefright/HTTPBase.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "HTTPBase"
+#include <utils/Log.h>
+
 #include "include/HTTPBase.h"
 
 #if CHROMIUM_AVAILABLE
@@ -22,11 +26,19 @@
 
 #include "include/NuHTTPDataSource.h"
 
+#include <media/stagefright/foundation/ALooper.h>
 #include <cutils/properties.h>
 
 namespace android {
 
-HTTPBase::HTTPBase() {}
+HTTPBase::HTTPBase()
+    : mNumBandwidthHistoryItems(0),
+      mTotalTransferTimeUs(0),
+      mTotalTransferBytes(0),
+      mPrevBandwidthMeasureTimeUs(0),
+      mPrevEstimatedBandWidthKbps(0),
+      mBandWidthCollectFreqMs(5000) {
+}
 
 // static
 sp<HTTPBase> HTTPBase::Create(uint32_t flags) {
@@ -42,4 +54,69 @@
     }
 }
 
+void HTTPBase::addBandwidthMeasurement(
+        size_t numBytes, int64_t delayUs) {
+    Mutex::Autolock autoLock(mLock);
+
+    BandwidthEntry entry;
+    entry.mDelayUs = delayUs;
+    entry.mNumBytes = numBytes;
+    mTotalTransferTimeUs += delayUs;
+    mTotalTransferBytes += numBytes;
+
+    mBandwidthHistory.push_back(entry);
+    if (++mNumBandwidthHistoryItems > 100) {
+        BandwidthEntry *entry = &*mBandwidthHistory.begin();
+        mTotalTransferTimeUs -= entry->mDelayUs;
+        mTotalTransferBytes -= entry->mNumBytes;
+        mBandwidthHistory.erase(mBandwidthHistory.begin());
+        --mNumBandwidthHistoryItems;
+
+        int64_t timeNowUs = ALooper::GetNowUs();
+        if (timeNowUs - mPrevBandwidthMeasureTimeUs >=
+                mBandWidthCollectFreqMs * 1000LL) {
+
+            if (mPrevBandwidthMeasureTimeUs != 0) {
+                mPrevEstimatedBandWidthKbps =
+                    (mTotalTransferBytes * 8E3 / mTotalTransferTimeUs);
+            }
+            mPrevBandwidthMeasureTimeUs = timeNowUs;
+        }
+    }
+
+}
+
+bool HTTPBase::estimateBandwidth(int32_t *bandwidth_bps) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mNumBandwidthHistoryItems < 2) {
+        return false;
+    }
+
+    *bandwidth_bps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs);
+
+    return true;
+}
+
+status_t HTTPBase::getEstimatedBandwidthKbps(int32_t *kbps) {
+    Mutex::Autolock autoLock(mLock);
+    *kbps = mPrevEstimatedBandWidthKbps;
+    return OK;
+}
+
+status_t HTTPBase::setBandwidthStatCollectFreq(int32_t freqMs) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (freqMs < kMinBandwidthCollectFreqMs
+            || freqMs > kMaxBandwidthCollectFreqMs) {
+
+        LOGE("frequency (%d ms) is out of range [1000, 60000]", freqMs);
+        return BAD_VALUE;
+    }
+
+    LOGI("frequency set to %d ms", freqMs);
+    mBandWidthCollectFreqMs = freqMs;
+    return OK;
+}
+
 }  // namespace android
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index 81f2e47..b2ed427 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -19,6 +19,7 @@
 #include <utils/Log.h>
 
 #include "include/NuCachedSource2.h"
+#include "include/HTTPBase.h"
 
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -201,6 +202,16 @@
     mCache = NULL;
 }
 
+status_t NuCachedSource2::getEstimatedBandwidthKbps(int32_t *kbps) {
+    HTTPBase* source = static_cast<HTTPBase *>(mSource.get());
+    return source->getEstimatedBandwidthKbps(kbps);
+}
+
+status_t NuCachedSource2::setCacheStatCollectFreq(int32_t freqMs) {
+    HTTPBase *source = static_cast<HTTPBase *>(mSource.get());
+    return source->setBandwidthStatCollectFreq(freqMs);
+}
+
 status_t NuCachedSource2::initCheck() const {
     return mSource->initCheck();
 }
diff --git a/media/libstagefright/NuHTTPDataSource.cpp b/media/libstagefright/NuHTTPDataSource.cpp
index 821ba9b..c3b5e8f 100644
--- a/media/libstagefright/NuHTTPDataSource.cpp
+++ b/media/libstagefright/NuHTTPDataSource.cpp
@@ -97,9 +97,6 @@
       mContentLengthValid(false),
       mHasChunkedTransferEncoding(false),
       mChunkDataBytesLeft(0),
-      mNumBandwidthHistoryItems(0),
-      mTotalTransferTimeUs(0),
-      mTotalTransferBytes(0),
       mDecryptHandle(NULL),
       mDrmManagerClient(NULL) {
 }
@@ -431,7 +428,7 @@
         }
 
         int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
-        addBandwidthMeasurement_l(n, delayUs);
+        addBandwidthMeasurement(n, delayUs);
 
         numBytesRead += (size_t)n;
 
@@ -517,36 +514,6 @@
     }
 }
 
-bool NuHTTPDataSource::estimateBandwidth(int32_t *bandwidth_bps) {
-    Mutex::Autolock autoLock(mLock);
-
-    if (mNumBandwidthHistoryItems < 2) {
-        return false;
-    }
-
-    *bandwidth_bps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs);
-
-    return true;
-}
-
-void NuHTTPDataSource::addBandwidthMeasurement_l(
-        size_t numBytes, int64_t delayUs) {
-    BandwidthEntry entry;
-    entry.mDelayUs = delayUs;
-    entry.mNumBytes = numBytes;
-    mTotalTransferTimeUs += delayUs;
-    mTotalTransferBytes += numBytes;
-
-    mBandwidthHistory.push_back(entry);
-    if (++mNumBandwidthHistoryItems > 100) {
-        BandwidthEntry *entry = &*mBandwidthHistory.begin();
-        mTotalTransferTimeUs -= entry->mDelayUs;
-        mTotalTransferBytes -= entry->mNumBytes;
-        mBandwidthHistory.erase(mBandwidthHistory.begin());
-        --mNumBandwidthHistoryItems;
-    }
-}
-
 sp<DecryptHandle> NuHTTPDataSource::DrmInitialization() {
     if (mDrmManagerClient == NULL) {
         mDrmManagerClient = new DrmManagerClient();
diff --git a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
index 1096717..ad1f342 100644
--- a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
+++ b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
@@ -34,9 +34,6 @@
       mCurrentOffset(0),
       mIOResult(OK),
       mContentSize(-1),
-      mNumBandwidthHistoryItems(0),
-      mTotalTransferTimeUs(0),
-      mTotalTransferBytes(0),
       mDecryptHandle(NULL),
       mDrmManagerClient(NULL) {
     mDelegate->setOwner(this);
@@ -188,7 +185,7 @@
 
         // The read operation was successful, mIOResult contains
         // the number of bytes read.
-        addBandwidthMeasurement_l(mIOResult, delayUs);
+        addBandwidthMeasurement(mIOResult, delayUs);
 
         mCurrentOffset += mIOResult;
         return mIOResult;
@@ -246,36 +243,6 @@
     clearDRMState_l();
 }
 
-void ChromiumHTTPDataSource::addBandwidthMeasurement_l(
-        size_t numBytes, int64_t delayUs) {
-    BandwidthEntry entry;
-    entry.mDelayUs = delayUs;
-    entry.mNumBytes = numBytes;
-    mTotalTransferTimeUs += delayUs;
-    mTotalTransferBytes += numBytes;
-
-    mBandwidthHistory.push_back(entry);
-    if (++mNumBandwidthHistoryItems > 100) {
-        BandwidthEntry *entry = &*mBandwidthHistory.begin();
-        mTotalTransferTimeUs -= entry->mDelayUs;
-        mTotalTransferBytes -= entry->mNumBytes;
-        mBandwidthHistory.erase(mBandwidthHistory.begin());
-        --mNumBandwidthHistoryItems;
-    }
-}
-
-bool ChromiumHTTPDataSource::estimateBandwidth(int32_t *bandwidth_bps) {
-    Mutex::Autolock autoLock(mLock);
-
-    if (mNumBandwidthHistoryItems < 2) {
-        return false;
-    }
-
-    *bandwidth_bps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs);
-
-    return true;
-}
-
 sp<DecryptHandle> ChromiumHTTPDataSource::DrmInitialization() {
     Mutex::Autolock autoLock(mLock);
 
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index a9e8e95..aebcdd1 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -93,6 +93,7 @@
 
     status_t setParameter(int key, const Parcel &request);
     status_t getParameter(int key, Parcel *reply);
+    status_t setCacheStatCollectFreq(const Parcel &request);
 
     status_t seekTo(int64_t timeUs);
 
@@ -291,6 +292,7 @@
     void setNativeWindow_l(const sp<ANativeWindow> &native);
 
     bool isStreamingHTTP() const;
+    void sendCacheStats();
 
     AwesomePlayer(const AwesomePlayer &);
     AwesomePlayer &operator=(const AwesomePlayer &);
diff --git a/media/libstagefright/include/ChromiumHTTPDataSource.h b/media/libstagefright/include/ChromiumHTTPDataSource.h
index 0e2927d..d833e2e 100644
--- a/media/libstagefright/include/ChromiumHTTPDataSource.h
+++ b/media/libstagefright/include/ChromiumHTTPDataSource.h
@@ -43,8 +43,6 @@
     virtual status_t getSize(off64_t *size);
     virtual uint32_t flags();
 
-    virtual bool estimateBandwidth(int32_t *bandwidth_bps);
-
     virtual sp<DecryptHandle> DrmInitialization();
 
     virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
@@ -67,11 +65,6 @@
         DISCONNECTING
     };
 
-    struct BandwidthEntry {
-        int64_t mDelayUs;
-        size_t mNumBytes;
-    };
-
     const uint32_t mFlags;
 
     mutable Mutex mLock;
@@ -94,11 +87,6 @@
 
     String8 mContentType;
 
-    List<BandwidthEntry> mBandwidthHistory;
-    size_t mNumBandwidthHistoryItems;
-    int64_t mTotalTransferTimeUs;
-    size_t mTotalTransferBytes;
-
     sp<DecryptHandle> mDecryptHandle;
     DrmManagerClient *mDrmManagerClient;
 
@@ -121,8 +109,6 @@
     void onReadCompleted(ssize_t size);
     void onDisconnectComplete();
 
-    void addBandwidthMeasurement_l(size_t numBytes, int64_t delayUs);
-
     void clearDRMState_l();
 
     DISALLOW_EVIL_CONSTRUCTORS(ChromiumHTTPDataSource);
diff --git a/media/libstagefright/include/HTTPBase.h b/media/libstagefright/include/HTTPBase.h
index 6cec390..3a7fbb6 100644
--- a/media/libstagefright/include/HTTPBase.h
+++ b/media/libstagefright/include/HTTPBase.h
@@ -20,6 +20,8 @@
 
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
 
 namespace android {
 
@@ -40,11 +42,41 @@
 
     // Returns true if bandwidth could successfully be estimated,
     // false otherwise.
-    virtual bool estimateBandwidth(int32_t *bandwidth_bps) = 0;
+    virtual bool estimateBandwidth(int32_t *bandwidth_bps);
+
+    virtual status_t getEstimatedBandwidthKbps(int32_t *kbps);
+
+    virtual status_t setBandwidthStatCollectFreq(int32_t freqMs);
 
     static sp<HTTPBase> Create(uint32_t flags = 0);
 
+protected:
+    void addBandwidthMeasurement(size_t numBytes, int64_t delayUs);
+
 private:
+
+    struct BandwidthEntry {
+        int64_t mDelayUs;
+        size_t mNumBytes;
+    };
+
+    Mutex mLock;
+
+    List<BandwidthEntry> mBandwidthHistory;
+    size_t mNumBandwidthHistoryItems;
+    int64_t mTotalTransferTimeUs;
+    size_t mTotalTransferBytes;
+
+    enum {
+        kMinBandwidthCollectFreqMs = 1000,   // 1 second
+        kMaxBandwidthCollectFreqMs = 60000,  // one minute
+    };
+
+    int64_t mPrevBandwidthMeasureTimeUs;
+    int32_t mPrevEstimatedBandWidthKbps;
+    int32_t mBandWidthCollectFreqMs;
+
+
     DISALLOW_EVIL_CONSTRUCTORS(HTTPBase);
 };
 
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index ed3e265..31fc0e5 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -47,8 +47,10 @@
 
     size_t cachedSize();
     size_t approxDataRemaining(status_t *finalStatus);
+    status_t setCacheStatCollectFreq(int32_t freqMs);
 
     void resumeFetchingIfNecessary();
+    status_t getEstimatedBandwidthKbps(int32_t *kbps);
 
 protected:
     virtual ~NuCachedSource2();
diff --git a/media/libstagefright/include/NuHTTPDataSource.h b/media/libstagefright/include/NuHTTPDataSource.h
index 2ab1f19..c265b3a 100644
--- a/media/libstagefright/include/NuHTTPDataSource.h
+++ b/media/libstagefright/include/NuHTTPDataSource.h
@@ -43,10 +43,6 @@
     virtual status_t getSize(off64_t *size);
     virtual uint32_t flags();
 
-    // Returns true if bandwidth could successfully be estimated,
-    // false otherwise.
-    virtual bool estimateBandwidth(int32_t *bandwidth_bps);
-
     virtual sp<DecryptHandle> DrmInitialization();
     virtual void getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client);
     virtual String8 getUri();
@@ -63,11 +59,6 @@
         CONNECTED
     };
 
-    struct BandwidthEntry {
-        int64_t mDelayUs;
-        size_t mNumBytes;
-    };
-
     Mutex mLock;
 
     uint32_t mFlags;
@@ -93,11 +84,6 @@
     // chunk header (or -1 if no more chunks).
     ssize_t mChunkDataBytesLeft;
 
-    List<BandwidthEntry> mBandwidthHistory;
-    size_t mNumBandwidthHistoryItems;
-    int64_t mTotalTransferTimeUs;
-    size_t mTotalTransferBytes;
-
     sp<DecryptHandle> mDecryptHandle;
     DrmManagerClient *mDrmManagerClient;
 
@@ -114,7 +100,6 @@
     ssize_t internalRead(void *data, size_t size);
 
     void applyTimeoutResponse();
-    void addBandwidthMeasurement_l(size_t numBytes, int64_t delayUs);
 
     static void MakeFullHeaders(
             const KeyedVector<String8, String8> *overrides,