Wire up RTX in VideoReceiveStream.

Also adds a test to make sure that a retransmitted frame is actually
received and decoded on the remote side. The previous NACK test checked
retransmission, but not that the receiver actually takes care of the
retransmitted packet.

BUG=2399
R=mflodman@webrtc.org, stefan@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5422 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/config.h b/webrtc/config.h
index 62f9fc8..9ff8a55 100644
--- a/webrtc/config.h
+++ b/webrtc/config.h
@@ -71,16 +71,6 @@
   int red_payload_type;
 };
 
-// Settings for RTP retransmission payload format, see RFC 4588 for details.
-struct RtxConfig {
-  RtxConfig() : rtx_payload_type(0) {}
-  // SSRCs to use for the RTX streams.
-  std::vector<uint32_t> ssrcs;
-
-  // Payload type to use for the RTX stream.
-  int rtx_payload_type;
-};
-
 // RTP header extension to use for the video stream, see RFC 5285.
 struct RtpExtension {
   static const char* kTOffset;
diff --git a/webrtc/test/rtp_rtcp_observer.h b/webrtc/test/rtp_rtcp_observer.h
index 3b4ad7b..5ed9a3f 100644
--- a/webrtc/test/rtp_rtcp_observer.h
+++ b/webrtc/test/rtp_rtcp_observer.h
@@ -13,6 +13,8 @@
 #include <map>
 #include <vector>
 
+#include "testing/gtest/include/gtest/gtest.h"
+
 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 #include "webrtc/typedefs.h"
 #include "webrtc/video_send_stream.h"
@@ -123,6 +125,7 @@
 
   private:
     virtual bool SendRtp(const uint8_t* packet, size_t length) OVERRIDE {
+      EXPECT_FALSE(RtpHeaderParser::IsRtcp(packet, static_cast<int>(length)));
       Action action;
       {
         CriticalSectionScoped crit_(lock_);
@@ -139,6 +142,7 @@
     }
 
     virtual bool SendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
+      EXPECT_TRUE(RtpHeaderParser::IsRtcp(packet, static_cast<int>(length)));
       Action action;
       {
         CriticalSectionScoped crit_(lock_);
diff --git a/webrtc/video/call.cc b/webrtc/video/call.cc
index 12daa8e..baa2d40 100644
--- a/webrtc/video/call.cc
+++ b/webrtc/video/call.cc
@@ -170,8 +170,9 @@
 Call* Call::Create(const Call::Config& config) {
   CreateTraceDispatcher();
 
-  VideoEngine* video_engine = config.webrtc_config != NULL ?
-      VideoEngine::Create(*config.webrtc_config) : VideoEngine::Create();
+  VideoEngine* video_engine = config.webrtc_config != NULL
+                                  ? VideoEngine::Create(*config.webrtc_config)
+                                  : VideoEngine::Create();
   assert(video_engine != NULL);
 
   return new internal::Call(video_engine, config);
@@ -294,6 +295,12 @@
   WriteLockScoped write_lock(*receive_lock_);
   assert(receive_ssrcs_.find(config.rtp.remote_ssrc) == receive_ssrcs_.end());
   receive_ssrcs_[config.rtp.remote_ssrc] = receive_stream;
+  // TODO(pbos): Configure different RTX payloads per receive payload.
+  VideoReceiveStream::Config::Rtp::RtxMap::const_iterator it =
+      config.rtp.rtx.begin();
+  if (it != config.rtp.rtx.end())
+    receive_ssrcs_[it->second.ssrc] = receive_stream;
+
   return receive_stream;
 }
 
@@ -304,14 +311,16 @@
   VideoReceiveStream* receive_stream_impl = NULL;
   {
     WriteLockScoped write_lock(*receive_lock_);
-    for (std::map<uint32_t, VideoReceiveStream*>::iterator it =
-             receive_ssrcs_.begin();
-         it != receive_ssrcs_.end();
-         ++it) {
+    // Remove all ssrcs pointing to a receive stream. As RTX retransmits on a
+    // separate SSRC there can be either one or two.
+    std::map<uint32_t, VideoReceiveStream*>::iterator it =
+        receive_ssrcs_.begin();
+    while (it != receive_ssrcs_.end()) {
       if (it->second == static_cast<VideoReceiveStream*>(receive_stream)) {
         receive_stream_impl = it->second;
-        receive_ssrcs_.erase(it);
-        break;
+        receive_ssrcs_.erase(it++);
+      } else {
+        ++it;
       }
     }
   }
diff --git a/webrtc/video/call_tests.cc b/webrtc/video/call_tests.cc
index 3ec66fd..bbd8ad0 100644
--- a/webrtc/video/call_tests.cc
+++ b/webrtc/video/call_tests.cc
@@ -18,7 +18,6 @@
 
 #include "webrtc/call.h"
 #include "webrtc/frame_callback.h"
-#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/event_wrapper.h"
@@ -41,8 +40,10 @@
 static unsigned int kDefaultTimeoutMs = 30 * 1000;
 static unsigned int kLongTimeoutMs = 120 * 1000;
 static const uint32_t kSendSsrc = 0x654321;
+static const uint32_t kSendRtxSsrc = 0x424242;
 static const uint32_t kReceiverLocalSsrc = 0x123456;
 static const uint8_t kSendPayloadType = 125;
+static const uint8_t kSendRtxPayloadType = 126;
 
 class CallTest : public ::testing::Test {
  public:
@@ -125,6 +126,7 @@
     receive_stream_ = NULL;
   }
 
+  void DecodesRetransmittedFrame(bool retransmit_over_rtx);
   void ReceivesPliAndRecovers(int rtp_history_ms);
   void RespectsRtcpMode(newapi::RtcpMode rtcp_mode);
   void TestXrReceiverReferenceTimeReport(bool enable_rrtr);
@@ -159,8 +161,6 @@
 
  private:
   virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
-    EXPECT_FALSE(RtpHeaderParser::IsRtcp(packet, static_cast<int>(length)));
-
     RTPHeader header;
     EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length), &header));
 
@@ -328,7 +328,7 @@
     EventTypeWrapper Wait() { return event_->Wait(kDefaultTimeoutMs); }
 
    private:
-    virtual void FrameCallback(I420VideoFrame* frame) {
+    virtual void FrameCallback(I420VideoFrame* frame) OVERRIDE {
       SleepMs(kDelayRenderCallbackMs);
       event_->Set();
     }
@@ -485,6 +485,99 @@
   DestroyStreams();
 }
 
+// This test drops second RTP packet with a marker bit set, makes sure it's
+// retransmitted and renders. Retransmission SSRCs are also checked.
+void CallTest::DecodesRetransmittedFrame(bool retransmit_over_rtx) {
+  static const int kDroppedFrameNumber = 2;
+  class RetransmissionObserver : public test::RtpRtcpObserver,
+                                 public I420FrameCallback {
+   public:
+    RetransmissionObserver(bool expect_rtx)
+        : RtpRtcpObserver(kDefaultTimeoutMs),
+          retransmission_ssrc_(expect_rtx ? kSendRtxSsrc : kSendSsrc),
+          retransmission_payload_type_(expect_rtx ? kSendRtxPayloadType
+                                                  : kSendPayloadType),
+          marker_bits_observed_(0),
+          retransmitted_timestamp_(0),
+          frame_retransmitted_(false) {}
+
+   private:
+    virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
+      RTPHeader header;
+      EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
+
+      if (header.timestamp == retransmitted_timestamp_) {
+        EXPECT_EQ(retransmission_ssrc_, header.ssrc);
+        EXPECT_EQ(retransmission_payload_type_, header.payloadType);
+        frame_retransmitted_ = true;
+        return SEND_PACKET;
+      }
+
+      EXPECT_EQ(kSendSsrc, header.ssrc);
+      EXPECT_EQ(kSendPayloadType, header.payloadType);
+
+      // Found the second frame's final packet, drop this and expect a
+      // retransmission.
+      if (header.markerBit && ++marker_bits_observed_ == kDroppedFrameNumber) {
+        retransmitted_timestamp_ = header.timestamp;
+        return DROP_PACKET;
+      }
+
+      return SEND_PACKET;
+    }
+
+    virtual void FrameCallback(I420VideoFrame* frame) OVERRIDE {
+      CriticalSectionScoped crit_(lock_.get());
+      if (frame->timestamp() == retransmitted_timestamp_) {
+        EXPECT_TRUE(frame_retransmitted_);
+        observation_complete_->Set();
+      }
+    }
+
+    const uint32_t retransmission_ssrc_;
+    const int retransmission_payload_type_;
+    int marker_bits_observed_;
+    uint32_t retransmitted_timestamp_;
+    bool frame_retransmitted_;
+  } observer(retransmit_over_rtx);
+
+  CreateCalls(Call::Config(observer.SendTransport()),
+              Call::Config(observer.ReceiveTransport()));
+
+  observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver());
+
+  CreateTestConfigs();
+  send_config_.rtp.nack.rtp_history_ms =
+      receive_config_.rtp.nack.rtp_history_ms = 1000;
+  if (retransmit_over_rtx) {
+    send_config_.rtp.rtx.ssrcs.push_back(kSendRtxSsrc);
+    send_config_.rtp.rtx.payload_type = kSendRtxPayloadType;
+    int payload_type = send_config_.codec.plType;
+    receive_config_.rtp.rtx[payload_type].ssrc = kSendRtxSsrc;
+    receive_config_.rtp.rtx[payload_type].payload_type = kSendRtxPayloadType;
+  }
+  receive_config_.pre_render_callback = &observer;
+
+  CreateStreams();
+  CreateFrameGenerator();
+  StartSending();
+
+  EXPECT_EQ(kEventSignaled, observer.Wait())
+      << "Timed out while waiting for retransmission to render.";
+
+  StopSending();
+  observer.StopSending();
+  DestroyStreams();
+}
+
+TEST_F(CallTest, DecodesRetransmittedFrame) {
+  DecodesRetransmittedFrame(false);
+}
+
+TEST_F(CallTest, DecodesRetransmittedFrameOverRtx) {
+  DecodesRetransmittedFrame(true);
+}
+
 TEST_F(CallTest, UsesFrameCallbacks) {
   static const int kWidth = 320;
   static const int kHeight = 240;
@@ -588,7 +681,6 @@
  public:
   explicit PliObserver(bool nack_enabled)
       : test::RtpRtcpObserver(kLongTimeoutMs),
-        rtp_header_parser_(RtpHeaderParser::Create()),
         nack_enabled_(nack_enabled),
         highest_dropped_timestamp_(0),
         frames_to_drop_(0),
@@ -596,8 +688,7 @@
 
   virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
     RTPHeader header;
-    EXPECT_TRUE(
-        rtp_header_parser_->Parse(packet, static_cast<int>(length), &header));
+    EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
 
     // Drop all retransmitted packets to force a PLI.
     if (header.timestamp <= highest_dropped_timestamp_)
@@ -643,7 +734,6 @@
  private:
   static const int kPacketsToDrop = 1;
 
-  scoped_ptr<RtpHeaderParser> rtp_header_parser_;
   bool nack_enabled_;
   uint32_t highest_dropped_timestamp_;
   int frames_to_drop_;
@@ -933,9 +1023,10 @@
 TEST_F(CallTest, ObserversEncodedFrames) {
   class EncodedFrameTestObserver : public EncodedFrameObserver {
    public:
-    EncodedFrameTestObserver() : length_(0),
-                                 frame_type_(kFrameEmpty),
-                                 called_(EventWrapper::Create()) {}
+    EncodedFrameTestObserver()
+        : length_(0),
+          frame_type_(kFrameEmpty),
+          called_(EventWrapper::Create()) {}
     virtual ~EncodedFrameTestObserver() {}
 
     virtual void EncodedFrameCallback(const EncodedFrame& encoded_frame) {
@@ -946,9 +1037,7 @@
       called_->Set();
     }
 
-    EventTypeWrapper Wait() {
-      return called_->Wait(kDefaultTimeoutMs);
-    }
+    EventTypeWrapper Wait() { return called_->Wait(kDefaultTimeoutMs); }
 
     void ExpectEqualFrames(const EncodedFrameTestObserver& observer) {
       ASSERT_EQ(length_, observer.length_)
@@ -1064,6 +1153,7 @@
           sent_rtcp_rr_(0),
           sent_rtcp_rrtr_(0),
           sent_rtcp_dlrr_(0) {}
+
    private:
     // Receive stream should send RR packets (and RRTR packets if enabled).
     virtual Action OnReceiveRtcp(const uint8_t* packet,
@@ -1075,8 +1165,8 @@
       while (packet_type != RTCPUtility::kRtcpNotValidCode) {
         if (packet_type == RTCPUtility::kRtcpRrCode) {
           ++sent_rtcp_rr_;
-        } else if (
-            packet_type == RTCPUtility::kRtcpXrReceiverReferenceTimeCode) {
+        } else if (packet_type ==
+                   RTCPUtility::kRtcpXrReceiverReferenceTimeCode) {
           ++sent_rtcp_rrtr_;
         }
         EXPECT_NE(packet_type, RTCPUtility::kRtcpSrCode);
@@ -1122,8 +1212,7 @@
 
   CreateCalls(Call::Config(observer.SendTransport()),
               Call::Config(observer.ReceiveTransport()));
-  observer.SetReceivers(receiver_call_->Receiver(),
-                        sender_call_->Receiver());
+  observer.SetReceivers(receiver_call_->Receiver(), sender_call_->Receiver());
 
   CreateTestConfigs();
   receive_config_.rtp.rtcp_mode = newapi::kRtcpReducedSize;
diff --git a/webrtc/video/rampup_tests.cc b/webrtc/video/rampup_tests.cc
index 0386bd0..7486357 100644
--- a/webrtc/video/rampup_tests.cc
+++ b/webrtc/video/rampup_tests.cc
@@ -36,8 +36,8 @@
 namespace webrtc {
 
 namespace {
-  static const int kAbsoluteSendTimeExtensionId = 7;
-  static const int kMaxPacketSize = 1500;
+static const int kAbsoluteSendTimeExtensionId = 7;
+static const int kMaxPacketSize = 1500;
 }
 
 class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
@@ -53,8 +53,9 @@
         rtp_parser_(RtpHeaderParser::Create()),
         feedback_transport_(feedback_transport),
         receive_stats_(ReceiveStatistics::Create(clock)),
-        payload_registry_(new RTPPayloadRegistry(
-            -1, RTPPayloadStrategy::CreateStrategy(false))),
+        payload_registry_(
+            new RTPPayloadRegistry(-1,
+                                   RTPPayloadStrategy::CreateStrategy(false))),
         clock_(clock),
         num_expected_ssrcs_(num_expected_ssrcs),
         rtx_media_ssrcs_(rtx_media_ssrcs),
@@ -88,19 +89,39 @@
     if (ssrcs.size() == num_expected_ssrcs_ && bitrate >= kExpectedBitrateBps) {
       if (rtx_media_ssrcs_.empty() || rtx_media_sent_ > 0) {
         const ::testing::TestInfo* const test_info =
-          ::testing::UnitTest::GetInstance()->current_test_info();
-        webrtc::test::PrintResult("total-sent", "", test_info->name(),
-                                  total_sent_, "bytes", false);
-        webrtc::test::PrintResult("padding-sent", "", test_info->name(),
-                                  padding_sent_, "bytes", false);
-        webrtc::test::PrintResult("rtx-media-sent", "", test_info->name(),
-                                  rtx_media_sent_, "bytes", false);
-        webrtc::test::PrintResult("total-packets-sent", "", test_info->name(),
-                                  total_packets_sent_, "packets", false);
-        webrtc::test::PrintResult("padding-packets-sent", "", test_info->name(),
-                                  padding_packets_sent_, "packets", false);
-        webrtc::test::PrintResult("rtx-packets-sent", "", test_info->name(),
-                                  rtx_media_packets_sent_, "packets", false);
+            ::testing::UnitTest::GetInstance()->current_test_info();
+        webrtc::test::PrintResult(
+            "total-sent", "", test_info->name(), total_sent_, "bytes", false);
+        webrtc::test::PrintResult("padding-sent",
+                                  "",
+                                  test_info->name(),
+                                  padding_sent_,
+                                  "bytes",
+                                  false);
+        webrtc::test::PrintResult("rtx-media-sent",
+                                  "",
+                                  test_info->name(),
+                                  rtx_media_sent_,
+                                  "bytes",
+                                  false);
+        webrtc::test::PrintResult("total-packets-sent",
+                                  "",
+                                  test_info->name(),
+                                  total_packets_sent_,
+                                  "packets",
+                                  false);
+        webrtc::test::PrintResult("padding-packets-sent",
+                                  "",
+                                  test_info->name(),
+                                  padding_packets_sent_,
+                                  "packets",
+                                  false);
+        webrtc::test::PrintResult("rtx-packets-sent",
+                                  "",
+                                  test_info->name(),
+                                  rtx_media_packets_sent_,
+                                  "packets",
+                                  false);
         all_ssrcs_sent_->Set();
       }
     }
@@ -132,13 +153,14 @@
       uint8_t restored_packet[kMaxPacketSize];
       uint8_t* restored_packet_ptr = restored_packet;
       int restored_length = static_cast<int>(length);
-      payload_registry_->RestoreOriginalPacket(
-              &restored_packet_ptr, packet, &restored_length,
-              rtx_media_ssrcs_[header.ssrc],
-              header);
+      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));
+      EXPECT_TRUE(rtp_parser_->Parse(
+          restored_packet, static_cast<int>(length), &header));
     } else {
       rtp_rtcp_->SetRemoteSSRC(header.ssrc);
     }
@@ -191,9 +213,10 @@
     }
     test::DirectTransport receiver_transport;
     int num_expected_ssrcs = kNumberOfStreams + (rtx ? 1 : 0);
-    StreamObserver stream_observer(
-        num_expected_ssrcs, rtx_ssrc_map, &receiver_transport,
-        Clock::GetRealTimeClock());
+    StreamObserver stream_observer(num_expected_ssrcs,
+                                   rtx_ssrc_map,
+                                   &receiver_transport,
+                                   Clock::GetRealTimeClock());
 
     Call::Config call_config(&stream_observer);
     webrtc::Config webrtc_config;
@@ -211,10 +234,10 @@
     send_config.codec.plType = 125;
     send_config.pacing = pacing;
     send_config.rtp.nack.rtp_history_ms = 1000;
-    send_config.rtp.ssrcs.insert(send_config.rtp.ssrcs.begin(), ssrcs.begin(),
-                                 ssrcs.end());
+    send_config.rtp.ssrcs.insert(
+        send_config.rtp.ssrcs.begin(), ssrcs.begin(), ssrcs.end());
     if (rtx) {
-      send_config.rtp.rtx.rtx_payload_type = 96;
+      send_config.rtp.rtx.payload_type = 96;
       send_config.rtp.rtx.ssrcs.insert(send_config.rtp.rtx.ssrcs.begin(),
                                        kRtxSsrcs,
                                        kRtxSsrcs + kNumberOfStreams);
@@ -244,16 +267,10 @@
   std::map<uint32_t, bool> reserved_ssrcs_;
 };
 
-TEST_F(RampUpTest, WithoutPacing) {
-  RunRampUpTest(false, false);
-}
+TEST_F(RampUpTest, WithoutPacing) { RunRampUpTest(false, false); }
 
-TEST_F(RampUpTest, WithPacing) {
-  RunRampUpTest(true, false);
-}
+TEST_F(RampUpTest, WithPacing) { RunRampUpTest(true, false); }
 
-TEST_F(RampUpTest, WithPacingAndRtx) {
-  RunRampUpTest(true, true);
-}
+TEST_F(RampUpTest, WithPacingAndRtx) { RunRampUpTest(true, true); }
 
 }  // namespace webrtc
diff --git a/webrtc/video/video_receive_stream.cc b/webrtc/video/video_receive_stream.cc
index 71fc152..38947c4 100644
--- a/webrtc/video/video_receive_stream.cc
+++ b/webrtc/video/video_receive_stream.cc
@@ -59,10 +59,21 @@
   }
 
   assert(config_.rtp.remote_ssrc != 0);
+  // TODO(pbos): What's an appropriate local_ssrc for receive-only streams?
   assert(config_.rtp.local_ssrc != 0);
   assert(config_.rtp.remote_ssrc != config_.rtp.local_ssrc);
 
   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()) {
+    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_->SetRembStatus(channel_, false, config_.rtp.remb);
 
   for (size_t i = 0; i < config_.rtp.extensions.size(); ++i) {
@@ -102,8 +113,7 @@
             decoder->payload_type,
             decoder->decoder,
             decoder->renderer,
-            decoder->expected_delay_ms) !=
-        0) {
+            decoder->expected_delay_ms) != 0) {
       // TODO(pbos): Abort gracefully? Can this be a runtime error?
       abort();
     }
@@ -182,8 +192,7 @@
 
 bool VideoReceiveStream::DeliverRtp(const uint8_t* packet, size_t length) {
   return network_->ReceivedRTPPacket(
-             channel_, packet, static_cast<int>(length),
-             PacketTime()) == 0;
+             channel_, packet, static_cast<int>(length), PacketTime()) == 0;
 }
 
 int32_t VideoReceiveStream::RenderFrame(const uint32_t stream_id,
diff --git a/webrtc/video/video_send_stream.cc b/webrtc/video/video_send_stream.cc
index 3f0e593..a9fd50d 100644
--- a/webrtc/video/video_send_stream.cc
+++ b/webrtc/video/video_send_stream.cc
@@ -156,9 +156,11 @@
 
   if (config.encoder) {
     external_codec_ = ViEExternalCodec::GetInterface(video_engine);
-    if (external_codec_->RegisterExternalSendCodec(
-        channel_, config.codec.plType, config.encoder,
-        config.internal_source) != 0) {
+    if (external_codec_->RegisterExternalSendCodec(channel_,
+                                                   config.codec.plType,
+                                                   config.encoder,
+                                                   config.internal_source) !=
+        0) {
       abort();
     }
   }
@@ -168,9 +170,8 @@
     abort();
 
   if (overuse_detection) {
-    overuse_observer_.reset(
-        new ResolutionAdaptor(codec_, channel_, config_.codec.width,
-                              config_.codec.height));
+    overuse_observer_.reset(new ResolutionAdaptor(
+        codec_, channel_, config_.codec.width, config_.codec.height));
     video_engine_base_->RegisterCpuOveruseObserver(channel_,
                                                    overuse_observer_.get());
   }
@@ -187,8 +188,7 @@
     codec_->SuspendBelowMinBitrate(channel_);
   }
 
-  stats_proxy_.reset(
-      new SendStatisticsProxy(config, this));
+  stats_proxy_.reset(new SendStatisticsProxy(config, this));
 
   rtp_rtcp_->RegisterSendChannelRtcpStatisticsCallback(channel_,
                                                        stats_proxy_.get());
@@ -282,7 +282,9 @@
                             static_cast<unsigned char>(i));
   }
 
-  config_.codec = codec;
+  if (&config_.codec != &codec)
+    config_.codec = codec;
+
   if (config_.rtp.rtx.ssrcs.empty())
     return true;
 
@@ -295,10 +297,8 @@
                             static_cast<unsigned char>(i));
   }
 
-  if (config_.rtp.rtx.rtx_payload_type != 0) {
-    rtp_rtcp_->SetRtxSendPayloadType(channel_,
-                                     config_.rtp.rtx.rtx_payload_type);
-  }
+  if (config_.rtp.rtx.payload_type != 0)
+    rtp_rtcp_->SetRtxSendPayloadType(channel_, config_.rtp.rtx.payload_type);
 
   return true;
 }
diff --git a/webrtc/video/video_send_stream_tests.cc b/webrtc/video/video_send_stream_tests.cc
index b69fbe2..3eb4f90 100644
--- a/webrtc/video/video_send_stream_tests.cc
+++ b/webrtc/video/video_send_stream_tests.cc
@@ -93,8 +93,8 @@
 const uint8_t VideoSendStreamTest::kFakeSendPayloadType = 125;
 const uint8_t VideoSendStreamTest::kSendRtxPayloadType = 98;
 const uint32_t VideoSendStreamTest::kSendRtxSsrc = 0xBADCAFE;
-const uint32_t VideoSendStreamTest::kSendSsrcs[kNumSendSsrcs] = { 0xC0FFED,
-    0xC0FFEE, 0xC0FFEF };
+const uint32_t VideoSendStreamTest::kSendSsrcs[kNumSendSsrcs] = {
+    0xC0FFED, 0xC0FFEE, 0xC0FFEF};
 const uint32_t VideoSendStreamTest::kSendSsrc =
     VideoSendStreamTest::kSendSsrcs[0];
 
@@ -121,10 +121,10 @@
       //             to fail on TSan as the codec gets set before the SSRCs are
       //             set up and some frames are sent on a random-generated SSRC
       //             before the correct SSRC gets set.
-      //EXPECT_TRUE(valid_ssrcs_[header.ssrc])
+      // EXPECT_TRUE(valid_ssrcs_[header.ssrc])
       //    << "Received unknown SSRC: " << header.ssrc;
       //
-      //if (!valid_ssrcs_[header.ssrc])
+      // if (!valid_ssrcs_[header.ssrc])
       //  observation_complete_->Set();
 
       if (!is_observed_[header.ssrc]) {
@@ -271,8 +271,7 @@
 
     virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       RTPHeader header;
-      EXPECT_TRUE(
-          parser_->Parse(packet, static_cast<int>(length), &header));
+      EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
 
       EXPECT_FALSE(header.extension.hasTransmissionTimeOffset);
       EXPECT_TRUE(header.extension.hasAbsoluteSendTime);
@@ -299,10 +298,10 @@
   class DelayedEncoder : public test::FakeEncoder {
    public:
     explicit DelayedEncoder(Clock* clock) : test::FakeEncoder(clock) {}
-    virtual int32_t Encode(
-        const I420VideoFrame& input_image,
-        const CodecSpecificInfo* codec_specific_info,
-        const std::vector<VideoFrameType>* frame_types) OVERRIDE {
+    virtual int32_t Encode(const I420VideoFrame& input_image,
+                           const CodecSpecificInfo* codec_specific_info,
+                           const std::vector<VideoFrameType>* frame_types)
+        OVERRIDE {
       // A delay needs to be introduced to assure that we get a timestamp
       // offset.
       SleepMs(5);
@@ -319,8 +318,7 @@
 
     virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       RTPHeader header;
-      EXPECT_TRUE(
-          parser_->Parse(packet, static_cast<int>(length), &header));
+      EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
 
       EXPECT_TRUE(header.extension.hasTransmissionTimeOffset);
       EXPECT_FALSE(header.extension.hasAbsoluteSendTime);
@@ -439,8 +437,7 @@
 
     virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       RTPHeader header;
-      EXPECT_TRUE(
-          parser_->Parse(packet, static_cast<int>(length), &header));
+      EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
 
       // Send lossy receive reports to trigger FEC enabling.
       if (send_count_++ % 2 != 0) {
@@ -511,8 +508,7 @@
 
     virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       RTPHeader header;
-      EXPECT_TRUE(
-          parser_->Parse(packet, static_cast<int>(length), &header));
+      EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
 
       // Nack second packet after receiving the third one.
       if (++send_count_ == 3) {
@@ -564,7 +560,7 @@
 
   VideoSendStream::Config send_config = GetSendTestConfig(call.get(), 1);
   send_config.rtp.nack.rtp_history_ms = 1000;
-  send_config.rtp.rtx.rtx_payload_type = retransmit_payload_type;
+  send_config.rtp.rtx.payload_type = retransmit_payload_type;
   send_config.pacing = enable_pacing;
   if (retransmit_ssrc != kSendSsrc)
     send_config.rtp.rtx.ssrcs.push_back(retransmit_ssrc);
@@ -611,8 +607,7 @@
 
     virtual Action OnSendRtp(const uint8_t* packet, size_t length) OVERRIDE {
       RTPHeader header;
-      EXPECT_TRUE(
-          parser_->Parse(packet, static_cast<int>(length), &header));
+      EXPECT_TRUE(parser_->Parse(packet, static_cast<int>(length), &header));
 
       EXPECT_LE(length, max_packet_size_);
 
@@ -721,9 +716,7 @@
       return kEventSignaled;
     }
 
-    void SetSecondCodec(const VideoCodec& codec) {
-      second_codec_ = codec;
-    }
+    void SetSecondCodec(const VideoCodec& codec) { second_codec_ = codec; }
 
    private:
     scoped_ptr<EventWrapper> received_first_payload_;
@@ -913,8 +906,9 @@
     virtual Action OnSendRtcp(const uint8_t* packet, size_t length) OVERRIDE {
       CriticalSectionScoped lock(crit_sect_.get());
       const int kVideoMutedThresholdMs = 10000;
-      if (last_packet_time_ms_ > 0 && clock_->TimeInMilliseconds() -
-          last_packet_time_ms_ > kVideoMutedThresholdMs)
+      if (last_packet_time_ms_ > 0 &&
+          clock_->TimeInMilliseconds() - last_packet_time_ms_ >
+              kVideoMutedThresholdMs)
         observation_complete_->Set();
       // Receive statistics reporting having lost 50% of the packets.
       FakeReceiveStatistics receive_stats(kSendSsrcs[0], 1, 1, 0);
diff --git a/webrtc/video_receive_stream.h b/webrtc/video_receive_stream.h
index ac1afc6..ae1b112 100644
--- a/webrtc/video_receive_stream.h
+++ b/webrtc/video_receive_stream.h
@@ -26,10 +26,7 @@
 namespace newapi {
 // RTCP mode to use. Compound mode is described by RFC 4585 and reduced-size
 // RTCP mode is described by RFC 5506.
-enum RtcpMode {
-  kRtcpCompound,
-  kRtcpReducedSize
-};
+enum RtcpMode { kRtcpCompound, kRtcpReducedSize };
 }  // namespace newapi
 
 class VideoDecoder;
@@ -138,9 +135,21 @@
       // See FecConfig for description.
       FecConfig fec;
 
-      // RTX settings for video payloads that may be received. RTX is disabled
-      // if there's no config present.
-      std::map<int, RtxConfig> rtx;
+      // RTX settings for incoming video payloads that may be received. RTX is
+      // disabled if there's no config present.
+      struct Rtx {
+        Rtx() : ssrc(0), payload_type(0) {}
+
+        // SSRCs to use for the RTX streams.
+        uint32_t ssrc;
+
+        // Payload type to use for the RTX stream.
+        int payload_type;
+      };
+
+      // Map from video RTP payload type -> RTX config.
+      typedef std::map<int, Rtx> RtxMap;
+      RtxMap rtx;
 
       // RTP header extensions used for the received stream.
       std::vector<RtpExtension> extensions;
diff --git a/webrtc/video_send_stream.h b/webrtc/video_send_stream.h
index e59ef60..d1c1726 100644
--- a/webrtc/video_send_stream.h
+++ b/webrtc/video_send_stream.h
@@ -96,8 +96,16 @@
       // See FecConfig for description.
       FecConfig fec;
 
-      // See RtxConfig for description.
-      RtxConfig rtx;
+      // Settings for RTP retransmission payload format, see RFC 4588 for
+      // details.
+      struct Rtx {
+        Rtx() : payload_type(0) {}
+        // SSRCs to use for the RTX streams.
+        std::vector<uint32_t> ssrcs;
+
+        // Payload type to use for the RTX stream.
+        int payload_type;
+      } rtx;
 
       // RTCP CNAME, see RFC 3550.
       std::string c_name;