Refactoring temporal layers implementation and adding VideoCodecMode for easier control of codec settings.

Review URL: https://webrtc-codereview.appspot.com/1105007

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3528 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/video_coding/utility/Android.mk b/webrtc/modules/video_coding/utility/Android.mk
new file mode 100644
index 0000000..4af007c
--- /dev/null
+++ b/webrtc/modules/video_coding/utility/Android.mk
@@ -0,0 +1,39 @@
+# Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS.  All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+include $(LOCAL_PATH)/../../../../android-webrtc.mk
+
+LOCAL_ARM_MODE := arm
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+LOCAL_MODULE := libvideo_coding_utility
+LOCAL_MODULE_TAGS := optional
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_SRC_FILES := \
+    exp_filter.cc \
+    frame_dropper.cc \
+
+# Flags passed to both C and C++ files.
+LOCAL_CFLAGS := \
+    $(MY_WEBRTC_COMMON_DEFS)
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/../../../../system_wrappers/interface
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libdl \
+    libstlport
+
+ifndef NDK_ROOT
+include external/stlport/libstlport.mk
+endif
+include $(BUILD_STATIC_LIBRARY)
diff --git a/webrtc/modules/video_coding/utility/exp_filter.cc b/webrtc/modules/video_coding/utility/exp_filter.cc
new file mode 100644
index 0000000..44f280bc
--- /dev/null
+++ b/webrtc/modules/video_coding/utility/exp_filter.cc
@@ -0,0 +1,60 @@
+/*
+ *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/video_coding/utility/include/exp_filter.h"
+
+#include <math.h>
+
+namespace webrtc {
+
+void
+VCMExpFilter::Reset(float alpha)
+{
+    _alpha = alpha;
+    _filtered = -1.0;
+}
+
+float
+VCMExpFilter::Apply(float exp, float sample)
+{
+    if (_filtered == -1.0)
+    {
+        // Initialize filtered bit rates
+        _filtered = sample;
+    }
+    else if (exp == 1.0)
+    {
+        _filtered = _alpha * _filtered + (1 - _alpha) * sample;
+    }
+    else
+    {
+        float alpha = pow(_alpha, exp);
+        _filtered = alpha * _filtered + (1 - alpha) * sample;
+    }
+    if (_max != -1 && _filtered > _max)
+    {
+        _filtered = _max;
+    }
+    return _filtered;
+}
+
+void
+VCMExpFilter::UpdateBase(float alpha)
+{
+    _alpha = alpha;
+}
+
+float
+VCMExpFilter::Value() const
+{
+    return _filtered;
+}
+
+}
diff --git a/webrtc/modules/video_coding/utility/frame_dropper.cc b/webrtc/modules/video_coding/utility/frame_dropper.cc
new file mode 100644
index 0000000..b871022
--- /dev/null
+++ b/webrtc/modules/video_coding/utility/frame_dropper.cc
@@ -0,0 +1,348 @@
+/*
+ *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/video_coding/utility/include/frame_dropper.h"
+
+#include "webrtc/system_wrappers/interface/trace.h"
+
+namespace webrtc
+{
+
+FrameDropper::FrameDropper()
+:
+_keyFrameSizeAvgKbits(0.9f),
+_keyFrameRatio(0.99f),
+_dropRatio(0.9f, 0.96f),
+_enabled(true)
+{
+    Reset();
+}
+
+void
+FrameDropper::Reset()
+{
+    _keyFrameRatio.Reset(0.99f);
+    _keyFrameRatio.Apply(1.0f, 1.0f/300.0f); // 1 key frame every 10th second in 30 fps
+    _keyFrameSizeAvgKbits.Reset(0.9f);
+    _keyFrameCount = 0;
+    _accumulator = 0.0f;
+    _accumulatorMax = 150.0f; // assume 300 kb/s and 0.5 s window
+    _targetBitRate = 300.0f;
+    _incoming_frame_rate = 30;
+    _keyFrameSpreadFrames = 0.5f * _incoming_frame_rate;
+    _dropNext = false;
+    _dropRatio.Reset(0.9f);
+    _dropRatio.Apply(0.0f, 0.0f); // Initialize to 0
+    _dropCount = 0;
+    _windowSize = 0.5f;
+    _wasBelowMax = true;
+    _fastMode = false; // start with normal (non-aggressive) mode
+    // Cap for the encoder buffer level/accumulator, in secs.
+    _cap_buffer_size = 3.0f;
+    // Cap on maximum amount of dropped frames between kept frames, in secs.
+    _max_time_drops = 4.0f;
+}
+
+void
+FrameDropper::Enable(bool enable)
+{
+    _enabled = enable;
+}
+
+void
+FrameDropper::Fill(WebRtc_UWord32 frameSizeBytes, bool deltaFrame)
+{
+    if (!_enabled)
+    {
+        return;
+    }
+    float frameSizeKbits = 8.0f * static_cast<float>(frameSizeBytes) / 1000.0f;
+    if (!deltaFrame && !_fastMode) // fast mode does not treat key-frames any different
+    {
+        _keyFrameSizeAvgKbits.Apply(1, frameSizeKbits);
+        _keyFrameRatio.Apply(1.0, 1.0);
+        if (frameSizeKbits > _keyFrameSizeAvgKbits.Value())
+        {
+            // Remove the average key frame size since we
+            // compensate for key frames when adding delta
+            // frames.
+            frameSizeKbits -= _keyFrameSizeAvgKbits.Value();
+        }
+        else
+        {
+            // Shouldn't be negative, so zero is the lower bound.
+            frameSizeKbits = 0;
+        }
+        if (_keyFrameRatio.Value() > 1e-5 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames)
+        {
+            // We are sending key frames more often than our upper bound for
+            // how much we allow the key frame compensation to be spread
+            // out in time. Therefor we must use the key frame ratio rather
+            // than keyFrameSpreadFrames.
+            _keyFrameCount = static_cast<WebRtc_Word32>(1 / _keyFrameRatio.Value() + 0.5);
+        }
+        else
+        {
+            // Compensate for the key frame the following frames
+            _keyFrameCount = static_cast<WebRtc_Word32>(_keyFrameSpreadFrames + 0.5);
+        }
+    }
+    else
+    {
+        // Decrease the keyFrameRatio
+        _keyFrameRatio.Apply(1.0, 0.0);
+    }
+    // Change the level of the accumulator (bucket)
+    _accumulator += frameSizeKbits;
+    CapAccumulator();
+}
+
+void
+FrameDropper::Leak(WebRtc_UWord32 inputFrameRate)
+{
+    if (!_enabled)
+    {
+        return;
+    }
+    if (inputFrameRate < 1)
+    {
+        return;
+    }
+    if (_targetBitRate < 0.0f)
+    {
+        return;
+    }
+    _keyFrameSpreadFrames = 0.5f * inputFrameRate;
+    // T is the expected bits per frame (target). If all frames were the same size,
+    // we would get T bits per frame. Notice that T is also weighted to be able to
+    // force a lower frame rate if wanted.
+    float T = _targetBitRate / inputFrameRate;
+    if (_keyFrameCount > 0)
+    {
+        // Perform the key frame compensation
+        if (_keyFrameRatio.Value() > 0 && 1 / _keyFrameRatio.Value() < _keyFrameSpreadFrames)
+        {
+            T -= _keyFrameSizeAvgKbits.Value() * _keyFrameRatio.Value();
+        }
+        else
+        {
+            T -= _keyFrameSizeAvgKbits.Value() / _keyFrameSpreadFrames;
+        }
+        _keyFrameCount--;
+    }
+    _accumulator -= T;
+    UpdateRatio();
+}
+
+void
+FrameDropper::UpdateNack(WebRtc_UWord32 nackBytes)
+{
+    if (!_enabled)
+    {
+        return;
+    }
+    _accumulator += static_cast<float>(nackBytes) * 8.0f / 1000.0f;
+}
+
+void
+FrameDropper::FillBucket(float inKbits, float outKbits)
+{
+    _accumulator += (inKbits - outKbits);
+}
+
+void
+FrameDropper::UpdateRatio()
+{
+    if (_accumulator > 1.3f * _accumulatorMax)
+    {
+        // Too far above accumulator max, react faster
+        _dropRatio.UpdateBase(0.8f);
+    }
+    else
+    {
+        // Go back to normal reaction
+        _dropRatio.UpdateBase(0.9f);
+    }
+    if (_accumulator > _accumulatorMax)
+    {
+        // We are above accumulator max, and should ideally
+        // drop a frame. Increase the dropRatio and drop
+        // the frame later.
+        if (_wasBelowMax)
+        {
+            _dropNext = true;
+        }
+        if (_fastMode)
+        {
+            // always drop in aggressive mode
+            _dropNext = true;
+        }
+
+        _dropRatio.Apply(1.0f, 1.0f);
+        _dropRatio.UpdateBase(0.9f);
+    }
+    else
+    {
+        _dropRatio.Apply(1.0f, 0.0f);
+    }
+    if (_accumulator < 0.0f)
+    {
+        _accumulator = 0.0f;
+    }
+    _wasBelowMax = _accumulator < _accumulatorMax;
+}
+
+// This function signals when to drop frames to the caller. It makes use of the dropRatio
+// to smooth out the drops over time.
+bool
+FrameDropper::DropFrame()
+{
+    if (!_enabled)
+    {
+        return false;
+    }
+    if (_dropNext)
+    {
+        _dropNext = false;
+        _dropCount = 0;
+    }
+
+    if (_dropRatio.Value() >= 0.5f) // Drops per keep
+    {
+        // limit is the number of frames we should drop between each kept frame
+        // to keep our drop ratio. limit is positive in this case.
+        float denom = 1.0f - _dropRatio.Value();
+        if (denom < 1e-5)
+        {
+            denom = (float)1e-5;
+        }
+        WebRtc_Word32 limit = static_cast<WebRtc_Word32>(1.0f / denom - 1.0f + 0.5f);
+        // Put a bound on the max amount of dropped frames between each kept
+        // frame, in terms of frame rate and window size (secs).
+        int max_limit = static_cast<int>(_incoming_frame_rate *
+                                         _max_time_drops);
+        if (limit > max_limit) {
+          limit = max_limit;
+        }
+        if (_dropCount < 0)
+        {
+            // Reset the _dropCount since it was negative and should be positive.
+            if (_dropRatio.Value() > 0.4f)
+            {
+                _dropCount = -_dropCount;
+            }
+            else
+            {
+                _dropCount = 0;
+            }
+        }
+        if (_dropCount < limit)
+        {
+            // As long we are below the limit we should drop frames.
+            _dropCount++;
+            return true;
+        }
+        else
+        {
+            // Only when we reset _dropCount a frame should be kept.
+            _dropCount = 0;
+            return false;
+        }
+    }
+    else if (_dropRatio.Value() > 0.0f && _dropRatio.Value() < 0.5f) // Keeps per drop
+    {
+        // limit is the number of frames we should keep between each drop
+        // in order to keep the drop ratio. limit is negative in this case,
+        // and the _dropCount is also negative.
+        float denom = _dropRatio.Value();
+        if (denom < 1e-5)
+        {
+            denom = (float)1e-5;
+        }
+        WebRtc_Word32 limit = -static_cast<WebRtc_Word32>(1.0f / denom - 1.0f + 0.5f);
+        if (_dropCount > 0)
+        {
+            // Reset the _dropCount since we have a positive
+            // _dropCount, and it should be negative.
+            if (_dropRatio.Value() < 0.6f)
+            {
+                _dropCount = -_dropCount;
+            }
+            else
+            {
+                _dropCount = 0;
+            }
+        }
+        if (_dropCount > limit)
+        {
+            if (_dropCount == 0)
+            {
+                // Drop frames when we reset _dropCount.
+                _dropCount--;
+                return true;
+            }
+            else
+            {
+                // Keep frames as long as we haven't reached limit.
+                _dropCount--;
+                return false;
+            }
+        }
+        else
+        {
+            _dropCount = 0;
+            return false;
+        }
+    }
+    _dropCount = 0;
+    return false;
+
+    // A simpler version, unfiltered and quicker
+    //bool dropNext = _dropNext;
+    //_dropNext = false;
+    //return dropNext;
+}
+
+void
+FrameDropper::SetRates(float bitRate, float incoming_frame_rate)
+{
+    // Bit rate of -1 means infinite bandwidth.
+    _accumulatorMax = bitRate * _windowSize; // bitRate * windowSize (in seconds)
+    if (_targetBitRate > 0.0f && bitRate < _targetBitRate && _accumulator > _accumulatorMax)
+    {
+        // Rescale the accumulator level if the accumulator max decreases
+        _accumulator = bitRate / _targetBitRate * _accumulator;
+    }
+    _targetBitRate = bitRate;
+    CapAccumulator();
+    _incoming_frame_rate = incoming_frame_rate;
+}
+
+float
+FrameDropper::ActualFrameRate(WebRtc_UWord32 inputFrameRate) const
+{
+    if (!_enabled)
+    {
+        return static_cast<float>(inputFrameRate);
+    }
+    return inputFrameRate * (1.0f - _dropRatio.Value());
+}
+
+// Put a cap on the accumulator, i.e., don't let it grow beyond some level.
+// This is a temporary fix for screencasting where very large frames from
+// encoder will cause very slow response (too many frame drops).
+void FrameDropper::CapAccumulator() {
+  float max_accumulator = _targetBitRate * _cap_buffer_size;
+  if (_accumulator > max_accumulator) {
+    _accumulator = max_accumulator;
+  }
+}
+
+}
diff --git a/webrtc/modules/video_coding/utility/include/exp_filter.h b/webrtc/modules/video_coding/utility/include/exp_filter.h
new file mode 100644
index 0000000..42fbe3f
--- /dev/null
+++ b/webrtc/modules/video_coding/utility/include/exp_filter.h
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_EXP_FILTER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_EXP_FILTER_H_
+
+namespace webrtc
+{
+
+/**********************/
+/* ExpFilter class    */
+/**********************/
+
+class VCMExpFilter
+{
+public:
+    VCMExpFilter(float alpha, float max = -1.0) : _alpha(alpha), _filtered(-1.0), _max(max) {}
+
+    // Resets the filter to its initial state, and resets alpha to the given value
+    //
+    // Input:
+    //          - alpha     : the new value of the filter factor base.
+    void Reset(float alpha);
+
+    // Applies the filter with the given exponent on the provided sample
+    //
+    // Input:
+    //          - exp       : Exponent T in y(k) = alpha^T * y(k-1) + (1 - alpha^T) * x(k)
+    //          - sample    : x(k) in the above filter equation
+    float Apply(float exp, float sample);
+
+    // Return current filtered value: y(k)
+    //
+    // Return value         : The current filter output
+    float Value() const;
+
+    // Change the filter factor base
+    //
+    // Input:
+    //          - alpha     : The new filter factor base.
+    void UpdateBase(float alpha);
+
+private:
+    float          _alpha;     // Filter factor base
+    float          _filtered;  // Current filter output
+    const float    _max;
+}; // end of ExpFilter class
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_EXP_FILTER_H_
diff --git a/webrtc/modules/video_coding/utility/include/frame_dropper.h b/webrtc/modules/video_coding/utility/include/frame_dropper.h
new file mode 100644
index 0000000..fd15dd0
--- /dev/null
+++ b/webrtc/modules/video_coding/utility/include/frame_dropper.h
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_FRAME_DROPPER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_FRAME_DROPPER_H_
+
+#include "webrtc/modules/video_coding/utility/include/exp_filter.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc
+{
+
+// The Frame Dropper implements a variant of the leaky bucket algorithm
+// for keeping track of when to drop frames to avoid bit rate
+// over use when the encoder can't keep its bit rate.
+class FrameDropper
+{
+public:
+    FrameDropper();
+    virtual ~FrameDropper() {}
+
+    // Resets the FrameDropper to its initial state.
+    // This means that the frameRateWeight is set to its
+    // default value as well.
+    virtual void Reset();
+
+    virtual void Enable(bool enable);
+    // Answers the question if it's time to drop a frame
+    // if we want to reach a given frame rate. Must be
+    // called for every frame.
+    //
+    // Return value     : True if we should drop the current frame
+    virtual bool DropFrame();
+    // Updates the FrameDropper with the size of the latest encoded
+    // frame. The FrameDropper calculates a new drop ratio (can be
+    // seen as the probability to drop a frame) and updates its
+    // internal statistics.
+    //
+    // Input:
+    //          - frameSizeBytes    : The size of the latest frame
+    //                                returned from the encoder.
+    //          - deltaFrame        : True if the encoder returned
+    //                                a key frame.
+    virtual void Fill(WebRtc_UWord32 frameSizeBytes, bool deltaFrame);
+
+    virtual void Leak(WebRtc_UWord32 inputFrameRate);
+
+    void UpdateNack(WebRtc_UWord32 nackBytes);
+
+    // Sets the target bit rate and the frame rate produced by
+    // the camera.
+    //
+    // Input:
+    //          - bitRate       : The target bit rate
+    virtual void SetRates(float bitRate, float incoming_frame_rate);
+
+    // Return value     : The current average frame rate produced
+    //                    if the DropFrame() function is used as
+    //                    instruction of when to drop frames.
+    virtual float ActualFrameRate(WebRtc_UWord32 inputFrameRate) const;
+
+
+private:
+    void FillBucket(float inKbits, float outKbits);
+    void UpdateRatio();
+    void CapAccumulator();
+
+    VCMExpFilter       _keyFrameSizeAvgKbits;
+    VCMExpFilter       _keyFrameRatio;
+    float           _keyFrameSpreadFrames;
+    WebRtc_Word32     _keyFrameCount;
+    float           _accumulator;
+    float           _accumulatorMax;
+    float           _targetBitRate;
+    bool            _dropNext;
+    VCMExpFilter       _dropRatio;
+    WebRtc_Word32     _dropCount;
+    float           _windowSize;
+    float           _incoming_frame_rate;
+    bool            _wasBelowMax;
+    bool            _enabled;
+    bool            _fastMode;
+    float           _cap_buffer_size;
+    float           _max_time_drops;
+}; // end of VCMFrameDropper class
+
+} // namespace webrtc
+
+#endif // WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_FRAME_DROPPER_H_
diff --git a/webrtc/modules/video_coding/utility/include/mock/mock_frame_dropper.h b/webrtc/modules/video_coding/utility/include/mock/mock_frame_dropper.h
new file mode 100644
index 0000000..e8a6acc
--- /dev/null
+++ b/webrtc/modules/video_coding/utility/include/mock/mock_frame_dropper.h
@@ -0,0 +1,41 @@
+/*
+ *  Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_MOCK_MOCK_FRAME_DROPPER_H_
+#define WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_MOCK_MOCK_FRAME_DROPPER_H_
+
+#include <string>
+
+#include "gmock/gmock.h"
+#include "webrtc/modules/video_coding/utility/include/frame_dropper.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class MockFrameDropper : public FrameDropper {
+ public:
+  MOCK_METHOD0(Reset,
+      void());
+  MOCK_METHOD1(Enable,
+      void(bool enable));
+  MOCK_METHOD0(DropFrame,
+      bool());
+  MOCK_METHOD2(Fill,
+      void(WebRtc_UWord32 frameSizeBytes, bool deltaFrame));
+  MOCK_METHOD1(Leak,
+      void(WebRtc_UWord32 inputFrameRate));
+  MOCK_METHOD2(SetRates,
+      void(float bitRate, float incoming_frame_rate));
+  MOCK_CONST_METHOD1(ActualFrameRate,
+      float(WebRtc_UWord32 inputFrameRate));
+};
+
+}  // namespace webrtc
+
+#endif  // WEBRTC_MODULES_VIDEO_CODING_UTILITY_INCLUDE_MOCK_MOCK_FRAME_DROPPER_H_
diff --git a/webrtc/modules/video_coding/utility/video_coding_utility.gyp b/webrtc/modules/video_coding/utility/video_coding_utility.gyp
new file mode 100644
index 0000000..24f8880
--- /dev/null
+++ b/webrtc/modules/video_coding/utility/video_coding_utility.gyp
@@ -0,0 +1,28 @@
+# Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS.  All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+{
+  'includes': [
+    '../../../build/common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'video_coding_utility',
+      'type': 'static_library',
+      'dependencies': [
+        '<(webrtc_root)/system_wrappers/source/system_wrappers.gyp:system_wrappers',
+      ],
+      'sources': [
+        'include/exp_filter.h',
+        'include/frame_dropper.h',
+        'exp_filter.cc',
+        'frame_dropper.cc',
+      ],
+    },
+  ], # targets
+}