Modified media_opt and qm_select to allow for robustness settings
based on the content metrics. Re-organized the class structure in qm_select
into a resolution class and robustness settings class, both derived from
a main (qm/content analysis) class.
Review URL: http://webrtc-codereview.appspot.com/55006

git-svn-id: http://webrtc.googlecode.com/svn/trunk@188 4adac7df-926f-26a2-2b94-8c16560cd09d
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 a0937b6..4f7f40c 100644
--- a/src/modules/video_coding/main/source/media_opt_util.cc
+++ b/src/modules/video_coding/main/source/media_opt_util.cc
@@ -21,6 +21,13 @@
 
 namespace webrtc {
 
+void
+VCMProtectionMethod::UpdateContentMetrics(
+                     const VideoContentMetrics*  contentMetrics)
+{
+   _qmRobustness->UpdateContent(contentMetrics);
+}
+
 bool
 VCMProtectionMethod::BetterThan(VCMProtectionMethod *pm)
 {
@@ -417,6 +424,10 @@
         codeRateDelta = plossMax - 1;
     }
 
+    codeRateDelta = _qmRobustness->AdjustFecFactor(codeRateDelta, bitRate,
+                                                   parameters->frameRate,
+                                                   parameters->rtt, packetLoss);
+
     // For Key frame:
     // Effectively at a higher rate, so we scale/boost the rate
     // The boost factor may depend on several factors: ratio of packet
@@ -462,6 +473,14 @@
     _protectionFactorK = codeRateKey;
     _protectionFactorD = codeRateDelta;
 
+
+     // Set the UEP protection on/off for Key and Delta frames
+    _uepKey = _qmRobustness->SetUepProtection(codeRateKey, bitRate,
+                                              packetLoss, 0);
+
+    _uepDelta = _qmRobustness->SetUepProtection(codeRateKey, bitRate,
+                                                packetLoss, 1);
+
     // DONE WITH FEC PROTECTION SETTINGS
     return true;
 }
diff --git a/src/modules/video_coding/main/source/media_opt_util.h b/src/modules/video_coding/main/source/media_opt_util.h
index 8298406..3f78dd7 100644
--- a/src/modules/video_coding/main/source/media_opt_util.h
+++ b/src/modules/video_coding/main/source/media_opt_util.h
@@ -17,6 +17,7 @@
 #include "exp_filter.h"
 #include "internal_defines.h"
 #include "tick_time.h"
+#include "qm_select.h"
 
 #include <cmath>
 #include <cstdlib>
@@ -97,8 +98,10 @@
     //friend VCMProtectionMethod;
     VCMProtectionMethod(VCMProtectionMethodEnum type) : _protectionFactorK(0),
         _protectionFactorD(0), _residualPacketLoss(0.0), _scaleProtKey(2.0),
-        _maxPayloadSize(1460), _efficiency(0), _score(0), _type(type) {}
-    virtual ~VCMProtectionMethod() {}
+        _maxPayloadSize(1460), _efficiency(0), _score(0), _type(type),
+        _uepKey(0), _uepDelta(1)
+        {_qmRobustness = new VCMQmRobustness();}
+    virtual ~VCMProtectionMethod() { delete _qmRobustness;}
 
     // Updates the efficiency of the method using the parameters provided
     //
@@ -142,6 +145,9 @@
     // Return value                 : Required protectionFactor for delta frame
     virtual WebRtc_UWord8 RequiredProtectionFactorD() { return _protectionFactorD; }
 
+    // Updates content metrics
+    void UpdateContentMetrics(const VideoContentMetrics*  contentMetrics);
+
     WebRtc_UWord8                        _effectivePacketLoss;
     WebRtc_UWord8                        _protectionFactorK;
     WebRtc_UWord8                        _protectionFactorD;
@@ -149,6 +155,10 @@
     float                                _scaleProtKey;
     WebRtc_Word32                        _maxPayloadSize;
 
+    VCMQmRobustness*                     _qmRobustness;
+    bool                                 _uepKey;
+    bool                                 _uepDelta;
+
 protected:
     float                                _efficiency;
     float                                _score;
diff --git a/src/modules/video_coding/main/source/media_optimization.cc b/src/modules/video_coding/main/source/media_optimization.cc
index e215986..3b03515 100644
--- a/src/modules/video_coding/main/source/media_optimization.cc
+++ b/src/modules/video_coding/main/source/media_optimization.cc
@@ -46,7 +46,7 @@
     _frameDropper  = new VCMFrameDropper(_id);
     _lossProtLogic = new VCMLossProtectionLogic();
     _content = new VCMContentMetricsProcessing();
-    _qms = new VCMQmSelect();
+    _qmResolution = new VCMQmResolution();
 }
 
 VCMMediaOptimization::~VCMMediaOptimization(void)
@@ -55,7 +55,7 @@
     delete _lossProtLogic;
     delete _frameDropper;
     delete _content;
-    delete _qms;
+    delete _qmResolution;
 }
 
 WebRtc_Word32
@@ -67,7 +67,7 @@
     _lossProtLogic->Reset();
     _frameDropper->SetRates(0, 0);
     _content->Reset();
-    _qms->Reset();
+    _qmResolution->Reset();
     _lossProtLogic->UpdateFrameRate(_incomingFrameRate);
     _lossProtLogic->Reset();
     _sendStatisticsZeroEncode = 0;
@@ -135,6 +135,10 @@
         selectedMethod->Type() == kNackFec ))
     {
 
+        // Update protection method with content metrics
+        selectedMethod->UpdateContentMetrics(_content->ShortTermAvgData());
+
+
         // Update method will compute the robustness settings for the given
         // protection method and the overhead cost
         // the protection method is set by the user via SetVideoProtection.
@@ -197,8 +201,8 @@
     if (_enableQm)
     {
         //Update QM with rates
-        _qms->UpdateRates((float)_targetBitRate, _avgSentBitRateBps,
-                          _incomingFrameRate, _fractionLost);
+        _qmResolution->UpdateRates((float)_targetBitRate, _avgSentBitRateBps,
+                                  _incomingFrameRate, _fractionLost);
         //Check for QM selection
         bool selectQM = checkStatusForQMchange();
         if (selectQM)
@@ -249,11 +253,12 @@
     _lossProtLogic->UpdateFrameSize(width, height);
     _frameDropper->Reset();
     _frameDropper->SetRates(static_cast<float>(bitRate), static_cast<float>(frameRate));
-    _userFrameRate = (float)frameRate;
+    _userFrameRate = static_cast<float>(frameRate);
     _codecWidth = width;
     _codecHeight = height;
     WebRtc_Word32 ret = VCM_OK;
-    ret = _qms->Initialize((float)_targetBitRate, _userFrameRate, _codecWidth, _codecHeight);
+    ret = _qmResolution->Initialize((float)_targetBitRate, _userFrameRate,
+                                    _codecWidth, _codecHeight);
     return ret;
 }
 
@@ -420,7 +425,8 @@
             if (_enableQm)
             {
                 // update quality select with encoded length
-                _qms->UpdateEncodedSize(encodedLength, encodedFrameType);
+                _qmResolution->UpdateEncodedSize(encodedLength,
+                                                 encodedFrameType);
             }
         }
         if (!deltaFrame && encodedLength > 0)
@@ -525,7 +531,7 @@
     {
          //No QM if metrics are NULL
          _enableQm = false;
-         _qms->Reset();
+         _qmResolution->Reset();
     }
     else
     {
@@ -537,14 +543,14 @@
 VCMMediaOptimization::SelectQuality()
 {
     // Reset quantities for QM select
-    _qms->ResetQM();
+    _qmResolution->ResetQM();
 
     // Update QM will long-term averaged content metrics.
-    _qms->UpdateContent(_content->LongTermAvgData());
+    _qmResolution->UpdateContent(_content->LongTermAvgData());
 
     // Select quality mode
-    VCMQualityMode* qm = NULL;
-    WebRtc_Word32 ret = _qms->SelectQuality(&qm);
+    VCMResolutionScale* qm = NULL;
+    WebRtc_Word32 ret = _qmResolution->SelectResolution(&qm);
     if (ret < 0)
     {
           return ret;
@@ -554,7 +560,7 @@
     QMUpdate(qm);
 
     // Reset all the rate and related frame counters quantities
-    _qms->ResetRates();
+    _qmResolution->ResetRates();
 
     // Reset counters
     _lastQMUpdateTime = VCMTickTime::MillisecondTimestamp();
@@ -592,7 +598,7 @@
 }
 
 bool
-VCMMediaOptimization::QMUpdate(VCMQualityMode* qm)
+VCMMediaOptimization::QMUpdate(VCMResolutionScale* qm)
 {
     // Check for no change
     if (qm->spatialHeightFact == 1 &&
@@ -606,7 +612,9 @@
     VideoContentMetrics* cm = _content->LongTermAvgData();
 
     // Temporal
-    WebRtc_UWord32 frameRate  = static_cast<WebRtc_UWord32>(_incomingFrameRate + 0.5f);
+    WebRtc_UWord32 frameRate = static_cast<WebRtc_UWord32>
+                               (_incomingFrameRate + 0.5f);
+
     // Check if go back up in temporal resolution
     if (qm->temporalFact == 0)
     {
diff --git a/src/modules/video_coding/main/source/media_optimization.h b/src/modules/video_coding/main/source/media_optimization.h
index 8a9993e..eecf270 100644
--- a/src/modules/video_coding/main/source/media_optimization.h
+++ b/src/modules/video_coding/main/source/media_optimization.h
@@ -159,7 +159,7 @@
     * verify if QM settings differ from default, i.e. if an update is required
     * Compute actual values, as will be sent to the encoder
     */
-    bool QMUpdate(VCMQualityMode* qm);
+    bool QMUpdate(VCMResolutionScale* qm);
     /**
     * check if we should make a QM change
     * will return 1 if yes, 0 otherwise
@@ -207,7 +207,7 @@
     WebRtc_UWord32                    _deltaFrameCnt;
 
     VCMContentMetricsProcessing*      _content;
-    VCMQmSelect*                      _qms;
+    VCMQmResolution*                  _qmResolution;
 
     WebRtc_Word64                     _lastQMUpdateTime;
     WebRtc_Word64                     _lastChangeTime; // content or user triggered
diff --git a/src/modules/video_coding/main/source/qm_select.cc b/src/modules/video_coding/main/source/qm_select.cc
index 3220db0..82c31da 100644
--- a/src/modules/video_coding/main/source/qm_select.cc
+++ b/src/modules/video_coding/main/source/qm_select.cc
@@ -20,36 +20,204 @@
 
 namespace webrtc {
 
-VCMQmSelect::VCMQmSelect()
+// QM-METHOD class
+
+VCMQmMethod::VCMQmMethod()
 {
-    _qm = new VCMQualityMode();
     _contentMetrics = new VideoContentMetrics();
-     Reset();
+    ResetQM();
 }
 
-VCMQmSelect::~VCMQmSelect()
+VCMQmMethod::~VCMQmMethod()
 {
-    delete _qm;
     delete _contentMetrics;
 }
 
 void
-VCMQmSelect::ResetQM()
+VCMQmMethod::ResetQM()
 {
     _motion.Reset();
     _spatial.Reset();
     _coherence.Reset();
     _stationaryMotion = 0;
     _aspectRatio = 1;
-    _maxRateQM = 0;
-    _imageType = 1;
-    _userResolutionPref = 50; // Neutral
-    _qm->Reset();
+    _imageType = 2;
     return;
 }
 
 void
-VCMQmSelect::ResetRates()
+VCMQmMethod::UpdateContent(const VideoContentMetrics*  contentMetrics)
+{
+    _contentMetrics = contentMetrics;
+}
+
+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::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::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;
+
+    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;
+    }
+}
+
+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;
+    }
+
+}
+
+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;
+}
+
+// DONE WITH QM CLASS
+
+
+//RESOLUTION CLASS
+
+VCMQmResolution::VCMQmResolution()
+{
+     _qm = new VCMResolutionScale();
+     Reset();
+}
+
+VCMQmResolution::~VCMQmResolution()
+{
+    delete _qm;
+}
+
+void
+VCMQmResolution::ResetRates()
 {
     _sumEncodedBytes = 0;
     _sumTargetRate = 0.0f;
@@ -65,7 +233,7 @@
 }
 
 void
-VCMQmSelect::Reset()
+VCMQmResolution::Reset()
 {
     _stateDecFactorSpatial = 1;
     _stateDecFactorTemp  = 1;
@@ -74,17 +242,14 @@
     _incomingFrameRate = 0.0f;
     _userFrameRate = 0.0f;
     _perFrameBandwidth =0.0f;
-    _prevTotalRate = 0.0f;
-    _prevRttTime = 0;
-    _prevPacketLoss = 0;
-     ResetQM();
-     ResetRates();
-     return;
+    ResetRates();
+    ResetQM();
+    return;
 }
 
-//Initialize after reset of encoder
+// Initialize rate control quantities after reset of encoder
 WebRtc_Word32
-VCMQmSelect::Initialize(float bitRate, float userFrameRate,
+VCMQmResolution::Initialize(float bitRate, float userFrameRate,
                         WebRtc_UWord32 width, WebRtc_UWord32 height)
 {
     if (userFrameRate == 0.0f || width == 0 || height == 0)
@@ -98,6 +263,12 @@
     _width = width;
     _height = height;
 
+    // Aspect ratio: used for selection of 1x2,2x1,2x2
+    _aspectRatio = static_cast<float>(_width) / static_cast<float>(_height);
+
+    // Set the imageType for the encoder width/height.
+    _imageType = GetImageType(_width, _height);
+
     // Initial buffer level
     _bufferLevel = INIT_BUFFER_LEVEL * _targetBitRate;
 
@@ -121,19 +292,9 @@
     return VCM_OK;
 }
 
-WebRtc_Word32
-VCMQmSelect::SetPreferences(WebRtc_Word8 resolPref)
-{
-    // Preference setting for temporal over spatial resolution
-    // 100 means temporal, 0 means spatial, 50 is neutral
-    _userResolutionPref = resolPref;
-
-    return VCM_OK;
-}
-
-//Update after every encoded frame
+// Update after every encoded frame
 void
-VCMQmSelect::UpdateEncodedSize(WebRtc_Word64 encodedSize,
+VCMQmResolution::UpdateEncodedSize(WebRtc_Word64 encodedSize,
                                FrameType encodedFrameType)
 {
     // Update encoded size;
@@ -167,16 +328,16 @@
     */
 
     // Counter for occurrences of low buffer level
-    if (_bufferLevel <= PERC_BUFFER_THR * INIT_BUFFER_LEVEL * _targetBitRate)
+    if (_bufferLevel <= PERC_BUFFER_THR * OPT_BUFFER_LEVEL * _targetBitRate)
     {
         _lowBufferCnt++;
     }
 
 }
 
-//Update various quantities after SetTargetRates in MediaOpt
+// Update various quantities after SetTargetRates in MediaOpt
 void
-VCMQmSelect::UpdateRates(float targetBitRate, float avgSentBitRate,
+VCMQmResolution::UpdateRates(float targetBitRate, float avgSentBitRate,
                          float incomingFrameRate, WebRtc_UWord8 packetLoss)
 {
 
@@ -219,42 +380,10 @@
 
 }
 
-// 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.
-WebRtc_UWord8
-VCMQmSelect::AdjustFecFactor(WebRtc_UWord8 codeRateDelta, float totalRate,
-                             float frameRate,WebRtc_UWord16 rttTime,
-                             WebRtc_UWord8 packetLoss)
-{
-    // Default: no adjustment
-    WebRtc_UWord8 codeRateDeltaAdjust = codeRateDelta;
-    float adjustFec =  1.0f;
-
-    // TODO (marpan):
-    // Set FEC adjustment factor
-
-    codeRateDeltaAdjust = static_cast<WebRtc_UWord8>(codeRateDelta * adjustFec);
-
-     // 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;
-
-    return codeRateDeltaAdjust;
-}
-
-void
-VCMQmSelect::UpdateContent(const VideoContentMetrics*  contentMetrics)
-{
-     _contentMetrics = contentMetrics;
-}
-
 // 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
-VCMQmSelect::SelectQuality(VCMQualityMode** qm)
+VCMQmResolution::SelectResolution(VCMResolutionScale** qm)
 {
     if (!_init)
     {
@@ -272,15 +401,11 @@
     _qm->spatialHeightFact = 1;
     _qm->temporalFact = 1;
 
-
     // Update native values
     _nativeWidth = _contentMetrics->nativeWidth;
     _nativeHeight = _contentMetrics->nativeHeight;
     _nativeFrameRate = _contentMetrics->nativeFrameRate;
 
-    // Aspect ratio: used for selection of 1x2,2x1,2x2
-    _aspectRatio = (float)_width / (float)_height;
-
     float avgTargetRate = 0.0f;
     float avgIncomingFrameRate = 0.0f;
     float ratioBufferLow = 0.0f;
@@ -317,13 +442,12 @@
     // 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).
-    SetMaxRateForQM(2 * _width, 2 * _height);
-    WebRtc_UWord8  imageType2  = _imageType;
-    WebRtc_UWord32 maxRateQM2 = _maxRateQM;
+    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.
-    SetMaxRateForQM(_width, _height);
+    WebRtc_UWord32 maxRateQM = kMaxRateQm[_imageType];
 
     // Compute class state of the content.
     MotionNFD();
@@ -342,18 +466,21 @@
 
     // Get image class and content class: for going up spatially
     WebRtc_UWord8 imageClass2 = 1;
-    if (imageType2 <= 3) imageClass2 = 0;
+    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);
+        (_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);
+         scaleTransRate * maxRateQM / 30);
 
    // Transitional rate for going up spatially
     WebRtc_UWord32 estimatedTransRateUpS = static_cast<WebRtc_UWord32>
@@ -530,7 +657,7 @@
 }
 
 WebRtc_Word32
-VCMQmSelect::SelectSpatialDirectionMode(float transRate)
+VCMQmResolution::SelectSpatialDirectionMode(float transRate)
 {
     // Default is 1x2 (H)
 
@@ -579,156 +706,82 @@
     return VCM_OK;
 }
 
-void
-VCMQmSelect::Coherence()
+// DONE WITH RESOLUTION CLASS
+
+
+// ROBUSTNESS CLASS
+
+VCMQmRobustness::VCMQmRobustness()
 {
-    float horizNZ  = _contentMetrics->motionHorizontalness;
-    float distortionNZ  = _contentMetrics->motionClusterDistortion;
+    Reset();
+}
 
-    // 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;
-    }
+VCMQmRobustness::~VCMQmRobustness()
+{
 
 }
 
 void
-VCMQmSelect::MotionNFD()
+VCMQmRobustness::Reset()
 {
-    _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;
-    }
-
+    _prevTotalRate = 0.0f;
+    _prevRttTime = 0;
+    _prevPacketLoss = 0;
+    ResetQM();
+    return;
 }
 
-void
-VCMQmSelect::Motion()
+// 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.
+WebRtc_UWord8
+VCMQmRobustness::AdjustFecFactor(WebRtc_UWord8 codeRateDelta, float totalRate,
+                                 float frameRate,WebRtc_UWord32 rttTime,
+                                 WebRtc_UWord8 packetLoss)
 {
+    if (_contentMetrics == NULL)
+    {
+        return VCM_OK;
+    }
 
-    float sizeZeroMotion = _contentMetrics->sizeZeroMotion;
-    float motionMagNZ = _contentMetrics->motionMagnitudeNZ;
+    // Default: no adjustment
+    WebRtc_UWord8 codeRateDeltaAdjust = codeRateDelta;
+    float adjustFec =  1.0f;
 
-    // Take product of size and magnitude with equal weight
-    _motion.value = (1.0f - sizeZeroMotion) * motionMagNZ;
+    // Compute class state of the content.
+    MotionNFD();
+    Spatial();
 
-    // 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;
-    }
+    // TODO (marpan):
+    // Set FEC adjustment factor
+
+    codeRateDeltaAdjust = static_cast<WebRtc_UWord8>(codeRateDelta * adjustFec);
+
+    // 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 codeRateDeltaAdjust;
 }
 
-
-void
-VCMQmSelect::Spatial()
+// Set the UEP (unequal-protection) on/off for the FEC
+bool
+VCMQmRobustness::SetUepProtection(WebRtc_UWord8 codeRateDelta, float totalRate,
+                                  WebRtc_UWord8 packetLoss, bool frameType)
 {
-    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;
-
-    float scale = 1.0f;
-    // Reduce thresholds for HD scenes
-    if (_imageType > 3)
+    if (_contentMetrics == NULL)
     {
-        scale = (float)SCALE_TEXTURE_HD;
+        return VCM_OK;
     }
 
-    if (_spatial.value > scale * HIGH_TEXTURE)
-    {
-        _spatial.level = kHigh;
-    }
-    else if (_spatial.value < scale * LOW_TEXTURE)
-    {
-        _spatial.level = kLow;
-    }
-    else
-    {
-         _spatial.level = kDefault;
-    }
-}
+    // Default: UEP on
+    bool uepProtection  = true;
 
-
-WebRtc_Word32
-VCMQmSelect::SetMaxRateForQM(WebRtc_UWord32 width, WebRtc_UWord32 height)
-{
-    // Match image type
-    WebRtc_UWord32 imageSize = width * height;
-
-    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;
-    }
-
-    // Set max rate based on image size
-    _maxRateQM = kMaxRateQm[_imageType];
-
-    return VCM_OK;
+    return uepProtection;
 }
 
 } // 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 3bca4bc..589a16d 100644
--- a/src/modules/video_coding/main/source/qm_select.h
+++ b/src/modules/video_coding/main/source/qm_select.h
@@ -13,25 +13,19 @@
 
 #include "typedefs.h"
 #include "common_types.h"
-/************************/
-/* Quality Modes       */
-/**********************/
+/******************************************************/
+/* Quality Modes: Resolution and Robustness settings  */
+/******************************************************/
 
 namespace webrtc
 {
 
 struct VideoContentMetrics;
 
-struct VCMQualityMode
+struct VCMResolutionScale
 {
-    VCMQualityMode():spatialWidthFact(1), spatialHeightFact(1),
+    VCMResolutionScale(): spatialWidthFact(1), spatialHeightFact(1),
         temporalFact(1){}
-    void Reset()
-    {
-        spatialWidthFact = 1;
-        spatialHeightFact = 1;
-        temporalFact = 1;
-    }
 
     WebRtc_UWord16  spatialWidthFact;
     WebRtc_UWord16  spatialHeightFact;
@@ -59,53 +53,20 @@
     VCMMagValues level;
 };
 
-class VCMQmSelect
+// QmMethod class: main class for resolution and robustness settings
+
+class VCMQmMethod
 {
 public:
-    VCMQmSelect();
-    ~VCMQmSelect();
+    VCMQmMethod();
+    ~VCMQmMethod();
 
-    // Initialize:
-    WebRtc_Word32 Initialize(float bitRate, float userFrameRate,
-                             WebRtc_UWord32 width, WebRtc_UWord32 height);
-
-    // Allow the user to set preferences: favor frame rate/resolution
-    WebRtc_Word32 SetPreferences(WebRtc_Word8 resolPref);
-
-    // Extract ST (spatio-temporal) QM behavior and make decision
-    // Inputs: qm: Reference to the quality modes pointer
-    WebRtc_Word32 SelectQuality(VCMQualityMode** qm);
-
-    // Update QM with actual bit rate
-    // (size of the latest encoded frame) and frame type.
-    void UpdateEncodedSize(WebRtc_Word64 encodedSize,
-                           FrameType encodedFrameType);
-
-    // Update QM with new bit/frame/loss rates from SetTargetRates
-    void UpdateRates(float targetBitRate, float avgSentRate,
-                     float incomingFrameRate, WebRtc_UWord8 packetLoss);
-
-    // Update QM with the content metrics
-    void UpdateContent(const VideoContentMetrics*  contentMetrics);
-
-    // Adjust FEC rate based on content
-    WebRtc_UWord8  AdjustFecFactor(WebRtc_UWord8 codeRateDelta, float totalRate,
-                                   float frameRate, WebRtc_UWord16 rttTime,
-                                   WebRtc_UWord8 packetLoss);
-
-
-    // Select 1x2,2x2,2x2 spatial sampling mode
-    WebRtc_Word32 SelectSpatialDirectionMode(float transRate);
-
-    // Reset values prior to QMSelect
+    // Reset values
     void ResetQM();
+    virtual void Reset() = 0;
 
-    // Reset rate quantities and counter values after every QMSelect call
-    void ResetRates();
-
-    // Reset all
-    void Reset();
-private:
+    // Update with the content metrics
+    void UpdateContent(const VideoContentMetrics* contentMetrics);
 
     // Compute spatial texture magnitude and level
     void Spatial();
@@ -119,36 +80,84 @@
     // Compute coherence magnitude and level
     void Coherence();
 
-    // Set the max rate for QM selection
-    WebRtc_Word32 SetMaxRateForQM(WebRtc_UWord32 width, WebRtc_UWord32 height);
+    // Get the imageType (CIF, VGA, HD, etc) for the system width/height
+    WebRtc_Word8 GetImageType(WebRtc_UWord32 width, WebRtc_UWord32 height);
 
     // Content Data
-    const VideoContentMetrics*    _contentMetrics;
+     const VideoContentMetrics*    _contentMetrics;
 
-    // Encoder rate control parameters, network parameters
+    // 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;
+
+};
+
+// Resolution settings class
+
+class VCMQmResolution : public VCMQmMethod
+{
+public:
+    VCMQmResolution();
+    ~VCMQmResolution();
+
+    // Reset all quantities
+    virtual void Reset();
+
+    // Reset rate quantities and counter values after every Select Quality 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);
+
+    // 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);
+
+    // Update QM with new bit/frame/loss rates every ~1 sec from SetTargetRates
+    void UpdateRates(float targetBitRate, float avgSentRate,
+                     float incomingFrameRate, WebRtc_UWord8 packetLoss);
+
+    // 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);
+
+    // Select 1x2,2x2,2x2 spatial sampling mode
+    WebRtc_Word32 SelectSpatialDirectionMode(float transRate);
+
+private:
+    // Encoder rate control parameter
     float                        _targetBitRate;
     float                        _userFrameRate;
     float                        _incomingFrameRate;
     float                        _perFrameBandwidth;
     float                        _bufferLevel;
+
+    // Data accumulated every ~1sec from MediaOpt
     float                        _sumTargetRate;
     float                        _sumIncomingFrameRate;
     float                        _sumSeqRateMM;
     float                        _sumFrameRateMM;
     float                        _sumPacketLoss;
-    float                        _prevTotalRate;
-    WebRtc_UWord16               _prevRttTime;
-    WebRtc_UWord8                _prevPacketLoss;
     WebRtc_Word64                _sumEncodedBytes;
 
-    // Encoder and native frame sizes
-    WebRtc_UWord32               _width;
-    WebRtc_UWord32               _height;
-    WebRtc_UWord32               _nativeWidth;
-    WebRtc_UWord32               _nativeHeight;
+    // Resolution state parameters
     WebRtc_UWord8                _stateDecFactorSpatial;
-
-    WebRtc_UWord32               _nativeFrameRate;
     WebRtc_UWord8                _stateDecFactorTemp;
 
     // Counters
@@ -157,24 +166,36 @@
     WebRtc_UWord32               _updateRateCnt;
     WebRtc_UWord32               _lowBufferCnt;
 
-    // Content L/M/H values
-    VCMContFeature               _motion;
-    VCMContFeature               _spatial;
-    VCMContFeature               _coherence;
-    bool                         _stationaryMotion;
+    VCMResolutionScale*          _qm;
+};
 
-    // Aspect ratio
-    float                        _aspectRatio;
+// Robustness settings class
 
-    // Max rate to saturate the transitionalRate
-    WebRtc_UWord32               _maxRateQM;
-    WebRtc_UWord8                _imageType;
+class VCMQmRobustness : public VCMQmMethod
+{
+public:
+    VCMQmRobustness();
+   ~VCMQmRobustness();
 
-    // User preference for resolution or qmax change
-    WebRtc_UWord8                _userResolutionPref;
-    bool                         _init;
-    VCMQualityMode*              _qm;
+   virtual void Reset();
 
+   // Adjust FEC rate based on content: every ~1 sec from SetTargetRates
+   WebRtc_UWord8  AdjustFecFactor(WebRtc_UWord8 codeRateDelta, float totalRate,
+                                  float frameRate, WebRtc_UWord32 rttTime,
+                                  WebRtc_UWord8 packetLoss);
+
+   // Set the UEP protection on/off
+   bool  SetUepProtection(WebRtc_UWord8 codeRateDelta, float totalRate,
+                          WebRtc_UWord8 packetLoss, bool frameType);
+
+private:
+    // Previous state of network parameters
+    float                        _prevTotalRate;
+    WebRtc_UWord32               _prevRttTime;
+    WebRtc_UWord8                _prevPacketLoss;
+
+    // Previous FEC rate
+    WebRtc_UWord8                _prevCodeRateDelta;
 };
 
 } // namespace webrtc
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 813c110..64870ea 100644
--- a/src/modules/video_coding/main/source/qm_select_data.h
+++ b/src/modules/video_coding/main/source/qm_select_data.h
@@ -28,6 +28,9 @@
 // Initial level of buffer in secs: should corresponds to wrapper settings
 #define INIT_BUFFER_LEVEL 0.5
 
+// Optimal level of buffer in secs: should corresponds to wrapper settings
+#define OPT_BUFFER_LEVEL 0.6
+
 // Threshold of (max) buffer size below which we consider too low (underflow)
 #define PERC_BUFFER_THR  0.10