VT: Postpone bitrate down for considering camera switch

1. If received packet is too few, it has a possibility that opponent switches camera
   For that possibility postpone bitrate down for 3 seconds.
2. A thread class form a concept of timer is implemented.
   These are introduced to support QualManager's timer operations like (1.)
3. License message added

Merged-in: I6b69cbb555eec99bf45dc77ba5e24affdbcd3776
Change-Id: I6b69cbb555eec99bf45dc77ba5e24affdbcd3776
Signed-off-by: Kim Sungyeon <sy85.kim@samsung.com>
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index ee4b7c2..01fa898 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -267,8 +267,6 @@
     mPrevNumBuffersReceived = mNumBuffersReceived;
     int32_t cumulativePacketLost = (int32_t)expected - mNumBuffersReceived;
 
-    ALOGI("UID %p expectedPkts %lld lostPkts %lld", this, (long long)intervalExpected, (long long)intervalPacketLost);
-
     uint8_t *data = buffer->data() + buffer->size();
 
     data[0] = 0x80 | 1;
@@ -392,14 +390,16 @@
     int64_t intervalReceived = mNumBuffersReceived - mPrevNumBuffersReceived;
     int64_t intervalPacketLost = intervalExpected - intervalReceived;
 
-    if (intervalPacketLost < 0)
+    ALOGI("UID %p expectedPkts %lld lostPkts %lld", this, (long long)intervalExpected, (long long)intervalPacketLost);
+
+    if (intervalPacketLost < 0 || intervalExpected == 0)
         fraction = 0;
-    else if (intervalExpected <= intervalPacketLost || intervalExpected == 0)
+    else if (intervalExpected <= intervalPacketLost)
         fraction = 255;
     else
         fraction = (intervalPacketLost << 8) / intervalExpected;
 
-    mQualManager.setTargetBitrate(fraction, ALooper::GetNowUs());
+    mQualManager.setTargetBitrate(fraction, ALooper::GetNowUs(), intervalExpected < 5);
 }
 
 bool ARTPSource::isNeedToReport() {
diff --git a/media/libstagefright/rtsp/Android.bp b/media/libstagefright/rtsp/Android.bp
index f197939..80717be 100644
--- a/media/libstagefright/rtsp/Android.bp
+++ b/media/libstagefright/rtsp/Android.bp
@@ -18,6 +18,7 @@
         "ARTSPConnection.cpp",
         "ASessionDescription.cpp",
         "SDPLoader.cpp",
+        "QualManager.cpp",
     ],
 
     shared_libs: [
diff --git a/media/libstagefright/rtsp/QualManager.cpp b/media/libstagefright/rtsp/QualManager.cpp
new file mode 100644
index 0000000..37aa326
--- /dev/null
+++ b/media/libstagefright/rtsp/QualManager.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "QualManager"
+
+#include <algorithm>
+
+#include <sys/prctl.h>
+#include <utils/Log.h>
+
+#include "QualManager.h"
+
+namespace android {
+
+QualManager::Watcher::Watcher(int32_t timeLimit)
+    : Thread(false), mWatching(false), mSwitch(false),
+      mTimeLimit(timeLimit * 1000000LL)     // timeLimit ms
+{
+}
+
+bool QualManager::Watcher::isExpired() const
+{
+    return mSwitch;
+}
+
+void QualManager::Watcher::setup() {
+    AutoMutex _l(mMyLock);
+    if (mWatching == false) {
+        mWatching = true;
+        mMyCond.signal();
+    }
+}
+
+void QualManager::Watcher::release() {
+    AutoMutex _l(mMyLock);
+    if (mSwitch) {
+        ALOGW("%s DISARMED", name);
+        mSwitch = false;
+    }
+    if (mWatching == true) {
+        ALOGW("%s DISARMED", name);
+        mWatching = false;
+        mMyCond.signal();
+    }
+}
+
+void QualManager::Watcher::exit() {
+    AutoMutex _l(mMyLock);
+    // The order is important to avoid dead lock.
+    Thread::requestExit();
+    mMyCond.signal();
+}
+
+QualManager::Watcher::~Watcher() {
+    ALOGI("%s thread dead", name);
+}
+
+bool QualManager::Watcher::threadLoop() {
+    AutoMutex _l(mMyLock);
+#if defined(__linux__)
+    prctl(PR_GET_NAME, name, 0, 0, 0);
+#endif
+    while (!exitPending()) {
+        ALOGW("%s Timer init", name);
+        mMyCond.wait(mMyLock);                      // waits as non-watching state
+        if (exitPending())
+            return false;
+        ALOGW("%s timer BOOM after %d msec", name, (int)(mTimeLimit / 1000000LL));
+        mMyCond.waitRelative(mMyLock, mTimeLimit);  // waits as watching satte
+        if (mWatching == true) {
+            mSwitch = true;
+            ALOGW("%s BOOM!!!!", name);
+        }
+        mWatching = false;
+    }
+    return false;
+}
+
+
+QualManager::QualManager()
+    : mMinBitrate(-1), mMaxBitrate(-1),
+      mTargetBitrate(512000), mLastTargetBitrate(-1),
+      mLastSetBitrateTime(0), mIsNewTargetBitrate(false)
+{
+    VFPWatcher = new Watcher(3000);     //Very Few Packet Watcher
+    VFPWatcher->run("VeryFewPtk");
+    LBRWatcher = new Watcher(10000);    //Low Bit Rate Watcher
+    LBRWatcher->run("LowBitRate");
+}
+
+QualManager::~QualManager() {
+    VFPWatcher->exit();
+    LBRWatcher->exit();
+}
+
+int32_t QualManager::getTargetBitrate() {
+    if (mIsNewTargetBitrate) {
+        mIsNewTargetBitrate = false;
+        mLastTargetBitrate = clampingBitrate(mTargetBitrate);
+        mTargetBitrate = mLastTargetBitrate;
+        return mTargetBitrate;
+    } else {
+        return -1;
+    }
+}
+
+bool QualManager::isNeedToDowngrade() {
+    return LBRWatcher->isExpired();
+}
+
+void QualManager::setTargetBitrate(uint8_t fraction, int64_t nowUs, bool isTooLowPkts) {
+    /* Too Low Packet. Maybe opponent is switching camera.
+     * If this condition goes longer, we should down bitrate.
+     */
+    if (isTooLowPkts) {
+        VFPWatcher->setup();
+    } else {
+        VFPWatcher->release();
+    }
+
+    if ((fraction > (256 * 5 / 100) && !isTooLowPkts) || VFPWatcher->isExpired()) {
+        // loss more than 5%                          or  VFPWatcher BOOMED
+        mTargetBitrate -= mBitrateStep * 3;
+    } else if (fraction <= (256 * 2 /100)) {
+        // loss less than 2%
+        mTargetBitrate += mBitrateStep;
+    }
+
+    if (mTargetBitrate > mMaxBitrate) {
+        mTargetBitrate = mMaxBitrate + mBitrateStep;
+    } else if (mTargetBitrate < mMinBitrate) {
+        LBRWatcher->setup();
+        mTargetBitrate = mMinBitrate - mBitrateStep;
+    }
+
+    if (mLastTargetBitrate != clampingBitrate(mTargetBitrate) ||
+        nowUs - mLastSetBitrateTime > 5000000ll) {
+        mIsNewTargetBitrate = true;
+        mLastSetBitrateTime = nowUs;
+    }
+}
+
+void QualManager::setMinMaxBitrate(int32_t min, int32_t max) {
+    mMinBitrate = min;
+    mMaxBitrate = max;
+    mBitrateStep = (max - min) / 8;
+}
+
+void QualManager::setBitrateData(int32_t bitrate, int64_t /*now*/) {
+    // A bitrate that is considered packetloss also should be good.
+    if (bitrate >= mMinBitrate && mTargetBitrate >= mMinBitrate) {
+        LBRWatcher->release();
+    } else if (bitrate < mMinBitrate){
+        LBRWatcher->setup();
+    }
+}
+
+int32_t QualManager::clampingBitrate(int32_t bitrate) {
+    return std::min(std::max(mMinBitrate, bitrate), mMaxBitrate);
+}
+} // namespace android
diff --git a/media/libstagefright/rtsp/QualManager.h b/media/libstagefright/rtsp/QualManager.h
index ee2fb40..a7dc921 100644
--- a/media/libstagefright/rtsp/QualManager.h
+++ b/media/libstagefright/rtsp/QualManager.h
@@ -18,92 +18,58 @@
 
 #define QUAL_MANAGER_H_
 
-namespace android {
+#include <stdint.h>
+#include <utils/Thread.h>
 
+namespace android {
 class QualManager {
 public:
-    QualManager() : mMinBitrate(-1), mMaxBitrate(-1), mTargetBitrate(512000),
-                    mLastTargetBitrate(-1), mLastSetBitrateTime(0),
-                    mLowBitrateStartTime(0), mAutoDowngrade(false),
-                    mIsNewTargetBitrate(false){};
+    QualManager();
+    ~QualManager();
 
-    int32_t getTargetBitrate() {
-        if (mIsNewTargetBitrate) {
-            mIsNewTargetBitrate = false;
-            mLastTargetBitrate = mTargetBitrate;
-            return mTargetBitrate;
-        } else {
-            return -1;
-        }
-    }
+    int32_t getTargetBitrate();
+    bool isNeedToDowngrade();
 
-    bool isNeedToDowngrade() {
-        return mAutoDowngrade;
-    }
-
-    void setTargetBitrate(uint8_t fraction, int64_t nowUs) {
-        if (fraction <= (256 * 2 /100)) {           // loss less than 2%
-            mTargetBitrate += mBitrateStep;
-        } else if (fraction > (256 * 5 / 100)) {    // loss more than 5%
-            mTargetBitrate -= mBitrateStep * 4;
-        }
-
-        if (mTargetBitrate > mMaxBitrate) {
-            mTargetBitrate = mMaxBitrate;
-        } else if (mTargetBitrate < mMinBitrate) {
-            if (mLowBitrateStartTime != 0) {
-                mLowBitrateStartTime = nowUs;
-            }
-            mTargetBitrate = mMinBitrate;
-        }
-
-        if (mLastTargetBitrate != mTargetBitrate || nowUs - mLastSetBitrateTime > 5000000ll) {
-            mIsNewTargetBitrate = true;
-            mLastSetBitrateTime = nowUs;
-        }
-    };
-
-    void setMinMaxBitrate(int32_t min, int32_t max) {
-        mMinBitrate = min;
-        mMaxBitrate = max;
-        mBitrateStep = (max - min) / 8;
-    };
-
-    void setBitrateData(int32_t bitrate, int64_t now) {
-        int64_t lowBitrateDuration = 0;
-        if (bitrate < mMinBitrate)
-        {
-            if (mLowBitrateStartTime == 0) {
-                mLowBitrateStartTime = now;
-            } else {
-                lowBitrateDuration = now - mLowBitrateStartTime;
-            }
-        } else {
-            mLowBitrateStartTime = 0;
-        }
-        if (lowBitrateDuration > mPatientTime) {
-            mAutoDowngrade = true;
-        } else {
-            mAutoDowngrade = false;
-        }
-    }
+    void setTargetBitrate(uint8_t fraction, int64_t nowUs, bool isTooLowPkts);
+    void setMinMaxBitrate(int32_t min, int32_t max);
+    void setBitrateData(int32_t bitrate, int64_t now);
 private:
+    class Watcher : public Thread
+    {
+    public:
+        Watcher(int32_t timeLimit);
+
+        void setup();
+        void release();
+        void exit();
+        bool isExpired() const;
+    private:
+        virtual ~Watcher();
+        virtual bool threadLoop();
+
+        char name[32] = {0,};
+
+        Condition mMyCond;
+        Mutex mMyLock;
+
+        bool mWatching;
+        bool mSwitch;
+        const nsecs_t mTimeLimit;
+    };
+    sp<Watcher> VFPWatcher;
+    sp<Watcher> LBRWatcher;
     int32_t mMinBitrate;
     int32_t mMaxBitrate;
     int32_t mBitrateStep;
 
     int32_t mTargetBitrate;
     int32_t mLastTargetBitrate;
-
     int64_t mLastSetBitrateTime;
 
-    const int64_t mPatientTime = 10000000ll;    // 10 sec
-    int64_t mLowBitrateStartTime;
-
-    bool mAutoDowngrade;
     bool mIsNewTargetBitrate;
-};
 
+    int32_t clampingBitrate(int32_t bitrate);
+};
 } //namespace android
 
 #endif  // QUAL_MANAGER_H_