Implement NACK over RTX for VideoSendStream.

BUG=2231
R=stefan@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4751 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/video_engine/internal/video_send_stream.cc b/webrtc/video_engine/internal/video_send_stream.cc
index 76d8bc3..5adf8b9 100644
--- a/webrtc/video_engine/internal/video_send_stream.cc
+++ b/webrtc/video_engine/internal/video_send_stream.cc
@@ -105,6 +105,18 @@
   }
   rtp_rtcp_->SetNACKStatus(channel_, config_.rtp.nack.rtp_history_ms > 0);
   rtp_rtcp_->SetTransmissionSmoothingStatus(channel_, config_.pacing);
+  if (!config_.rtp.rtx.ssrcs.empty()) {
+    assert(config_.rtp.rtx.ssrcs.size() == config_.rtp.ssrcs.size());
+    for (size_t i = 0; i < config_.rtp.rtx.ssrcs.size(); ++i) {
+      rtp_rtcp_->SetLocalSSRC(
+          channel_, config_.rtp.rtx.ssrcs[i], kViEStreamTypeRtx, i);
+    }
+
+    if (config_.rtp.rtx.rtx_payload_type != 0) {
+      rtp_rtcp_->SetRtxSendPayloadType(channel_,
+                                       config_.rtp.rtx.rtx_payload_type);
+    }
+  }
 
   for (size_t i = 0; i < config_.rtp.extensions.size(); ++i) {
     const std::string& extension = config_.rtp.extensions[i].name;
diff --git a/webrtc/video_engine/new_include/config.h b/webrtc/video_engine/new_include/config.h
index 5eff973..d19c8d9 100644
--- a/webrtc/video_engine/new_include/config.h
+++ b/webrtc/video_engine/new_include/config.h
@@ -12,6 +12,7 @@
 #define WEBRTC_VIDEO_ENGINE_NEW_INCLUDE_CONFIG_H_
 
 #include <string>
+#include <vector>
 
 namespace webrtc {
 
@@ -60,9 +61,9 @@
 
 // Settings for RTP retransmission payload format, see RFC 4588 for details.
 struct RtxConfig {
-  RtxConfig() : ssrc(0), rtx_payload_type(0), video_payload_type(0) {}
-  // SSRC to use for the RTX stream.
-  uint32_t ssrc;
+  RtxConfig() : rtx_payload_type(0), video_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;
diff --git a/webrtc/video_engine/test/send_stream_tests.cc b/webrtc/video_engine/test/send_stream_tests.cc
index 1753b43..879a3e4 100644
--- a/webrtc/video_engine/test/send_stream_tests.cc
+++ b/webrtc/video_engine/test/send_stream_tests.cc
@@ -47,6 +47,7 @@
 
  protected:
   static const uint32_t kSendSsrc;
+  static const uint32_t kSendRtxSsrc;
   void RunSendTest(Call* call,
                    const VideoSendStream::Config& config,
                    SendTransportObserver* observer) {
@@ -75,10 +76,13 @@
     return config;
   }
 
+  void TestNackRetransmission(uint32_t retransmit_ssrc);
+
   test::FakeEncoder fake_encoder_;
 };
 
 const uint32_t VideoSendStreamTest::kSendSsrc = 0xC0FFEE;
+const uint32_t VideoSendStreamTest::kSendRtxSsrc = 0xBADCAFE;
 
 TEST_F(VideoSendStreamTest, SendsSetSsrc) {
   class SendSsrcObserver : public SendTransportObserver {
@@ -215,15 +219,15 @@
   RunSendTest(call.get(), send_config, &observer);
 }
 
-TEST_F(VideoSendStreamTest, RespondsToNack) {
+void VideoSendStreamTest::TestNackRetransmission(uint32_t retransmit_ssrc) {
   class NackObserver : public SendTransportObserver, webrtc::Transport {
    public:
-    NackObserver()
+    NackObserver(uint32_t retransmit_ssrc)
         : SendTransportObserver(30 * 1000),
           thread_(ThreadWrapper::CreateThread(NackProcess, this)),
           send_call_receiver_(NULL),
           send_count_(0),
-          ssrc_(0),
+          retransmit_ssrc_(retransmit_ssrc),
           nacked_sequence_number_(0) {}
 
     ~NackObserver() {
@@ -247,7 +251,7 @@
       EXPECT_EQ(0, rtcp_sender.RegisterSendTransport(this));
 
       rtcp_sender.SetRTCPStatus(kRtcpNonCompound);
-      rtcp_sender.SetRemoteSSRC(ssrc_);
+      rtcp_sender.SetRemoteSSRC(kSendSsrc);
 
       RTCPSender::FeedbackState feedback_state;
       EXPECT_EQ(0, rtcp_sender.SendRTCP(
@@ -277,14 +281,23 @@
 
       // Nack second packet after receiving the third one.
       if (++send_count_ == 3) {
-        ssrc_ = header.ssrc;
         nacked_sequence_number_ = header.sequenceNumber - 1;
         unsigned int id;
         EXPECT_TRUE(thread_->Start(id));
       }
 
-      if (header.sequenceNumber == nacked_sequence_number_)
+      uint16_t sequence_number = header.sequenceNumber;
+
+      if (header.ssrc == retransmit_ssrc_ && retransmit_ssrc_ != kSendSsrc) {
+        // Not kSendSsrc, assume correct RTX packet. Extract sequence number.
+        const uint8_t* rtx_header = packet + header.headerLength;
+        sequence_number = (rtx_header[0] << 8) + rtx_header[1];
+      }
+
+      if (sequence_number == nacked_sequence_number_) {
+        EXPECT_EQ(retransmit_ssrc_, header.ssrc);
         send_test_complete_->Set();
+      }
 
       return true;
     }
@@ -292,9 +305,9 @@
     scoped_ptr<ThreadWrapper> thread_;
     PacketReceiver* send_call_receiver_;
     int send_count_;
-    uint32_t ssrc_;
+    uint32_t retransmit_ssrc_;
     uint16_t nacked_sequence_number_;
-  } observer;
+  } observer(retransmit_ssrc);
 
   Call::Config call_config(&observer);
   scoped_ptr<Call> call(Call::Create(call_config));
@@ -302,8 +315,20 @@
 
   VideoSendStream::Config send_config = GetSendTestConfig(call.get());
   send_config.rtp.nack.rtp_history_ms = 1000;
+  if (retransmit_ssrc != kSendSsrc)
+    send_config.rtp.rtx.ssrcs.push_back(retransmit_ssrc);
 
   RunSendTest(call.get(), send_config, &observer);
 }
 
+TEST_F(VideoSendStreamTest, RetransmitsNack) {
+  // Normal NACKs should use the send SSRC.
+  TestNackRetransmission(kSendSsrc);
+}
+
+TEST_F(VideoSendStreamTest, RetransmitsNackOverRtx) {
+  // NACKs over RTX should use a separate SSRC.
+  TestNackRetransmission(kSendRtxSsrc);
+}
+
 }  // namespace webrtc