Updates for resolution adaptation:
1) code cleanup and some updates to selection logic for qm_select.
2) added unit test for the QmResolution class.
3) update codec frame size and reset/update frame rate in media-opt:
4) removed unused motion vector metrics and some related code of content metrics processing.
Review URL: https://webrtc-codereview.appspot.com/405008
git-svn-id: http://webrtc.googlecode.com/svn/trunk@1791 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/src/modules/interface/module_common_types.h b/src/modules/interface/module_common_types.h
index 6d571a4..2e1beaa 100644
--- a/src/modules/interface/module_common_types.h
+++ b/src/modules/interface/module_common_types.h
@@ -393,30 +393,24 @@
VideoCodecType codec;
};
-// Video Content Metrics
-struct VideoContentMetrics
-{
- VideoContentMetrics(): motionMagnitudeNZ(0), sizeZeroMotion(0), spatialPredErr(0),
- spatialPredErrH(0), spatialPredErrV(0), motionPredErr(0),
- motionHorizontalness(0), motionClusterDistortion(0),
- nativeWidth(0), nativeHeight(0), contentChange(false) { }
- void Reset(){ motionMagnitudeNZ = 0; sizeZeroMotion = 0; spatialPredErr = 0;
- spatialPredErrH = 0; spatialPredErrV = 0; motionPredErr = 0;
- motionHorizontalness = 0; motionClusterDistortion = 0;
- nativeWidth = 0; nativeHeight = 0; contentChange = false; }
+struct VideoContentMetrics {
+ VideoContentMetrics()
+ : motion_magnitude(0.0f),
+ spatial_pred_err(0.0f),
+ spatial_pred_err_h(0.0f),
+ spatial_pred_err_v(0.0f) {
+ }
- float motionMagnitudeNZ;
- float sizeZeroMotion;
- float spatialPredErr;
- float spatialPredErrH;
- float spatialPredErrV;
- float motionPredErr;
- float motionHorizontalness;
- float motionClusterDistortion;
- WebRtc_UWord32 nativeWidth;
- WebRtc_UWord32 nativeHeight;
- WebRtc_UWord32 nativeFrameRate;
- bool contentChange;
+ void Reset() {
+ motion_magnitude = 0.0f;
+ spatial_pred_err = 0.0f;
+ spatial_pred_err_h = 0.0f;
+ spatial_pred_err_v = 0.0f;
+ }
+ float motion_magnitude;
+ float spatial_pred_err;
+ float spatial_pred_err_h;
+ float spatial_pred_err_v;
};
/*************************************************
diff --git a/src/modules/video_coding/main/source/content_metrics_processing.cc b/src/modules/video_coding/main/source/content_metrics_processing.cc
index 0805178..99160c9 100644
--- a/src/modules/video_coding/main/source/content_metrics_processing.cc
+++ b/src/modules/video_coding/main/source/content_metrics_processing.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ * Copyright (c) 2012 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
@@ -8,205 +8,118 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "content_metrics_processing.h"
-#include "module_common_types.h"
-#include "video_coding_defines.h"
+#include "modules/video_coding/main/source/content_metrics_processing.h"
#include <math.h>
-namespace webrtc {
+#include "modules/interface/module_common_types.h"
+#include "modules/video_coding/main/interface/video_coding_defines.h"
+namespace webrtc {
//////////////////////////////////
/// VCMContentMetricsProcessing //
//////////////////////////////////
-VCMContentMetricsProcessing::VCMContentMetricsProcessing():
-_frameRate(0),
-_recAvgFactor(1 / 150.0f), // matched to 30fps
-_frameCntRecursiveAvg(0),
-_frameCntUniformAvg(0),
-_avgMotionLevel(0.0f),
-_avgSpatialLevel(0.0f)
-{
- _recursiveAvg = new VideoContentMetrics();
- _uniformAvg = new VideoContentMetrics();
+VCMContentMetricsProcessing::VCMContentMetricsProcessing()
+ : recursive_avg_factor_(1 / 150.0f), // matched to 30fps.
+ frame_cnt_uniform_avg_(0),
+ avg_motion_level_(0.0f),
+ avg_spatial_level_(0.0f) {
+ recursive_avg_ = new VideoContentMetrics();
+ uniform_avg_ = new VideoContentMetrics();
}
-VCMContentMetricsProcessing::~VCMContentMetricsProcessing()
-{
- delete _recursiveAvg;
- delete _uniformAvg;
+VCMContentMetricsProcessing::~VCMContentMetricsProcessing() {
+ delete recursive_avg_;
+ delete uniform_avg_;
}
-WebRtc_Word32
-VCMContentMetricsProcessing::Reset()
-{
- _recursiveAvg->Reset();
- _uniformAvg->Reset();
- _frameRate = 0;
- _frameCntRecursiveAvg = 0;
- _frameCntUniformAvg = 0;
- _avgMotionLevel = 0.0f;
- _avgSpatialLevel = 0.0f;
+int VCMContentMetricsProcessing::Reset() {
+ recursive_avg_->Reset();
+ uniform_avg_->Reset();
+ frame_cnt_uniform_avg_ = 0;
+ avg_motion_level_ = 0.0f;
+ avg_spatial_level_ = 0.0f;
+ return VCM_OK;
+}
+
+void VCMContentMetricsProcessing::UpdateFrameRate(uint32_t frameRate) {
+ // Update factor for recursive averaging.
+ recursive_avg_factor_ = static_cast<float> (1000.0f) /
+ static_cast<float>(frameRate * kQmMinIntervalMs);
+}
+
+VideoContentMetrics* VCMContentMetricsProcessing::LongTermAvgData() {
+ return recursive_avg_;
+}
+
+VideoContentMetrics* VCMContentMetricsProcessing::ShortTermAvgData() {
+ if (frame_cnt_uniform_avg_ == 0) {
+ return NULL;
+ }
+ // Two metrics are used: motion and spatial level.
+ uniform_avg_->motion_magnitude = avg_motion_level_ /
+ static_cast<float>(frame_cnt_uniform_avg_);
+ uniform_avg_->spatial_pred_err = avg_spatial_level_ /
+ static_cast<float>(frame_cnt_uniform_avg_);
+ return uniform_avg_;
+}
+
+void VCMContentMetricsProcessing::ResetShortTermAvgData() {
+ // Reset.
+ avg_motion_level_ = 0.0f;
+ avg_spatial_level_ = 0.0f;
+ frame_cnt_uniform_avg_ = 0;
+}
+
+int VCMContentMetricsProcessing::UpdateContentData(
+ const VideoContentMetrics *contentMetrics) {
+ if (contentMetrics == NULL) {
return VCM_OK;
+ }
+ return ProcessContent(contentMetrics);
}
-void
-VCMContentMetricsProcessing::UpdateFrameRate(WebRtc_UWord32 frameRate)
-{
- _frameRate = frameRate;
- // Update factor for recursive averaging.
- _recAvgFactor = (float) 1000.0f / ((float)(_frameRate * kQmMinIntervalMs));
-
+int VCMContentMetricsProcessing::ProcessContent(
+ const VideoContentMetrics *contentMetrics) {
+ // Update the recursive averaged metrics: average is over longer window
+ // of time: over QmMinIntervalMs ms.
+ UpdateRecursiveAvg(contentMetrics);
+ // Update the uniform averaged metrics: average is over shorter window
+ // of time: based on ~RTCP reports.
+ UpdateUniformAvg(contentMetrics);
+ return VCM_OK;
}
-VideoContentMetrics*
-VCMContentMetricsProcessing::LongTermAvgData()
-{
- if (_frameCntRecursiveAvg == 0)
- {
- return NULL;
- }
- return _recursiveAvg;
+void VCMContentMetricsProcessing::UpdateUniformAvg(
+ const VideoContentMetrics *contentMetrics) {
+ // Update frame counter.
+ frame_cnt_uniform_avg_ += 1;
+ // Update averaged metrics: motion and spatial level are used.
+ avg_motion_level_ += contentMetrics->motion_magnitude;
+ avg_spatial_level_ += contentMetrics->spatial_pred_err;
+ return;
}
-VideoContentMetrics*
-VCMContentMetricsProcessing::ShortTermAvgData()
-{
- if (_frameCntUniformAvg == 0)
- {
- return NULL;
- }
+void VCMContentMetricsProcessing::UpdateRecursiveAvg(
+ const VideoContentMetrics *contentMetrics) {
- // Two metrics are used: motion and spatial level.
- _uniformAvg->motionMagnitudeNZ = _avgMotionLevel /
- (float)(_frameCntUniformAvg);
- _uniformAvg->spatialPredErr = _avgSpatialLevel /
- (float)(_frameCntUniformAvg);
+ // Spatial metrics: 2x2, 1x2(H), 2x1(V).
+ recursive_avg_->spatial_pred_err = (1 - recursive_avg_factor_) *
+ recursive_avg_->spatial_pred_err +
+ recursive_avg_factor_ * contentMetrics->spatial_pred_err;
- return _uniformAvg;
+ recursive_avg_->spatial_pred_err_h = (1 - recursive_avg_factor_) *
+ recursive_avg_->spatial_pred_err_h +
+ recursive_avg_factor_ * contentMetrics->spatial_pred_err_h;
+
+ recursive_avg_->spatial_pred_err_v = (1 - recursive_avg_factor_) *
+ recursive_avg_->spatial_pred_err_v +
+ recursive_avg_factor_ * contentMetrics->spatial_pred_err_v;
+
+ // Motion metric: Derived from NFD (normalized frame difference).
+ recursive_avg_->motion_magnitude = (1 - recursive_avg_factor_) *
+ recursive_avg_->motion_magnitude +
+ recursive_avg_factor_ * contentMetrics->motion_magnitude;
}
-
-void
-VCMContentMetricsProcessing::ResetShortTermAvgData()
-{
- // Reset
- _avgMotionLevel = 0.0f;
- _avgSpatialLevel = 0.0f;
- _frameCntUniformAvg = 0;
-}
-
-WebRtc_Word32
-VCMContentMetricsProcessing::UpdateContentData(const VideoContentMetrics *contentMetrics)
-{
- if (contentMetrics == NULL)
- {
- return VCM_OK;
- }
- return ProcessContent(contentMetrics);
-
-}
-
-WebRtc_UWord32
-VCMContentMetricsProcessing::ProcessContent(const VideoContentMetrics *contentMetrics)
-{
- // Update the recursive averaged metrics
- // average is over longer window of time: over QmMinIntervalMs ms.
- UpdateRecursiveAvg(contentMetrics);
-
- // Update the uniform averaged metrics:
- // average is over shorter window of time: based on ~RTCP reports.
- UpdateUniformAvg(contentMetrics);
-
- return VCM_OK;
-}
-
-void
-VCMContentMetricsProcessing::UpdateUniformAvg(const VideoContentMetrics *contentMetrics)
-{
-
- // Update frame counter
- _frameCntUniformAvg += 1;
-
- // Update averaged metrics: motion and spatial level are used.
- _avgMotionLevel += contentMetrics->motionMagnitudeNZ;
- _avgSpatialLevel += contentMetrics->spatialPredErr;
-
- return;
-
-}
-void
-VCMContentMetricsProcessing::UpdateRecursiveAvg(const VideoContentMetrics *contentMetrics)
-{
-
- // Threshold for size of zero motion cluster:
- // Use for updating 3 motion vector derived metrics:
- // motion magnitude, cluster distortion, and horizontalness.
- float nonZeroMvThr = 0.1f;
-
- float tmpRecAvgFactor = _recAvgFactor;
-
- // Take value as is for first frame (no motion search in frame zero).
- if (_frameCntRecursiveAvg < 1)
- {
- tmpRecAvgFactor = 1;
- }
-
- _recursiveAvg->motionPredErr = (1 - tmpRecAvgFactor) *
- _recursiveAvg->motionPredErr +
- tmpRecAvgFactor * contentMetrics->motionPredErr;
-
- _recursiveAvg->sizeZeroMotion = (1 - tmpRecAvgFactor) *
- _recursiveAvg->sizeZeroMotion +
- tmpRecAvgFactor * contentMetrics->sizeZeroMotion;
-
- _recursiveAvg->spatialPredErr = (1 - tmpRecAvgFactor) *
- _recursiveAvg->spatialPredErr +
- tmpRecAvgFactor * contentMetrics->spatialPredErr;
-
- _recursiveAvg->spatialPredErrH = (1 - tmpRecAvgFactor) *
- _recursiveAvg->spatialPredErrH +
- tmpRecAvgFactor * contentMetrics->spatialPredErrH;
-
- _recursiveAvg->spatialPredErrV = (1 - tmpRecAvgFactor) *
- _recursiveAvg->spatialPredErrV +
- tmpRecAvgFactor * contentMetrics->spatialPredErrV;
-
- // motionMag metric is derived from NFD (normalized frame difference).
- if (kNfdMetric == 1)
- {
- _recursiveAvg->motionMagnitudeNZ = (1 - tmpRecAvgFactor) *
- _recursiveAvg->motionMagnitudeNZ +
- tmpRecAvgFactor * contentMetrics->motionMagnitudeNZ;
- }
-
- if (contentMetrics->sizeZeroMotion > nonZeroMvThr)
- {
- _recursiveAvg->motionClusterDistortion = (1 - tmpRecAvgFactor) *
- _recursiveAvg->motionClusterDistortion +
- tmpRecAvgFactor *contentMetrics->motionClusterDistortion;
-
- _recursiveAvg->motionHorizontalness = (1 - _recAvgFactor) *
- _recursiveAvg->motionHorizontalness +
- tmpRecAvgFactor * contentMetrics->motionHorizontalness;
-
- // motionMag metric is derived from motion vectors.
- if (kNfdMetric == 0)
- {
- _recursiveAvg->motionMagnitudeNZ = (1 - tmpRecAvgFactor) *
- _recursiveAvg->motionMagnitudeNZ +
- tmpRecAvgFactor * contentMetrics->motionMagnitudeNZ;
- }
- }
-
- // Update native values:
- // TODO (marpan): we don't need to update this every frame.
- _recursiveAvg->nativeHeight = contentMetrics->nativeHeight;
- _recursiveAvg->nativeWidth = contentMetrics->nativeWidth;
- _recursiveAvg->nativeFrameRate = contentMetrics->nativeFrameRate;
-
- _frameCntRecursiveAvg++;
-
- return;
-}
-} //end of namespace
+} // end of namespace
diff --git a/src/modules/video_coding/main/source/content_metrics_processing.h b/src/modules/video_coding/main/source/content_metrics_processing.h
index 155c4ad..0317add 100644
--- a/src/modules/video_coding/main/source/content_metrics_processing.h
+++ b/src/modules/video_coding/main/source/content_metrics_processing.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ * Copyright (c) 2012 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
@@ -13,66 +13,64 @@
#include "typedefs.h"
-namespace webrtc
-{
+namespace webrtc {
struct VideoContentMetrics;
// QM interval time (in ms)
-enum { kQmMinIntervalMs = 10000 };
+enum {
+ kQmMinIntervalMs = 10000
+};
// Flag for NFD metric vs motion metric
-enum { kNfdMetric = 1 };
+enum {
+ kNfdMetric = 1
+};
/**********************************/
/* Content Metrics Processing */
/**********************************/
-class VCMContentMetricsProcessing
-{
-public:
- VCMContentMetricsProcessing();
- ~VCMContentMetricsProcessing();
+class VCMContentMetricsProcessing {
+ public:
+ VCMContentMetricsProcessing();
+ ~VCMContentMetricsProcessing();
- // Update class with latest metrics
- WebRtc_Word32 UpdateContentData(const VideoContentMetrics *contentMetrics);
+ // Update class with latest metrics.
+ int UpdateContentData(const VideoContentMetrics *contentMetrics);
- // Reset the short-term averaged content data
- void ResetShortTermAvgData();
+ // Reset the short-term averaged content data.
+ void ResetShortTermAvgData();
- // Initialize to
- WebRtc_Word32 Reset();
+ // Initialize.
+ int Reset();
- // Inform class of current frame rate
- void UpdateFrameRate(WebRtc_UWord32 frameRate);
+ // Inform class of current frame rate.
+ void UpdateFrameRate(uint32_t frameRate);
- // Returns the long-term averaged content data:
- // recursive average over longer time scale
- VideoContentMetrics* LongTermAvgData();
+ // Returns the long-term averaged content data: recursive average over longer
+ // time scale.
+ VideoContentMetrics* LongTermAvgData();
- // Returns the short-term averaged content data:
- // uniform average over shorter time scale
- VideoContentMetrics* ShortTermAvgData();
-private:
+ // Returns the short-term averaged content data: uniform average over
+ // shorter time scalE.
+ VideoContentMetrics* ShortTermAvgData();
- // Compute working avg
- WebRtc_UWord32 ProcessContent(const VideoContentMetrics *contentMetrics);
+ private:
+ // Compute working average.
+ int ProcessContent(const VideoContentMetrics *contentMetrics);
- // Update the recursive averaged metrics: longer time average (~5/10 secs).
- void UpdateRecursiveAvg(const VideoContentMetrics *contentMetrics);
+ // Update the recursive averaged metrics: longer time average (~5/10 secs).
+ void UpdateRecursiveAvg(const VideoContentMetrics *contentMetrics);
- // Update the uniform averaged metrics: shorter time average (~RTCP reports).
- void UpdateUniformAvg(const VideoContentMetrics *contentMetrics);
+ // Update the uniform averaged metrics: shorter time average (~RTCP report).
+ void UpdateUniformAvg(const VideoContentMetrics *contentMetrics);
- VideoContentMetrics* _recursiveAvg;
- VideoContentMetrics* _uniformAvg;
- WebRtc_UWord32 _frameRate;
- float _recAvgFactor;
- WebRtc_UWord32 _frameCntRecursiveAvg;
- WebRtc_UWord32 _frameCntUniformAvg;
- float _avgMotionLevel;
- float _avgSpatialLevel;
+ VideoContentMetrics* recursive_avg_;
+ VideoContentMetrics* uniform_avg_;
+ float recursive_avg_factor_;
+ uint32_t frame_cnt_uniform_avg_;
+ float avg_motion_level_;
+ float avg_spatial_level_;
};
-
-} // namespace webrtc
-
-#endif // WEBRTC_MODULES_VIDEO_CODING_CONTENT_METRICS_PROCESSING_H_
+} // namespace webrtc
+#endif // WEBRTC_MODULES_VIDEO_CODING_CONTENT_METRICS_PROCESSING_H_
diff --git a/src/modules/video_coding/main/source/media_opt_util.cc b/src/modules/video_coding/main/source/media_opt_util.cc
index 2c6f50b..b520278 100644
--- a/src/modules/video_coding/main/source/media_opt_util.cc
+++ b/src/modules/video_coding/main/source/media_opt_util.cc
@@ -20,8 +20,6 @@
#include "modules/video_coding/main/source/er_tables_xor.h"
#include "modules/video_coding/main/source/fec_tables_xor.h"
#include "modules/video_coding/main/source/nack_fec_tables.h"
-#include "modules/video_coding/main/source/qm_select_data.h"
-
namespace webrtc {
diff --git a/src/modules/video_coding/main/source/media_optimization.cc b/src/modules/video_coding/main/source/media_optimization.cc
index e2c4329..cae4912 100644
--- a/src/modules/video_coding/main/source/media_optimization.cc
+++ b/src/modules/video_coding/main/source/media_optimization.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ * Copyright (c) 2012 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
@@ -24,6 +24,8 @@
_sendCodecType(kVideoCodecUnknown),
_codecWidth(0),
_codecHeight(0),
+_initCodecWidth(0),
+_initCodecHeight(0),
_userFrameRate(0),
_packetLossEnc(0),
_fractionLost(0),
@@ -64,7 +66,7 @@
VCMMediaOptimization::Reset()
{
memset(_incomingFrameTimes, -1, sizeof(_incomingFrameTimes));
- InputFrameRate(); // Resets _incomingFrameRate
+ _incomingFrameRate = 0.0;
_frameDropper->Reset();
_lossProtLogic->Reset(_clock->MillisecondTimestamp());
_frameDropper->SetRates(0, 0);
@@ -131,6 +133,7 @@
uint32_t protection_overhead_kbps = 0;
// Update protection settings, when applicable
+ float sent_video_rate = 0.0f;
if (selectedMethod)
{
// Update protection method with content metrics
@@ -168,6 +171,7 @@
// Get the effective packet loss for encoder ER
// when applicable, should be passed to encoder via fractionLost
packetLossEnc = selectedMethod->RequiredPacketLossER();
+ sent_video_rate = static_cast<float>(sent_video_rate_bps / 1000.0);
}
// Source coding rate: total rate - protection overhead
@@ -179,7 +183,7 @@
if (_enableQm && _numLayers == 1)
{
// Update QM with rates
- _qmResolution->UpdateRates((float)_targetBitRate, _avgSentBitRateBps,
+ _qmResolution->UpdateRates((float)_targetBitRate, sent_video_rate,
_incomingFrameRate, _fractionLost);
// Check for QM selection
bool selectQM = checkStatusForQMchange();
@@ -282,6 +286,8 @@
_userFrameRate = static_cast<float>(frameRate);
_codecWidth = width;
_codecHeight = height;
+ _initCodecWidth = width;
+ _initCodecHeight = height;
_numLayers = (numLayers <= 1) ? 1 : numLayers; // Can also be zero.
WebRtc_Word32 ret = VCM_OK;
ret = _qmResolution->Initialize((float)_targetBitRate, _userFrameRate,
@@ -525,7 +531,7 @@
WebRtc_Word32 ret = _qmResolution->SelectResolution(&qm);
if (ret < 0)
{
- return ret;
+ return ret;
}
// Check for updates to spatial/temporal modes
@@ -575,43 +581,50 @@
// Check for no change
if (qm->spatialHeightFact == 1 &&
qm->spatialWidthFact == 1 &&
- qm->temporalFact == 1)
- {
+ qm->temporalFact == 1) {
return false;
}
- // Content metrics hold native values
- VideoContentMetrics* cm = _content->LongTermAvgData();
-
// Temporal
WebRtc_UWord32 frameRate = static_cast<WebRtc_UWord32>
(_incomingFrameRate + 0.5f);
// Check if go back up in temporal resolution
- if (qm->temporalFact == 0)
- {
- frameRate = (WebRtc_UWord32) 2 * _incomingFrameRate;
+ if (qm->temporalFact == 0) {
+ // Currently only allow for 1/2 frame rate reduction per action.
+ // TODO (marpan): allow for 2/3 reduction.
+ frameRate = (WebRtc_UWord32) 2 * _incomingFrameRate;
}
// go down in temporal resolution
- else
- {
- frameRate = (WebRtc_UWord32)(_incomingFrameRate / qm->temporalFact + 1);
+ else {
+ frameRate = (WebRtc_UWord32)(_incomingFrameRate / qm->temporalFact + 1);
+ }
+ // Reset _incomingFrameRate if temporal action was selected.
+ if (qm->temporalFact != 1) {
+ memset(_incomingFrameTimes, -1, sizeof(_incomingFrameTimes));
+ _incomingFrameRate = frameRate;
}
// Spatial
WebRtc_UWord32 height = _codecHeight;
WebRtc_UWord32 width = _codecWidth;
- // Check if go back up in spatial resolution
- if (qm->spatialHeightFact == 0 && qm->spatialWidthFact == 0)
- {
- height = cm->nativeHeight;
- width = cm->nativeWidth;
+ // Check if go back up in spatial resolution, and update frame sizes.
+ // Currently only allow for 2x2 spatial down-sampling.
+ // TODO (marpan): allow for 1x2, 2x1, and 4/3x4/3 (or 3/2x3/2).
+ if (qm->spatialHeightFact == 0 && qm->spatialWidthFact == 0) {
+ width = _codecWidth * 2;
+ height = _codecHeight * 2;
+ } else {
+ width = _codecWidth / qm->spatialWidthFact;
+ height = _codecHeight / qm->spatialHeightFact;
}
- else
- {
- height = _codecHeight / qm->spatialHeightFact;
- width = _codecWidth / qm->spatialWidthFact;
- }
+ _codecWidth = width;
+ _codecHeight = height;
+
+ // New frame sizes should never exceed the original sizes
+ // from SetEncodingData().
+ assert(_codecWidth <= _initCodecWidth);
+ assert(_codecHeight <= _initCodecHeight);
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, _id,
"Quality Mode Update: W = %d, H = %d, FR = %f",
@@ -620,11 +633,12 @@
// Update VPM with new target frame rate and size
_videoQMSettingsCallback->SetVideoQMSettings(frameRate, width, height);
+ _content->UpdateFrameRate(frameRate);
+ _qmResolution->UpdateCodecFrameSize(width, height);
+
return true;
}
-
-
void
VCMMediaOptimization::UpdateIncomingFrameRate()
{
@@ -671,10 +685,6 @@
_incomingFrameRate = nrOfFrames * 1000.0f / static_cast<float>(diff);
}
}
- else
- {
- _incomingFrameRate = static_cast<float>(nrOfFrames);
- }
}
WebRtc_UWord32
diff --git a/src/modules/video_coding/main/source/media_optimization.h b/src/modules/video_coding/main/source/media_optimization.h
index 7d87a6d..14e5d1a 100644
--- a/src/modules/video_coding/main/source/media_optimization.h
+++ b/src/modules/video_coding/main/source/media_optimization.h
@@ -168,6 +168,8 @@
VideoCodecType _sendCodecType;
WebRtc_UWord16 _codecWidth;
WebRtc_UWord16 _codecHeight;
+ WebRtc_UWord16 _initCodecWidth;
+ WebRtc_UWord16 _initCodecHeight;
float _userFrameRate;
VCMFrameDropper* _frameDropper;
diff --git a/src/modules/video_coding/main/source/qm_select.cc b/src/modules/video_coding/main/source/qm_select.cc
index 99fc89d..c4bd707 100644
--- a/src/modules/video_coding/main/source/qm_select.cc
+++ b/src/modules/video_coding/main/source/qm_select.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ * Copyright (c) 2012 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
@@ -8,784 +8,657 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "qm_select.h"
-#include "internal_defines.h"
-#include "qm_select_data.h"
-
-#include "module_common_types.h"
-#include "video_coding_defines.h"
-#include "trace.h"
+#include "modules/video_coding/main/source/qm_select.h"
#include <math.h>
+#include "modules/interface/module_common_types.h"
+#include "modules/video_coding/main/source/internal_defines.h"
+#include "modules/video_coding/main/source/qm_select_data.h"
+#include "modules/video_coding/main/interface/video_coding_defines.h"
+#include "system_wrappers/interface/trace.h"
+
namespace webrtc {
// QM-METHOD class
VCMQmMethod::VCMQmMethod()
- : _contentMetrics(new VideoContentMetrics()),
+ : _contentMetrics(NULL),
_width(0),
_height(0),
_nativeWidth(0),
_nativeHeight(0),
- _nativeFrameRate(0),
+ _frameRateLevel(kDefault),
_init(false) {
ResetQM();
}
-VCMQmMethod::~VCMQmMethod()
-{
- delete _contentMetrics;
+VCMQmMethod::~VCMQmMethod() {
}
-void
-VCMQmMethod::ResetQM()
-{
- _motion.Reset();
- _spatial.Reset();
- _coherence.Reset();
- _stationaryMotion = 0;
- _aspectRatio = 1;
- _imageType = 2;
- return;
+void VCMQmMethod::ResetQM() {
+ _aspectRatio = 1.0f;
+ _imageType = 2;
+ _motion.Reset();
+ _spatial.Reset();
+ _contentClass = 0;
}
-void
-VCMQmMethod::UpdateContent(const VideoContentMetrics* contentMetrics)
-{
- _contentMetrics = contentMetrics;
+uint8_t VCMQmMethod::ComputeContentClass() {
+ ComputeMotionNFD();
+ ComputeSpatial();
+ return _contentClass = 3 * _motion.level + _spatial.level;
}
-void
-VCMQmMethod::MotionNFD()
-{
- _motion.value = _contentMetrics->motionMagnitudeNZ;
-
- // Determine motion level
- if (_motion.value < LOW_MOTION_NFD)
- {
- _motion.level = kLow;
- }
- else if (_motion.value > HIGH_MOTION_NFD)
- {
- _motion.level = kHigh;
- }
- else
- {
- _motion.level = kDefault;
- }
-
+void VCMQmMethod::UpdateContent(const VideoContentMetrics* contentMetrics) {
+ _contentMetrics = contentMetrics;
}
-void
-VCMQmMethod::Motion()
-{
-
- float sizeZeroMotion = _contentMetrics->sizeZeroMotion;
- float motionMagNZ = _contentMetrics->motionMagnitudeNZ;
-
- // Take product of size and magnitude with equal weight
- _motion.value = (1.0f - sizeZeroMotion) * motionMagNZ;
-
- // Stabilize: motionMagNZ could be large when only a
- // few motion blocks are non-zero
- _stationaryMotion = false;
- if (sizeZeroMotion > HIGH_ZERO_MOTION_SIZE)
- {
- _motion.value = 0.0f;
- _stationaryMotion = true;
- }
- // Determine motion level
- if (_motion.value < LOW_MOTION)
- {
- _motion.level = kLow;
- }
- else if (_motion.value > HIGH_MOTION)
- {
- _motion.level = kHigh;
- }
- else
- {
- _motion.level = kDefault;
- }
+void VCMQmMethod::ComputeMotionNFD() {
+ if (_contentMetrics) {
+ _motion.value = _contentMetrics->motion_magnitude;
+ }
+ // Determine motion level.
+ if (_motion.value < kLowMotionNfd) {
+ _motion.level = kLow;
+ } else if (_motion.value > kHighMotionNfd) {
+ _motion.level = kHigh;
+ } else {
+ _motion.level = kDefault;
+ }
}
+void VCMQmMethod::ComputeSpatial() {
+ float spatialErr = 0.0;
+ float spatialErrH = 0.0;
+ float spatialErrV = 0.0;
+ if (_contentMetrics) {
+ spatialErr = _contentMetrics->spatial_pred_err;
+ spatialErrH = _contentMetrics->spatial_pred_err_h;
+ spatialErrV = _contentMetrics->spatial_pred_err_v;
+ }
+ // Spatial measure: take average of 3 prediction errors.
+ _spatial.value = (spatialErr + spatialErrH + spatialErrV) / 3.0f;
-void
-VCMQmMethod::Spatial()
-{
- float spatialErr = _contentMetrics->spatialPredErr;
- float spatialErrH = _contentMetrics->spatialPredErrH;
- float spatialErrV = _contentMetrics->spatialPredErrV;
- // Spatial measure: take average of 3 prediction errors
- _spatial.value = (spatialErr + spatialErrH + spatialErrV) / 3.0f;
+ // Reduce thresholds for large scenes/higher pixel correlation (~>=WHD).
+ float scale2 = _imageType > 3 ? kScaleTexture : 1.0;
- float scale = 1.0f;
- // Reduce thresholds for HD scenes
- if (_imageType > 3)
- {
- scale = (float)SCALE_TEXTURE_HD;
- }
-
- if (_spatial.value > scale * HIGH_TEXTURE)
- {
- _spatial.level = kHigh;
- }
- else if (_spatial.value < scale * LOW_TEXTURE)
- {
- _spatial.level = kLow;
- }
- else
- {
- _spatial.level = kDefault;
- }
+ if (_spatial.value > scale2 * kHighTexture) {
+ _spatial.level = kHigh;
+ } else if (_spatial.value < scale2 * kLowTexture) {
+ _spatial.level = kLow;
+ } else {
+ _spatial.level = kDefault;
+ }
}
-void
-VCMQmMethod::Coherence()
-{
- float horizNZ = _contentMetrics->motionHorizontalness;
- float distortionNZ = _contentMetrics->motionClusterDistortion;
-
- // Coherence measure: combine horizontalness with cluster distortion
- _coherence.value = COH_MAX;
- if (distortionNZ > 0.)
- {
- _coherence.value = horizNZ / distortionNZ;
- }
- _coherence.value = VCM_MIN(COH_MAX, _coherence.value);
-
- if (_coherence.value < COHERENCE_THR)
- {
- _coherence.level = kLow;
- }
- else
- {
- _coherence.level = kHigh;
- }
-
+uint8_t VCMQmMethod::GetImageType(uint16_t width,
+ uint16_t height) {
+ // Get the closest image type for encoder frame size.
+ uint32_t imageSize = width * height;
+ if (imageSize < kFrameSizeTh[0]) {
+ return 0; // QCIF
+ } else if (imageSize < kFrameSizeTh[1]) {
+ return 1; // CIF
+ } else if (imageSize < kFrameSizeTh[2]) {
+ return 2; // VGA
+ } else if (imageSize < kFrameSizeTh[3]) {
+ return 3; // 4CIF
+ } else if (imageSize < kFrameSizeTh[4]) {
+ return 4; // 720,4:3
+ } else if (imageSize < kFrameSizeTh[5]) {
+ return 5; // WHD
+ } else {
+ return 6; // HD
+ }
}
-WebRtc_Word8
-VCMQmMethod::GetImageType(WebRtc_UWord32 width, WebRtc_UWord32 height)
-{
- // Match image type
- WebRtc_UWord32 imageSize = width * height;
- WebRtc_Word8 imageType;
-
- if (imageSize < kFrameSizeTh[0])
- {
- imageType = 0;
- }
- else if (imageSize < kFrameSizeTh[1])
- {
- imageType = 1;
- }
- else if (imageSize < kFrameSizeTh[2])
- {
- imageType = 2;
- }
- else if (imageSize < kFrameSizeTh[3])
- {
- imageType = 3;
- }
- else if (imageSize < kFrameSizeTh[4])
- {
- imageType = 4;
- }
- else if (imageSize < kFrameSizeTh[5])
- {
- imageType = 5;
- }
- else
- {
- imageType = 6;
- }
-
- return imageType;
+LevelClass VCMQmMethod::FrameRateLevel(float avgFrameRate) {
+ if (avgFrameRate < kLowFrameRate) {
+ return kLow;
+ } else if (avgFrameRate > kHighFrameRate) {
+ return kHigh;
+ } else {
+ return kDefault;
+ }
}
-// DONE WITH QM CLASS
-
-
-//RESOLUTION CLASS
+// RESOLUTION CLASS
VCMQmResolution::VCMQmResolution()
-{
- _qm = new VCMResolutionScale();
- Reset();
+ : _qm(new VCMResolutionScale()) {
+ Reset();
}
-VCMQmResolution::~VCMQmResolution()
-{
- delete _qm;
+VCMQmResolution::~VCMQmResolution() {
+ delete _qm;
}
-void
-VCMQmResolution::ResetRates()
-{
- _sumEncodedBytes = 0;
- _sumTargetRate = 0.0f;
- _sumIncomingFrameRate = 0.0f;
- _sumFrameRateMM = 0.0f;
- _sumSeqRateMM = 0.0f;
- _sumPacketLoss = 0.0f;
- _frameCnt = 0;
- _frameCntDelta = 0;
- _lowBufferCnt = 0;
- _updateRateCnt = 0;
- return;
+void VCMQmResolution::ResetRates() {
+ _sumTargetRate = 0.0f;
+ _sumIncomingFrameRate = 0.0f;
+ _sumRateMM = 0.0f;
+ _sumRateMMSgn = 0;
+ _sumPacketLoss = 0.0f;
+ _frameCnt = 0;
+ _frameCntDelta = 0;
+ _lowBufferCnt = 0;
+ _updateRateCnt = 0;
}
-void
-VCMQmResolution::Reset()
-{
- _stateDecFactorSpatial = 1;
- _stateDecFactorTemp = 1;
- _bufferLevel = 0.0f;
- _targetBitRate = 0.0f;
- _incomingFrameRate = 0.0f;
- _userFrameRate = 0.0f;
- _perFrameBandwidth =0.0f;
- ResetRates();
- ResetQM();
- return;
+void VCMQmResolution::ResetDownSamplingState() {
+ _stateDecFactorSpatial = 1;
+ _stateDecFactorTemp = 1;
}
-// Initialize rate control quantities after reset of encoder
-WebRtc_Word32
-VCMQmResolution::Initialize(float bitRate, float userFrameRate,
- WebRtc_UWord32 width, WebRtc_UWord32 height)
-{
- if (userFrameRate == 0.0f || width == 0 || height == 0)
- {
- return VCM_PARAMETER_ERROR;
- }
- _targetBitRate = bitRate;
- _userFrameRate = userFrameRate;
+void VCMQmResolution::Reset() {
+ _targetBitRate = 0.0f;
+ _userFrameRate = 0.0f;
+ _incomingFrameRate = 0.0f;
+ _perFrameBandwidth =0.0f;
+ _bufferLevel = 0.0f;
+ _avgTargetRate = 0.0f;
+ _avgIncomingFrameRate = 0.0f;
+ _avgRatioBufferLow = 0.0f;
+ _avgRateMisMatch = 0.0f;
+ _avgRateMisMatchSgn = 0.0f;
+ _avgPacketLoss = 0.0f;
+ _encoderState = kStableEncoding;
+ ResetRates();
+ ResetDownSamplingState();
+ ResetQM();
+}
- // Encoder width and height
- _width = width;
- _height = height;
+EncoderState VCMQmResolution::GetEncoderState() {
+ return _encoderState;
+}
- // Aspect ratio: used for selection of 1x2,2x1,2x2
- _aspectRatio = static_cast<float>(_width) / static_cast<float>(_height);
+// Initialize state after re-initializing the encoder,
+// i.e., after SetEncodingData() in mediaOpt.
+int VCMQmResolution::Initialize(float bitRate,
+ float userFrameRate,
+ uint16_t width,
+ uint16_t height) {
+ if (userFrameRate == 0.0f || width == 0 || height == 0) {
+ return VCM_PARAMETER_ERROR;
+ }
+ Reset();
+ _targetBitRate = bitRate;
+ _userFrameRate = userFrameRate;
+ _incomingFrameRate = userFrameRate;
+ UpdateCodecFrameSize(width, height);
+ _nativeWidth = width;
+ _nativeHeight = height;
+ // Initial buffer level.
+ _bufferLevel = kInitBufferLevel * _targetBitRate;
+ // Per-frame bandwidth.
+ _perFrameBandwidth = _targetBitRate / _userFrameRate;
+ _init = true;
+ return VCM_OK;
+}
- // Set the imageType for the encoder width/height.
- _imageType = GetImageType(_width, _height);
+void VCMQmResolution::UpdateCodecFrameSize(uint16_t width, uint16_t height) {
+ _width = width;
+ _height = height;
+ // Set the imageType for the encoder width/height.
+ _imageType = GetImageType(width, height);
+}
- // Initial buffer level
- _bufferLevel = INIT_BUFFER_LEVEL * _targetBitRate;
+// Update rate data after every encoded frame.
+void VCMQmResolution::UpdateEncodedSize(int encodedSize,
+ FrameType encodedFrameType) {
+ _frameCnt++;
+ // Convert to Kbps.
+ float encodedSizeKbits = static_cast<float>((encodedSize * 8.0) / 1000.0);
- // Per-frame bandwidth
- if ( _incomingFrameRate == 0 )
- {
- _perFrameBandwidth = _targetBitRate / _userFrameRate;
- _incomingFrameRate = _userFrameRate;
- }
- else
- {
- // Take average: this is due to delay in update of new encoder frame rate:
- // userFrameRate is the new one,
- // incomingFrameRate is the old one (based on previous ~ 1sec/RTCP report)
- _perFrameBandwidth = 0.5 *( _targetBitRate / _userFrameRate +
- _targetBitRate / _incomingFrameRate );
- }
- _init = true;
+ // Update the buffer level:
+ // Note this is not the actual encoder buffer level.
+ // |_bufferLevel| is reset to 0 every time SelectResolution is called, and
+ // does not account for frame dropping by encoder or VCM.
+ _bufferLevel += _perFrameBandwidth - encodedSizeKbits;
+ // Counter for occurrences of low buffer level:
+ // low/negative values means encoder is likely dropping frames.
+ if (_bufferLevel <= kPercBufferThr * kOptBufferLevel * _targetBitRate) {
+ _lowBufferCnt++;
+ }
+}
+// Update various quantities after SetTargetRates in MediaOpt.
+void VCMQmResolution::UpdateRates(float targetBitRate,
+ float encoderSentRate,
+ float incomingFrameRate,
+ uint8_t packetLoss) {
+ // Sum the target bitrate and incoming frame rate:
+ // these values are the encoder rates (from previous update ~1sec),
+ // i.e, before the update for next ~1sec.
+ _sumTargetRate += _targetBitRate;
+ _sumIncomingFrameRate += _incomingFrameRate;
+ _updateRateCnt++;
+ // Sum the received (from RTCP reports) packet loss rates.
+ _sumPacketLoss += static_cast<float>(packetLoss / 255.0);
+
+ // Sum the sequence rate mismatch:
+ // Mismatch here is based on the difference between the target rate
+ // used (in previous ~1sec) and the average actual encoding rate measured
+ // at previous ~1sec.
+ float diff = _targetBitRate - encoderSentRate;
+ if (_targetBitRate > 0.0)
+ _sumRateMM += fabs(diff) / _targetBitRate;
+ int sgnDiff = diff > 0 ? 1 : (diff < 0 ? -1 : 0);
+ // To check for consistent under(+)/over_shooting(-) of target rate.
+ _sumRateMMSgn += sgnDiff;
+
+ // Update with the current new target and frame rate:
+ // these values are ones the encoder will use for the current/next ~1sec
+ _targetBitRate = targetBitRate;
+ _incomingFrameRate = incomingFrameRate;
+
+ // Update the per_frame_bandwidth:
+ // this is the per_frame_bw for the current/next ~1sec
+ _perFrameBandwidth = 0.0f;
+ if (_incomingFrameRate > 0.0f) {
+ _perFrameBandwidth = _targetBitRate / _incomingFrameRate;
+ }
+}
+
+// Select the resolution factors: frame size and frame rate change (qm scales).
+// Selection is for going down in resolution, or for going back up
+// (if a previous down-sampling action was taken).
+
+// In the current version the following constraints are imposed:
+// 1) we only allow for one action (either down or back up) at a given time.
+// 2) the possible down-sampling actions are: 2x2 spatial and 1/2 temporal.
+// 3) the total amount of down-sampling (spatial and/or temporal) from the
+// initial (native) resolution is limited by various factors.
+
+// TODO(marpan): extend to allow options for: 4/3x4/3, 1x2, 2x1 spatial,
+// and 2/3 temporal (i.e., skip every third frame).
+int VCMQmResolution::SelectResolution(VCMResolutionScale** qm) {
+ if (!_init) {
+ return VCM_UNINITIALIZED;
+ }
+ if (_contentMetrics == NULL) {
+ Reset();
+ *qm = _qm;
return VCM_OK;
-}
+ }
-// Update after every encoded frame
-void
-VCMQmResolution::UpdateEncodedSize(WebRtc_Word64 encodedSize,
- FrameType encodedFrameType)
-{
- // Update encoded size;
- _sumEncodedBytes += encodedSize;
- _frameCnt++;
+ // Default settings: no action.
+ _qm->spatialWidthFact = 1;
+ _qm->spatialHeightFact = 1;
+ _qm->temporalFact = 1;
+ *qm = _qm;
- // Convert to Kbps
- float encodedSizeKbits = (float)((encodedSize * 8.0) / 1000.0);
+ // Compute content class for selection.
+ _contentClass = ComputeContentClass();
- // Update the buffer level:
- // per_frame_BW is updated when encoder is updated, every RTCP reports
- _bufferLevel += _perFrameBandwidth - encodedSizeKbits;
+ // Compute various rate quantities for selection.
+ ComputeRatesForSelection();
- // Mismatch here is based on difference of actual encoded frame size and
- // per-frame bandwidth, for delta frames
- // This is a much stronger condition on rate mismatch than sumSeqRateMM
- // Note: not used in this version
- /*
- const bool deltaFrame = (encodedFrameType != kVideoFrameKey &&
- encodedFrameType != kVideoFrameGolden);
+ // Get the encoder state.
+ ComputeEncoderState();
- // Sum the frame mismatch:
- if (deltaFrame)
- {
- _frameCntDelta++;
- if (encodedSizeKbits > 0)
- _sumFrameRateMM +=
- (float) (fabs(encodedSizeKbits - _perFrameBandwidth) /
- encodedSizeKbits);
- }
- */
-
- // Counter for occurrences of low buffer level
- if (_bufferLevel <= PERC_BUFFER_THR * OPT_BUFFER_LEVEL * _targetBitRate)
- {
- _lowBufferCnt++;
- }
-
-}
-
-// Update various quantities after SetTargetRates in MediaOpt
-void
-VCMQmResolution::UpdateRates(float targetBitRate, float avgSentBitRate,
- float incomingFrameRate, WebRtc_UWord8 packetLoss)
-{
-
- // Sum the target bitrate and incoming frame rate:
- // these values are the encoder rates (from previous update ~1sec),
- // i.e, before the update for next ~1sec
- _sumTargetRate += _targetBitRate;
- _sumIncomingFrameRate += _incomingFrameRate;
- _updateRateCnt++;
-
- // Sum the received (from RTCP reports) packet loss rates
- _sumPacketLoss += (float) packetLoss / 255.0f;
-
- // Convert average sent bitrate to kbps
- float avgSentBitRatekbps = avgSentBitRate / 1000.0f;
-
- // Sum the sequence rate mismatch:
- // Mismatch here is based on difference between target rate the encoder
- // used (in previous ~1sec) and the average actual
- // encoding rate measured at current time
- if (fabs(_targetBitRate - avgSentBitRatekbps) < THRESH_SUM_MM &&
- _targetBitRate > 0.0 )
- {
- _sumSeqRateMM += (float)
- (fabs(_targetBitRate - avgSentBitRatekbps) / _targetBitRate );
- }
-
- // Update QM with the current new target and frame rate:
- // these values are ones the encoder will use for the current/next ~1sec
- _targetBitRate = targetBitRate;
- _incomingFrameRate = incomingFrameRate;
-
- // Update QM with an (average) encoder per_frame_bandwidth:
- // this is the per_frame_bw for the current/next ~1sec
- _perFrameBandwidth = 0.0f;
- if (_incomingFrameRate > 0.0f)
- {
- _perFrameBandwidth = _targetBitRate / _incomingFrameRate;
- }
-
-}
-
-// Select the resolution factors: frame size and frame rate change: (QM modes)
-// Selection is for going back up in resolution, or going down in.
-WebRtc_Word32
-VCMQmResolution::SelectResolution(VCMResolutionScale** qm)
-{
- if (!_init)
- {
- return VCM_UNINITIALIZED;
- }
- if (_contentMetrics == NULL)
- {
- Reset(); //default values
- *qm = _qm;
- return VCM_OK;
- }
-
- // Default settings
- _qm->spatialWidthFact = 1;
- _qm->spatialHeightFact = 1;
- _qm->temporalFact = 1;
-
- // Update native values
- _nativeWidth = _contentMetrics->nativeWidth;
- _nativeHeight = _contentMetrics->nativeHeight;
- _nativeFrameRate = _contentMetrics->nativeFrameRate;
-
- float avgTargetRate = 0.0f;
- float avgIncomingFrameRate = 0.0f;
- float ratioBufferLow = 0.0f;
- float rateMisMatch = 0.0f;
- float avgPacketLoss = 0.0f;
- if (_frameCnt > 0)
- {
- ratioBufferLow = (float)_lowBufferCnt / (float)_frameCnt;
- }
- if (_updateRateCnt > 0)
- {
- // Use seq-rate mismatch for now
- rateMisMatch = (float)_sumSeqRateMM / (float)_updateRateCnt;
- //rateMisMatch = (float)_sumFrameRateMM / (float)_frameCntDelta;
-
- // Average target and incoming frame rates
- avgTargetRate = (float)_sumTargetRate / (float)_updateRateCnt;
- avgIncomingFrameRate = (float)_sumIncomingFrameRate /
- (float)_updateRateCnt;
-
- // Average received packet loss rate
- avgPacketLoss = (float)_sumPacketLoss / (float)_updateRateCnt;
- }
-
- // For QM selection below, may want to weight the average encoder rates
- // with the current (for next ~1sec) rate values.
- // Uniform average for now:
- float w1 = 0.5f;
- float w2 = 0.5f;
- avgTargetRate = w1 * avgTargetRate + w2 * _targetBitRate;
- avgIncomingFrameRate = w1 * avgIncomingFrameRate + w2 * _incomingFrameRate;
-
- // Set the maximum transitional rate and image type:
- // for up-sampled spatial dimensions.
- // This is needed to get the transRate for going back up in
- // spatial resolution (only 2x2 allowed in this version).
- WebRtc_UWord8 imageType2 = GetImageType(2 * _width, 2 * _height);
- WebRtc_UWord32 maxRateQM2 = kMaxRateQm[imageType2];
-
- // Set the maximum transitional rate and image type:
- // for the encoder spatial dimensions.
- WebRtc_UWord32 maxRateQM = kMaxRateQm[_imageType];
-
- // Compute class state of the content.
- MotionNFD();
- Spatial();
-
- //
- // Get transitional rate from table, based on image type and content class.
- //
-
- // Get image class and content class: for going down spatially
- WebRtc_UWord8 imageClass = 1;
- if (_imageType <= 3) imageClass = 0;
- WebRtc_UWord8 contentClass = 3 * _motion.level + _spatial.level;
- WebRtc_UWord8 tableIndex = imageClass * 9 + contentClass;
- float scaleTransRate = kScaleTransRateQm[tableIndex];
-
- // Get image class and content class: for going up spatially
- WebRtc_UWord8 imageClass2 = 1;
- if (imageType2 <= 3)
- {
- imageClass2 = 0;
- }
- WebRtc_UWord8 tableIndex2 = imageClass2 * 9 + contentClass;
- float scaleTransRate2 = kScaleTransRateQm[tableIndex2];
-
- // Transitonal rate for going down
- WebRtc_UWord32 estimatedTransRateDown = static_cast<WebRtc_UWord32>
- (_incomingFrameRate * scaleTransRate * maxRateQM / 30);
-
- // Transitional rate for going up temporally
- WebRtc_UWord32 estimatedTransRateUpT = static_cast<WebRtc_UWord32>
- (TRANS_RATE_SCALE_UP_TEMP * 2 * _incomingFrameRate *
- scaleTransRate * maxRateQM / 30);
-
- // Transitional rate for going up spatially
- WebRtc_UWord32 estimatedTransRateUpS = static_cast<WebRtc_UWord32>
- (TRANS_RATE_SCALE_UP_SPATIAL * _incomingFrameRate *
- scaleTransRate2 * maxRateQM2 / 30);
-
- //
- // Done with transitional rates
- //
-
- //
- //CHECK FOR GOING BACK UP IN RESOLUTION
- //
- bool selectedUp = false;
- // Check if native has been spatially down-sampled
- if (_stateDecFactorSpatial > 1)
- {
- // Check conditions on buffer level and rate_mismatch
- if ( (avgTargetRate > estimatedTransRateUpS) &&
- (ratioBufferLow < MAX_BUFFER_LOW) && (rateMisMatch < MAX_RATE_MM))
- {
- // width/height scaled back up:
- // setting 0 indicates scaling back to native
- _qm->spatialHeightFact = 0;
- _qm->spatialWidthFact = 0;
- selectedUp = true;
- }
- }
- //Check if native has been temporally down-sampled
- if (_stateDecFactorTemp > 1)
- {
- if ( (avgTargetRate > estimatedTransRateUpT) &&
- (ratioBufferLow < MAX_BUFFER_LOW) && (rateMisMatch < MAX_RATE_MM))
- {
- // temporal scale back up:
- // setting 0 indicates scaling back to native
- _qm->temporalFact = 0;
- selectedUp = true;
- }
- }
-
- // Leave QM if we selected to go back up in either spatial or temporal
- if (selectedUp == true)
- {
- // Update down-sampling state
- // Note: only temp reduction by 2 is allowed
- if (_qm->temporalFact == 0)
- {
- _stateDecFactorTemp = _stateDecFactorTemp / 2;
- }
- // Update down-sampling state
- // Note: only spatial reduction by 2x2 is allowed
- if (_qm->spatialHeightFact == 0 && _qm->spatialWidthFact == 0 )
- {
- _stateDecFactorSpatial = _stateDecFactorSpatial / 4;
- }
- *qm = _qm;
- return VCM_OK;
- }
-
- //
- // Done with checking for going back up in resolution
- //
-
- //
- //CHECK FOR RESOLUTION REDUCTION
- //
-
- // Resolution reduction if:
- // (1) target rate is lower than transitional rate, or
- // (2) buffer level is not stable, or
- // (3) rate mismatch is larger than threshold
-
- // Bias down-sampling based on packet loss conditions
- if (avgPacketLoss > LOSS_THR)
- {
- estimatedTransRateDown = LOSS_RATE_FAC * estimatedTransRateDown;
- }
-
- if ((avgTargetRate < estimatedTransRateDown ) ||
- (ratioBufferLow > MAX_BUFFER_LOW)
- || (rateMisMatch > MAX_RATE_MM))
- {
-
- WebRtc_UWord8 spatialFact = 1;
- WebRtc_UWord8 tempFact = 1;
-
- // Get the action
- spatialFact = kSpatialAction[contentClass];
- tempFact = kTemporalAction[contentClass];
-
- switch(spatialFact)
- {
- case 4:
- _qm->spatialWidthFact = 2;
- _qm->spatialHeightFact = 2;
- break;
- case 2:
- //default is 1x2 (H)
- _qm->spatialWidthFact = 2;
- _qm->spatialHeightFact = 1;
- // Select 1x2,2x1, or back to 2x2
- // Note: directional selection not used in this version
- // SelectSpatialDirectionMode((float) estimatedTransRateDown);
- break;
- default:
- _qm->spatialWidthFact = 1;
- _qm->spatialHeightFact = 1;
- break;
- }
- _qm->temporalFact = tempFact;
-
- // Sanity check on ST QM selection:
- // override the settings for too small image size and frame rate
- // Also check the limit on current down-sampling state
-
- // No spatial sampling if image size is too small (QCIF)
- if ( (_width * _height) <= MIN_IMAGE_SIZE ||
- _stateDecFactorSpatial >= MAX_SPATIAL_DOWN_FACT)
- {
- _qm->spatialWidthFact = 1;
- _qm->spatialHeightFact = 1;
- }
-
- // No frame rate reduction below some point:
- // use the (average) incoming frame rate
- if ( avgIncomingFrameRate <= MIN_FRAME_RATE_QM ||
- _stateDecFactorTemp >= MAX_TEMP_DOWN_FACT)
- {
- _qm->temporalFact = 1;
- }
-
- // No down-sampling if current downsampling state is above threshold
- if (_stateDecFactorTemp * _stateDecFactorSpatial >=
- MAX_SPATIAL_TEMP_DOWN_FACT)
- {
- _qm->spatialWidthFact = 1;
- _qm->spatialHeightFact = 1;
- _qm->temporalFact = 1;
- }
- //
- // Done with sanity checks on ST QM selection
- //
-
- // Update down-sampling states
- _stateDecFactorSpatial = _stateDecFactorSpatial * _qm->spatialWidthFact
- * _qm->spatialHeightFact;
- _stateDecFactorTemp = _stateDecFactorTemp * _qm->temporalFact;
-
- if (_qm->spatialWidthFact != 1 || _qm->spatialHeightFact != 1 ||
- _qm->temporalFact != 1)
- {
-
- WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, -1,
- "Resolution reduction occurred"
- "Content Metrics are: Motion = %d , Spatial = %d, "
- "Rates are: Est. Trans. BR = %d, Avg.Target BR = %f",
- _motion.level, _spatial.level,
- estimatedTransRateDown, avgTargetRate);
- }
-
- }
- else
- {
+ // Check for going back up in resolution, if we have had some down-sampling
+ // relative to native state in Initialize (i.e., after SetEncodingData()
+ // in mediaOpt.).
+ if (_stateDecFactorSpatial > 1 || _stateDecFactorTemp > 1) {
+ if (GoingUpResolution()) {
*qm = _qm;
return VCM_OK;
}
- // Done with checking for resolution reduction
+ }
- *qm = _qm;
- return VCM_OK;
-
-
+ // Check for going down in resolution, only if current total amount of
+ // down-sampling state is below threshold.
+ if (_stateDecFactorTemp * _stateDecFactorSpatial < kMaxDownSample) {
+ if (GoingDownResolution()) {
+ *qm = _qm;
+ return VCM_OK;
+ }
+ }
+ return VCM_OK;
}
-WebRtc_Word32
-VCMQmResolution::SelectSpatialDirectionMode(float transRate)
-{
- // Default is 1x2 (H)
+void VCMQmResolution::ComputeRatesForSelection() {
+ _avgTargetRate = 0.0f;
+ _avgIncomingFrameRate = 0.0f;
+ _avgRatioBufferLow = 0.0f;
+ _avgRateMisMatch = 0.0f;
+ _avgRateMisMatchSgn = 0.0f;
+ _avgPacketLoss = 0.0f;
+ if (_frameCnt > 0) {
+ _avgRatioBufferLow = static_cast<float>(_lowBufferCnt) /
+ static_cast<float>(_frameCnt);
+ }
+ if (_updateRateCnt > 0) {
+ _avgRateMisMatch = static_cast<float>(_sumRateMM) /
+ static_cast<float>(_updateRateCnt);
+ _avgRateMisMatchSgn = static_cast<float>(_sumRateMMSgn) /
+ static_cast<float>(_updateRateCnt);
+ _avgTargetRate = static_cast<float>(_sumTargetRate) /
+ static_cast<float>(_updateRateCnt);
+ _avgIncomingFrameRate = static_cast<float>(_sumIncomingFrameRate) /
+ static_cast<float>(_updateRateCnt);
+ _avgPacketLoss = static_cast<float>(_sumPacketLoss) /
+ static_cast<float>(_updateRateCnt);
+ }
+ // For selection we may want to weight some quantities more heavily
+ // with the current (i.e., next ~1sec) rate values.
+ float weight = 0.7f;
+ _avgTargetRate = weight * _avgTargetRate + (1.0 - weight) * _targetBitRate;
+ _avgIncomingFrameRate = weight * _avgIncomingFrameRate +
+ (1.0 - weight) * _incomingFrameRate;
+ _frameRateLevel = FrameRateLevel(_avgIncomingFrameRate);
+}
- // For bit rates well below transitional rate, we select 2x2
- if ( _targetBitRate < transRate * RATE_RED_SPATIAL_2X2 )
- {
+void VCMQmResolution::ComputeEncoderState() {
+ // Default.
+ _encoderState = kStableEncoding;
+
+ // Assign stressed state if:
+ // 1) occurrences of low buffer levels is high, or
+ // 2) rate mis-match is high, and consistent over-shooting by encoder.
+ if ((_avgRatioBufferLow > kMaxBufferLow) ||
+ ((_avgRateMisMatch > kMaxRateMisMatch) &&
+ (_avgRateMisMatchSgn < -kRateOverShoot))) {
+ _encoderState = kStressedEncoding;
+ }
+ // Assign easy state if:
+ // 1) rate mis-match is high, and
+ // 2) consistent under-shooting by encoder.
+ if ((_avgRateMisMatch > kMaxRateMisMatch) &&
+ (_avgRateMisMatchSgn > kRateUnderShoot)) {
+ _encoderState = kEasyEncoding;
+ }
+}
+
+bool VCMQmResolution::GoingUpResolution() {
+ // Check if we should go up both spatially and temporally.
+ if (_stateDecFactorSpatial > 1 && _stateDecFactorTemp > 1) {
+ if (ConditionForGoingUp(2, 2, 2, kTransRateScaleUpSpatialTemp)) {
+ _qm->spatialHeightFact = 0;
+ _qm->spatialWidthFact = 0;
+ _qm->temporalFact = 0;
+ UpdateDownsamplingState(kUpResolution);
+ return true;
+ }
+ } else {
+ // Check if we should go up either spatially or temporally.
+ bool selectedUpS = false;
+ bool selectedUpT = false;
+ if (_stateDecFactorSpatial > 1) {
+ selectedUpS = ConditionForGoingUp(2, 2, 1, kTransRateScaleUpSpatial);
+ }
+ if (_stateDecFactorTemp > 1) {
+ selectedUpT = ConditionForGoingUp(1, 1, 2, kTransRateScaleUpTemp);
+ }
+ if (selectedUpS && !selectedUpT) {
+ _qm->spatialHeightFact = 0;
+ _qm->spatialWidthFact = 0;
+ UpdateDownsamplingState(kUpResolution);
+ return true;
+ } else if (!selectedUpS && selectedUpT) {
+ _qm->temporalFact = 0;
+ UpdateDownsamplingState(kUpResolution);
+ return true;
+ } else if (selectedUpS && selectedUpT) {
+ // TODO(marpan): which one to pick?
+ // pickSpatialOrTemporal()
+ // For now take spatial over temporal.
+ _qm->spatialHeightFact = 0;
+ _qm->spatialWidthFact = 0;
+ UpdateDownsamplingState(kUpResolution);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool VCMQmResolution::ConditionForGoingUp(uint8_t facWidth,
+ uint8_t facHeight,
+ uint8_t facTemp,
+ float scaleFac) {
+ float estimatedTransitionRateUp = GetTransitionRate(facWidth, facHeight,
+ facTemp, scaleFac);
+ // Go back up if:
+ // 1) target rate is above threshold and current encoder state is stable, or
+ // 2) encoder state is easy (encoder is significantly under-shooting target).
+ if (((_avgTargetRate > estimatedTransitionRateUp) &&
+ (_encoderState == kStableEncoding)) ||
+ (_encoderState == kEasyEncoding)) {
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool VCMQmResolution::GoingDownResolution() {
+ float estimatedTransitionRateDown = GetTransitionRate(1, 1, 1, 1.0);
+ float maxRate = kFrameRateFac[_frameRateLevel] * kMaxRateQm[_imageType];
+
+ // TODO(marpan): Bias down-sampling based on packet loss conditions.
+
+ // Resolution reduction if:
+ // (1) target rate is below transition rate, or
+ // (2) encoder is in stressed state and target rate below a max threshold.
+ if ((_avgTargetRate < estimatedTransitionRateDown ) ||
+ (_encoderState == kStressedEncoding && _avgTargetRate < maxRate)) {
+ // Get the down-sampling action.
+ uint8_t spatialFact = kSpatialAction[_contentClass];
+ uint8_t tempFact = kTemporalAction[_contentClass];
+
+ switch (spatialFact) {
+ case 4: {
_qm->spatialWidthFact = 2;
_qm->spatialHeightFact = 2;
- return VCM_OK;
- }
-
- // Otherwise check prediction errors, aspect ratio, horizontalness
-
- float spatialErr = _contentMetrics->spatialPredErr;
- float spatialErrH = _contentMetrics->spatialPredErrH;
- float spatialErrV = _contentMetrics->spatialPredErrV;
-
- // Favor 1x2 if aspect_ratio is 16:9
- if (_aspectRatio >= 16.0f / 9.0f )
- {
- //check if 1x2 has lowest prediction error
- if (spatialErrH < spatialErr && spatialErrH < spatialErrV)
- {
- return VCM_OK;
- }
- }
-
- // Check for 2x2 selection: favor 2x2 over 1x2 and 2x1
- if (spatialErr < spatialErrH * (1.0f + SPATIAL_ERR_2X2_VS_H) &&
- spatialErr < spatialErrV * (1.0f + SPATIAL_ERR_2X2_VS_V))
- {
- _qm->spatialWidthFact = 2;
- _qm->spatialHeightFact = 2;
- return VCM_OK;
- }
-
- // Check for 2x1 selection:
- if (spatialErrV < spatialErrH * (1.0f - SPATIAL_ERR_V_VS_H) &&
- spatialErrV < spatialErr * (1.0f - SPATIAL_ERR_2X2_VS_V))
- {
+ break;
+ }
+ case 2: {
+ assert(false); // Currently not used.
+ // Select 1x2,2x1, or 4/3x4/3.
+ // SelectSpatialDirectionMode((float) estimatedTransitionRateDown);
+ break;
+ }
+ case 1: {
_qm->spatialWidthFact = 1;
- _qm->spatialHeightFact = 2;
- return VCM_OK;
+ _qm->spatialHeightFact = 1;
+ break;
+ }
+ default: {
+ assert(false);
+ }
+ }
+ switch (tempFact) {
+ case 2: {
+ _qm->temporalFact = 2;
+ break;
+ }
+ case 1: {
+ _qm->temporalFact = 1;
+ break;
+ }
+ default: {
+ assert(false);
+ }
+ }
+ // Adjust some cases based on frame rate.
+ // TODO(marpan): will be modified when we add 1/2 spatial and 2/3 temporal.
+ AdjustAction();
+
+ // Sanity checks on down-sampling selection:
+ // override the settings for too small image size and/or frame rate.
+ // Also check the limit on current down-sampling states.
+
+ // No spatial sampling if current frame size is too small (QCIF),
+ // or if amount of spatial down-sampling is already too much.
+ if ((_width * _height) <= kMinImageSize ||
+ _stateDecFactorSpatial >= kMaxSpatialDown) {
+ _qm->spatialWidthFact = 1;
+ _qm->spatialHeightFact = 1;
+ }
+ // No frame rate reduction if average frame rate is below some point,
+ // or if the amount of temporal down-sampling is already too much.
+ if (_avgIncomingFrameRate <= kMinFrameRate ||
+ _stateDecFactorTemp >= kMaxTempDown) {
+ _qm->temporalFact = 1;
}
- return VCM_OK;
+ // Update down-sampling state.
+ if (_qm->spatialWidthFact != 1 || _qm->spatialHeightFact != 1 ||
+ _qm->temporalFact != 1) {
+ UpdateDownsamplingState(kDownResolution);
+ return true;
+ }
+ }
+ return false;
}
-// DONE WITH RESOLUTION CLASS
+float VCMQmResolution::GetTransitionRate(uint8_t facWidth,
+ uint8_t facHeight,
+ uint8_t facTemp,
+ float scaleFac) {
+ uint8_t imageType = GetImageType(facWidth * _width,
+ facHeight * _height);
+ LevelClass frameRateLevel = FrameRateLevel(facTemp * _avgIncomingFrameRate);
+ // The maximum allowed rate below which down-sampling is allowed:
+ // Nominal values based on image format (frame size and frame rate).
+ float maxRate = kFrameRateFac[frameRateLevel] * kMaxRateQm[imageType];
+
+ uint8_t imageClass = imageType > 3 ? 1: 0;
+ uint8_t tableIndex = imageClass * 9 + _contentClass;
+ // Scale factor for down-sampling transition threshold:
+ // factor based on the content class and the image size.
+ float scaleTransRate = kScaleTransRateQm[tableIndex];
+
+ // Threshold bitrate for resolution action.
+ return static_cast<float> (scaleFac * facTemp * _incomingFrameRate *
+ scaleTransRate * maxRate / 30);
+}
+
+void VCMQmResolution::UpdateDownsamplingState(ResolutionAction action) {
+ // Assumes for now only actions are 1/2 frame rate of 2x2 spatial.
+ if (action == kUpResolution) {
+ if (_qm->spatialHeightFact == 0 && _qm->spatialWidthFact == 0) {
+ _stateDecFactorSpatial = _stateDecFactorSpatial / 4;
+ assert(_stateDecFactorSpatial >= 1);
+ }
+ if (_qm->temporalFact == 0) {
+ _stateDecFactorTemp = _stateDecFactorTemp / 2;
+ assert(_stateDecFactorTemp >= 1);
+ }
+ } else if (action == kDownResolution) {
+ _stateDecFactorSpatial = _stateDecFactorSpatial * _qm->spatialWidthFact
+ * _qm->spatialHeightFact;
+ _stateDecFactorTemp = _stateDecFactorTemp * _qm->temporalFact;
+ assert(_stateDecFactorSpatial >= 1);
+ assert(_stateDecFactorTemp >= 1);
+ } else {
+ assert(false);
+ }
+}
+
+void VCMQmResolution::AdjustAction() {
+ if (_spatial.level == kDefault && _motion.level != kHigh &&
+ _frameRateLevel == kHigh) {
+ _qm->temporalFact = 2;
+ _qm->spatialWidthFact = 1;
+ _qm->spatialHeightFact = 1;
+ }
+}
+
+// TODO(marpan): Update this when we allow for 1/2 spatial down-sampling.
+void VCMQmResolution::SelectSpatialDirectionMode(float transRate) {
+ // Default is 1x2 (H)
+ // For bit rates well below transitional rate, we select 2x2.
+ if (_targetBitRate < transRate * kRateRedSpatial2X2) {
+ _qm->spatialWidthFact = 2;
+ _qm->spatialHeightFact = 2;
+ }
+ // Otherwise check prediction errors and aspect ratio.
+ float spatialErr = 0.0;
+ float spatialErrH = 0.0;
+ float spatialErrV = 0.0;
+ if (_contentMetrics) {
+ spatialErr = _contentMetrics->spatial_pred_err;
+ spatialErrH = _contentMetrics->spatial_pred_err_h;
+ spatialErrV = _contentMetrics->spatial_pred_err_v;
+ }
+
+ // Favor 1x2 if aspect_ratio is 16:9.
+ if (_aspectRatio >= 16.0f / 9.0f) {
+ // Check if 1x2 has lowest prediction error.
+ if (spatialErrH < spatialErr && spatialErrH < spatialErrV) {
+ _qm->spatialWidthFact = 2;
+ _qm->spatialHeightFact = 1;
+ }
+ }
+ // Check for 2x2 selection: favor 2x2 over 1x2 and 2x1.
+ if (spatialErr < spatialErrH * (1.0f + kSpatialErr2x2VsHoriz) &&
+ spatialErr < spatialErrV * (1.0f + kSpatialErr2X2VsVert)) {
+ _qm->spatialWidthFact = 2;
+ _qm->spatialHeightFact = 2;
+ }
+ // Check for 2x1 selection.
+ if (spatialErrV < spatialErrH * (1.0f - kSpatialErrVertVsHoriz) &&
+ spatialErrV < spatialErr * (1.0f - kSpatialErr2X2VsVert)) {
+ _qm->spatialWidthFact = 1;
+ _qm->spatialHeightFact = 2;
+ }
+}
// ROBUSTNESS CLASS
-VCMQmRobustness::VCMQmRobustness()
-{
- Reset();
+VCMQmRobustness::VCMQmRobustness() {
+ Reset();
}
-VCMQmRobustness::~VCMQmRobustness()
-{
-
+VCMQmRobustness::~VCMQmRobustness() {
}
-void
-VCMQmRobustness::Reset()
-{
- _prevTotalRate = 0.0f;
- _prevRttTime = 0;
- _prevPacketLoss = 0;
- _prevCodeRateDelta = 0;
- ResetQM();
- return;
+void VCMQmRobustness::Reset() {
+ _prevTotalRate = 0.0f;
+ _prevRttTime = 0;
+ _prevPacketLoss = 0;
+ _prevCodeRateDelta = 0;
+ ResetQM();
}
// Adjust the FEC rate based on the content and the network state
// (packet loss rate, total rate/bandwidth, round trip time).
// Note that packetLoss here is the filtered loss value.
-float
-VCMQmRobustness::AdjustFecFactor(WebRtc_UWord8 codeRateDelta, float totalRate,
- float frameRate,WebRtc_UWord32 rttTime,
- WebRtc_UWord8 packetLoss)
-{
- // Default: no adjustment
- float adjustFec = 1.0f;
-
- if (_contentMetrics == NULL)
- {
- return adjustFec;
- }
-
- // Compute class state of the content.
- MotionNFD();
- Spatial();
-
- // TODO (marpan): Set FEC adjustment factor
-
- // Keep track of previous values of network state:
- // adjustment may be also based on pattern of changes in network state
- _prevTotalRate = totalRate;
- _prevRttTime = rttTime;
- _prevPacketLoss = packetLoss;
-
- _prevCodeRateDelta = codeRateDelta;
-
+float VCMQmRobustness::AdjustFecFactor(uint8_t codeRateDelta,
+ float totalRate,
+ float frameRate,
+ uint32_t rttTime,
+ uint8_t packetLoss) {
+ // Default: no adjustment
+ float adjustFec = 1.0f;
+ if (_contentMetrics == NULL) {
return adjustFec;
+ }
+ // Compute class state of the content.
+ ComputeMotionNFD();
+ ComputeSpatial();
+ // TODO(marpan): Set FEC adjustment factor.
+
+ // Keep track of previous values of network state:
+ // adjustment may be also based on pattern of changes in network state.
+ _prevTotalRate = totalRate;
+ _prevRttTime = rttTime;
+ _prevPacketLoss = packetLoss;
+ _prevCodeRateDelta = codeRateDelta;
+ return adjustFec;
}
-// Set the UEP (unequal-protection) on/off for the FEC
-bool
-VCMQmRobustness::SetUepProtection(WebRtc_UWord8 codeRateDelta, float totalRate,
- WebRtc_UWord8 packetLoss, bool frameType)
-{
- // Default:
- bool uepProtection = false;
-
- if (_contentMetrics == NULL)
- {
- return uepProtection;
- }
-
-
- return uepProtection;
+// Set the UEP (unequal-protection across packets) on/off for the FEC.
+bool VCMQmRobustness::SetUepProtection(uint8_t codeRateDelta,
+ float totalRate,
+ uint8_t packetLoss,
+ bool frameType) {
+ // Default.
+ return false;
}
-
-} // end of namespace
+} // end of namespace
diff --git a/src/modules/video_coding/main/source/qm_select.h b/src/modules/video_coding/main/source/qm_select.h
index 3fb9040..1859530 100644
--- a/src/modules/video_coding/main/source/qm_select.h
+++ b/src/modules/video_coding/main/source/qm_select.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ * Copyright (c) 2012 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
@@ -11,194 +11,249 @@
#ifndef WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_
#define WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_
-#include "typedefs.h"
#include "common_types.h"
+#include "typedefs.h"
+
/******************************************************/
/* Quality Modes: Resolution and Robustness settings */
/******************************************************/
-namespace webrtc
-{
-
+namespace webrtc {
struct VideoContentMetrics;
-struct VCMResolutionScale
-{
- VCMResolutionScale(): spatialWidthFact(1), spatialHeightFact(1),
- temporalFact(1){}
-
- WebRtc_UWord16 spatialWidthFact;
- WebRtc_UWord16 spatialHeightFact;
- WebRtc_UWord16 temporalFact;
+struct VCMResolutionScale {
+ VCMResolutionScale()
+ : spatialWidthFact(1),
+ spatialHeightFact(1),
+ temporalFact(1) {
+ }
+ uint8_t spatialWidthFact;
+ uint8_t spatialHeightFact;
+ uint8_t temporalFact;
};
-enum VCMMagValues
-{
- kLow,
- kHigh,
- kDefault //default do nothing mode
+enum LevelClass {
+ kLow,
+ kHigh,
+ kDefault
};
-struct VCMContFeature
-{
- VCMContFeature(): value(0.0f), level(kDefault){}
+struct VCMContFeature {
+ VCMContFeature()
+ : value(0.0f),
+ level(kDefault) {
+ }
+ void Reset() {
+ value = 0.0f;
+ level = kDefault;
+ }
+ float value;
+ LevelClass level;
+};
- void Reset()
- {
- value = 0.0f;
- level = kDefault;
- }
+enum ResolutionAction {
+ kDownResolution,
+ kUpResolution,
+ kNoChangeResolution
+};
- float value;
- VCMMagValues level;
+enum EncoderState {
+ kStableEncoding, // Low rate mis-match, stable buffer levels.
+ kStressedEncoding, // Significant over-shooting of target rate,
+ // Buffer under-flow, etc.
+ kEasyEncoding // Significant under-shooting of target rate.
};
// QmMethod class: main class for resolution and robustness settings
-class VCMQmMethod
-{
-public:
- VCMQmMethod();
- virtual ~VCMQmMethod();
+class VCMQmMethod {
+ public:
+ VCMQmMethod();
+ virtual ~VCMQmMethod();
- // Reset values
- void ResetQM();
- virtual void Reset() = 0;
+ // Reset values
+ void ResetQM();
+ virtual void Reset() = 0;
- // Update with the content metrics
- void UpdateContent(const VideoContentMetrics* contentMetrics);
+ // Compute content class.
+ uint8_t ComputeContentClass();
- // Compute spatial texture magnitude and level
- void Spatial();
+ // Update with the content metrics.
+ void UpdateContent(const VideoContentMetrics* contentMetrics);
- // Compute motion magnitude and level
- void Motion();
+ // Compute spatial texture magnitude and level.
+ // Spatial texture is a spatial prediction error measure.
+ void ComputeSpatial();
- // Compute motion magnitude and level for NFD metric
- void MotionNFD();
+ // Compute motion magnitude and level for NFD metric.
+ // NFD is normalized frame difference (normalized by spatial variance).
+ void ComputeMotionNFD();
- // Compute coherence magnitude and level
- void Coherence();
+ // Get the imageType (CIF, VGA, HD, etc) for the system width/height.
+ uint8_t GetImageType(uint16_t width, uint16_t height);
- // Get the imageType (CIF, VGA, HD, etc) for the system width/height
- WebRtc_Word8 GetImageType(WebRtc_UWord32 width, WebRtc_UWord32 height);
+ // Get the frame rate level.
+ LevelClass FrameRateLevel(float frame_rate);
- // Content Data
- const VideoContentMetrics* _contentMetrics;
+ protected:
+ // Content Data.
+ const VideoContentMetrics* _contentMetrics;
- // Encoder and native frame sizes, frame rate, aspect ratio, imageType
- WebRtc_UWord32 _width;
- WebRtc_UWord32 _height;
- WebRtc_UWord32 _nativeWidth;
- WebRtc_UWord32 _nativeHeight;
- WebRtc_UWord32 _nativeFrameRate;
- float _aspectRatio;
- // Image type for the current encoder system size.
- WebRtc_UWord8 _imageType;
-
- // Content L/M/H values. stationary flag
- VCMContFeature _motion;
- VCMContFeature _spatial;
- VCMContFeature _coherence;
- bool _stationaryMotion;
- bool _init;
-
+ // Encoder frame sizes and native frame sizes.
+ uint16_t _width;
+ uint16_t _height;
+ uint16_t _nativeWidth;
+ uint16_t _nativeHeight;
+ float _aspectRatio;
+ // Image type and frame rate leve, for the current encoder resolution.
+ uint8_t _imageType;
+ LevelClass _frameRateLevel;
+ // Content class data.
+ VCMContFeature _motion;
+ VCMContFeature _spatial;
+ uint8_t _contentClass;
+ bool _init;
};
// Resolution settings class
-class VCMQmResolution : public VCMQmMethod
-{
-public:
- VCMQmResolution();
- ~VCMQmResolution();
+class VCMQmResolution : public VCMQmMethod {
+ public:
+ VCMQmResolution();
+ virtual ~VCMQmResolution();
- // Reset all quantities
- virtual void Reset();
+ // Reset all quantities.
+ virtual void Reset();
- // Reset rate quantities and counter values after every Select Quality call
- void ResetRates();
+ // Reset rate quantities and counters after every SelectResolution() call.
+ void ResetRates();
- // Initialize rate control quantities after re-init of encoder.
- WebRtc_Word32 Initialize(float bitRate, float userFrameRate,
- WebRtc_UWord32 width, WebRtc_UWord32 height);
+ // Reset down-sampling state.
+ void ResetDownSamplingState();
- // Update QM with actual bit rate (size of the latest encoded frame)
- // and frame type, after every encoded frame.
- void UpdateEncodedSize(WebRtc_Word64 encodedSize,
- FrameType encodedFrameType);
+ // Get the encoder state.
+ EncoderState GetEncoderState();
- // Update QM with new bit/frame/loss rates every ~1 sec from SetTargetRates
- void UpdateRates(float targetBitRate, float avgSentRate,
- float incomingFrameRate, WebRtc_UWord8 packetLoss);
+ // Initialize after SetEncodingData in media_opt.
+ int Initialize(float bitRate, float userFrameRate,
+ uint16_t width, uint16_t height);
- // Extract ST (spatio-temporal) QM behavior and make decision
- // Inputs: qm: Reference to the quality modes pointer
- // Output: the spatial and/or temporal scale change
- WebRtc_Word32 SelectResolution(VCMResolutionScale** qm);
+ // Update the encoder frame size.
+ void UpdateCodecFrameSize(uint16_t width, uint16_t height);
- // Select 1x2,2x2,2x2 spatial sampling mode
- WebRtc_Word32 SelectSpatialDirectionMode(float transRate);
+ // Update with actual bit rate (size of the latest encoded frame)
+ // and frame type, after every encoded frame.
+ void UpdateEncodedSize(int encodedSize,
+ FrameType encodedFrameType);
-private:
- // Encoder rate control parameter
- float _targetBitRate;
- float _userFrameRate;
- float _incomingFrameRate;
- float _perFrameBandwidth;
- float _bufferLevel;
+ // Update with new target bitrate, actual encoder sent rate, frame_rate,
+ // loss rate: every ~1 sec from SetTargetRates in media_opt.
+ void UpdateRates(float targetBitRate, float encoderSentRate,
+ float incomingFrameRate, uint8_t packetLoss);
- // Data accumulated every ~1sec from MediaOpt
- float _sumTargetRate;
- float _sumIncomingFrameRate;
- float _sumSeqRateMM;
- float _sumFrameRateMM;
- float _sumPacketLoss;
- WebRtc_Word64 _sumEncodedBytes;
+ // Extract ST (spatio-temporal) resolution action.
+ // Inputs: qm: Reference to the quality modes pointer.
+ // Output: the spatial and/or temporal scale change.
+ int SelectResolution(VCMResolutionScale** qm);
- // Resolution state parameters
- WebRtc_UWord8 _stateDecFactorSpatial;
- WebRtc_UWord8 _stateDecFactorTemp;
+ // Compute rates for the selection of down-sampling action.
+ void ComputeRatesForSelection();
- // Counters
- WebRtc_UWord32 _frameCnt;
- WebRtc_UWord32 _frameCntDelta;
- WebRtc_UWord32 _updateRateCnt;
- WebRtc_UWord32 _lowBufferCnt;
+ // Compute the encoder state.
+ void ComputeEncoderState();
- VCMResolutionScale* _qm;
+ // Return true if the action is to go back up in resolution.
+ bool GoingUpResolution();
+
+ // Return true if the action is to go down in resolution.
+ bool GoingDownResolution();
+
+ // Check the condition for going up in resolution by the scale factors:
+ // |facWidth|, |facHeight|, |facTemp|.
+ // |scaleFac| is a scale factor for the transition rate.
+ bool ConditionForGoingUp(uint8_t facWidth, uint8_t facHeight,
+ uint8_t facTemp,
+ float scaleFac);
+
+ // Get the bitrate threshold for the resolution action.
+ // The case |facWidth|=|facHeight|=|facTemp|==1 is for down-sampling action.
+ // |scaleFac| is a scale factor for the transition rate.
+ float GetTransitionRate(uint8_t facWidth, uint8_t facHeight,
+ uint8_t facTemp, float scaleFac);
+
+ // Update the downsampling state.
+ void UpdateDownsamplingState(ResolutionAction action);
+
+ void AdjustAction();
+
+ // Select the directional (1x2 or 2x1) spatial down-sampling action.
+ void SelectSpatialDirectionMode(float transRate);
+
+ private:
+ VCMResolutionScale* _qm;
+ // Encoder rate control parameters.
+ float _targetBitRate;
+ float _userFrameRate;
+ float _incomingFrameRate;
+ float _perFrameBandwidth;
+ float _bufferLevel;
+
+ // Data accumulated every ~1sec from MediaOpt.
+ float _sumTargetRate;
+ float _sumIncomingFrameRate;
+ float _sumRateMM;
+ float _sumRateMMSgn;
+ float _sumPacketLoss;
+ // Counters.
+ uint32_t _frameCnt;
+ uint32_t _frameCntDelta;
+ uint32_t _updateRateCnt;
+ uint32_t _lowBufferCnt;
+
+ // Resolution state parameters.
+ uint8_t _stateDecFactorSpatial;
+ uint8_t _stateDecFactorTemp;
+
+ // Quantities used for selection.
+ float _avgTargetRate;
+ float _avgIncomingFrameRate;
+ float _avgRatioBufferLow;
+ float _avgRateMisMatch;
+ float _avgRateMisMatchSgn;
+ float _avgPacketLoss;
+ EncoderState _encoderState;
};
-// Robustness settings class
+// Robustness settings class.
-class VCMQmRobustness : public VCMQmMethod
-{
-public:
- VCMQmRobustness();
- ~VCMQmRobustness();
+class VCMQmRobustness : public VCMQmMethod {
+ public:
+ VCMQmRobustness();
+ ~VCMQmRobustness();
- virtual void Reset();
+ virtual void Reset();
- // Adjust FEC rate based on content: every ~1 sec from SetTargetRates.
- // Returns an adjustment factor.
- float AdjustFecFactor(WebRtc_UWord8 codeRateDelta, float totalRate,
- float frameRate, WebRtc_UWord32 rttTime,
- WebRtc_UWord8 packetLoss);
+ // Adjust FEC rate based on content: every ~1 sec from SetTargetRates.
+ // Returns an adjustment factor.
+ float AdjustFecFactor(uint8_t codeRateDelta,
+ float totalRate,
+ float frameRate,
+ uint32_t rttTime,
+ uint8_t packetLoss);
- // Set the UEP protection on/off
- bool SetUepProtection(WebRtc_UWord8 codeRateDelta, float totalRate,
- WebRtc_UWord8 packetLoss, bool frameType);
+ // Set the UEP protection on/off.
+ bool SetUepProtection(uint8_t codeRateDelta,
+ float totalRate,
+ uint8_t packetLoss,
+ bool frameType);
-private:
- // Previous state of network parameters
- float _prevTotalRate;
- WebRtc_UWord32 _prevRttTime;
- WebRtc_UWord8 _prevPacketLoss;
-
- // Previous FEC rate
- WebRtc_UWord8 _prevCodeRateDelta;
+ private:
+ // Previous state of network parameters.
+ float _prevTotalRate;
+ uint32_t _prevRttTime;
+ uint8_t _prevPacketLoss;
+ uint8_t _prevCodeRateDelta;
};
+} // namespace webrtc
+#endif // WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_
-} // namespace webrtc
-
-#endif // WEBRTC_MODULES_VIDEO_CODING_QM_SELECT_H_
diff --git a/src/modules/video_coding/main/source/qm_select_data.h b/src/modules/video_coding/main/source/qm_select_data.h
index 64870ea..d4af642 100644
--- a/src/modules/video_coding/main/source/qm_select_data.h
+++ b/src/modules/video_coding/main/source/qm_select_data.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ * Copyright (c) 2012 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
@@ -18,171 +18,167 @@
#include "typedefs.h"
-namespace webrtc
-{
-
+namespace webrtc {
//
// PARAMETERS FOR RESOLUTION ADAPTATION
//
-// Initial level of buffer in secs: should corresponds to wrapper settings
-#define INIT_BUFFER_LEVEL 0.5
+// Initial level of buffer in secs: should corresponds to wrapper settings.
+const float kInitBufferLevel = 0.5f;
-// Optimal level of buffer in secs: should corresponds to wrapper settings
-#define OPT_BUFFER_LEVEL 0.6
+// Optimal level of buffer in secs: should corresponds to wrapper settings.
+const float kOptBufferLevel = 0.6f;
-// Threshold of (max) buffer size below which we consider too low (underflow)
-#define PERC_BUFFER_THR 0.10
+// Threshold of (max) buffer size below which we consider too low (underflow).
+const float kPercBufferThr = 0.10f;
+
+// Threshold on the occurrences of low buffer levels.
+const float kMaxBufferLow = 0.5f;
// Threshold on rate mismatch
-#define MAX_RATE_MM 0.5
+const float kMaxRateMisMatch = 0.5f;
-// Avoid outliers in seq-rate MM
-#define THRESH_SUM_MM 1000
+// Threshold on amount of under/over encoder shooting.
+const float kRateOverShoot = 0.75f;
+const float kRateUnderShoot = 0.75f;
-// Threshold on the occurrences of low buffer levels
-#define MAX_BUFFER_LOW 0.5
+// Factor for transitional rate for going back up in resolution.
+const float kTransRateScaleUpSpatial = 1.25f;
+const float kTransRateScaleUpTemp = 1.25f;
+const float kTransRateScaleUpSpatialTemp = 1.25f;
-// Factor for transitional rate for going back up in resolution
-#define TRANS_RATE_SCALE_UP_SPATIAL 1.25
-#define TRANS_RATE_SCALE_UP_TEMP 1.25
+// Threshold on packet loss rate, above which favor resolution reduction.
+const float kPacketLossThr = 0.1f;
-// Threshold on packet loss rate, above which favor resolution reduction
-#define LOSS_THR 0.1
-
-// Factor for reducing transitonal bitrate under packet loss
-#define LOSS_RATE_FAC 1.0
+// Factor for reducing transitonal bitrate under packet loss.
+const float kPacketLossRateFac = 1.0f;
// Maximum possible transitional rate for down-sampling:
-// (units in kbps), for 30fps
-const WebRtc_UWord16 kMaxRateQm[7] = {
- 100, //QCIF
- 500, //CIF
- 800, //VGA
- 1500, //4CIF
- 2000, //720 HD 4:3,
- 2500, //720 HD 16:9
- 3000 //1080HD
+// (units in kbps), for 30fps.
+const uint16_t kMaxRateQm[7] = {
+ 100, // QCIF
+ 250, // CIF
+ 500, // VGA
+ 800, // 4CIF
+ 1000, // 720 HD 4:3,
+ 1500, // 720 HD 16:9
+ 2000 // 1080HD
+};
+
+// Frame rate scale for maximum transition rate.
+const float kFrameRateFac[3] = {
+ 0.7f, // L
+ 1.0f, // H
+ 0.8f // D
};
// Scale for transitional rate: based on content class
// motion=L/H/D,spatial==L/H/D: for low, high, middle levels
const float kScaleTransRateQm[18] = {
- //4CIF and lower
- 0.25f, // L, L
- 0.75f, // L, H
- 0.75f, // L, D
- 0.75f, // H ,L
- 0.50f, // H, H
- 0.50f, // H, D
+ // 4CIF and lower
+ 0.50f, // L, L
+ 0.50f, // L, H
+ 0.50f, // L, D
+ 0.50f, // H ,L
+ 0.25f, // H, H
+ 0.25f, // H, D
0.50f, // D, L
- 0.63f, // D, D
+ 0.50f, // D, D
0.25f, // D, H
- //over 4CIF: WHD, HD
- 0.25f, // L, L
- 0.75f, // L, H
- 0.75f, // L, D
- 0.75f, // H ,L
- 0.50f, // H, H
- 0.50f, // H, D
+ // over 4CIF: WHD, HD
+ 0.50f, // L, L
+ 0.50f, // L, H
+ 0.50f, // L, D
+ 0.50f, // H ,L
+ 0.25f, // H, H
+ 0.25f, // H, D
0.50f, // D, L
- 0.63f, // D, D
- 0.25f // D, H
+ 0.50f, // D, D
+ 0.25f, // D, H
};
// Action for down-sampling:
// motion=L/H/D,spatial==L/H/D: for low, high, middle levels
-const WebRtc_UWord8 kSpatialAction[9] = {
- 1, // L, L
- 1, // L, H
- 1, // L, D
- 4, // H ,L
- 1, // H, H
- 4, // H, D
- 4, // D, L
- 1, // D, D
- 1, // D, H
+const uint8_t kSpatialAction[9] = {
+ 1, // L, L
+ 1, // L, H
+ 1, // L, D
+ 4, // H ,L
+ 1, // H, H
+ 4, // H, D
+ 4, // D, L
+ 1, // D, H
+ 1, // D, D
};
-const WebRtc_UWord8 kTemporalAction[9] = {
- 1, // L, L
- 2, // L, H
- 2, // L, D
- 1, // H ,L
- 2, // H, H
- 1, // H, D
- 1, // D, L
- 2, // D, D
- 1, // D, H
+const uint8_t kTemporalAction[9] = {
+ 1, // L, L
+ 2, // L, H
+ 2, // L, D
+ 1, // H ,L
+ 2, // H, H
+ 1, // H, D
+ 1, // D, L
+ 2, // D, H
+ 1, // D, D
};
-// Control the total amount of down-sampling allowed
-#define MAX_SPATIAL_DOWN_FACT 4
-#define MAX_TEMP_DOWN_FACT 4
-#define MAX_SPATIAL_TEMP_DOWN_FACT 8
+// Control the total amount of down-sampling allowed.
+const int kMaxSpatialDown = 16;
+const int kMaxTempDown = 4;
+const int kMaxDownSample = 16;
-// Minimum image size for a spatial down-sampling:
-// no spatial down-sampling if input size <= MIN_IMAGE_SIZE
-#define MIN_IMAGE_SIZE 25344 //176*144
+// Minimum image size for a spatial down-sampling.
+const int kMinImageSize= 176 * 144;
// Minimum frame rate for temporal down-sampling:
// no frame rate reduction if incomingFrameRate <= MIN_FRAME_RATE
-#define MIN_FRAME_RATE_QM 8
+const int kMinFrameRate = 8;
// Boundaries for the closest standard frame size
-const WebRtc_UWord32 kFrameSizeTh[6] = {
- 63360, //between 176*144 and 352*288
- 204288, //between 352*288 and 640*480
- 356352, //between 640*480 and 704*576
- 548352, //between 704*576 and 960*720
- 806400, //between 960*720 and 1280*720
+const uint32_t kFrameSizeTh[6] = {
+ 63360, // between 176*144 and 352*288
+ 204288, // between 352*288 and 640*480
+ 356352, // between 640*480 and 704*576
+ 548352, // between 704*576 and 960*720
+ 806400, // between 960*720 and 1280*720
1497600, // between 1280*720 and 1920*1080
};
-
//
// PARAMETERS FOR FEC ADJUSTMENT: TODO (marpan)
//
-
//
// PARAMETETS FOR SETTING LOW/HIGH STATES OF CONTENT METRICS:
//
-// Threshold to determine if high amount of zero_motion
-#define HIGH_ZERO_MOTION_SIZE 0.95
-
-// Thresholds for motion:
-// motion level is derived from motion vectors: motion = size_nz*magn_nz
-#define HIGH_MOTION 0.7
-#define LOW_MOTION 0.4
+// Thresholds for frame rate:
+const int kLowFrameRate = 10;
+const int kHighFrameRate = 25;
// Thresholds for motion: motion level is from NFD
-#define HIGH_MOTION_NFD 0.075
-#define LOW_MOTION_NFD 0.04
+const float kHighMotionNfd = 0.075f;
+const float kLowMotionNfd = 0.04f;
// Thresholds for spatial prediction error:
-// this is appLied on the min(2x2,1x2,2x1)
-#define HIGH_TEXTURE 0.035
-#define LOW_TEXTURE 0.025
+// this is applied on the min(2x2,1x2,2x1)
+const float kHighTexture = 0.035f;
+const float kLowTexture = 0.025f;
-// Used to reduce thresholds for HD scenes: correction factor since higher
-// correlation in HD scenes means lower spatial prediction error
-#define SCALE_TEXTURE_HD 0.9;
-
-// Thresholds for distortion and horizontalness:
-// applied on product: horiz_nz/dist_nz
-#define COHERENCE_THR 1.0
-#define COH_MAX 10
+// Used to reduce thresholds for larger/HD scenes: correction factor since
+// higher correlation in HD scenes means lower spatial prediction error.
+const float kScaleTexture = 0.9f;
// percentage reduction in transitional bitrate for 2x2 selected over 1x2/2x1
-#define RATE_RED_SPATIAL_2X2 0.6
+const float kRateRedSpatial2X2 = 0.6f;
-#define SPATIAL_ERR_2X2_VS_H 0.1 //percentage to favor 2x2
-#define SPATIAL_ERR_2X2_VS_V 0.1 //percentage to favor 2x2 over V
-#define SPATIAL_ERR_V_VS_H 0.1 //percentage to favor H over V
+const float kSpatialErr2x2VsHoriz = 0.1f; // percentage to favor 2x2 over H
+const float kSpatialErr2X2VsVert = 0.1f; // percentage to favor 2x2 over V
+const float kSpatialErrVertVsHoriz = 0.1f; // percentage to favor H over V
-} // namespace webrtc
+} // namespace webrtc
-#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_QM_SELECT_DATA_H_
+#endif // WEBRTC_MODULES_VIDEO_CODING_SOURCE_QM_SELECT_DATA_H_
+
diff --git a/src/modules/video_coding/main/source/qm_select_unittest.cc b/src/modules/video_coding/main/source/qm_select_unittest.cc
new file mode 100644
index 0000000..3e0cef4
--- /dev/null
+++ b/src/modules/video_coding/main/source/qm_select_unittest.cc
@@ -0,0 +1,834 @@
+/*
+ * Copyright (c) 2012 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.
+ */
+
+/*
+ * This file includes unit tests the QmResolution class
+ * In particular, for the selection of spatial and/or temporal down-sampling.
+ */
+
+#include <gtest/gtest.h>
+
+#include "modules/video_coding/main/source/qm_select.h"
+#include "modules/interface/module_common_types.h"
+
+namespace webrtc {
+
+class QmSelectTest : public ::testing::Test {
+ protected:
+ QmSelectTest()
+ : qm_resolution_(new VCMQmResolution()),
+ content_metrics_(new VideoContentMetrics()),
+ qm_scale_(NULL) {
+ }
+ VCMQmResolution* qm_resolution_;
+ VideoContentMetrics* content_metrics_;
+ VCMResolutionScale* qm_scale_;
+
+ void InitQmNativeData(float initial_bit_rate, int user_frame_rate,
+ int native_width, int native_height);
+
+ void UpdateQmEncodedFrame(int* encoded_size, int num_updates);
+
+ void UpdateQmRateData(int* target_rate,
+ int* encoder_sent_rate,
+ int* incoming_frame_rate,
+ uint8_t* fraction_lost,
+ int num_updates);
+
+ void UpdateQmContentData(float motion_metric,
+ float spatial_metric,
+ float spatial_metric_horiz,
+ float spatial_metric_vert);
+
+ bool IsSelectedActionCorrect(VCMResolutionScale* qm_scale,
+ uint8_t fac_width,
+ uint8_t fac_height,
+ uint8_t fac_temp);
+
+ void TearDown() {
+ delete qm_resolution_;
+ delete content_metrics_;
+ }
+};
+
+TEST_F(QmSelectTest, HandleInputs) {
+ // Expect parameter error. Initialize with invalid inputs.
+ EXPECT_EQ(-4, qm_resolution_->Initialize(1000, 0, 640, 480));
+ EXPECT_EQ(-4, qm_resolution_->Initialize(1000, 30, 640, 0));
+ EXPECT_EQ(-4, qm_resolution_->Initialize(1000, 30, 0, 480));
+
+ // Expect uninitialized error.: No valid initialization before selection.
+ EXPECT_EQ(-7, qm_resolution_->SelectResolution(&qm_scale_));
+
+ VideoContentMetrics* content_metrics = NULL;
+ EXPECT_EQ(0, qm_resolution_->Initialize(1000, 30, 640, 480));
+ qm_resolution_->UpdateContent(content_metrics);
+ // Content metrics are NULL: Expect success and no down-sampling action.
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+}
+
+// No down-sampling action at high rates.
+TEST_F(QmSelectTest, NoActionHighRate) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(800, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {800, 800, 800};
+ int encoder_sent_rate[] = {800, 800, 800};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ UpdateQmContentData(0.01, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(0, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+}
+
+// Rate is well below transition, down-sampling action is taken,
+// depending on the content state.
+TEST_F(QmSelectTest, DownActionLowRate) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(100, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {100, 100, 100};
+ int encoder_sent_rate[] = {100, 100, 100};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // High motion, low spatial: 2x2 spatial expected.
+ UpdateQmContentData(0.1, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2, 2, 1));
+
+ qm_resolution_->ResetDownSamplingState();
+ // Low motion, low spatial: no action expected: content is too low.
+ UpdateQmContentData(0.01, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(0, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+
+ qm_resolution_->ResetDownSamplingState();
+ // Medium motion, low spatial: 2x2 spatial expected.
+ UpdateQmContentData(0.06, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(6, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2, 2, 1));
+
+ qm_resolution_->ResetDownSamplingState();
+ // High motion, high spatial: 1/2 temporal expected.
+ UpdateQmContentData(0.1, 0.1, 0.1, 0.1);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(4, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2));
+
+ qm_resolution_->ResetDownSamplingState();
+ // Low motion, high spatial: 1/2 temporal expected.
+ UpdateQmContentData(0.01, 0.1, 0.1, 0.1);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2));
+
+ qm_resolution_->ResetDownSamplingState();
+ // Medium motion, high spatial: 1/2 temporal expected.
+ UpdateQmContentData(0.06, 0.1, 0.1, 0.1);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(7, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2));
+
+ qm_resolution_->ResetDownSamplingState();
+ // High motion, medium spatial: 2x2 spatial expected.
+ UpdateQmContentData(0.1, 0.03, 0.03, 0.03);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(5, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2, 2, 1));
+
+ qm_resolution_->ResetDownSamplingState();
+ // Low motion, medium spatial: high frame rate, so 1/2 temporal expected.
+ UpdateQmContentData(0.01, 0.03, 0.03, 0.03);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(2, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2));
+
+ qm_resolution_->ResetDownSamplingState();
+ // Medium motion, medium spatial: high frame rate, so 1/2 temporal expected.
+ UpdateQmContentData(0.06, 0.03, 0.03, 0.03);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(8, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2));
+}
+
+// Rate mis-match is high, and we have over-shooting.
+// since target rate is below max for down-sampling, down-sampling is selected.
+TEST_F(QmSelectTest, DownActionHighRateMMOvershoot) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(450, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {450, 450, 450};
+ int encoder_sent_rate[] = {900, 900, 900};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // High motion, low spatial.
+ UpdateQmContentData(0.1, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2, 2, 1));
+
+ qm_resolution_->ResetDownSamplingState();
+ // Low motion, high spatial
+ UpdateQmContentData(0.01, 0.1, 0.1, 0.1);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2));
+}
+
+// Rate mis-match is high, target rate is below max for down-sampling,
+// but since we have consistent under-shooting, no down-sampling action.
+TEST_F(QmSelectTest, NoActionHighRateMMUndershoot) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(450, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {450, 450, 450};
+ int encoder_sent_rate[] = {100, 100, 100};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // High motion, low spatial.
+ UpdateQmContentData(0.1, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kEasyEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+
+ qm_resolution_->ResetDownSamplingState();
+ // Low motion, high spatial
+ UpdateQmContentData(0.01, 0.1, 0.1, 0.1);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+}
+
+// Buffer is underflowing, and target rate is below max for down-sampling,
+// so action is taken.
+TEST_F(QmSelectTest, DownActionBufferUnderflow) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(450, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update with encoded size over a number of frames.
+ // per-frame bandwidth = 15 = 450/30: simulate (decoder) buffer underflow:
+ int encoded_size[] = {200, 100, 50, 30, 60, 40, 20, 30, 20, 40};
+ UpdateQmEncodedFrame(encoded_size, 10);
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {450, 450, 450};
+ int encoder_sent_rate[] = {450, 450, 450};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // High motion, low spatial.
+ UpdateQmContentData(0.1, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2, 2, 1));
+
+ qm_resolution_->ResetDownSamplingState();
+ // Low motion, high spatial
+ UpdateQmContentData(0.01, 0.1, 0.1, 0.1);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2));
+}
+
+// Target rate is below max for down-sampling, but buffer level is stable,
+// so no action is taken.
+TEST_F(QmSelectTest, NoActionBufferStable) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(450, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update with encoded size over a number of frames.
+ // per-frame bandwidth = 15 = 450/30: simulate stable (decoder) buffer levels.
+ int32_t encoded_size[] = {40, 10, 10, 16, 18, 20, 17, 20, 16, 15};
+ UpdateQmEncodedFrame(encoded_size, 10);
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {450, 450, 450};
+ int encoder_sent_rate[] = {450, 450, 450};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // High motion, low spatial.
+ UpdateQmContentData(0.1, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+
+ qm_resolution_->ResetDownSamplingState();
+ // Low motion, high spatial
+ UpdateQmContentData(0.01, 0.1, 0.1, 0.1);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+}
+
+// Very low rate, but no spatial down-sampling below some size (QCIF).
+TEST_F(QmSelectTest, LimitDownSpatialAction) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(10, 30, 176, 144);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 176;
+ uint16_t codec_height = 144;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(0, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {10, 10, 10};
+ int encoder_sent_rate[] = {10, 10, 10};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // High motion, low spatial.
+ UpdateQmContentData(0.1, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+}
+
+// Very low rate, but no frame reduction below some frame_rate (8fps).
+TEST_F(QmSelectTest, LimitDownTemporalAction) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(10, 8, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {10, 10, 10};
+ int encoder_sent_rate[] = {10, 10, 10};
+ int incoming_frame_rate[] = {8, 8, 8};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // Low motion, medium spatial.
+ UpdateQmContentData(0.01, 0.03, 0.03, 0.03);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(2, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+}
+
+// Two stages: spatial down-sample and then back up spatially,
+// as rate as increased.
+TEST_F(QmSelectTest, 2StageDownSpatialUpSpatial) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(100, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {100, 100, 100};
+ int encoder_sent_rate[] = {100, 100, 100};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // High motion, low spatial.
+ UpdateQmContentData(0.1, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2, 2, 1));
+
+ // Reset and go up in rate: expected to go back up.
+ qm_resolution_->ResetRates();
+ qm_resolution_->UpdateCodecFrameSize(320, 240);
+ EXPECT_EQ(1, qm_resolution_->GetImageType(320, 240));
+ // Update rates for a sequence of intervals.
+ int target_rate2[] = {400, 400, 400, 400, 400};
+ int encoder_sent_rate2[] = {400, 400, 400, 400, 400};
+ int incoming_frame_rate2[] = {30, 30, 30, 30, 30};
+ uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
+ UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
+ fraction_lost2, 5);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0, 0, 1));
+}
+
+// Two stages: spatial down-sample and then back up spatially, since encoder
+// is under-shooting target even though rate has not increased much.
+TEST_F(QmSelectTest, 2StageDownSpatialUpSpatialUndershoot) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(100, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {100, 100, 100};
+ int encoder_sent_rate[] = {100, 100, 100};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // High motion, low spatial.
+ UpdateQmContentData(0.1, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2, 2, 1));
+
+ // Reset rates and simulate under-shooting scenario.: expect to go back up.
+ qm_resolution_->ResetRates();
+ qm_resolution_->UpdateCodecFrameSize(320, 240);
+ EXPECT_EQ(1, qm_resolution_->GetImageType(320, 240));
+ // Update rates for a sequence of intervals.
+ int target_rate2[] = {200, 200, 200, 200, 200};
+ int encoder_sent_rate2[] = {50, 50, 50, 50, 50};
+ int incoming_frame_rate2[] = {30, 30, 30, 30, 30};
+ uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
+ UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
+ fraction_lost2, 5);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(kEasyEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0, 0, 1));
+}
+
+// Two stages: spatial down-sample and then no action to go up,
+// as encoding rate mis-match is too high.
+TEST_F(QmSelectTest, 2StageDownSpatialNoActionUp) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(100, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {100, 100, 100};
+ int encoder_sent_rate[] = {100, 100, 100};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // High motion, low spatial.
+ UpdateQmContentData(0.1, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2, 2, 1));
+
+ // Reset and simulate large rate mis-match: expect no action to go back up.
+ qm_resolution_->ResetRates();
+ qm_resolution_->UpdateCodecFrameSize(320, 240);
+ EXPECT_EQ(1, qm_resolution_->GetImageType(320, 240));
+ // Update rates for a sequence of intervals.
+ int target_rate2[] = {400, 400, 400, 400, 400};
+ int encoder_sent_rate2[] = {1000, 1000, 1000, 1000, 1000};
+ int incoming_frame_rate2[] = {30, 30, 30, 30, 30};
+ uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
+ UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
+ fraction_lost2, 5);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+}
+// Two stages: temporally down-sample and then back up temporally,
+// as rate as increased.
+TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporal) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(100, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {100, 100, 100};
+ int encoder_sent_rate[] = {100, 100, 100};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // Low motion, high spatial.
+ UpdateQmContentData(0.01, 0.1, 0.1, 0.1);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2));
+
+ // Reset rates and go up in rate: expect to go back up.
+ qm_resolution_->ResetRates();
+ // Update rates for a sequence of intervals.
+ int target_rate2[] = {400, 400, 400, 400, 400};
+ int encoder_sent_rate2[] = {400, 400, 400, 400, 400};
+ int incoming_frame_rate2[] = {15, 15, 15, 15, 15};
+ uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
+ UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
+ fraction_lost2, 5);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 0));
+}
+
+// Two stages: temporal down-sample and then back up temporally, since encoder
+// is under-shooting target even though rate has not increased much.
+TEST_F(QmSelectTest, 2StatgeDownTemporalUpTemporalUndershoot) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(100, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {100, 100, 100};
+ int encoder_sent_rate[] = {100, 100, 100};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // Low motion, high spatial.
+ UpdateQmContentData(0.01, 0.1, 0.1, 0.1);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2));
+
+ // Reset rates and simulate under-shooting scenario.: expect to go back up.
+ qm_resolution_->ResetRates();
+ // Update rates for a sequence of intervals.
+ int target_rate2[] = {200, 200, 200, 200, 200};
+ int encoder_sent_rate2[] = {50, 50, 50, 50, 50};
+ int incoming_frame_rate2[] = {15, 15, 15, 15, 15};
+ uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
+ UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
+ fraction_lost2, 5);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(kEasyEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 0));
+}
+
+// Two stages: temporal down-sample and then no action to go up,
+// as encoding rate mis-match is too high.
+TEST_F(QmSelectTest, 2StageDownTemporalNoActionUp) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(100, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {100, 100, 100};
+ int encoder_sent_rate[] = {100, 100, 100};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // Low motion, high spatial.
+ UpdateQmContentData(0.01, 0.1, 0.1, 0.1);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2));
+
+ // Reset and simulate large rate mis-match: expect no action to go back up.
+ qm_resolution_->ResetRates();
+ // Update rates for a sequence of intervals.
+ int target_rate2[] = {600, 600, 600, 600, 600};
+ int encoder_sent_rate2[] = {1000, 1000, 1000, 1000, 1000};
+ int incoming_frame_rate2[] = {15, 15, 15, 15, 15};
+ uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
+ UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
+ fraction_lost2, 5);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(kStressedEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+}
+// 3 stages: spatial down-sample, followed by temporal down-sample,
+// and then go up to full state, as encoding rate has increased.
+TEST_F(QmSelectTest, 3StageDownSpatialTemporlaUpSpatialTemporal) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(100, 30, 640, 480);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 640;
+ uint16_t codec_height = 480;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {100, 100, 100};
+ int encoder_sent_rate[] = {100, 100, 100};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // High motion, low spatial.
+ UpdateQmContentData(0.1, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2, 2, 1));
+
+ // Reset rate and change content data: expect temporal down-sample.
+ qm_resolution_->ResetRates();
+ qm_resolution_->UpdateCodecFrameSize(320, 240);
+ EXPECT_EQ(1, qm_resolution_->GetImageType(320, 240));
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // Low motion, high spatial.
+ UpdateQmContentData(0.01, 0.1, 0.1, 0.1);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 2));
+
+ // Reset rates and go high up in rate: expect to go back up both spatial
+ // and temporally.
+ qm_resolution_->ResetRates();
+ // Update rates for a sequence of intervals.
+ int target_rate2[] = {1000, 1000, 1000, 1000, 1000};
+ int encoder_sent_rate2[] = {1000, 1000, 1000, 1000, 1000};
+ int incoming_frame_rate2[] = {15, 15, 15, 15, 15};
+ uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
+ UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
+ fraction_lost2, 5);
+
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(1, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 0, 0, 0));
+}
+
+// No down-sampling below some totol amount (factor of 16)
+TEST_F(QmSelectTest, NoActionTooMuchDownSampling) {
+ // Initialize with bitrate, frame rate, and native system width/height.
+ InitQmNativeData(400, 30, 1280, 720);
+
+ // Update with encoder frame size.
+ uint16_t codec_width = 1280;
+ uint16_t codec_height = 720;
+ qm_resolution_->UpdateCodecFrameSize(codec_width, codec_height);
+ EXPECT_EQ(5, qm_resolution_->GetImageType(codec_width, codec_height));
+
+ // Update rates for a sequence of intervals.
+ int target_rate[] = {400, 400, 400};
+ int encoder_sent_rate[] = {400, 400, 400};
+ int incoming_frame_rate[] = {30, 30, 30};
+ uint8_t fraction_lost[] = {10, 10, 10};
+ UpdateQmRateData(target_rate, encoder_sent_rate, incoming_frame_rate,
+ fraction_lost, 3);
+
+ // Update content: motion level, and 3 spatial prediction errors.
+ // High motion, low spatial: 2x2 spatial expected.
+ UpdateQmContentData(0.1, 0.01, 0.01, 0.01);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2, 2, 1));
+
+ // Reset and lower rates to get another spatial action.
+ qm_resolution_->ResetRates();
+ qm_resolution_->UpdateCodecFrameSize(640, 360);
+ EXPECT_EQ(2, qm_resolution_->GetImageType(640, 360));
+ // Update rates for a sequence of intervals.
+ int target_rate2[] = {100, 100, 100, 100, 100};
+ int encoder_sent_rate2[] = {100, 100, 100, 100, 100};
+ int incoming_frame_rate2[] = {30, 30, 30, 30, 30};
+ uint8_t fraction_lost2[] = {10, 10, 10, 10, 10};
+ UpdateQmRateData(target_rate2, encoder_sent_rate2, incoming_frame_rate2,
+ fraction_lost2, 5);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 2, 2, 1));
+
+ // Reset and go to low rate: no action should be taken,
+ // we went down too much already.
+ qm_resolution_->ResetRates();
+ qm_resolution_->UpdateCodecFrameSize(320, 180);
+ EXPECT_EQ(0, qm_resolution_->GetImageType(320, 180));
+ // Update rates for a sequence of intervals.
+ int target_rate3[] = {10, 10, 10, 10, 10};
+ int encoder_sent_rate3[] = {10, 10, 10, 10, 10};
+ int incoming_frame_rate3[] = {30, 30, 30, 30, 30};
+ uint8_t fraction_lost3[] = {10, 10, 10, 10, 10};
+ UpdateQmRateData(target_rate3, encoder_sent_rate3, incoming_frame_rate3,
+ fraction_lost3, 5);
+ EXPECT_EQ(0, qm_resolution_->SelectResolution(&qm_scale_));
+ EXPECT_EQ(3, qm_resolution_->ComputeContentClass());
+ EXPECT_EQ(kStableEncoding, qm_resolution_->GetEncoderState());
+ EXPECT_TRUE(IsSelectedActionCorrect(qm_scale_, 1, 1, 1));
+}
+
+void QmSelectTest::InitQmNativeData(float initial_bit_rate,
+ int user_frame_rate,
+ int native_width,
+ int native_height) {
+ EXPECT_EQ(0, qm_resolution_->Initialize(initial_bit_rate, user_frame_rate,
+ native_width, native_height));
+}
+
+void QmSelectTest::UpdateQmContentData(float motion_metric,
+ float spatial_metric,
+ float spatial_metric_horiz,
+ float spatial_metric_vert) {
+ content_metrics_->motion_magnitude = motion_metric;
+ content_metrics_->spatial_pred_err = spatial_metric;
+ content_metrics_->spatial_pred_err_h = spatial_metric_horiz;
+ content_metrics_->spatial_pred_err_v = spatial_metric_vert;
+ qm_resolution_->UpdateContent(content_metrics_);
+}
+
+void QmSelectTest::UpdateQmEncodedFrame(int* encoded_size, int num_updates) {
+ FrameType frame_type = kVideoFrameDelta;
+ for (int i = 0; i < num_updates; i++) {
+ // Convert to bytes.
+ int32_t encoded_size_update = 1000 * encoded_size[i] / 8;
+ qm_resolution_->UpdateEncodedSize(encoded_size_update, frame_type);
+ }
+}
+
+void QmSelectTest::UpdateQmRateData(int* target_rate,
+ int* encoder_sent_rate,
+ int* incoming_frame_rate,
+ uint8_t* fraction_lost,
+ int num_updates) {
+ for (int i = 0; i < num_updates; i++) {
+ float target_rate_update = target_rate[i];
+ float encoder_sent_rate_update = encoder_sent_rate[i];
+ float incoming_frame_rate_update = incoming_frame_rate[i];
+ uint8_t fraction_lost_update = fraction_lost[i];
+ qm_resolution_->UpdateRates(target_rate_update,
+ encoder_sent_rate_update,
+ incoming_frame_rate_update,
+ fraction_lost_update);
+ }
+}
+
+// Check is the selected action from the QmResolution class is the same
+// as the expected scales from |fac_width|, |fac_height|, |fac_temp|.
+bool QmSelectTest::IsSelectedActionCorrect(VCMResolutionScale* qm_scale,
+ uint8_t fac_width,
+ uint8_t fac_height,
+ uint8_t fac_temp) {
+ if (qm_scale->spatialWidthFact == fac_width &&
+ qm_scale->spatialHeightFact == fac_height &&
+ qm_scale->temporalFact == fac_temp) {
+ return true;
+ } else {
+ return false;
+ }
+}
+} // namespace webrtc
diff --git a/src/modules/video_coding/main/source/video_coding_test.gypi b/src/modules/video_coding/main/source/video_coding_test.gypi
index a0ff037..7ab265c 100644
--- a/src/modules/video_coding/main/source/video_coding_test.gypi
+++ b/src/modules/video_coding/main/source/video_coding_test.gypi
@@ -83,6 +83,7 @@
'jitter_buffer_unittest.cc',
'session_info_unittest.cc',
'video_coding_robustness_unittest.cc',
+ 'qm_select_unittest.cc',
],
},
],
diff --git a/src/modules/video_processing/main/source/content_analysis.cc b/src/modules/video_processing/main/source/content_analysis.cc
index 32ee09a..8ea319c 100644
--- a/src/modules/video_processing/main/source/content_analysis.cc
+++ b/src/modules/video_processing/main/source/content_analysis.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ * Copyright (c) 2012 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
@@ -23,14 +23,10 @@
_height(0),
_skipNum(1),
_border(8),
-_motionMagnitudeNZ(0.0f),
+_motionMagnitude(0.0f),
_spatialPredErr(0.0f),
_spatialPredErrH(0.0f),
_spatialPredErrV(0.0f),
-_sizeZeroMotion(0.0f),
-_motionPredErr(0.0f),
-_motionHorizontalness(0.0f),
-_motionClusterDistortion(0.0f),
_firstFrame(true),
_CAInit(false),
_cMetrics(NULL)
@@ -224,7 +220,7 @@
}
// default
- _motionMagnitudeNZ = 0.0f;
+ _motionMagnitude = 0.0f;
if (tempDiffSum == 0)
{
@@ -240,7 +236,7 @@
if (contrast > 0.0)
{
contrast = sqrt(contrast);
- _motionMagnitudeNZ = tempDiffAvg/contrast;
+ _motionMagnitude = tempDiffAvg/contrast;
}
return VPM_OK;
@@ -329,18 +325,11 @@
return NULL;
}
-
- _cMetrics->spatialPredErr = _spatialPredErr;
- _cMetrics->spatialPredErrH = _spatialPredErrH;
- _cMetrics->spatialPredErrV = _spatialPredErrV;
- // normalized temporal difference (MAD)
- _cMetrics->motionMagnitudeNZ = _motionMagnitudeNZ;
-
- // Set to zero: not computed
- _cMetrics->motionPredErr = _motionPredErr;
- _cMetrics->sizeZeroMotion = _sizeZeroMotion;
- _cMetrics->motionHorizontalness = _motionHorizontalness;
- _cMetrics->motionClusterDistortion = _motionClusterDistortion;
+ _cMetrics->spatial_pred_err = _spatialPredErr;
+ _cMetrics->spatial_pred_err_h = _spatialPredErrH;
+ _cMetrics->spatial_pred_err_v = _spatialPredErrV;
+ // Motion metric: normalized temporal difference (MAD)
+ _cMetrics->motion_magnitude = _motionMagnitude;
return _cMetrics;
diff --git a/src/modules/video_processing/main/source/content_analysis.h b/src/modules/video_processing/main/source/content_analysis.h
index 5051650..588712a 100644
--- a/src/modules/video_processing/main/source/content_analysis.h
+++ b/src/modules/video_processing/main/source/content_analysis.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ * Copyright (c) 2012 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
@@ -74,15 +74,10 @@
// Content Metrics:
// stores the local average of the metrics
- float _motionMagnitudeNZ; // motion class
+ float _motionMagnitude; // motion class
float _spatialPredErr; // spatial class
float _spatialPredErrH; // spatial class
float _spatialPredErrV; // spatial class
- float _sizeZeroMotion; // motion class
- float _motionPredErr; // complexity class:
- float _motionHorizontalness; // coherence class
- float _motionClusterDistortion; // coherence class
-
bool _firstFrame;
bool _CAInit;
diff --git a/src/modules/video_processing/main/source/content_analysis_sse2.cc b/src/modules/video_processing/main/source/content_analysis_sse2.cc
index 810c3cc..f505850 100644
--- a/src/modules/video_processing/main/source/content_analysis_sse2.cc
+++ b/src/modules/video_processing/main/source/content_analysis_sse2.cc
@@ -102,7 +102,7 @@
const WebRtc_UWord32 tempDiffSum = sad_final_64[0] + sad_final_64[1];
// default
- _motionMagnitudeNZ = 0.0f;
+ _motionMagnitude = 0.0f;
if (tempDiffSum == 0)
{
@@ -118,7 +118,7 @@
if (contrast > 0.0)
{
contrast = sqrt(contrast);
- _motionMagnitudeNZ = tempDiffAvg/contrast;
+ _motionMagnitude = tempDiffAvg/contrast;
}
return VPM_OK;
diff --git a/src/modules/video_processing/main/source/frame_preprocessor.cc b/src/modules/video_processing/main/source/frame_preprocessor.cc
index fa13b2d..76fdac8 100644
--- a/src/modules/video_processing/main/source/frame_preprocessor.cc
+++ b/src/modules/video_processing/main/source/frame_preprocessor.cc
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ * Copyright (c) 2012 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
@@ -16,8 +16,6 @@
VPMFramePreprocessor::VPMFramePreprocessor():
_id(0),
_contentMetrics(NULL),
-_nativeHeight(0),
-_nativeWidth(0),
_maxFrameRate(0),
_resampledFrame(),
_enableCA(false)
@@ -46,8 +44,6 @@
void
VPMFramePreprocessor::Reset()
{
- _nativeWidth = 0;
- _nativeHeight = 0;
_ca->Release();
_vd->Reset();
_contentMetrics = NULL;
@@ -172,11 +168,6 @@
} else {
_contentMetrics = _ca->ComputeContentMetrics(&_resampledFrame);
}
- // Update native values:
- _contentMetrics->nativeHeight = frame->Height();
- _contentMetrics->nativeWidth = frame->Width();
- // Max value as set by user
- _contentMetrics->nativeFrameRate = _maxFrameRate;
}
return VPM_OK;
}
diff --git a/src/modules/video_processing/main/source/frame_preprocessor.h b/src/modules/video_processing/main/source/frame_preprocessor.h
index cb5f97d..3c07a47 100644
--- a/src/modules/video_processing/main/source/frame_preprocessor.h
+++ b/src/modules/video_processing/main/source/frame_preprocessor.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
+ * Copyright (c) 2012 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
@@ -66,8 +66,6 @@
WebRtc_Word32 _id;
VideoContentMetrics* _contentMetrics;
- WebRtc_UWord32 _nativeHeight;
- WebRtc_UWord32 _nativeWidth;
WebRtc_UWord32 _maxFrameRate;
VideoFrame _resampledFrame;
VPMSpatialResampler* _spatialResampler;
diff --git a/src/modules/video_processing/main/test/unit_test/content_metrics_test.cc b/src/modules/video_processing/main/test/unit_test/content_metrics_test.cc
index b25c45f..54a1390 100644
--- a/src/modules/video_processing/main/test/unit_test/content_metrics_test.cc
+++ b/src/modules/video_processing/main/test/unit_test/content_metrics_test.cc
@@ -29,10 +29,10 @@
_cM_c = _ca_c.ComputeContentMetrics(&_videoFrame);
_cM_SSE = _ca_sse.ComputeContentMetrics(&_videoFrame);
- ASSERT_EQ(_cM_c->spatialPredErr, _cM_SSE->spatialPredErr);
- ASSERT_EQ(_cM_c->spatialPredErrV, _cM_SSE->spatialPredErrV);
- ASSERT_EQ(_cM_c->spatialPredErrH, _cM_SSE->spatialPredErrH);
- ASSERT_EQ(_cM_c->motionMagnitudeNZ, _cM_SSE->motionMagnitudeNZ);
+ ASSERT_EQ(_cM_c->spatial_pred_err, _cM_SSE->spatial_pred_err);
+ ASSERT_EQ(_cM_c->spatial_pred_err_v, _cM_SSE->spatial_pred_err_v);
+ ASSERT_EQ(_cM_c->spatial_pred_err_h, _cM_SSE->spatial_pred_err_h);
+ ASSERT_EQ(_cM_c->motion_magnitude, _cM_SSE->motion_magnitude);
}
ASSERT_NE(0, feof(_sourceFile)) << "Error reading source file";
}