Allow sending abs-send-time for audio streams.

Bug: webrtc:10742
Change-Id: I088c8221e04e84152cfce925051bf6bc23d5fe68
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/149061
Reviewed-by: Henrik Andreassson <henrika@webrtc.org>
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28861}
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index b27e29c..4ee5109 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -207,6 +207,8 @@
   for (const auto& extension : extensions) {
     if (extension.uri == RtpExtension::kAudioLevelUri) {
       ids.audio_level = extension.id;
+    } else if (extension.uri == RtpExtension::kAbsSendTimeUri) {
+      ids.abs_send_time = extension.id;
     } else if (extension.uri == RtpExtension::kTransportSequenceNumberUri) {
       ids.transport_sequence_number = extension.id;
     } else if (extension.uri == RtpExtension::kMidUri) {
@@ -273,6 +275,16 @@
     channel_send->SetSendAudioLevelIndicationStatus(new_ids.audio_level != 0,
                                                     new_ids.audio_level);
   }
+
+  if (first_time || new_ids.abs_send_time != old_ids.abs_send_time) {
+    channel_send->GetRtpRtcp()->DeregisterSendRtpHeaderExtension(
+        kRtpExtensionAbsoluteSendTime);
+    if (new_ids.abs_send_time) {
+      channel_send->GetRtpRtcp()->RegisterSendRtpHeaderExtension(
+          kRtpExtensionAbsoluteSendTime, new_ids.abs_send_time);
+    }
+  }
+
   bool transport_seq_num_id_changed =
       new_ids.transport_sequence_number != old_ids.transport_sequence_number;
   if (first_time || (transport_seq_num_id_changed &&
diff --git a/audio/audio_send_stream.h b/audio/audio_send_stream.h
index 37eb89a..3649ddf 100644
--- a/audio/audio_send_stream.h
+++ b/audio/audio_send_stream.h
@@ -183,6 +183,7 @@
   // So it should be safe to use 0 here to indicate "not configured".
   struct ExtensionIds {
     int audio_level = 0;
+    int abs_send_time = 0;
     int transport_sequence_number = 0;
     int mid = 0;
     int rid = 0;
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 540623e..7e62bc6 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -516,6 +516,8 @@
   int id = 1;
   capabilities.header_extensions.push_back(
       webrtc::RtpExtension(webrtc::RtpExtension::kAudioLevelUri, id++));
+  capabilities.header_extensions.push_back(
+      webrtc::RtpExtension(webrtc::RtpExtension::kAbsSendTimeUri, id++));
   capabilities.header_extensions.push_back(webrtc::RtpExtension(
       webrtc::RtpExtension::kTransportSequenceNumberUri, id++));
   return capabilities;
diff --git a/test/network/network_emulation.cc b/test/network/network_emulation.cc
index 8a19486..7d3ed44 100644
--- a/test/network/network_emulation.cc
+++ b/test/network/network_emulation.cc
@@ -102,6 +102,9 @@
 
 void NetworkRouterNode::OnPacketReceived(EmulatedIpPacket packet) {
   RTC_DCHECK_RUN_ON(task_queue_);
+  if (watcher_) {
+    watcher_(packet);
+  }
   auto receiver_it = routing_.find(packet.to.ipaddr());
   if (receiver_it == routing_.end()) {
     return;
@@ -128,6 +131,14 @@
   routing_.erase(dest_ip);
 }
 
+void NetworkRouterNode::SetWatcher(
+    std::function<void(const EmulatedIpPacket&)> watcher) {
+  task_queue_->PostTask([=] {
+    RTC_DCHECK_RUN_ON(task_queue_);
+    watcher_ = watcher;
+  });
+}
+
 EmulatedNetworkNode::EmulatedNetworkNode(
     Clock* clock,
     rtc::TaskQueue* task_queue,
diff --git a/test/network/network_emulation.h b/test/network/network_emulation.h
index 24e2fd9..c5ed539 100644
--- a/test/network/network_emulation.h
+++ b/test/network/network_emulation.h
@@ -102,11 +102,14 @@
   void SetReceiver(rtc::IPAddress dest_ip,
                    EmulatedNetworkReceiverInterface* receiver);
   void RemoveReceiver(rtc::IPAddress dest_ip);
+  void SetWatcher(std::function<void(const EmulatedIpPacket&)> watcher);
 
  private:
   rtc::TaskQueue* const task_queue_;
   std::map<rtc::IPAddress, EmulatedNetworkReceiverInterface*> routing_
       RTC_GUARDED_BY(task_queue_);
+  std::function<void(const EmulatedIpPacket&)> watcher_
+      RTC_GUARDED_BY(task_queue_);
 };
 
 // Represents node in the emulated network. Nodes can be connected with each
diff --git a/test/peer_scenario/peer_scenario_client.cc b/test/peer_scenario/peer_scenario_client.cc
index 4509197..c72b9d2 100644
--- a/test/peer_scenario/peer_scenario_client.cc
+++ b/test/peer_scenario/peer_scenario_client.cc
@@ -241,7 +241,7 @@
       SdpCreateObserver([=](SessionDescriptionInterface* offer) {
         std::string sdp_offer;
         offer->ToString(&sdp_offer);
-        printf("%s\n", sdp_offer.c_str());
+        RTC_LOG(LS_INFO) << sdp_offer;
         peer_connection_->SetLocalDescription(
             SdpSetObserver([sdp_offer, offer_handler]() {
               offer_handler(std::move(sdp_offer));
@@ -261,7 +261,7 @@
             SdpCreateObserver([=](SessionDescriptionInterface* answer) {
               std::string sdp_answer;
               answer->ToString(&sdp_answer);
-              printf("%s\n", sdp_answer.c_str());
+              RTC_LOG(LS_INFO) << sdp_answer;
               peer_connection_->SetLocalDescription(
                   SdpSetObserver([answer_handler, sdp_answer]() {
                     answer_handler(sdp_answer);
diff --git a/test/peer_scenario/tests/BUILD.gn b/test/peer_scenario/tests/BUILD.gn
index 6c1c75b..d799d2c 100644
--- a/test/peer_scenario/tests/BUILD.gn
+++ b/test/peer_scenario/tests/BUILD.gn
@@ -17,7 +17,9 @@
     ]
     deps = [
       "..:peer_scenario",
+      "../../:field_trial",
       "../../:test_support",
+      "../../../modules/rtp_rtcp:rtp_rtcp",
       "../../../pc:rtc_pc_base",
     ]
   }
diff --git a/test/peer_scenario/tests/remote_estimate_test.cc b/test/peer_scenario/tests/remote_estimate_test.cc
index 05addc2..81d788c 100644
--- a/test/peer_scenario/tests/remote_estimate_test.cc
+++ b/test/peer_scenario/tests/remote_estimate_test.cc
@@ -8,12 +8,37 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
+#include "modules/rtp_rtcp/source/rtp_utility.h"
+#include "pc/media_session.h"
 #include "pc/session_description.h"
+#include "test/field_trial.h"
 #include "test/gtest.h"
 #include "test/peer_scenario/peer_scenario.h"
 
 namespace webrtc {
 namespace test {
+namespace {
+RtpHeaderExtensionMap AudioExtensions(
+    const SessionDescriptionInterface& session) {
+  auto* audio_desc =
+      cricket::GetFirstAudioContentDescription(session.description());
+  return RtpHeaderExtensionMap(audio_desc->rtp_header_extensions());
+}
+
+absl::optional<RTPHeaderExtension> GetRtpPacketExtensions(
+    const rtc::ArrayView<const uint8_t> packet,
+    const RtpHeaderExtensionMap& extension_map) {
+  RtpUtility::RtpHeaderParser rtp_parser(packet.data(), packet.size());
+  if (!rtp_parser.RTCP()) {
+    RTPHeader header;
+    if (rtp_parser.Parse(&header, &extension_map, true)) {
+      return header.extension;
+    }
+  }
+  return absl::nullopt;
+}
+
+}  // namespace
 
 TEST(RemoteEstimateEndToEnd, OfferedCapabilityIsInAnswer) {
   PeerScenario s;
@@ -45,5 +70,45 @@
   EXPECT_TRUE(s.WaitAndProcess(&offer_exchange_done));
 }
 
+TEST(RemoteEstimateEndToEnd, AudioUsesAbsSendTimeExtension) {
+  ScopedFieldTrials trials("WebRTC-KeepAbsSendTimeExtension/Enabled/");
+  PeerScenario s;
+
+  auto* caller = s.CreateClient(PeerScenarioClient::Config());
+  auto* callee = s.CreateClient(PeerScenarioClient::Config());
+
+  auto send_node = s.net()->NodeBuilder().Build().node;
+  auto ret_node = s.net()->NodeBuilder().Build().node;
+
+  s.net()->CreateRoute(caller->endpoint(), {send_node}, callee->endpoint());
+  s.net()->CreateRoute(callee->endpoint(), {ret_node}, caller->endpoint());
+
+  auto signaling = s.ConnectSignaling(caller, callee, {send_node}, {ret_node});
+  caller->CreateAudio("AUDIO", cricket::AudioOptions());
+  signaling.StartIceSignaling();
+  RtpHeaderExtensionMap extension_map;
+  rtc::Event offer_exchange_done;
+  signaling.NegotiateSdp(
+      [&extension_map](SessionDescriptionInterface* offer) {
+        extension_map = AudioExtensions(*offer);
+        EXPECT_TRUE(extension_map.IsRegistered(kRtpExtensionAbsoluteSendTime));
+      },
+      [&](const SessionDescriptionInterface& answer) {
+        EXPECT_TRUE(AudioExtensions(answer).IsRegistered(
+            kRtpExtensionAbsoluteSendTime));
+        offer_exchange_done.Set();
+      });
+  EXPECT_TRUE(s.WaitAndProcess(&offer_exchange_done));
+  rtc::Event received_abs_send_time;
+  send_node->router()->SetWatcher(
+      [extension_map, &received_abs_send_time](const EmulatedIpPacket& packet) {
+        auto extensions = GetRtpPacketExtensions(packet.data, extension_map);
+        if (extensions) {
+          EXPECT_TRUE(extensions->hasAbsoluteSendTime);
+          received_abs_send_time.Set();
+        }
+      });
+  EXPECT_TRUE(s.WaitAndProcess(&received_abs_send_time));
+}
 }  // namespace test
 }  // namespace webrtc