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
This commit is contained in:
stefan@webrtc.org
2013-02-18 14:40:18 +00:00
parent 3897255b63
commit eb91792cfd
20 changed files with 322 additions and 148 deletions

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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;
}
}
}

View File

@ -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_

View File

@ -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_

View File

@ -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_

View File

@ -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
}