Implement RTP extension support in WebRtcVideoEngine2.

BUG=1788
R=pthatcher@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@6453 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/media/webrtc/webrtcvideoengine2.cc b/talk/media/webrtc/webrtcvideoengine2.cc
index d6d1354..716c5a8 100644
--- a/talk/media/webrtc/webrtcvideoengine2.cc
+++ b/talk/media/webrtc/webrtcvideoengine2.cc
@@ -278,6 +278,13 @@
 
   video_codecs_ = DefaultVideoCodecs();
   default_codec_format_ = VideoFormat(kDefaultVideoFormat);
+
+  rtp_header_extensions_.push_back(
+      RtpHeaderExtension(kRtpTimestampOffsetHeaderExtension,
+                         kRtpTimestampOffsetHeaderExtensionDefaultId));
+  rtp_header_extensions_.push_back(
+      RtpHeaderExtension(kRtpAbsoluteSenderTimeHeaderExtension,
+                         kRtpAbsoluteSenderTimeHeaderExtensionDefaultId));
 }
 
 WebRtcVideoEngine2::~WebRtcVideoEngine2() {
@@ -774,6 +781,20 @@
   return true;
 }
 
+static std::string RtpExtensionsToString(
+    const std::vector<RtpHeaderExtension>& extensions) {
+  std::stringstream out;
+  out << '{';
+  for (size_t i = 0; i < extensions.size(); ++i) {
+    out << "{" << extensions[i].uri << ": " << extensions[i].id << "}";
+    if (i != extensions.size() - 1) {
+      out << ", ";
+    }
+  }
+  out << '}';
+  return out.str();
+}
+
 }  // namespace
 
 bool WebRtcVideoChannel2::SetRecvCodecs(const std::vector<VideoCodec>& codecs) {
@@ -967,6 +988,8 @@
     config.rtp.rtx.payload_type = codec_settings.rtx_payload_type;
   }
 
+  config.rtp.extensions = send_rtp_extensions_;
+
   if (IsNackEnabled(codec_settings.codec)) {
     config.rtp.nack.rtp_history_ms = kNackHistoryMs;
   }
@@ -1047,6 +1070,7 @@
     config.rtp.nack.rtp_history_ms = kNackHistoryMs;
   }
   config.rtp.remb = true;
+  config.rtp.extensions = recv_rtp_extensions_;
   // TODO(pbos): This protection is against setting the same local ssrc as
   // remote which is not permitted by the lower-level API. RTCP requires a
   // corresponding sender SSRC. Figure out what to do when we don't have
@@ -1280,15 +1304,31 @@
 
 bool WebRtcVideoChannel2::SetRecvRtpHeaderExtensions(
     const std::vector<RtpHeaderExtension>& extensions) {
-  // TODO(pbos): Implement.
-  LOG(LS_VERBOSE) << "SetRecvRtpHeaderExtensions()";
+  LOG(LS_INFO) << "SetRecvRtpHeaderExtensions: "
+               << RtpExtensionsToString(extensions);
+  std::vector<webrtc::RtpExtension> webrtc_extensions;
+  for (size_t i = 0; i < extensions.size(); ++i) {
+    // TODO(pbos): Make sure we don't pass unsupported extensions!
+    webrtc::RtpExtension webrtc_extension(extensions[i].uri.c_str(),
+                                          extensions[i].id);
+    webrtc_extensions.push_back(webrtc_extension);
+  }
+  recv_rtp_extensions_ = webrtc_extensions;
   return true;
 }
 
 bool WebRtcVideoChannel2::SetSendRtpHeaderExtensions(
     const std::vector<RtpHeaderExtension>& extensions) {
-  // TODO(pbos): Implement.
-  LOG(LS_VERBOSE) << "SetSendRtpHeaderExtensions()";
+  LOG(LS_INFO) << "SetSendRtpHeaderExtensions: "
+               << RtpExtensionsToString(extensions);
+  std::vector<webrtc::RtpExtension> webrtc_extensions;
+  for (size_t i = 0; i < extensions.size(); ++i) {
+    // TODO(pbos): Make sure we don't pass unsupported extensions!
+    webrtc::RtpExtension webrtc_extension(extensions[i].uri.c_str(),
+                                          extensions[i].id);
+    webrtc_extensions.push_back(webrtc_extension);
+  }
+  send_rtp_extensions_ = webrtc_extensions;
   return true;
 }
 
diff --git a/talk/media/webrtc/webrtcvideoengine2.h b/talk/media/webrtc/webrtcvideoengine2.h
index d1a784d..81466eb 100644
--- a/talk/media/webrtc/webrtcvideoengine2.h
+++ b/talk/media/webrtc/webrtcvideoengine2.h
@@ -236,6 +236,9 @@
       OVERRIDE;
   virtual void OnReadyToSend(bool ready) OVERRIDE;
   virtual bool MuteStream(uint32 ssrc, bool mute) OVERRIDE;
+
+  // Set send/receive RTP header extensions. This must be done before creating
+  // streams as it only has effect on future streams.
   virtual bool SetRecvRtpHeaderExtensions(
       const std::vector<RtpHeaderExtension>& extensions) OVERRIDE;
   virtual bool SetSendRtpHeaderExtensions(
@@ -351,8 +354,11 @@
   std::map<uint32, webrtc::VideoReceiveStream*> receive_streams_;
 
   Settable<VideoCodecSettings> send_codec_;
+  std::vector<webrtc::RtpExtension> send_rtp_extensions_;
+
   WebRtcVideoEncoderFactory2* const encoder_factory_;
   std::vector<VideoCodecSettings> recv_codecs_;
+  std::vector<webrtc::RtpExtension> recv_rtp_extensions_;
   VideoOptions options_;
 };
 
diff --git a/talk/media/webrtc/webrtcvideoengine2_unittest.cc b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
index c9ff182..6886300 100644
--- a/talk/media/webrtc/webrtcvideoengine2_unittest.cc
+++ b/talk/media/webrtc/webrtcvideoengine2_unittest.cc
@@ -396,6 +396,31 @@
   FAIL() << "No RTX codec found among default codecs.";
 }
 
+TEST_F(WebRtcVideoEngine2Test, SupportsTimestampOffsetHeaderExtension) {
+  std::vector<RtpHeaderExtension> extensions = engine_.rtp_header_extensions();
+  ASSERT_FALSE(extensions.empty());
+  for (size_t i = 0; i < extensions.size(); ++i) {
+    if (extensions[i].uri == kRtpTimestampOffsetHeaderExtension) {
+      EXPECT_EQ(kRtpTimestampOffsetHeaderExtensionDefaultId, extensions[i].id);
+      return;
+    }
+  }
+  FAIL() << "Timestamp offset extension not in header-extension list.";
+}
+
+TEST_F(WebRtcVideoEngine2Test, SupportsAbsoluteSenderTimeHeaderExtension) {
+  std::vector<RtpHeaderExtension> extensions = engine_.rtp_header_extensions();
+  ASSERT_FALSE(extensions.empty());
+  for (size_t i = 0; i < extensions.size(); ++i) {
+    if (extensions[i].uri == kRtpAbsoluteSenderTimeHeaderExtension) {
+      EXPECT_EQ(kRtpAbsoluteSenderTimeHeaderExtensionDefaultId,
+                extensions[i].id);
+      return;
+    }
+  }
+  FAIL() << "Absolute Sender Time extension not in header-extension list.";
+}
+
 class WebRtcVideoChannel2BaseTest
     : public VideoMediaChannelTest<WebRtcVideoEngine2, WebRtcVideoChannel2> {
  protected:
@@ -598,6 +623,67 @@
     EXPECT_EQ(video_codec.height, webrtc_codec.height);
     EXPECT_EQ(video_codec.framerate, webrtc_codec.maxFramerate);
   }
+
+  void TestSetSendRtpHeaderExtensions(const std::string& cricket_ext,
+                                      const std::string& webrtc_ext) {
+    // Enable extension.
+    const int id = 1;
+    std::vector<cricket::RtpHeaderExtension> extensions;
+    extensions.push_back(cricket::RtpHeaderExtension(cricket_ext, id));
+    EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions));
+
+    FakeVideoSendStream* send_stream =
+        AddSendStream(cricket::StreamParams::CreateLegacy(123));
+
+    // Verify the send extension id.
+    ASSERT_EQ(1u, send_stream->GetConfig().rtp.extensions.size());
+    EXPECT_EQ(id, send_stream->GetConfig().rtp.extensions[0].id);
+    EXPECT_EQ(webrtc_ext, send_stream->GetConfig().rtp.extensions[0].name);
+    // Verify call with same set of extensions returns true.
+    EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(extensions));
+    // Verify that SetSendRtpHeaderExtensions doesn't implicitly add them for
+    // receivers.
+    EXPECT_TRUE(AddRecvStream(cricket::StreamParams::CreateLegacy(123))
+                    ->GetConfig()
+                    .rtp.extensions.empty());
+
+    // Remove the extension id, verify that this doesn't reset extensions as
+    // they should be set before creating channels.
+    std::vector<cricket::RtpHeaderExtension> empty_extensions;
+    EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(empty_extensions));
+    EXPECT_FALSE(send_stream->GetConfig().rtp.extensions.empty());
+  }
+
+  void TestSetRecvRtpHeaderExtensions(const std::string& cricket_ext,
+                                      const std::string& webrtc_ext) {
+    // Enable extension.
+    const int id = 1;
+    std::vector<cricket::RtpHeaderExtension> extensions;
+    extensions.push_back(cricket::RtpHeaderExtension(cricket_ext, id));
+    EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions));
+
+    FakeVideoReceiveStream* recv_stream =
+        AddRecvStream(cricket::StreamParams::CreateLegacy(123));
+
+    // Verify the recv extension id.
+    ASSERT_EQ(1u, recv_stream->GetConfig().rtp.extensions.size());
+    EXPECT_EQ(id, recv_stream->GetConfig().rtp.extensions[0].id);
+    EXPECT_EQ(webrtc_ext, recv_stream->GetConfig().rtp.extensions[0].name);
+    // Verify call with same set of extensions returns true.
+    EXPECT_TRUE(channel_->SetRecvRtpHeaderExtensions(extensions));
+    // Verify that SetRecvRtpHeaderExtensions doesn't implicitly add them for
+    // senders.
+    EXPECT_TRUE(AddSendStream(cricket::StreamParams::CreateLegacy(123))
+                    ->GetConfig()
+                    .rtp.extensions.empty());
+
+    // Remove the extension id, verify that this doesn't reset extensions as
+    // they should be set before creating channels.
+    std::vector<cricket::RtpHeaderExtension> empty_extensions;
+    EXPECT_TRUE(channel_->SetSendRtpHeaderExtensions(empty_extensions));
+    EXPECT_FALSE(recv_stream->GetConfig().rtp.extensions.empty());
+  }
+
   talk_base::scoped_ptr<VideoMediaChannel> channel_;
   FakeWebRtcVideoChannel2* fake_channel_;
   uint32 last_ssrc_;
@@ -723,12 +809,34 @@
   ASSERT_TRUE(recv_stream->GetConfig().rtp.rtx.empty());
 }
 
-TEST_F(WebRtcVideoChannel2Test, DISABLED_RtpTimestampOffsetHeaderExtensions) {
-  FAIL() << "Not implemented.";  // TODO(pbos): Implement.
+TEST_F(WebRtcVideoChannel2Test, NoHeaderExtesionsByDefault) {
+  FakeVideoSendStream* send_stream =
+      AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcs1[0]));
+  ASSERT_TRUE(send_stream->GetConfig().rtp.extensions.empty());
+
+  FakeVideoReceiveStream* recv_stream =
+      AddRecvStream(cricket::StreamParams::CreateLegacy(kSsrcs1[0]));
+  ASSERT_TRUE(recv_stream->GetConfig().rtp.extensions.empty());
 }
 
-TEST_F(WebRtcVideoChannel2Test, DISABLED_AbsoluteSendTimeHeaderExtensions) {
-  FAIL() << "Not implemented.";  // TODO(pbos): Implement.
+// Test support for RTP timestamp offset header extension.
+TEST_F(WebRtcVideoChannel2Test, SendRtpTimestampOffsetHeaderExtensions) {
+  TestSetSendRtpHeaderExtensions(kRtpTimestampOffsetHeaderExtension,
+                                 webrtc::RtpExtension::kTOffset);
+}
+TEST_F(WebRtcVideoChannel2Test, RecvRtpTimestampOffsetHeaderExtensions) {
+  TestSetRecvRtpHeaderExtensions(kRtpTimestampOffsetHeaderExtension,
+                                 webrtc::RtpExtension::kTOffset);
+}
+
+// Test support for absolute send time header extension.
+TEST_F(WebRtcVideoChannel2Test, SendAbsoluteSendTimeHeaderExtensions) {
+  TestSetSendRtpHeaderExtensions(kRtpAbsoluteSenderTimeHeaderExtension,
+                                 webrtc::RtpExtension::kAbsSendTime);
+}
+TEST_F(WebRtcVideoChannel2Test, RecvAbsoluteSendTimeHeaderExtensions) {
+  TestSetRecvRtpHeaderExtensions(kRtpAbsoluteSenderTimeHeaderExtension,
+                                 webrtc::RtpExtension::kAbsSendTime);
 }
 
 TEST_F(WebRtcVideoChannel2Test, DISABLED_LeakyBucketTest) {