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_