This CL is to adding feedback of packet loss rate to encoder in voice engine. A direct reason for doing it is to make use of Opus FEC, which can adapt itself to changes in the packet loss rate.

This CL is going to be combined with another CL in ACM, which is to be landed.

TEST=passed_try_bots
BUG=
R=stefan@webrtc.org, xians@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6262 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc
index 37ec4d7..cd27569 100644
--- a/webrtc/voice_engine/channel.cc
+++ b/webrtc/voice_engine/channel.cc
@@ -89,6 +89,24 @@
   ChannelStatistics stats_;
 };
 
+class VoEBitrateObserver : public BitrateObserver {
+ public:
+  explicit VoEBitrateObserver(Channel* owner)
+      : owner_(owner) {}
+  virtual ~VoEBitrateObserver() {}
+
+  // Implements BitrateObserver.
+  virtual void OnNetworkChanged(const uint32_t bitrate_bps,
+                                const uint8_t fraction_lost,
+                                const uint32_t rtt) OVERRIDE {
+    // |fraction_lost| has a scale of 0 - 255.
+    owner_->OnNetworkChanged(bitrate_bps, fraction_lost, rtt);
+  }
+
+ private:
+  Channel* owner_;
+};
+
 int32_t
 Channel::SendData(FrameType frameType,
                   uint8_t   payloadType,
@@ -900,7 +918,13 @@
     _RxVadDetection(false),
     _rxAgcIsEnabled(false),
     _rxNsIsEnabled(false),
-    restored_packet_in_use_(false)
+    restored_packet_in_use_(false),
+    bitrate_controller_(
+        BitrateController::CreateBitrateController(Clock::GetRealTimeClock(),
+                                                   true)),
+    rtcp_bandwidth_observer_(
+        bitrate_controller_->CreateRtcpBandwidthObserver()),
+    send_bitrate_observer_(new VoEBitrateObserver(this))
 {
     WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId),
                  "Channel::Channel() - ctor");
@@ -915,6 +939,7 @@
     configuration.rtcp_feedback = this;
     configuration.audio_messages = this;
     configuration.receive_statistics = rtp_receive_statistics_.get();
+    configuration.bandwidth_callback = rtcp_bandwidth_observer_.get();
 
     _rtpRtcpModule.reset(RtpRtcp::CreateRtpRtcp(configuration));
 
@@ -1489,9 +1514,27 @@
         return -1;
     }
 
+    bitrate_controller_->SetBitrateObserver(send_bitrate_observer_.get(),
+                                            codec.rate, 0, 0);
+
     return 0;
 }
 
+void
+Channel::OnNetworkChanged(const uint32_t bitrate_bps,
+                          const uint8_t fraction_lost,  // 0 - 255.
+                          const uint32_t rtt) {
+  WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId,_channelId),
+      "Channel::OnNetworkChanged(bitrate_bps=%d, fration_lost=%d, rtt=%d)",
+      bitrate_bps, fraction_lost, rtt);
+  // Normalizes rate to 0 - 100.
+  if (audio_coding_->SetPacketLossRate(100 * fraction_lost / 255) != 0) {
+    _engineStatisticsPtr->SetLastError(VE_AUDIO_CODING_MODULE_ERROR,
+        kTraceError, "OnNetworkChanged() failed to set packet loss rate");
+    assert(false);  // This should not happen.
+  }
+}
+
 int32_t
 Channel::SetVADStatus(bool enableVAD, ACMVADMode mode, bool disableDTX)
 {
@@ -3508,15 +3551,15 @@
     return 0;
 }
 
-int Channel::SetFECStatus(bool enable, int redPayloadtype) {
+int Channel::SetREDStatus(bool enable, int redPayloadtype) {
   WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
-               "Channel::SetFECStatus()");
+               "Channel::SetREDStatus()");
 
   if (enable) {
     if (redPayloadtype < 0 || redPayloadtype > 127) {
       _engineStatisticsPtr->SetLastError(
           VE_PLTYPE_ERROR, kTraceError,
-          "SetFECStatus() invalid RED payload type");
+          "SetREDStatus() invalid RED payload type");
       return -1;
     }
 
@@ -3538,7 +3581,7 @@
 }
 
 int
-Channel::GetFECStatus(bool& enabled, int& redPayloadtype)
+Channel::GetREDStatus(bool& enabled, int& redPayloadtype)
 {
     enabled = audio_coding_->REDStatus();
     if (enabled)
@@ -3548,22 +3591,43 @@
         {
             _engineStatisticsPtr->SetLastError(
                 VE_RTP_RTCP_MODULE_ERROR, kTraceError,
-                "GetFECStatus() failed to retrieve RED PT from RTP/RTCP "
+                "GetREDStatus() failed to retrieve RED PT from RTP/RTCP "
                 "module");
             return -1;
         }
         WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
                    VoEId(_instanceId, _channelId),
-                   "GetFECStatus() => enabled=%d, redPayloadtype=%d",
+                   "GetREDStatus() => enabled=%d, redPayloadtype=%d",
                    enabled, redPayloadtype);
         return 0;
     }
     WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
                  VoEId(_instanceId, _channelId),
-                 "GetFECStatus() => enabled=%d", enabled);
+                 "GetREDStatus() => enabled=%d", enabled);
     return 0;
 }
 
+int Channel::SetCodecFECStatus(bool enable) {
+  WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
+               "Channel::SetCodecFECStatus()");
+
+  if (audio_coding_->SetCodecFEC(enable) != 0) {
+    _engineStatisticsPtr->SetLastError(
+        VE_AUDIO_CODING_MODULE_ERROR, kTraceError,
+        "SetCodecFECStatus() failed to set FEC state");
+    return -1;
+  }
+  return 0;
+}
+
+bool Channel::GetCodecFECStatus() {
+  bool enabled = audio_coding_->CodecFEC();
+  WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
+               VoEId(_instanceId, _channelId),
+               "GetCodecFECStatus() => enabled=%d", enabled);
+  return enabled;
+}
+
 void Channel::SetNACKStatus(bool enable, int maxNumberOfPackets) {
   // None of these functions can fail.
   _rtpRtcpModule->SetStorePacketsStatus(enable, maxNumberOfPackets);
@@ -4501,5 +4565,6 @@
   }
   return error;
 }
+
 }  // namespace voe
 }  // namespace webrtc
diff --git a/webrtc/voice_engine/channel.h b/webrtc/voice_engine/channel.h
index 2eba91e..7c8a3e2 100644
--- a/webrtc/voice_engine/channel.h
+++ b/webrtc/voice_engine/channel.h
@@ -16,6 +16,7 @@
 #include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
 #include "webrtc/modules/audio_conference_mixer/interface/audio_conference_mixer_defines.h"
 #include "webrtc/modules/audio_processing/rms_level.h"
+#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
 #include "webrtc/modules/utility/interface/file_player.h"
@@ -335,8 +336,10 @@
                          unsigned int& discardedPackets);
     int GetRemoteRTCPReportBlocks(std::vector<ReportBlock>* report_blocks);
     int GetRTPStatistics(CallStatistics& stats);
-    int SetFECStatus(bool enable, int redPayloadtype);
-    int GetFECStatus(bool& enabled, int& redPayloadtype);
+    int SetREDStatus(bool enable, int redPayloadtype);
+    int GetREDStatus(bool& enabled, int& redPayloadtype);
+    int SetCodecFECStatus(bool enable);
+    bool GetCodecFECStatus();
     void SetNACKStatus(bool enable, int maxNumberOfPackets);
     int StartRTPDump(const char fileNameUTF8[1024], RTPDirections direction);
     int StopRTPDump(RTPDirections direction);
@@ -471,6 +474,11 @@
     uint32_t PrepareEncodeAndSend(int mixingFrequency);
     uint32_t EncodeAndSend();
 
+    // From BitrateObserver (called by the RTP/RTCP module).
+    void OnNetworkChanged(const uint32_t bitrate_bps,
+                          const uint8_t fraction_lost,  // 0 - 255.
+                          const uint32_t rtt);
+
 private:
     bool ReceivePacket(const uint8_t* packet, int packet_length,
                        const RTPHeader& header, bool in_order);
@@ -603,6 +611,10 @@
     bool _rxAgcIsEnabled;
     bool _rxNsIsEnabled;
     bool restored_packet_in_use_;
+    // RtcpBandwidthObserver
+    scoped_ptr<BitrateController> bitrate_controller_;
+    scoped_ptr<RtcpBandwidthObserver> rtcp_bandwidth_observer_;
+    scoped_ptr<BitrateObserver> send_bitrate_observer_;
 };
 
 }  // namespace voe
diff --git a/webrtc/voice_engine/include/voe_codec.h b/webrtc/voice_engine/include/voe_codec.h
index f653f46..34a2232 100644
--- a/webrtc/voice_engine/include/voe_codec.h
+++ b/webrtc/voice_engine/include/voe_codec.h
@@ -102,6 +102,18 @@
     virtual int SetSendCNPayloadType(
         int channel, int type, PayloadFrequencies frequency = kFreq16000Hz) = 0;
 
+    // Sets the codec internal FEC (forward error correction) status for a
+    // specified |channel|. Returns 0 if success, and -1 if failed.
+    // TODO(minyue): Make SetFECStatus() pure virtual when fakewebrtcvoiceengine
+    // in talk is ready.
+    virtual int SetFECStatus(int channel, bool enable) { return -1; }
+
+    // Gets the codec internal FEC status for a specified |channel|. Returns 0
+    // with the status stored in |enabled| if success, and -1 if encountered
+    // error.
+    // TODO(minyue): Make GetFECStatus() pure virtual when fakewebrtcvoiceengine
+    // in talk is ready.
+    virtual int GetFECStatus(int channel, bool& enabled) { return -1; }
 
     // Sets the VAD/DTX (silence suppression) status and |mode| for a
     // specified |channel|. Disabling VAD (through |enable|) will also disable
diff --git a/webrtc/voice_engine/include/voe_errors.h b/webrtc/voice_engine/include/voe_errors.h
index 87546e2..572fc92 100644
--- a/webrtc/voice_engine/include/voe_errors.h
+++ b/webrtc/voice_engine/include/voe_errors.h
@@ -67,7 +67,7 @@
 #define VE_SEND_ERROR 8092
 #define VE_CANNOT_REMOVE_CONF_CHANNEL 8093
 #define VE_PLTYPE_ERROR 8094
-#define VE_SET_FEC_FAILED 8095
+#define VE_SET_RED_FAILED 8095
 #define VE_CANNOT_GET_PLAY_DATA 8096
 #define VE_APM_ERROR 8097
 #define VE_RUNTIME_PLAY_WARNING 8098
diff --git a/webrtc/voice_engine/include/voe_rtp_rtcp.h b/webrtc/voice_engine/include/voe_rtp_rtcp.h
index 2fb09cc..ce6f849 100644
--- a/webrtc/voice_engine/include/voe_rtp_rtcp.h
+++ b/webrtc/voice_engine/include/voe_rtp_rtcp.h
@@ -15,7 +15,7 @@
 //  - Transmission of RTCP sender reports.
 //  - Obtaining RTCP data from incoming RTCP sender reports.
 //  - RTP and RTCP statistics (jitter, packet loss, RTT etc.).
-//  - Forward Error Correction (FEC).
+//  - Redundant Coding (RED)
 //  - Writing RTP and RTCP packets to binary files for off-line analysis of
 //    the call quality.
 //
@@ -200,13 +200,33 @@
     virtual int GetRemoteRTCPReportBlocks(
         int channel, std::vector<ReportBlock>* receive_blocks) = 0;
 
+    // Sets the Redundant Coding (RED) status on a specific |channel|.
+    // TODO(minyue): Make SetREDStatus() pure virtual when fakewebrtcvoiceengine
+    // in talk is ready.
+    virtual int SetREDStatus(
+        int channel, bool enable, int redPayloadtype = -1) { return -1; }
+
+    // Gets the RED status on a specific |channel|.
+    // TODO(minyue): Make GetREDStatus() pure virtual when fakewebrtcvoiceengine
+    // in talk is ready.
+    virtual int GetREDStatus(
+        int channel, bool& enabled, int& redPayloadtype) { return -1; }
+
     // Sets the Forward Error Correction (FEC) status on a specific |channel|.
+    // TODO(minyue): Remove SetFECStatus() when SetFECStatus() is replaced by
+    // SetREDStatus() in fakewebrtcvoiceengine.
     virtual int SetFECStatus(
-        int channel, bool enable, int redPayloadtype = -1) = 0;
+        int channel, bool enable, int redPayloadtype = -1) {
+      return SetREDStatus(channel, enable, redPayloadtype);
+    };
 
     // Gets the FEC status on a specific |channel|.
+    // TODO(minyue): Remove GetFECStatus() when GetFECStatus() is replaced by
+    // GetREDStatus() in fakewebrtcvoiceengine.
     virtual int GetFECStatus(
-        int channel, bool& enabled, int& redPayloadtype) = 0;
+        int channel, bool& enabled, int& redPayloadtype) {
+      return SetREDStatus(channel, enabled, redPayloadtype);
+    }
 
     // This function enables Negative Acknowledgment (NACK) using RTCP,
     // implemented based on RFC 4585. NACK retransmits RTP packets if lost on
diff --git a/webrtc/voice_engine/test/android/android_test/jni/android_test.cc b/webrtc/voice_engine/test/android/android_test/jni/android_test.cc
index 9addbe5..6b97d49 100644
--- a/webrtc/voice_engine/test/android/android_test/jni/android_test.cc
+++ b/webrtc/voice_engine/test/android/android_test/jni/android_test.cc
@@ -710,10 +710,10 @@
      return -1;
      } */
 
-    /* if (veData1.rtp_rtcp->SetFECStatus(channel, 1) != 0)
+    /* if (veData1.rtp_rtcp->SetREDStatus(channel, 1) != 0)
      {
      __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG,
-         "Failed to enable FEC");
+         "Failed to enable RED");
      return -1;
      } */
 
@@ -755,10 +755,10 @@
         jobject,
         jint channel)
 {
-    /* if (veData1.rtp_rtcp->SetFECStatus(channel, 0) != 0)
+    /* if (veData1.rtp_rtcp->SetREDStatus(channel, 0) != 0)
      {
      __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG,
-         "Failed to disable FEC");
+         "Failed to disable RED");
      return -1;
      } */
 
@@ -1142,21 +1142,21 @@
 
     /*VALIDATE_RTP_RTCP_POINTER;
 
-     if (veData1.rtp_rtcp->SetFECStatus(0, enable, -1) != 0)
+     if (veData1.rtp_rtcp->SetREDStatus(0, enable, -1) != 0)
      {
      __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG,
-         "Could not set FEC");
+         "Could not set RED");
      return -1;
      }
      else if(enable)
      {
      __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG,
-         "Could enable FEC");
+         "Could enable RED");
      }
      else
      {
      __android_log_write(ANDROID_LOG_ERROR, WEBRTC_LOG_TAG,
-         "Could disable FEC");
+         "Could disable RED");
      }*/
 
     return 0;
diff --git a/webrtc/voice_engine/test/win_test/Resource.h b/webrtc/voice_engine/test/win_test/Resource.h
index 5ae9c5f..cd5f55a 100644
--- a/webrtc/voice_engine/test/win_test/Resource.h
+++ b/webrtc/voice_engine/test/win_test/Resource.h
@@ -173,7 +173,7 @@
 #define IDC_CHECK_TYPING_DETECTION      1101
 #define IDC_CHECK_START_STOP_MODE2      1102
 #define IDC_CHECK_DIRECT_FEEDBACK       1102
-#define IDC_CHECK_FEC                   1102
+#define IDC_CHECK_RED                   1102
 #define IDC_BUTTON_SET_RX_TELEPHONE_PT_TYPE 1103
 #define IDC_BUTTON_SET_RX_TELEPHONE_PT  1103
 #define IDC_BUTTON_CLEAR_ERROR_CALLBACK 1103
diff --git a/webrtc/voice_engine/test/win_test/WinTest.rc b/webrtc/voice_engine/test/win_test/WinTest.rc
index dfe503f..240036c 100644
--- a/webrtc/voice_engine/test/win_test/WinTest.rc
+++ b/webrtc/voice_engine/test/win_test/WinTest.rc
@@ -207,7 +207,7 @@
     CONTROL         "TypingDetect",IDC_CHECK_TYPING_DETECTION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,572,156,60,14,WS_EX_DLGMODALFRAME

     EDITTEXT        IDC_EDIT_AUDIO_LAYER,28,224,116,14,ES_AUTOHSCROLL | ES_READONLY

     EDITTEXT        IDC_EDIT_CPU_LOAD,152,224,116,14,ES_AUTOHSCROLL | ES_READONLY

-    CONTROL         "FEC",IDC_CHECK_FEC,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,176,55,28,14,WS_EX_DLGMODALFRAME

+    CONTROL         "RED",IDC_CHECK_RED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,176,55,28,14,WS_EX_DLGMODALFRAME

     LTEXT           "=> Callbacks",IDC_STATIC_ERROR_CALLBACK,283,226,43,8

     EDITTEXT        IDC_EDIT_ERROR_CALLBACK,328,224,312,14,ES_AUTOHSCROLL

     PUSHBUTTON      "Clear",IDC_BUTTON_CLEAR_ERROR_CALLBACK,644,224,24,14

diff --git a/webrtc/voice_engine/test/win_test/WinTestDlg.cc b/webrtc/voice_engine/test/win_test/WinTestDlg.cc
index a37cc44..8b45e27 100644
--- a/webrtc/voice_engine/test/win_test/WinTestDlg.cc
+++ b/webrtc/voice_engine/test/win_test/WinTestDlg.cc
@@ -1238,7 +1238,7 @@
     ON_BN_CLICKED(IDC_CHECK_NS_1, &CWinTestDlg::OnBnClickedCheckNs1)
     ON_BN_CLICKED(IDC_CHECK_REC_CALL, &CWinTestDlg::OnBnClickedCheckRecCall)
     ON_BN_CLICKED(IDC_CHECK_TYPING_DETECTION, &CWinTestDlg::OnBnClickedCheckTypingDetection)
-    ON_BN_CLICKED(IDC_CHECK_FEC, &CWinTestDlg::OnBnClickedCheckFEC)
+    ON_BN_CLICKED(IDC_CHECK_RED, &CWinTestDlg::OnBnClickedCheckRED)
     ON_BN_CLICKED(IDC_BUTTON_CLEAR_ERROR_CALLBACK, &CWinTestDlg::OnBnClickedButtonClearErrorCallback)
 END_MESSAGE_MAP()
 
@@ -1440,7 +1440,7 @@
     GetDlgItem(IDC_CHECK_RXVAD)->EnableWindow(FALSE);
     GetDlgItem(IDC_CHECK_AGC_1)->EnableWindow(FALSE);
     GetDlgItem(IDC_CHECK_NS_1)->EnableWindow(FALSE);
-    GetDlgItem(IDC_CHECK_FEC)->EnableWindow(FALSE);
+    GetDlgItem(IDC_CHECK_RED)->EnableWindow(FALSE);
 
     CComboBox* comboIP(NULL);
     comboIP = (CComboBox*)GetDlgItem(IDC_COMBO_IP_1);
@@ -1709,7 +1709,7 @@
         GetDlgItem(IDC_CHECK_RXVAD)->EnableWindow(TRUE);
         GetDlgItem(IDC_CHECK_AGC_1)->EnableWindow(TRUE);
         GetDlgItem(IDC_CHECK_NS_1)->EnableWindow(TRUE);
-        GetDlgItem(IDC_CHECK_FEC)->EnableWindow(TRUE);
+        GetDlgItem(IDC_CHECK_RED)->EnableWindow(TRUE);
 
         // Always set send codec to default codec <=> index 0.
         CodecInst codec;
@@ -1799,7 +1799,7 @@
         GetDlgItem(IDC_CHECK_AGC_1)->EnableWindow(FALSE);
         GetDlgItem(IDC_CHECK_NS_1)->EnableWindow(FALSE);
         GetDlgItem(IDC_CHECK_RXVAD)->EnableWindow(FALSE);
-        GetDlgItem(IDC_CHECK_FEC)->EnableWindow(FALSE);
+        GetDlgItem(IDC_CHECK_RED)->EnableWindow(FALSE);
         SetDlgItemText(IDC_EDIT_RXVAD, _T(""));
         GetDlgItem(IDC_EDIT_RXVAD)->EnableWindow(FALSE);
         CButton* button = (CButton*)GetDlgItem(IDC_CHECK_EXT_TRANS_1);
@@ -1834,7 +1834,7 @@
         button->SetCheck(BST_UNCHECKED);
         button = (CButton*)GetDlgItem(IDC_CHECK_RXVAD);
         button->SetCheck(BST_UNCHECKED);
-        button = (CButton*)GetDlgItem(IDC_CHECK_FEC);
+        button = (CButton*)GetDlgItem(IDC_CHECK_RED);
         button->SetCheck(BST_UNCHECKED);
     }
 }
@@ -3250,13 +3250,14 @@
     TEST(_veApmPtr->SetTypingDetectionStatus(enable) == 0, _T("SetTypingDetectionStatus(enable=%d)"), enable);
 }
 
-void CWinTestDlg::OnBnClickedCheckFEC()
+void CWinTestDlg::OnBnClickedCheckRED()
 {
-    CButton* button = (CButton*)GetDlgItem(IDC_CHECK_FEC);
+    CButton* button = (CButton*)GetDlgItem(IDC_CHECK_RED);
     int channel = GetDlgItemInt(IDC_EDIT_1);
     int check = button->GetCheck();
     const bool enable = (check == BST_CHECKED);
-    TEST(_veRtpRtcpPtr->SetFECStatus(channel, enable) == 0, _T("SetFECStatus(enable=%d)"), enable);
+    TEST(_veRtpRtcpPtr->SetREDStatus(channel, enable) == 0,
+         _T("SetREDStatus(enable=%d)"), enable);
 }
 
 // ----------------------------------------------------------------------------
diff --git a/webrtc/voice_engine/test/win_test/WinTestDlg.h b/webrtc/voice_engine/test/win_test/WinTestDlg.h
index 41041c2..c53c3cf 100644
--- a/webrtc/voice_engine/test/win_test/WinTestDlg.h
+++ b/webrtc/voice_engine/test/win_test/WinTestDlg.h
@@ -268,7 +268,7 @@
     afx_msg void OnBnClickedCheckNs1();
     afx_msg void OnBnClickedCheckRecCall();
     afx_msg void OnBnClickedCheckTypingDetection();
-    afx_msg void OnBnClickedCheckFEC();
+    afx_msg void OnBnClickedCheckRED();
     afx_msg void OnBnClickedButtonClearErrorCallback();
     afx_msg void OnBnClickedCheckBwe1();
 };
diff --git a/webrtc/voice_engine/voe_codec_impl.cc b/webrtc/voice_engine/voe_codec_impl.cc
index a7735bb..4aa0556 100644
--- a/webrtc/voice_engine/voe_codec_impl.cc
+++ b/webrtc/voice_engine/voe_codec_impl.cc
@@ -295,6 +295,41 @@
     return channelPtr->SetSendCNPayloadType(type, frequency);
 }
 
+int VoECodecImpl::SetFECStatus(int channel, bool enable) {
+  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
+               "SetCodecFECStatus(channel=%d, enable=%d)", channel, enable);
+  if (!_shared->statistics().Initialized()) {
+    _shared->SetLastError(VE_NOT_INITED, kTraceError);
+    return -1;
+  }
+  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
+  voe::Channel* channelPtr = ch.channel();
+  if (channelPtr == NULL) {
+    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
+                          "SetCodecFECStatus() failed to locate channel");
+    return -1;
+  }
+  return channelPtr->SetCodecFECStatus(enable);
+}
+
+int VoECodecImpl::GetFECStatus(int channel, bool& enabled) {
+  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
+               "GetCodecFECStatus(channel=%d)", channel);
+  if (!_shared->statistics().Initialized()) {
+    _shared->SetLastError(VE_NOT_INITED, kTraceError);
+    return -1;
+  }
+  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
+  voe::Channel* channelPtr = ch.channel();
+  if (channelPtr == NULL) {
+    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
+                          "GetFECStatus() failed to locate channel");
+    return -1;
+  }
+  enabled = channelPtr->GetCodecFECStatus();
+  return 0;
+}
+
 int VoECodecImpl::SetVADStatus(int channel, bool enable, VadModes mode,
                                bool disableDTX)
 {
diff --git a/webrtc/voice_engine/voe_codec_impl.h b/webrtc/voice_engine/voe_codec_impl.h
index 6757695..1b9f00e 100644
--- a/webrtc/voice_engine/voe_codec_impl.h
+++ b/webrtc/voice_engine/voe_codec_impl.h
@@ -40,6 +40,10 @@
 
     virtual int GetRecPayloadType(int channel, CodecInst& codec);
 
+    virtual int SetFECStatus(int channel, bool enable);
+
+    virtual int GetFECStatus(int channel, bool& enabled);
+
     virtual int SetVADStatus(int channel,
                              bool enable,
                              VadModes mode = kVadConventional,
diff --git a/webrtc/voice_engine/voe_rtp_rtcp_impl.cc b/webrtc/voice_engine/voe_rtp_rtcp_impl.cc
index e69aa2d..8f0e717 100644
--- a/webrtc/voice_engine/voe_rtp_rtcp_impl.cc
+++ b/webrtc/voice_engine/voe_rtp_rtcp_impl.cc
@@ -427,10 +427,10 @@
   return channel_ptr->GetRemoteRTCPReportBlocks(report_blocks);
 }
 
-int VoERTP_RTCPImpl::SetFECStatus(int channel, bool enable, int redPayloadtype)
+int VoERTP_RTCPImpl::SetREDStatus(int channel, bool enable, int redPayloadtype)
 {
     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
-                 "SetFECStatus(channel=%d, enable=%d, redPayloadtype=%d)",
+                 "SetREDStatus(channel=%d, enable=%d, redPayloadtype=%d)",
                  channel, enable, redPayloadtype);
 #ifdef WEBRTC_CODEC_RED
     if (!_shared->statistics().Initialized())
@@ -443,23 +443,23 @@
     if (channelPtr == NULL)
     {
         _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
-            "SetFECStatus() failed to locate channel");
+            "SetREDStatus() failed to locate channel");
         return -1;
     }
-    return channelPtr->SetFECStatus(enable, redPayloadtype);
+    return channelPtr->SetREDStatus(enable, redPayloadtype);
 #else
     _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
-        "SetFECStatus() RED is not supported");
+        "SetREDStatus() RED is not supported");
     return -1;
 #endif
 }
 
-int VoERTP_RTCPImpl::GetFECStatus(int channel,
+int VoERTP_RTCPImpl::GetREDStatus(int channel,
                                   bool& enabled,
                                   int& redPayloadtype)
 {
     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
-                 "GetFECStatus(channel=%d, enabled=?, redPayloadtype=?)",
+                 "GetREDStatus(channel=%d, enabled=?, redPayloadtype=?)",
                  channel);
 #ifdef WEBRTC_CODEC_RED
     if (!_shared->statistics().Initialized())
@@ -472,18 +472,17 @@
     if (channelPtr == NULL)
     {
         _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
-            "GetFECStatus() failed to locate channel");
+            "GetREDStatus() failed to locate channel");
         return -1;
     }
-    return channelPtr->GetFECStatus(enabled, redPayloadtype);
+    return channelPtr->GetREDStatus(enabled, redPayloadtype);
 #else
     _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
-        "GetFECStatus() RED is not supported");
+        "GetREDStatus() RED is not supported");
     return -1;
 #endif
 }
 
-
 int VoERTP_RTCPImpl::SetNACKStatus(int channel,
                                    bool enable,
                                    int maxNoPackets)
diff --git a/webrtc/voice_engine/voe_rtp_rtcp_impl.h b/webrtc/voice_engine/voe_rtp_rtcp_impl.h
index ddb0170..c73fcdd 100644
--- a/webrtc/voice_engine/voe_rtp_rtcp_impl.h
+++ b/webrtc/voice_engine/voe_rtp_rtcp_impl.h
@@ -73,12 +73,12 @@
     virtual int GetRemoteRTCPReportBlocks(
         int channel, std::vector<ReportBlock>* report_blocks);
 
-    // FEC
-    virtual int SetFECStatus(int channel,
+    // RED
+    virtual int SetREDStatus(int channel,
                              bool enable,
                              int redPayloadtype = -1);
 
-    virtual int GetFECStatus(int channel, bool& enabled, int& redPayloadtype);
+    virtual int GetREDStatus(int channel, bool& enabled, int& redPayloadtype);
 
     //NACK
     virtual int SetNACKStatus(int channel,
diff --git a/webrtc/voice_engine/voice_engine.gyp b/webrtc/voice_engine/voice_engine.gyp
index 8e8b98f..ded468f 100644
--- a/webrtc/voice_engine/voice_engine.gyp
+++ b/webrtc/voice_engine/voice_engine.gyp
@@ -20,6 +20,7 @@
         '<(webrtc_root)/modules/modules.gyp:audio_conference_mixer',
         '<(webrtc_root)/modules/modules.gyp:audio_device',
         '<(webrtc_root)/modules/modules.gyp:audio_processing',
+        '<(webrtc_root)/modules/modules.gyp:bitrate_controller',
         '<(webrtc_root)/modules/modules.gyp:media_file',
         '<(webrtc_root)/modules/modules.gyp:rtp_rtcp',
         '<(webrtc_root)/modules/modules.gyp:webrtc_utility',