Support associated payload type when registering Rtx payload type.

Major changes include,
- Add associated payload type for SetRtxSendPayloadType & SetRtxReceivePayloadType.
- Receiver: Restore RTP packets by the new RTX-APT map.
- Sender: Send RTP packets by checking RTX-APT map.
- Add RTX payload type for RED in the default codec list.

BUG=4024
R=pbos@webrtc.org, stefan@webrtc.org
TBR=mflodman@webrtc.org

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

Patch from Changbin Shao <changbin.shao@intel.com>.

git-svn-id: http://webrtc.googlecode.com/svn/trunk@8028 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/libjingle_tests.gyp b/talk/libjingle_tests.gyp
index ac13a4c..19ce82b 100755
--- a/talk/libjingle_tests.gyp
+++ b/talk/libjingle_tests.gyp
@@ -88,10 +88,16 @@
       'target_name': 'libjingle_media_unittest',
       'type': 'executable',
       'dependencies': [
+        '<(DEPTH)/testing/gmock.gyp:gmock',
         '<(webrtc_root)/base/base_tests.gyp:rtc_base_tests_utils',
         'libjingle.gyp:libjingle_media',
         'libjingle_unittest_main',
       ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '<(DEPTH)/testing/gmock/include',
+        ],
+      },
       'sources': [
         # TODO(ronghuawu): Reenable this test.
         # 'media/base/capturemanager_unittest.cc',
diff --git a/talk/media/webrtc/fakewebrtcvideoengine.h b/talk/media/webrtc/fakewebrtcvideoengine.h
index aaa2f3b..dff6d01 100644
--- a/talk/media/webrtc/fakewebrtcvideoengine.h
+++ b/talk/media/webrtc/fakewebrtcvideoengine.h
@@ -266,8 +266,6 @@
           receive_(false),
           can_transmit_(true),
           remote_rtx_ssrc_(-1),
-          rtx_send_payload_type(-1),
-          rtx_recv_payload_type(-1),
           rtcp_status_(webrtc::kRtcpNone),
           key_frame_request_method_(webrtc::kViEKeyFrameRequestNone),
           tmmbr_(false),
@@ -305,8 +303,8 @@
     std::map<int, int> ssrcs_;
     std::map<int, int> rtx_ssrcs_;
     int remote_rtx_ssrc_;
-    int rtx_send_payload_type;
-    int rtx_recv_payload_type;
+    std::map<int, int> rtx_send_payload_types;
+    std::map<int, int> rtx_recv_payload_types;
     std::string cname_;
     webrtc::ViERTCPMode rtcp_status_;
     webrtc::ViEKeyFrameRequestMethod key_frame_request_method_;
@@ -614,14 +612,30 @@
     WEBRTC_ASSERT_CHANNEL(channel);
     channels_[GetOriginalChannelId(channel)]->receive_bandwidth_ =
         receive_bandwidth;
-  };
-  int GetRtxSendPayloadType(int channel) {
-    WEBRTC_CHECK_CHANNEL(channel);
-    return channels_[channel]->rtx_send_payload_type;
   }
-  int GetRtxRecvPayloadType(int channel) {
-    WEBRTC_CHECK_CHANNEL(channel);
-    return channels_[channel]->rtx_recv_payload_type;
+  std::set<int> GetRtxSendPayloadType(int channel) {
+    std::set<int> rtx_payload_types;
+    if (channels_.find(channel) == channels_.end())
+      return rtx_payload_types;
+
+    std::map<int, int>::const_iterator it;
+    for (it = channels_[channel]->rtx_send_payload_types.begin();
+         it != channels_[channel]->rtx_send_payload_types.end(); ++it) {
+      rtx_payload_types.insert(it->first);
+    }
+    return rtx_payload_types;
+  }
+  std::set<int> GetRtxRecvPayloadType(int channel) {
+    std::set<int> rtx_payload_types;
+    if (channels_.find(channel) == channels_.end())
+      return rtx_payload_types;
+
+    std::map<int, int>::const_iterator it;
+    for (it = channels_[channel]->rtx_recv_payload_types.begin();
+         it != channels_[channel]->rtx_recv_payload_types.end(); ++it) {
+      rtx_payload_types.insert(it->first);
+    }
+    return rtx_payload_types;
   }
   int GetRemoteRtxSsrc(int channel) {
     WEBRTC_CHECK_CHANNEL(channel);
@@ -1006,17 +1020,27 @@
   WEBRTC_STUB_CONST(GetRemoteSSRC, (const int, unsigned int&));
   WEBRTC_STUB_CONST(GetRemoteCSRCs, (const int, unsigned int*));
 
-  WEBRTC_FUNC(SetRtxSendPayloadType, (const int channel,
-                                      const uint8 payload_type)) {
+  WEBRTC_FUNC(SetRtxSendPayloadType,
+              (const int channel,
+               const uint8 payload_type,
+               const uint8 associated_payload_type)) {
     WEBRTC_CHECK_CHANNEL(channel);
-    channels_[channel]->rtx_send_payload_type = payload_type;
+    assert(payload_type >= 0);
+    assert(associated_payload_type >= 0);
+    channels_[channel]->rtx_send_payload_types[payload_type] =
+        associated_payload_type;
     return 0;
   }
 
-  WEBRTC_FUNC(SetRtxReceivePayloadType, (const int channel,
-                                         const uint8 payload_type)) {
+  WEBRTC_FUNC(SetRtxReceivePayloadType,
+              (const int channel,
+               const uint8 payload_type,
+               const uint8 associated_payload_type)) {
     WEBRTC_CHECK_CHANNEL(channel);
-    channels_[channel]->rtx_recv_payload_type = payload_type;
+    assert(payload_type >= 0);
+    assert(associated_payload_type >= 0);
+    channels_[channel]->rtx_recv_payload_types[payload_type] =
+        associated_payload_type;
     return 0;
   }
 
diff --git a/talk/media/webrtc/webrtcvideoengine.cc b/talk/media/webrtc/webrtcvideoengine.cc
index 689aef3..3cce243 100644
--- a/talk/media/webrtc/webrtcvideoengine.cc
+++ b/talk/media/webrtc/webrtcvideoengine.cc
@@ -180,6 +180,13 @@
 const char kVp8CodecName[] = "VP8";
 const char kVp9CodecName[] = "VP9";
 
+const int kDefaultVp8PlType = 100;
+const int kDefaultVp9PlType = 101;
+const int kDefaultRedPlType = 116;
+const int kDefaultUlpfecType = 117;
+const int kDefaultRtxVp8PlType = 96;
+const int kDefaultRtxRedPlType = 97;
+
 // TODO(ronghuawu): Change to 640x360.
 const int kDefaultVideoMaxWidth = 640;
 const int kDefaultVideoMaxHeight = 400;
@@ -305,14 +312,16 @@
 std::vector<VideoCodec> DefaultVideoCodecList() {
   std::vector<VideoCodec> codecs;
   if (CodecIsInternallySupported(kVp9CodecName)) {
-    codecs.push_back(
-        MakeVideoCodecWithDefaultFeedbackParams(101, kVp9CodecName));
+    codecs.push_back(MakeVideoCodecWithDefaultFeedbackParams(kDefaultVp9PlType,
+                                                             kVp9CodecName));
     // TODO(andresp): Add rtx codec for vp9 and verify it works.
   }
-  codecs.push_back(MakeVideoCodecWithDefaultFeedbackParams(100, kVp8CodecName));
-  codecs.push_back(MakeRtxCodec(96, 100));
-  codecs.push_back(MakeVideoCodec(116, kRedCodecName));
-  codecs.push_back(MakeVideoCodec(117, kUlpfecCodecName));
+  codecs.push_back(MakeVideoCodecWithDefaultFeedbackParams(kDefaultVp8PlType,
+                                                           kVp8CodecName));
+  codecs.push_back(MakeRtxCodec(kDefaultRtxVp8PlType, kDefaultVp8PlType));
+  codecs.push_back(MakeVideoCodec(kDefaultRedPlType, kRedCodecName));
+  codecs.push_back(MakeRtxCodec(kDefaultRtxRedPlType, kDefaultRedPlType));
+  codecs.push_back(MakeVideoCodec(kDefaultUlpfecType, kUlpfecCodecName));
   return codecs;
 }
 
@@ -1729,7 +1738,6 @@
       first_receive_ssrc_(kSsrcUnset),
       receiver_report_ssrc_(kSsrcUnset),
       num_unsignalled_recv_channels_(0),
-      send_rtx_type_(-1),
       send_red_type_(-1),
       send_fec_type_(-1),
       sending_(false),
@@ -1819,7 +1827,6 @@
   std::vector<webrtc::VideoCodec> send_codecs;
   VideoCodec checked_codec;
   VideoCodec dummy_current;  // Will be ignored by CanSendCodec.
-  std::map<int, int> primary_rtx_pt_mapping;
   bool nack_enabled = nack_enabled_;
   bool remb_enabled = remb_enabled_;
   for (std::vector<VideoCodec>::const_iterator iter = codecs.begin();
@@ -1832,7 +1839,7 @@
       int rtx_type = iter->id;
       int rtx_primary_type = -1;
       if (iter->GetParam(kCodecParamAssociatedPayloadType, &rtx_primary_type)) {
-        primary_rtx_pt_mapping[rtx_primary_type] = rtx_type;
+        send_rtx_apt_types_[rtx_type] = rtx_primary_type;
       }
     } else if (engine()->CanSendCodec(*iter, dummy_current, &checked_codec)) {
       webrtc::VideoCodec wcodec;
@@ -1897,14 +1904,6 @@
   // Select the first matched codec.
   webrtc::VideoCodec& codec(send_codecs[0]);
 
-  // Set RTX payload type if primary now active. This value will be used  in
-  // SetSendCodec.
-  std::map<int, int>::const_iterator rtx_it =
-    primary_rtx_pt_mapping.find(static_cast<int>(codec.plType));
-  if (rtx_it != primary_rtx_pt_mapping.end()) {
-    send_rtx_type_ = rtx_it->second;
-  }
-
   if (BitrateIsSet(codec.minBitrate) && BitrateIsSet(codec.maxBitrate) &&
       codec.minBitrate > codec.maxBitrate) {
     // TODO(pthatcher): This behavior contradicts other behavior in
@@ -3831,8 +3830,11 @@
                  << vie_codec.codecSpecific.VP8.keyFrameInterval;
   }
 
-  if (send_rtx_type_ != -1) {
-    LOG(LS_INFO) << "RTX payload type: " << send_rtx_type_;
+  std::map<int, int>::const_iterator it;
+  for (it = send_rtx_apt_types_.begin(); it != send_rtx_apt_types_.end();
+       ++it) {
+    LOG(LS_INFO) << "RTX payload type: " << it->first
+                 << ", associated payload type:" << it->second;
   }
 
   LogSimulcastSubstreams(vie_codec);
@@ -3850,7 +3852,6 @@
        it != receive_codecs_.end(); ++it) {
     pt_to_codec[it->plType] = &(*it);
   }
-  bool rtx_registered = false;
   for (std::vector<webrtc::VideoCodec>::iterator it = receive_codecs_.begin();
        it != receive_codecs_.end(); ++it) {
     if (it->codecType == webrtc::kVideoCodecRED) {
@@ -3861,11 +3862,6 @@
     // If this is an RTX codec we have to verify that it is associated with
     // a valid video codec which we have RTX support for.
     if (_stricmp(it->plName, kRtxCodecName) == 0) {
-      // WebRTC only supports one RTX codec at a time.
-      if (rtx_registered) {
-        LOG(LS_ERROR) << "Only one RTX codec at a time is supported.";
-        return false;
-      }
       std::map<int, int>::iterator apt_it = associated_payload_types_.find(
           it->plType);
       bool valid_apt = false;
@@ -3880,11 +3876,10 @@
         return false;
       }
       if (engine()->vie()->rtp()->SetRtxReceivePayloadType(
-          channel_id, it->plType) != 0) {
+              channel_id, it->plType, apt_it->second) != 0) {
         LOG_RTCERR2(SetRtxReceivePayloadType, channel_id, it->plType);
         return false;
       }
-      rtx_registered = true;
       continue;
     }
     if (engine()->vie()->codec()->SetReceiveCodec(channel_id, *it) != 0) {
@@ -4020,11 +4015,14 @@
   // NOTE: SetRtxSendPayloadType must be called after all SSRCs are
   // configured. Otherwise ssrc's configured after this point will use
   // the primary PT for RTX.
-  if (send_rtx_type_ != -1 &&
-      engine()->vie()->rtp()->SetRtxSendPayloadType(channel_id,
-                                                    send_rtx_type_) != 0) {
-    LOG_RTCERR2(SetRtxSendPayloadType, channel_id, send_rtx_type_);
-    return false;
+  std::map<int, int>::const_iterator it;
+  for (it = send_rtx_apt_types_.begin(); it != send_rtx_apt_types_.end();
+       ++it) {
+    if (engine()->vie()->rtp()->SetRtxSendPayloadType(channel_id, it->first,
+                                                      it->second) != 0) {
+      LOG_RTCERR3(SetRtxSendPayloadType, channel_id, it->first, it->second);
+      return false;
+    }
   }
 
   send_channel->set_send_params(send_params);
diff --git a/talk/media/webrtc/webrtcvideoengine.h b/talk/media/webrtc/webrtcvideoengine.h
index 78a10e0..85d4611 100644
--- a/talk/media/webrtc/webrtcvideoengine.h
+++ b/talk/media/webrtc/webrtcvideoengine.h
@@ -511,7 +511,8 @@
   // Global send side state.
   SendChannelMap send_channels_;
   rtc::scoped_ptr<webrtc::VideoCodec> send_codec_;
-  int send_rtx_type_;
+  // A map from RTX payload types to their associated payload types.
+  std::map<int, int> send_rtx_apt_types_;
   int send_red_type_;
   int send_fec_type_;
   bool sending_;
diff --git a/talk/media/webrtc/webrtcvideoengine2.cc b/talk/media/webrtc/webrtcvideoengine2.cc
index d066eb9..0239c89 100644
--- a/talk/media/webrtc/webrtcvideoengine2.cc
+++ b/talk/media/webrtc/webrtcvideoengine2.cc
@@ -121,6 +121,15 @@
     }
     output->red_payload_type = other.red_payload_type;
   }
+  if (other.rtx_payload_type != -1) {
+    if (output->rtx_payload_type != -1 &&
+        output->rtx_payload_type != other.rtx_payload_type) {
+      LOG(LS_WARNING) << "Conflict merging rtx_payload_type configs: "
+                      << output->rtx_payload_type << " and "
+                      << other.rtx_payload_type;
+    }
+    output->rtx_payload_type = other.rtx_payload_type;
+  }
 }
 }  // namespace
 
@@ -2070,6 +2079,7 @@
   return codec == other.codec &&
          fec.ulpfec_payload_type == other.fec.ulpfec_payload_type &&
          fec.red_payload_type == other.fec.red_payload_type &&
+         fec.rtx_payload_type == other.fec.rtx_payload_type &&
          rtx_payload_type == other.rtx_payload_type;
 }
 
@@ -2142,17 +2152,24 @@
       LOG(LS_ERROR) << "RTX mapped to payload not in codec list.";
       return std::vector<VideoCodecSettings>();
     }
-    if (payload_codec_type[it->first] != VideoCodec::CODEC_VIDEO) {
-      LOG(LS_ERROR) << "RTX not mapped to regular video codec.";
+    if (payload_codec_type[it->first] != VideoCodec::CODEC_VIDEO &&
+        payload_codec_type[it->first] != VideoCodec::CODEC_RED) {
+      LOG(LS_ERROR) << "RTX not mapped to regular video codec or RED codec.";
       return std::vector<VideoCodecSettings>();
     }
+
+    if (it->first == fec_settings.red_payload_type) {
+      fec_settings.rtx_payload_type = it->second;
+    }
   }
 
   // TODO(pbos): Write tests that figure out that I have not verified that RTX
   // codecs aren't mapped to bogus payloads.
   for (size_t i = 0; i < video_codecs.size(); ++i) {
     video_codecs[i].fec = fec_settings;
-    if (rtx_mapping[video_codecs[i].codec.id] != 0) {
+    if (rtx_mapping[video_codecs[i].codec.id] != 0 &&
+        rtx_mapping[video_codecs[i].codec.id] !=
+            fec_settings.red_payload_type) {
       video_codecs[i].rtx_payload_type = rtx_mapping[video_codecs[i].codec.id];
     }
   }
diff --git a/talk/media/webrtc/webrtcvideoengine2.h b/talk/media/webrtc/webrtcvideoengine2.h
index 2318971..52428d3 100644
--- a/talk/media/webrtc/webrtcvideoengine2.h
+++ b/talk/media/webrtc/webrtcvideoengine2.h
@@ -285,7 +285,6 @@
 
   struct VideoCodecSettings {
     VideoCodecSettings();
-
     bool operator ==(const VideoCodecSettings& other) const;
 
     VideoCodec codec;
diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
index 93660a5..7ceabca 100644
--- a/talk/media/webrtc/webrtcvideoengine2_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
@@ -34,6 +34,7 @@
 #include "talk/media/webrtc/fakewebrtcvideoengine.h"
 #include "talk/media/webrtc/simulcast.h"
 #include "talk/media/webrtc/webrtcvideochannelfactory.h"
+#include "talk/media/webrtc/webrtcvideoengine.h"
 #include "talk/media/webrtc/webrtcvideoengine2.h"
 #include "talk/media/webrtc/webrtcvideoengine2_unittest.h"
 #include "talk/media/webrtc/webrtcvoiceengine.h"
@@ -73,6 +74,19 @@
       cricket::kRtcpFbParamCcm, cricket::kRtcpFbCcmParamFir)));
 }
 
+void VerifySendStreamHasRtxTypes(const webrtc::VideoSendStream::Config& config,
+                                 const std::map<int, int>& rtx_types) {
+  std::map<int, int>::const_iterator it;
+  it = rtx_types.find(config.encoder_settings.payload_type);
+  EXPECT_TRUE(it != rtx_types.end() &&
+              it->second == config.rtp.rtx.payload_type);
+
+  if (config.rtp.fec.rtx_payload_type != -1) {
+    it = rtx_types.find(config.rtp.fec.red_payload_type);
+    EXPECT_TRUE(it != rtx_types.end() &&
+                it->second == config.rtp.fec.rtx_payload_type);
+  }
+}
 }  // namespace
 
 namespace cricket {
@@ -349,7 +363,11 @@
       } else if (engine_codecs[i].name == "ulpfec") {
         default_ulpfec_codec_ = engine_codecs[i];
       } else if (engine_codecs[i].name == "rtx") {
-        default_rtx_codec_ = engine_codecs[i];
+        int associated_payload_type;
+        if (engine_codecs[i].GetParam(kCodecParamAssociatedPayloadType,
+                                      &associated_payload_type)) {
+          default_apt_rtx_types_[associated_payload_type] = engine_codecs[i].id;
+        }
       } else if (!codec_set) {
         default_codec_ = engine_codecs[i];
         codec_set = true;
@@ -388,7 +406,7 @@
   VideoCodec default_codec_;
   VideoCodec default_red_codec_;
   VideoCodec default_ulpfec_codec_;
-  VideoCodec default_rtx_codec_;
+  std::map<int, int> default_apt_rtx_types_;
 };
 
 TEST_F(WebRtcVideoEngine2Test, ConfiguresAvSyncForFirstReceiveChannel) {
@@ -431,7 +449,7 @@
 
 TEST_F(WebRtcVideoEngine2Test, FindCodec) {
   const std::vector<cricket::VideoCodec>& c = engine_.codecs();
-  EXPECT_EQ(4U, c.size());
+  EXPECT_EQ(cricket::DefaultVideoCodecList().size(), c.size());
 
   cricket::VideoCodec vp8(104, "VP8", 320, 200, 30, 0);
   EXPECT_TRUE(engine_.FindCodec(vp8));
@@ -1583,8 +1601,7 @@
 
   EXPECT_EQ(1u, config.rtp.rtx.ssrcs.size());
   EXPECT_EQ(kRtxSsrcs1[0], config.rtp.rtx.ssrcs[0]);
-  EXPECT_EQ(static_cast<int>(default_rtx_codec_.id),
-            config.rtp.rtx.payload_type);
+  VerifySendStreamHasRtxTypes(config, default_apt_rtx_types_);
   // TODO(juberti): Check RTCP, PLI, TMMBR.
 }
 
diff --git a/talk/media/webrtc/webrtcvideoengine_unittest.cc b/talk/media/webrtc/webrtcvideoengine_unittest.cc
index 24e9af2..93560a9 100644
--- a/talk/media/webrtc/webrtcvideoengine_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine_unittest.cc
@@ -38,6 +38,8 @@
 #include "talk/media/webrtc/webrtcvideoframe.h"
 #include "talk/media/webrtc/webrtcvoiceengine.h"
 #include "talk/session/media/mediasession.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
 #include "webrtc/base/fakecpumonitor.h"
 #include "webrtc/base/gunit.h"
 #include "webrtc/base/logging.h"
@@ -754,7 +756,8 @@
   EXPECT_FALSE(vie_.ReceiveCodecRegistered(channel_num, wcodec));
 
   // The RTX payload type should have been set.
-  EXPECT_EQ(rtx_codec.id, vie_.GetRtxRecvPayloadType(channel_num));
+  EXPECT_THAT(vie_.GetRtxRecvPayloadType(channel_num),
+              ::testing::ElementsAre(rtx_codec.id));
 }
 
 // Test that RTX packets are routed to the default video channel if
@@ -2079,7 +2082,8 @@
   codecs.push_back(rtx_codec);
   EXPECT_TRUE(channel_->SetSendCodecs(codecs));
 
-  EXPECT_EQ(96, vie_.GetRtxSendPayloadType(channel_num));
+  EXPECT_THAT(vie_.GetRtxSendPayloadType(channel_num),
+              ::testing::ElementsAre(96));
 
   cricket::StreamParams params =
     cricket::StreamParams::CreateLegacy(kSsrcs1[0]);
@@ -2117,7 +2121,8 @@
 
   // The first matched codec should be set, i.e., H.264.
 
-  EXPECT_EQ(96, vie_.GetRtxSendPayloadType(channel_num));
+  EXPECT_THAT(vie_.GetRtxSendPayloadType(channel_num),
+              ::testing::ElementsAre(96, 97));
 
   cricket::StreamParams params =
     cricket::StreamParams::CreateLegacy(kSsrcs1[0]);
@@ -2154,7 +2159,8 @@
   codecs.push_back(rtx_codec);
   EXPECT_TRUE(channel_->SetRecvCodecs(codecs));
 
-  EXPECT_EQ(96, vie_.GetRtxRecvPayloadType(channel_num));
+  EXPECT_THAT(vie_.GetRtxRecvPayloadType(channel_num),
+              ::testing::ElementsAre(96));
 
   cricket::StreamParams params =
     cricket::StreamParams::CreateLegacy(kSsrcs1[0]);
@@ -2189,17 +2195,21 @@
   cricket::VideoCodec rtx_codec2(96, "rtx", 0, 0, 0, 0);
   rtx_codec2.SetParam("apt", kVP8Codec.id);
   codecs.push_back(kVP8Codec);
-  codecs.push_back(rtx_codec);
-  // Should fail since WebRTC only supports one RTX codec at a time.
-  EXPECT_FALSE(channel_->SetRecvCodecs(codecs));
+  codecs.push_back(rtx_codec2);
+  // Now WebRTC supports setting multiple RTX codecs at a time.
+  EXPECT_TRUE(channel_->SetRecvCodecs(codecs));
+  EXPECT_THAT(vie_.GetRtxRecvPayloadType(channel_num),
+              ::testing::ElementsAre(96, 97));
 
   codecs.pop_back();
-
   // One RTX codec should be fine.
   EXPECT_TRUE(channel_->SetRecvCodecs(codecs));
 
-  // The RTX payload type should have been set.
-  EXPECT_EQ(rtx_codec.id, vie_.GetRtxRecvPayloadType(channel_num));
+  // TODO(changbin): The Rtx key can still be found from the Rtx-Apt map
+  // if the new codec list doesn't assign it with a new value.
+  // Should pass a map to SetRtxRecvPayloadType in future.
+  EXPECT_THAT(vie_.GetRtxRecvPayloadType(channel_num),
+              ::testing::ElementsAre(96, 97));
 }
 #endif
 
@@ -2249,7 +2259,7 @@
 TEST_F(WebRtcVideoEngineTest, FindCodec) {
   // We should not need to init engine in order to get codecs.
   const std::vector<cricket::VideoCodec>& c = engine_.codecs();
-  EXPECT_EQ(4U, c.size());
+  EXPECT_EQ(cricket::DefaultVideoCodecList().size(), c.size());
 
   cricket::VideoCodec vp8(104, "VP8", 320, 200, 30, 0);
   EXPECT_TRUE(engine_.FindCodec(vp8));
@@ -2296,7 +2306,7 @@
   std::vector<cricket::VideoCodec>::const_iterator it;
   bool apt_checked = false;
   for (it = engine_.codecs().begin(); it != engine_.codecs().end(); ++it) {
-    if (_stricmp(cricket::kRtxCodecName, it->name.c_str()) && it->id != 96) {
+    if (_stricmp(cricket::kRtxCodecName, it->name.c_str()) || it->id != 96) {
       continue;
     }
     int apt;
@@ -3202,7 +3212,8 @@
   EXPECT_TRUE(channel_->SetSendCodecs(codec_list));
 
   // RTX payload type should now be set.
-  EXPECT_EQ(96, vie_.GetRtxSendPayloadType(channel_num));
+  EXPECT_THAT(vie_.GetRtxSendPayloadType(channel_num),
+              ::testing::ElementsAre(96));
 
   // Verify all SSRCs are set after SetSendCodecs.
   EXPECT_EQ(3, vie_.GetNumSsrcs(channel_num));
diff --git a/webrtc/config.cc b/webrtc/config.cc
index 357f636..a8cb523 100644
--- a/webrtc/config.cc
+++ b/webrtc/config.cc
@@ -17,6 +17,7 @@
   std::stringstream ss;
   ss << "{ulpfec_payload_type: " << ulpfec_payload_type;
   ss << ", red_payload_type: " << red_payload_type;
+  ss << ", rtx_payload_type: " << rtx_payload_type;
   ss << '}';
   return ss.str();
 }
diff --git a/webrtc/config.h b/webrtc/config.h
index cd5a23f..f5a5144 100644
--- a/webrtc/config.h
+++ b/webrtc/config.h
@@ -54,13 +54,17 @@
 // Settings for forward error correction, see RFC 5109 for details. Set the
 // payload types to '-1' to disable.
 struct FecConfig {
-  FecConfig() : ulpfec_payload_type(-1), red_payload_type(-1) {}
+  FecConfig()
+      : ulpfec_payload_type(-1), red_payload_type(-1), rtx_payload_type(-1) {}
   std::string ToString() const;
   // Payload type used for ULPFEC packets.
   int ulpfec_payload_type;
 
   // Payload type used for RED packets.
   int red_payload_type;
+
+  // Rtx payload type for RED payload.
+  int rtx_payload_type;
 };
 
 // RTP header extension to use for the video stream, see RFC 5285.
diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h b/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h
index f58eaea..e73bac3 100644
--- a/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h
+++ b/webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h
@@ -79,7 +79,7 @@
 
   bool GetRtxSsrc(uint32_t* ssrc) const;
 
-  void SetRtxPayloadType(int payload_type);
+  void SetRtxPayloadType(int payload_type, int associated_payload_type);
 
   bool IsRtx(const RTPHeader& header) const;
 
@@ -158,7 +158,8 @@
   int8_t  last_received_payload_type_;
   int8_t  last_received_media_payload_type_;
   bool rtx_;
-  int8_t payload_type_rtx_;
+  // Mapping rtx_apt_types_[rtx] = apt.
+  std::map<int, int> rtx_apt_types_;
   uint32_t ssrc_rtx_;
 };
 
diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
index 6b9a554..f1466ae 100644
--- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -228,7 +228,8 @@
 
     // Sets the payload type to use when sending RTX packets. Note that this
     // doesn't enable RTX, only the payload type is set.
-    virtual void SetRtxSendPayloadType(int payload_type) = 0;
+    virtual void SetRtxSendPayloadType(int payload_type,
+                                       int associated_payload_type) = 0;
 
     /*
     * Get status of sending RTX (RFC 4588) on a specific SSRC.
diff --git a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index 31fade4..7c81715 100644
--- a/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/webrtc/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -101,8 +101,7 @@
       void(int* modes, uint32_t* ssrc, int* payload_type));
   MOCK_METHOD1(SetRtxSsrc,
       void(uint32_t));
-  MOCK_METHOD1(SetRtxSendPayloadType,
-      void(int));
+  MOCK_METHOD2(SetRtxSendPayloadType, void(int, int));
   MOCK_METHOD1(SetSendingStatus,
       int32_t(const bool sending));
   MOCK_CONST_METHOD0(Sending,
diff --git a/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc b/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc
index bfe0af0..ad553ac 100644
--- a/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/nack_rtx_unittest.cc
@@ -32,6 +32,8 @@
 const uint32_t kTestNumberOfPackets = 1350;
 const int kTestNumberOfRtxPackets = 149;
 const int kNumFrames = 30;
+const int kPayloadType = 123;
+const int kRtxPayloadType = 98;
 
 class VerifyingRtxReceiver : public NullRtpData
 {
@@ -206,15 +208,17 @@
 
     VideoCodec video_codec;
     memset(&video_codec, 0, sizeof(video_codec));
-    video_codec.plType = 123;
+    video_codec.plType = kPayloadType;
     memcpy(video_codec.plName, "I420", 5);
 
     EXPECT_EQ(0, rtp_rtcp_module_->RegisterSendPayload(video_codec));
+    rtp_rtcp_module_->SetRtxSendPayloadType(kRtxPayloadType, kPayloadType);
     EXPECT_EQ(0, rtp_receiver_->RegisterReceivePayload(video_codec.plName,
                                                        video_codec.plType,
                                                        90000,
                                                        0,
                                                        video_codec.maxBitrate));
+    rtp_payload_registry_.SetRtxPayloadType(kRtxPayloadType, kPayloadType);
 
     for (size_t n = 0; n < payload_data_length; n++) {
       payload_data[n] = n % 10;
@@ -265,12 +269,9 @@
     uint32_t timestamp = 3000;
     uint16_t nack_list[kVideoNackListSize];
     for (int frame = 0; frame < kNumFrames; ++frame) {
-      EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(webrtc::kVideoFrameDelta,
-                                                      123,
-                                                      timestamp,
-                                                      timestamp / 90,
-                                                      payload_data,
-                                                      payload_data_length));
+      EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(
+                       webrtc::kVideoFrameDelta, kPayloadType, timestamp,
+                       timestamp / 90, payload_data, payload_data_length));
       int length = BuildNackList(nack_list);
       if (length > 0)
         rtp_rtcp_module_->SendNACK(nack_list, length);
@@ -313,12 +314,9 @@
   // Send 30 frames which at the default size is roughly what we need to get
   // enough packets.
   for (int frame = 0; frame < kNumFrames; ++frame) {
-    EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(webrtc::kVideoFrameDelta,
-                                                    123,
-                                                    timestamp,
-                                                    timestamp / 90,
-                                                    payload_data,
-                                                    payload_data_length));
+    EXPECT_EQ(0, rtp_rtcp_module_->SendOutgoingData(
+                     webrtc::kVideoFrameDelta, kPayloadType, timestamp,
+                     timestamp / 90, payload_data, payload_data_length));
     // Prepare next frame.
     timestamp += 3000;
     fake_clock.AdvanceTimeMilliseconds(33);
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc b/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc
index 727a4d3..18aeb11 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_payload_registry.cc
@@ -24,7 +24,6 @@
       last_received_payload_type_(-1),
       last_received_media_payload_type_(-1),
       rtx_(false),
-      payload_type_rtx_(-1),
       ssrc_rtx_(0) {}
 
 RTPPayloadRegistry::~RTPPayloadRegistry() {
@@ -264,19 +263,25 @@
   RtpUtility::AssignUWord32ToBuffer(*restored_packet + 8, original_ssrc);
 
   CriticalSectionScoped cs(crit_sect_.get());
+  if (!rtx_)
+    return true;
 
-  if (payload_type_rtx_ != -1) {
-    if (header.payloadType == payload_type_rtx_ &&
-        incoming_payload_type_ != -1) {
-      (*restored_packet)[1] = static_cast<uint8_t>(incoming_payload_type_);
-      if (header.markerBit) {
-        (*restored_packet)[1] |= kRtpMarkerBitMask;  // Marker bit is set.
-      }
-    } else {
-      LOG(LS_WARNING) << "Incorrect RTX configuration, dropping packet.";
-      return false;
-    }
+  if (rtx_apt_types_.empty()) {
+    LOG(LS_WARNING) << "No RTX is set, dropping packet.";
+    return false;
   }
+  std::map<int, int>::const_iterator it =
+      rtx_apt_types_.find(header.payloadType);
+  if (it != rtx_apt_types_.end()) {
+    (*restored_packet)[1] = static_cast<uint8_t>(it->second);
+    if (header.markerBit) {
+      (*restored_packet)[1] |= kRtpMarkerBitMask;  // Marker bit is set.
+    }
+  } else {
+    LOG(LS_WARNING) << "Incorrect RTX configuration, dropping packet.";
+    return false;
+  }
+
   return true;
 }
 
@@ -292,10 +297,12 @@
   return rtx_;
 }
 
-void RTPPayloadRegistry::SetRtxPayloadType(int payload_type) {
+void RTPPayloadRegistry::SetRtxPayloadType(int payload_type,
+                                           int associated_payload_type) {
   CriticalSectionScoped cs(crit_sect_.get());
   assert(payload_type >= 0);
-  payload_type_rtx_ = payload_type;
+  assert(associated_payload_type >= 0);
+  rtx_apt_types_[payload_type] = associated_payload_type;
   rtx_ = true;
 }
 
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index 4dd74ce..15286cb 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -257,8 +257,9 @@
   rtp_sender_.SetRtxSsrc(ssrc);
 }
 
-void ModuleRtpRtcpImpl::SetRtxSendPayloadType(int payload_type) {
-  rtp_sender_.SetRtxPayloadType(payload_type);
+void ModuleRtpRtcpImpl::SetRtxSendPayloadType(int payload_type,
+                                              int associated_payload_type) {
+  rtp_sender_.SetRtxPayloadType(payload_type, associated_payload_type);
 }
 
 int32_t ModuleRtpRtcpImpl::IncomingRtcpPacket(
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index ec06783..6e0e907 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -94,7 +94,8 @@
 
   virtual void SetRtxSsrc(uint32_t ssrc) OVERRIDE;
 
-  virtual void SetRtxSendPayloadType(int payload_type) OVERRIDE;
+  virtual void SetRtxSendPayloadType(int payload_type,
+                                     int associated_payload_type) OVERRIDE;
 
   // Sends kRtcpByeCode when going from true to false.
   virtual int32_t SetSendingStatus(bool sending) OVERRIDE;
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
index 3f9e95f..726d6f1 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl_unittest.cc
@@ -691,7 +691,7 @@
     // as otherwise the timestmap used for BWE will be broken.
     senders_[i]->RegisterSendRtpHeaderExtension(kRtpExtensionAbsoluteSendTime,
                                                 1);
-    senders_[i]->SetRtxSendPayloadType(96);
+    senders_[i]->SetRtxSendPayloadType(96, 100);
     senders_[i]->SetRtxSsrc(kSenderRtxSsrc + i);
     senders_[i]->SetRTXSendStatus(kRtxRetransmitted);
   }
@@ -716,7 +716,7 @@
 
 TEST_F(RtpSendingTest, DISABLED_RoundRobinPaddingRtxRedundantPayloads) {
   for (int i = 1; i < codec_.numberOfSimulcastStreams + 1; ++i) {
-    senders_[i]->SetRtxSendPayloadType(96);
+    senders_[i]->SetRtxSendPayloadType(96, 100);
     senders_[i]->SetRtxSsrc(kSenderRtxSsrc + i);
     senders_[i]->SetRTXSendStatus(kRtxRetransmitted | kRtxRedundantPayloads);
     senders_[i]->SetStorePacketsStatus(true, 100);
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
index a1810f2..42df579 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
@@ -147,7 +147,6 @@
       last_packet_marker_bit_(false),
       csrcs_(),
       rtx_(kRtxOff),
-      payload_type_rtx_(-1),
       target_bitrate_critsect_(CriticalSectionWrapper::CreateCriticalSection()),
       target_bitrate_(0) {
   memset(nack_byte_count_times_, 0, sizeof(nack_byte_count_times_));
@@ -403,12 +402,17 @@
   CriticalSectionScoped cs(send_critsect_);
   *mode = rtx_;
   *ssrc = ssrc_rtx_;
-  *payload_type = payload_type_rtx_;
+
+  std::map<int, int>::const_iterator it = apt_rtx_types_.find(payload_type_);
+  *payload_type = (it == apt_rtx_types_.end()) ? -1 : it->second;
 }
 
-void RTPSender::SetRtxPayloadType(int payload_type) {
+void RTPSender::SetRtxPayloadType(int payload_type,
+                                  int associated_payload_type) {
   CriticalSectionScoped cs(send_critsect_);
-  payload_type_rtx_ = payload_type;
+  assert(payload_type >= 0);
+  assert(associated_payload_type >= 0);
+  apt_rtx_types_[associated_payload_type] = payload_type;
 }
 
 int32_t RTPSender::CheckPayloadType(int8_t payload_type,
@@ -610,8 +614,14 @@
         ssrc = ssrc_rtx_;
         sequence_number = sequence_number_rtx_;
         ++sequence_number_rtx_;
-        payload_type = ((rtx_ & kRtxRedundantPayloads) > 0) ? payload_type_rtx_
-                                                            : payload_type_;
+
+        if (apt_rtx_types_.empty())
+          return 0;
+        std::map<int, int>::const_iterator it =
+            apt_rtx_types_.find(payload_type_);
+        payload_type = it != apt_rtx_types_.end()
+                           ? it->second
+                           : apt_rtx_types_.begin()->second;
         over_rtx = true;
       }
     }
@@ -1660,9 +1670,10 @@
   // Add original RTP header.
   memcpy(data_buffer_rtx, buffer, rtp_header.headerLength);
 
-  // Replace payload type, if a specific type is set for RTX.
-  if (payload_type_rtx_ != -1) {
-    data_buffer_rtx[1] = static_cast<uint8_t>(payload_type_rtx_);
+  std::map<int, int>::const_iterator it =
+      apt_rtx_types_.find(rtp_header.payloadType);
+  if (it != apt_rtx_types_.end()) {
+    data_buffer_rtx[1] = static_cast<uint8_t>(it->second);
     if (rtp_header.markerBit)
       data_buffer_rtx[1] |= kRtpMarkerBitMask;
   }
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.h b/webrtc/modules/rtp_rtcp/source/rtp_sender.h
index 2703fea..c807a5b 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.h
@@ -191,7 +191,7 @@
   uint32_t RtxSsrc() const;
   void SetRtxSsrc(uint32_t ssrc);
 
-  void SetRtxPayloadType(int payloadType);
+  void SetRtxPayloadType(int payloadType, int associated_payload_type);
 
   // Functions wrapping RTPSenderInterface.
   virtual int32_t BuildRTPheader(
@@ -391,7 +391,8 @@
   std::vector<uint32_t> csrcs_ GUARDED_BY(send_critsect_);
   int rtx_ GUARDED_BY(send_critsect_);
   uint32_t ssrc_rtx_ GUARDED_BY(send_critsect_);
-  int payload_type_rtx_ GUARDED_BY(send_critsect_);
+  // Mapping apt_rtx_types_[apt] = rtx.
+  std::map<int, int> apt_rtx_types_ GUARDED_BY(send_critsect_);
 
   // Note: Don't access this variable directly, always go through
   // SetTargetBitrateKbps or GetTargetBitrateKbps. Also remember
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index 3946c44..0a95807 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -30,6 +30,7 @@
 const int kTransmissionTimeOffsetExtensionId = 1;
 const int kAbsoluteSendTimeExtensionId = 14;
 const int kPayload = 100;
+const int kRtxPayload = 98;
 const uint32_t kTimestamp = 10;
 const uint16_t kSeqNum = 33;
 const int kTimeOffset = 22222;
@@ -654,6 +655,7 @@
   rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport, NULL,
                                   &mock_paced_sender_, NULL, NULL, NULL));
   rtp_sender_->SetSequenceNumber(kSeqNum);
+  rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload);
   // Make all packets go through the pacer.
   EXPECT_CALL(mock_paced_sender_,
               SendPacket(PacedSender::kNormalPriority, _, _, _, _, _)).
@@ -1123,7 +1125,7 @@
   const uint8_t kPayloadType = 127;
   rtp_sender_->SetSSRC(1234);
   rtp_sender_->SetRtxSsrc(4321);
-  rtp_sender_->SetRtxPayloadType(kPayloadType - 1);
+  rtp_sender_->SetRtxPayloadType(kPayloadType - 1, kPayloadType);
   rtp_sender_->SetRTXStatus(kRtxRetransmitted | kRtxRedundantPayloads);
 
   ASSERT_EQ(
diff --git a/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc b/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc
index ccc8cf2..f8b6011 100644
--- a/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc
+++ b/webrtc/modules/rtp_rtcp/test/testAPI/test_api.cc
@@ -106,33 +106,31 @@
   unsigned int ssrc = 0;
   int rtx_mode = kRtxOff;
   const int kRtxPayloadType = 119;
-  int payload_type = -1;
+  const int kPayloadType = 100;
   module->SetRTXSendStatus(kRtxRetransmitted);
-  module->SetRtxSendPayloadType(kRtxPayloadType);
+  module->SetRtxSendPayloadType(kRtxPayloadType, kPayloadType);
   module->SetRtxSsrc(1);
+  int payload_type;
   module->RTXSendStatus(&rtx_mode, &ssrc, &payload_type);
   EXPECT_EQ(kRtxRetransmitted, rtx_mode);
   EXPECT_EQ(1u, ssrc);
-  EXPECT_EQ(kRtxPayloadType, payload_type);
   rtx_mode = kRtxOff;
   module->SetRTXSendStatus(kRtxOff);
-  payload_type = -1;
-  module->SetRtxSendPayloadType(kRtxPayloadType);
+  module->SetRtxSendPayloadType(kRtxPayloadType, kPayloadType);
   module->RTXSendStatus(&rtx_mode, &ssrc, &payload_type);
   EXPECT_EQ(kRtxOff, rtx_mode);
-  EXPECT_EQ(kRtxPayloadType, payload_type);
   module->SetRTXSendStatus(kRtxRetransmitted);
   module->RTXSendStatus(&rtx_mode, &ssrc, &payload_type);
   EXPECT_EQ(kRtxRetransmitted, rtx_mode);
-  EXPECT_EQ(kRtxPayloadType, payload_type);
 }
 
 TEST_F(RtpRtcpAPITest, RtxReceiver) {
   const uint32_t kRtxSsrc = 1;
   const int kRtxPayloadType = 119;
+  const int kPayloadType = 100;
   EXPECT_FALSE(rtp_payload_registry_->RtxEnabled());
   rtp_payload_registry_->SetRtxSsrc(kRtxSsrc);
-  rtp_payload_registry_->SetRtxPayloadType(kRtxPayloadType);
+  rtp_payload_registry_->SetRtxPayloadType(kRtxPayloadType, kPayloadType);
   EXPECT_TRUE(rtp_payload_registry_->RtxEnabled());
   RTPHeader rtx_header;
   rtx_header.ssrc = kRtxSsrc;
diff --git a/webrtc/test/call_test.cc b/webrtc/test/call_test.cc
index 126c716..dbd514e 100644
--- a/webrtc/test/call_test.cc
+++ b/webrtc/test/call_test.cc
@@ -151,6 +151,7 @@
 const uint8_t CallTest::kFakeSendPayloadType = 125;
 const uint8_t CallTest::kSendRtxPayloadType = 98;
 const uint8_t CallTest::kRedPayloadType = 118;
+const uint8_t CallTest::kRtxRedPayloadType = 99;
 const uint8_t CallTest::kUlpfecPayloadType = 119;
 const uint32_t CallTest::kSendRtxSsrcs[kNumSsrcs] = {0xBADCAFD, 0xBADCAFE,
                                                      0xBADCAFF};
diff --git a/webrtc/test/call_test.h b/webrtc/test/call_test.h
index b9a091b..1e01733 100644
--- a/webrtc/test/call_test.h
+++ b/webrtc/test/call_test.h
@@ -37,6 +37,7 @@
   static const uint8_t kSendRtxPayloadType;
   static const uint8_t kFakeSendPayloadType;
   static const uint8_t kRedPayloadType;
+  static const uint8_t kRtxRedPayloadType;
   static const uint8_t kUlpfecPayloadType;
   static const uint32_t kSendRtxSsrcs[kNumSsrcs];
   static const uint32_t kSendSsrcs[kNumSsrcs];
diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc
index f820554..65d9568 100644
--- a/webrtc/video/end_to_end_tests.cc
+++ b/webrtc/video/end_to_end_tests.cc
@@ -69,7 +69,7 @@
     }
   };
 
-  void DecodesRetransmittedFrame(bool retransmit_over_rtx);
+  void DecodesRetransmittedFrame(bool use_rtx, bool use_red);
   void ReceivesPliAndRecovers(int rtp_history_ms);
   void RespectsRtcpMode(newapi::RtcpMode rtcp_mode);
   void TestXrReceiverReferenceTimeReport(bool enable_rrtr);
@@ -538,16 +538,16 @@
 
 // This test drops second RTP packet with a marker bit set, makes sure it's
 // retransmitted and renders. Retransmission SSRCs are also checked.
-void EndToEndTest::DecodesRetransmittedFrame(bool retransmit_over_rtx) {
+void EndToEndTest::DecodesRetransmittedFrame(bool use_rtx, bool use_red) {
   static const int kDroppedFrameNumber = 2;
   class RetransmissionObserver : public test::EndToEndTest,
                                  public I420FrameCallback {
    public:
-    explicit RetransmissionObserver(bool expect_rtx)
+    explicit RetransmissionObserver(bool use_rtx, bool use_red)
         : EndToEndTest(kDefaultTimeoutMs),
-          retransmission_ssrc_(expect_rtx ? kSendRtxSsrcs[0] : kSendSsrcs[0]),
-          retransmission_payload_type_(expect_rtx ? kSendRtxPayloadType
-                                                  : kFakeSendPayloadType),
+          payload_type_(GetPayloadType(false, use_red)),
+          retransmission_ssrc_(use_rtx ? kSendRtxSsrcs[0] : kSendSsrcs[0]),
+          retransmission_payload_type_(GetPayloadType(use_rtx, use_red)),
           marker_bits_observed_(0),
           retransmitted_timestamp_(0),
           frame_retransmitted_(false) {}
@@ -565,7 +565,7 @@
       }
 
       EXPECT_EQ(kSendSsrcs[0], header.ssrc);
-      EXPECT_EQ(kFakeSendPayloadType, header.payloadType);
+      EXPECT_EQ(payload_type_, header.payloadType);
 
       // Found the second frame's final packet, drop this and expect a
       // retransmission.
@@ -592,13 +592,25 @@
       send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
       (*receive_configs)[0].pre_render_callback = this;
       (*receive_configs)[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
+
+      if (payload_type_ == kRedPayloadType) {
+        send_config->rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
+        send_config->rtp.fec.red_payload_type = kRedPayloadType;
+        (*receive_configs)[0].rtp.fec.red_payload_type = kRedPayloadType;
+        (*receive_configs)[0].rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
+      }
+
       if (retransmission_ssrc_ == kSendRtxSsrcs[0]) {
         send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[0]);
         send_config->rtp.rtx.payload_type = kSendRtxPayloadType;
-        (*receive_configs)[0].rtp.rtx[kSendRtxPayloadType].ssrc =
+        (*receive_configs)[0].rtp.rtx[kFakeSendPayloadType].ssrc =
             kSendRtxSsrcs[0];
-        (*receive_configs)[0].rtp.rtx[kSendRtxPayloadType].payload_type =
+        (*receive_configs)[0].rtp.rtx[kFakeSendPayloadType].payload_type =
             kSendRtxPayloadType;
+        if (payload_type_ == kRedPayloadType) {
+          send_config->rtp.fec.rtx_payload_type = kRtxRedPayloadType;
+          (*receive_configs)[0].rtp.fec.rtx_payload_type = kRtxRedPayloadType;
+        }
       }
     }
 
@@ -607,22 +619,36 @@
           << "Timed out while waiting for retransmission to render.";
     }
 
+    int GetPayloadType(bool use_rtx, bool use_red) {
+      return use_red ? (use_rtx ? kRtxRedPayloadType : kRedPayloadType)
+                     : (use_rtx ? kSendRtxPayloadType : kFakeSendPayloadType);
+    }
+
+    const int payload_type_;
     const uint32_t retransmission_ssrc_;
     const int retransmission_payload_type_;
     int marker_bits_observed_;
     uint32_t retransmitted_timestamp_;
     bool frame_retransmitted_;
-  } test(retransmit_over_rtx);
+  } test(use_rtx, use_red);
 
   RunBaseTest(&test);
 }
 
 TEST_F(EndToEndTest, DecodesRetransmittedFrame) {
-  DecodesRetransmittedFrame(false);
+  DecodesRetransmittedFrame(false, false);
 }
 
 TEST_F(EndToEndTest, DecodesRetransmittedFrameOverRtx) {
-  DecodesRetransmittedFrame(true);
+  DecodesRetransmittedFrame(true, false);
+}
+
+TEST_F(EndToEndTest, DecodesRetransmittedFrameByRed) {
+  DecodesRetransmittedFrame(false, true);
+}
+
+TEST_F(EndToEndTest, DecodesRetransmittedFrameByRedOverRtx) {
+  DecodesRetransmittedFrame(true, true);
 }
 
 TEST_F(EndToEndTest, UsesFrameCallbacks) {
@@ -2268,5 +2294,4 @@
     call->DestroyVideoReceiveStream(receive_stream);
   }
 }
-
 }  // namespace webrtc
diff --git a/webrtc/video/loopback.cc b/webrtc/video/loopback.cc
index a1ebed1..52cfaa3 100644
--- a/webrtc/video/loopback.cc
+++ b/webrtc/video/loopback.cc
@@ -103,7 +103,8 @@
 static const uint32_t kSendRtxSsrc = 0x654322;
 static const uint32_t kReceiverLocalSsrc = 0x123456;
 
-static const uint8_t kRtxPayloadType = 96;
+static const uint8_t kRtxVideoPayloadType = 96;
+static const uint8_t kVideoPayloadType = 124;
 
 void Loopback() {
   scoped_ptr<test::TraceToStderr> trace_to_stderr_;
@@ -137,7 +138,7 @@
   VideoSendStream::Config send_config;
   send_config.rtp.ssrcs.push_back(kSendSsrc);
   send_config.rtp.rtx.ssrcs.push_back(kSendRtxSsrc);
-  send_config.rtp.rtx.payload_type = kRtxPayloadType;
+  send_config.rtp.rtx.payload_type = kRtxVideoPayloadType;
   send_config.rtp.nack.rtp_history_ms = 1000;
   send_config.rtp.extensions.push_back(
       RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId));
@@ -155,7 +156,7 @@
   }
   send_config.encoder_settings.encoder = encoder.get();
   send_config.encoder_settings.payload_name = flags::Codec();
-  send_config.encoder_settings.payload_type = 124;
+  send_config.encoder_settings.payload_type = kVideoPayloadType;
   VideoEncoderConfig encoder_config;
   encoder_config.streams = test::CreateVideoStreams(1);
   VideoStream* stream = &encoder_config.streams[0];
@@ -183,8 +184,8 @@
   receive_config.rtp.remote_ssrc = send_config.rtp.ssrcs[0];
   receive_config.rtp.local_ssrc = kReceiverLocalSsrc;
   receive_config.rtp.nack.rtp_history_ms = 1000;
-  receive_config.rtp.rtx[kRtxPayloadType].ssrc = kSendRtxSsrc;
-  receive_config.rtp.rtx[kRtxPayloadType].payload_type = kRtxPayloadType;
+  receive_config.rtp.rtx[kVideoPayloadType].ssrc = kSendRtxSsrc;
+  receive_config.rtp.rtx[kVideoPayloadType].payload_type = kRtxVideoPayloadType;
   receive_config.rtp.extensions.push_back(
       RtpExtension(RtpExtension::kAbsSendTime, kAbsSendTimeExtensionId));
   receive_config.renderer = loopback_video.get();
diff --git a/webrtc/video/rampup_tests.cc b/webrtc/video/rampup_tests.cc
index 4b26d05..b3f0522 100644
--- a/webrtc/video/rampup_tests.cc
+++ b/webrtc/video/rampup_tests.cc
@@ -76,6 +76,10 @@
   remote_bitrate_estimator_.reset(
       rbe_factory->Create(this, clock, control_type,
                           kRemoteBitrateEstimatorMinBitrateBps));
+  payload_registry_->SetRtxPayloadType(RampUpTest::kSendRtxPayloadType,
+                                       RampUpTest::kFakeSendPayloadType);
+  payload_registry_->SetRtxPayloadType(RampUpTest::kRtxRedPayloadType,
+                                       RampUpTest::kRedPayloadType);
 }
 
 void StreamObserver::set_expected_bitrate_bps(
@@ -118,7 +122,6 @@
   RTPHeader header;
   EXPECT_TRUE(rtp_parser_->Parse(packet, length, &header));
   receive_stats_->IncomingPacket(header, length, false);
-  payload_registry_->SetIncomingPayloadType(header);
   remote_bitrate_estimator_->IncomingPacket(
       clock_->TimeInMilliseconds(), length - 12, header);
   if (remote_bitrate_estimator_->TimeUntilNextProcess() <= 0) {
@@ -136,11 +139,9 @@
     uint8_t restored_packet[kMaxPacketSize];
     uint8_t* restored_packet_ptr = restored_packet;
     size_t restored_length = length;
-    payload_registry_->RestoreOriginalPacket(&restored_packet_ptr,
-                                             packet,
-                                             &restored_length,
-                                             rtx_media_ssrcs_[header.ssrc],
-                                             header);
+    EXPECT_TRUE(payload_registry_->RestoreOriginalPacket(
+        &restored_packet_ptr, packet, &restored_length,
+        rtx_media_ssrcs_[header.ssrc], header));
     length = restored_length;
     EXPECT_TRUE(rtp_parser_->Parse(
         restored_packet, static_cast<int>(length), &header));
@@ -366,10 +367,11 @@
   return test_done_->Wait(test::CallTest::kLongTimeoutMs);
 }
 
-void RampUpTest::RunRampUpTest(bool rtx,
-                               size_t num_streams,
+void RampUpTest::RunRampUpTest(size_t num_streams,
                                unsigned int start_bitrate_bps,
-                               const std::string& extension_type) {
+                               const std::string& extension_type,
+                               bool rtx,
+                               bool red) {
   std::vector<uint32_t> ssrcs(GenerateSsrcs(num_streams, 100));
   std::vector<uint32_t> rtx_ssrcs(GenerateSsrcs(num_streams, 200));
   StreamObserver::SsrcMap rtx_ssrc_map;
@@ -422,6 +424,11 @@
     send_config_.rtp.rtx.payload_type = kSendRtxPayloadType;
     send_config_.rtp.rtx.ssrcs = rtx_ssrcs;
   }
+  if (red) {
+    send_config_.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
+    send_config_.rtp.fec.red_payload_type = kRedPayloadType;
+    send_config_.rtp.fec.rtx_payload_type = kRtxRedPayloadType;
+  }
 
   if (num_streams == 1) {
     // For single stream rampup until 1mbps
@@ -448,7 +455,9 @@
   DestroyStreams();
 }
 
-void RampUpTest::RunRampUpDownUpTest(size_t number_of_streams, bool rtx) {
+void RampUpTest::RunRampUpDownUpTest(size_t number_of_streams,
+                                     bool rtx,
+                                     bool red) {
   test::DirectTransport receiver_transport;
   LowRateStreamObserver stream_observer(
       &receiver_transport, Clock::GetRealTimeClock(), number_of_streams, rtx);
@@ -467,6 +476,11 @@
     send_config_.rtp.rtx.payload_type = kSendRtxPayloadType;
     send_config_.rtp.rtx.ssrcs = GenerateSsrcs(number_of_streams, 200);
   }
+  if (red) {
+    send_config_.rtp.fec.ulpfec_payload_type = kUlpfecPayloadType;
+    send_config_.rtp.fec.red_payload_type = kRedPayloadType;
+    send_config_.rtp.fec.rtx_payload_type = kRtxRedPayloadType;
+  }
 
   CreateStreams();
   stream_observer.SetSendStream(send_stream_);
@@ -482,43 +496,68 @@
 }
 
 TEST_F(RampUpTest, SingleStream) {
-  RunRampUpTest(false, 1, 0, RtpExtension::kTOffset);
+  RunRampUpTest(1, 0, RtpExtension::kTOffset, false, false);
 }
 
 TEST_F(RampUpTest, Simulcast) {
-  RunRampUpTest(false, 3, 0, RtpExtension::kTOffset);
+  RunRampUpTest(3, 0, RtpExtension::kTOffset, false, false);
 }
 
 TEST_F(RampUpTest, SimulcastWithRtx) {
-  RunRampUpTest(true, 3, 0, RtpExtension::kTOffset);
+  RunRampUpTest(3, 0, RtpExtension::kTOffset, true, false);
+}
+
+TEST_F(RampUpTest, SimulcastByRedWithRtx) {
+  RunRampUpTest(3, 0, RtpExtension::kTOffset, true, true);
 }
 
 TEST_F(RampUpTest, SingleStreamWithHighStartBitrate) {
-  RunRampUpTest(false, 1, 0.9 * kSingleStreamTargetBps, RtpExtension::kTOffset);
+  RunRampUpTest(1, 0.9 * kSingleStreamTargetBps, RtpExtension::kTOffset, false,
+                false);
 }
 
-TEST_F(RampUpTest, UpDownUpOneStream) { RunRampUpDownUpTest(1, false); }
+TEST_F(RampUpTest, UpDownUpOneStream) {
+  RunRampUpDownUpTest(1, false, false);
+}
 
-TEST_F(RampUpTest, UpDownUpThreeStreams) { RunRampUpDownUpTest(3, false); }
+TEST_F(RampUpTest, UpDownUpThreeStreams) {
+  RunRampUpDownUpTest(3, false, false);
+}
 
-TEST_F(RampUpTest, UpDownUpOneStreamRtx) { RunRampUpDownUpTest(1, true); }
+TEST_F(RampUpTest, UpDownUpOneStreamRtx) {
+  RunRampUpDownUpTest(1, true, false);
+}
 
-TEST_F(RampUpTest, UpDownUpThreeStreamsRtx) { RunRampUpDownUpTest(3, true); }
+TEST_F(RampUpTest, UpDownUpThreeStreamsRtx) {
+  RunRampUpDownUpTest(3, true, false);
+}
+
+TEST_F(RampUpTest, UpDownUpOneStreamByRedRtx) {
+  RunRampUpDownUpTest(1, true, true);
+}
+
+TEST_F(RampUpTest, UpDownUpThreeStreamsByRedRtx) {
+  RunRampUpDownUpTest(3, true, true);
+}
 
 TEST_F(RampUpTest, AbsSendTimeSingleStream) {
-  RunRampUpTest(false, 1, 0, RtpExtension::kAbsSendTime);
+  RunRampUpTest(1, 0, RtpExtension::kAbsSendTime, false, false);
 }
 
 TEST_F(RampUpTest, AbsSendTimeSimulcast) {
-  RunRampUpTest(false, 3, 0, RtpExtension::kAbsSendTime);
+  RunRampUpTest(3, 0, RtpExtension::kAbsSendTime, false, false);
 }
 
 TEST_F(RampUpTest, AbsSendTimeSimulcastWithRtx) {
-  RunRampUpTest(true, 3, 0, RtpExtension::kAbsSendTime);
+  RunRampUpTest(3, 0, RtpExtension::kAbsSendTime, true, false);
+}
+
+TEST_F(RampUpTest, AbsSendTimeSimulcastByRedWithRtx) {
+  RunRampUpTest(3, 0, RtpExtension::kAbsSendTime, true, true);
 }
 
 TEST_F(RampUpTest, AbsSendTimeSingleStreamWithHighStartBitrate) {
-  RunRampUpTest(false, 1, 0.9 * kSingleStreamTargetBps,
-                RtpExtension::kAbsSendTime);
+  RunRampUpTest(1, 0.9 * kSingleStreamTargetBps, RtpExtension::kAbsSendTime,
+                false, false);
 }
 }  // namespace webrtc
diff --git a/webrtc/video/rampup_tests.h b/webrtc/video/rampup_tests.h
index e506cd4..b09de9b 100644
--- a/webrtc/video/rampup_tests.h
+++ b/webrtc/video/rampup_tests.h
@@ -148,12 +148,13 @@
 
 class RampUpTest : public test::CallTest {
  protected:
-  void RunRampUpTest(bool rtx,
-                     size_t num_streams,
+  void RunRampUpTest(size_t num_streams,
                      unsigned int start_bitrate_bps,
-                     const std::string& extension_type);
+                     const std::string& extension_type,
+                     bool rtx,
+                     bool red);
 
-  void RunRampUpDownUpTest(size_t number_of_streams, bool rtx);
+  void RunRampUpDownUpTest(size_t number_of_streams, bool rtx, bool red);
 };
 }  // namespace webrtc
 #endif  // WEBRTC_VIDEO_RAMPUP_TESTS_H_
diff --git a/webrtc/video/video_receive_stream.cc b/webrtc/video/video_receive_stream.cc
index 73f6e7c..9c4a687 100644
--- a/webrtc/video/video_receive_stream.cc
+++ b/webrtc/video/video_receive_stream.cc
@@ -92,12 +92,13 @@
   rtp_rtcp_->SetLocalSSRC(channel_, config_.rtp.local_ssrc);
   // TODO(pbos): Support multiple RTX, per video payload.
   Config::Rtp::RtxMap::const_iterator it = config_.rtp.rtx.begin();
-  if (it != config_.rtp.rtx.end()) {
+  for (; it != config_.rtp.rtx.end(); ++it) {
     assert(it->second.ssrc != 0);
     assert(it->second.payload_type != 0);
 
     rtp_rtcp_->SetRemoteSSRCType(channel_, kViEStreamTypeRtx, it->second.ssrc);
-    rtp_rtcp_->SetRtxReceivePayloadType(channel_, it->second.payload_type);
+    rtp_rtcp_->SetRtxReceivePayloadType(channel_, it->second.payload_type,
+                                        it->first);
   }
 
   rtp_rtcp_->SetRembStatus(channel_, false, config_.rtp.remb);
@@ -146,6 +147,11 @@
       LOG(LS_ERROR) << "Could not set RED codec. This shouldn't happen.";
       abort();
     }
+    if (config_.rtp.fec.rtx_payload_type != -1) {
+      rtp_rtcp_->SetRtxReceivePayloadType(channel_,
+                                          config_.rtp.fec.rtx_payload_type,
+                                          config_.rtp.fec.red_payload_type);
+    }
   }
 
   stats_proxy_.reset(
diff --git a/webrtc/video/video_send_stream.cc b/webrtc/video/video_send_stream.cc
index b8ef4d3..f531f11 100644
--- a/webrtc/video/video_send_stream.cc
+++ b/webrtc/video/video_send_stream.cc
@@ -49,7 +49,6 @@
       ss << "}, {";
   }
   ss << '}';
-
   ss << ", payload_type: " << payload_type;
   ss << '}';
   return ss.str();
@@ -173,6 +172,11 @@
           static_cast<unsigned char>(config_.rtp.fec.red_payload_type),
           static_cast<unsigned char>(config_.rtp.fec.ulpfec_payload_type));
     }
+    if (config_.rtp.fec.rtx_payload_type != -1) {
+      rtp_rtcp_->SetRtxSendPayloadType(channel_,
+                                       config_.rtp.fec.rtx_payload_type,
+                                       config_.rtp.fec.red_payload_type);
+    }
   } else {
     rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0);
   }
@@ -475,7 +479,8 @@
   }
 
   assert(config_.rtp.rtx.payload_type >= 0);
-  rtp_rtcp_->SetRtxSendPayloadType(channel_, config_.rtp.rtx.payload_type);
+  rtp_rtcp_->SetRtxSendPayloadType(channel_, config_.rtp.rtx.payload_type,
+                                   config_.encoder_settings.payload_type);
 }
 
 std::map<uint32_t, RtpState> VideoSendStream::GetRtpStates() const {
diff --git a/webrtc/video_engine/include/vie_rtp_rtcp.h b/webrtc/video_engine/include/vie_rtp_rtcp.h
index 103a196..2c53475 100644
--- a/webrtc/video_engine/include/vie_rtp_rtcp.h
+++ b/webrtc/video_engine/include/vie_rtp_rtcp.h
@@ -114,10 +114,13 @@
   // This sets a specific payload type for the RTX stream. Note that this
   // doesn't enable RTX, SetLocalSSRC must still be called to enable RTX.
   virtual int SetRtxSendPayloadType(const int video_channel,
-                                    const uint8_t payload_type) = 0;
+                                    const uint8_t payload_type,
+                                    const uint8_t associated_payload_type) = 0;
 
-  virtual int SetRtxReceivePayloadType(const int video_channel,
-                                       const uint8_t payload_type) = 0;
+  virtual int SetRtxReceivePayloadType(
+      const int video_channel,
+      const uint8_t payload_type,
+      const uint8_t associated_payload_type) = 0;
 
   // This function enables manual initialization of the sequence number. The
   // start sequence number is normally a random number.
diff --git a/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc b/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc
index 4711122..ce4b71f 100644
--- a/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc
+++ b/webrtc/video_engine/test/auto_test/source/vie_autotest_loopback.cc
@@ -40,6 +40,7 @@
 const uint32_t kSsrc = 0x01234567;
 const uint32_t kRtxSsrc = 0x01234568;
 const int kRtxPayloadType = 98;
+const int kPayloadType = 100;
 #define VCM_RED_PAYLOAD_TYPE        96
 #define VCM_ULPFEC_PAYLOAD_TYPE     97
 
@@ -244,14 +245,15 @@
       return -1;
     }
 
-    error = ptrViERtpRtcp->SetRtxSendPayloadType(videoChannel, kRtxPayloadType);
+    error = ptrViERtpRtcp->SetRtxSendPayloadType(videoChannel, kRtxPayloadType,
+                                                 kPayloadType);
     if (error == -1) {
       printf("ERROR in ViERTP_RTCP::SetRtxSendPayloadType\n");
       return -1;
     }
 
-    error = ptrViERtpRtcp->SetRtxReceivePayloadType(videoChannel,
-                                                    kRtxPayloadType);
+    error = ptrViERtpRtcp->SetRtxReceivePayloadType(
+        videoChannel, kRtxPayloadType, kPayloadType);
     if (error == -1) {
       printf("ERROR in ViERTP_RTCP::SetRtxReceivePayloadType\n");
       return -1;
diff --git a/webrtc/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc b/webrtc/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc
index 923fe41..ae63558 100644
--- a/webrtc/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc
+++ b/webrtc/video_engine/test/auto_test/source/vie_autotest_rtp_rtcp.cc
@@ -172,14 +172,15 @@
     myTransport.ClearStats();
 
     const uint8_t kRtxPayloadType = 96;
+    const uint8_t kPayloadType = 100;
     // Temporarily disable pacing.
     EXPECT_EQ(0, ViE.rtp_rtcp->SetTransmissionSmoothingStatus(
         tbChannel.videoChannel, false));
     EXPECT_EQ(0, ViE.rtp_rtcp->SetNACKStatus(tbChannel.videoChannel, true));
-    EXPECT_EQ(0, ViE.rtp_rtcp->SetRtxSendPayloadType(tbChannel.videoChannel,
-                                                     kRtxPayloadType));
-    EXPECT_EQ(0, ViE.rtp_rtcp->SetRtxReceivePayloadType(tbChannel.videoChannel,
-                                                        kRtxPayloadType));
+    EXPECT_EQ(0, ViE.rtp_rtcp->SetRtxSendPayloadType(
+                     tbChannel.videoChannel, kRtxPayloadType, kPayloadType));
+    EXPECT_EQ(0, ViE.rtp_rtcp->SetRtxReceivePayloadType(
+                     tbChannel.videoChannel, kRtxPayloadType, kPayloadType));
     EXPECT_EQ(0, ViE.rtp_rtcp->SetLocalSSRC(tbChannel.videoChannel, 1234,
                                             webrtc::kViEStreamTypeRtx, 0));
     EXPECT_EQ(0, ViE.rtp_rtcp->SetRemoteSSRCType(tbChannel.videoChannel,
diff --git a/webrtc/video_engine/vie_channel.cc b/webrtc/video_engine/vie_channel.cc
index 79ebe70..eb28bd0 100644
--- a/webrtc/video_engine/vie_channel.cc
+++ b/webrtc/video_engine/vie_channel.cc
@@ -899,11 +899,12 @@
   return 0;
 }
 
-int ViEChannel::SetRtxSendPayloadType(int payload_type) {
-  rtp_rtcp_->SetRtxSendPayloadType(payload_type);
+int ViEChannel::SetRtxSendPayloadType(int payload_type,
+                                      int associated_payload_type) {
+  rtp_rtcp_->SetRtxSendPayloadType(payload_type, associated_payload_type);
   for (std::list<RtpRtcp*>::iterator it = simulcast_rtp_rtcp_.begin();
        it != simulcast_rtp_rtcp_.end(); it++) {
-    (*it)->SetRtxSendPayloadType(payload_type);
+    (*it)->SetRtxSendPayloadType(payload_type, associated_payload_type);
   }
   SetRtxSendStatus(true);
   return 0;
@@ -920,8 +921,9 @@
   }
 }
 
-void ViEChannel::SetRtxReceivePayloadType(int payload_type) {
-  vie_receiver_.SetRtxPayloadType(payload_type);
+void ViEChannel::SetRtxReceivePayloadType(int payload_type,
+                                          int associated_payload_type) {
+  vie_receiver_.SetRtxPayloadType(payload_type, associated_payload_type);
 }
 
 int32_t ViEChannel::SetStartSequenceNumber(uint16_t sequence_number) {
diff --git a/webrtc/video_engine/vie_channel.h b/webrtc/video_engine/vie_channel.h
index 7a46d1f..0096c70 100644
--- a/webrtc/video_engine/vie_channel.h
+++ b/webrtc/video_engine/vie_channel.h
@@ -145,8 +145,8 @@
   // Gets the CSRC for the incoming stream.
   int32_t GetRemoteCSRC(uint32_t CSRCs[kRtpCsrcSize]);
 
-  int SetRtxSendPayloadType(int payload_type);
-  void SetRtxReceivePayloadType(int payload_type);
+  int SetRtxSendPayloadType(int payload_type, int associated_payload_type);
+  void SetRtxReceivePayloadType(int payload_type, int associated_payload_type);
 
   // Sets the starting sequence number, must be called before StartSend.
   int32_t SetStartSequenceNumber(uint16_t sequence_number);
diff --git a/webrtc/video_engine/vie_receiver.cc b/webrtc/video_engine/vie_receiver.cc
index 6dec985..048eecd 100644
--- a/webrtc/video_engine/vie_receiver.cc
+++ b/webrtc/video_engine/vie_receiver.cc
@@ -102,8 +102,10 @@
   rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff);
 }
 
-void ViEReceiver::SetRtxPayloadType(int payload_type) {
-  rtp_payload_registry_->SetRtxPayloadType(payload_type);
+void ViEReceiver::SetRtxPayloadType(int payload_type,
+                                    int associated_payload_type) {
+  rtp_payload_registry_->SetRtxPayloadType(payload_type,
+                                           associated_payload_type);
 }
 
 void ViEReceiver::SetRtxSsrc(uint32_t ssrc) {
diff --git a/webrtc/video_engine/vie_receiver.h b/webrtc/video_engine/vie_receiver.h
index 39a85e4..aaeba17 100644
--- a/webrtc/video_engine/vie_receiver.h
+++ b/webrtc/video_engine/vie_receiver.h
@@ -47,7 +47,7 @@
   bool RegisterPayload(const VideoCodec& video_codec);
 
   void SetNackStatus(bool enable, int max_nack_reordering_threshold);
-  void SetRtxPayloadType(int payload_type);
+  void SetRtxPayloadType(int payload_type, int associated_payload_type);
   void SetRtxSsrc(uint32_t ssrc);
   bool GetRtxSsrc(uint32_t* ssrc) const;
 
diff --git a/webrtc/video_engine/vie_rtp_rtcp_impl.cc b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
index ae21307..354e1d6 100644
--- a/webrtc/video_engine/vie_rtp_rtcp_impl.cc
+++ b/webrtc/video_engine/vie_rtp_rtcp_impl.cc
@@ -191,8 +191,10 @@
   return 0;
 }
 
-int ViERTP_RTCPImpl::SetRtxSendPayloadType(const int video_channel,
-                                           const uint8_t payload_type) {
+int ViERTP_RTCPImpl::SetRtxSendPayloadType(
+    const int video_channel,
+    const uint8_t payload_type,
+    const uint8_t associated_payload_type) {
   LOG_F(LS_INFO) << "channel: " << video_channel
                  << " payload_type: " << static_cast<int>(payload_type);
   ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
@@ -201,14 +203,17 @@
     shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
     return -1;
   }
-  if (vie_channel->SetRtxSendPayloadType(payload_type) != 0) {
+  if (vie_channel->SetRtxSendPayloadType(payload_type,
+                                         associated_payload_type) != 0) {
     return -1;
   }
   return 0;
 }
 
-int ViERTP_RTCPImpl::SetRtxReceivePayloadType(const int video_channel,
-                                              const uint8_t payload_type) {
+int ViERTP_RTCPImpl::SetRtxReceivePayloadType(
+    const int video_channel,
+    const uint8_t payload_type,
+    const uint8_t associated_payload_type) {
   LOG_F(LS_INFO) << "channel: " << video_channel
                  << " payload_type: " << static_cast<int>(payload_type);
   ViEChannelManagerScoped cs(*(shared_data_->channel_manager()));
@@ -217,7 +222,7 @@
     shared_data_->SetLastError(kViERtpRtcpInvalidChannelId);
     return -1;
   }
-  vie_channel->SetRtxReceivePayloadType(payload_type);
+  vie_channel->SetRtxReceivePayloadType(payload_type, associated_payload_type);
   return 0;
 }
 
diff --git a/webrtc/video_engine/vie_rtp_rtcp_impl.h b/webrtc/video_engine/vie_rtp_rtcp_impl.h
index 8e9f097..7374d1a 100644
--- a/webrtc/video_engine/vie_rtp_rtcp_impl.h
+++ b/webrtc/video_engine/vie_rtp_rtcp_impl.h
@@ -40,9 +40,11 @@
   virtual int GetRemoteCSRCs(const int video_channel,
                              unsigned int CSRCs[kRtpCsrcSize]) const;
   virtual int SetRtxSendPayloadType(const int video_channel,
-                                    const uint8_t payload_type);
+                                    const uint8_t payload_type,
+                                    const uint8_t associated_payload_type);
   virtual int SetRtxReceivePayloadType(const int video_channel,
-                                       const uint8_t payload_type);
+                                       const uint8_t payload_type,
+                                       const uint8_t associated_payload_type);
   virtual int SetStartSequenceNumber(const int video_channel,
                                      uint16_t sequence_number);
   virtual void SetRtpStateForSsrc(int video_channel,