diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc index b27e29ceb3..4ee51090ab 100644 --- a/audio/audio_send_stream.cc +++ b/audio/audio_send_stream.cc @@ -207,6 +207,8 @@ AudioSendStream::ExtensionIds AudioSendStream::FindExtensionIds( 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 @@ void AudioSendStream::ConfigureStream( 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 37eb89a265..3649ddf026 100644 --- a/audio/audio_send_stream.h +++ b/audio/audio_send_stream.h @@ -183,6 +183,7 @@ class AudioSendStream final : public webrtc::AudioSendStream, // 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 540623e4d5..7e62bc64ac 100644 --- a/media/engine/webrtc_voice_engine.cc +++ b/media/engine/webrtc_voice_engine.cc @@ -516,6 +516,8 @@ RtpCapabilities WebRtcVoiceEngine::GetCapabilities() const { 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 8a1948661d..7d3ed444db 100644 --- a/test/network/network_emulation.cc +++ b/test/network/network_emulation.cc @@ -102,6 +102,9 @@ NetworkRouterNode::NetworkRouterNode(rtc::TaskQueue* task_queue) 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 @@ void NetworkRouterNode::RemoveReceiver(rtc::IPAddress dest_ip) { routing_.erase(dest_ip); } +void NetworkRouterNode::SetWatcher( + std::function 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 24e2fd9098..c5ed53961d 100644 --- a/test/network/network_emulation.h +++ b/test/network/network_emulation.h @@ -102,11 +102,14 @@ class NetworkRouterNode : public EmulatedNetworkReceiverInterface { void SetReceiver(rtc::IPAddress dest_ip, EmulatedNetworkReceiverInterface* receiver); void RemoveReceiver(rtc::IPAddress dest_ip); + void SetWatcher(std::function watcher); private: rtc::TaskQueue* const task_queue_; std::map routing_ RTC_GUARDED_BY(task_queue_); + std::function 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 45091971cb..c72b9d28a1 100644 --- a/test/peer_scenario/peer_scenario_client.cc +++ b/test/peer_scenario/peer_scenario_client.cc @@ -241,7 +241,7 @@ void PeerScenarioClient::CreateAndSetSdp( 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 @@ void PeerScenarioClient::SetSdpOfferAndGetAnswer( 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 6c1c75b79d..d799d2cb34 100644 --- a/test/peer_scenario/tests/BUILD.gn +++ b/test/peer_scenario/tests/BUILD.gn @@ -17,7 +17,9 @@ if (rtc_include_tests) { ] 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 05addc26ee..81d788cd9f 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 GetRtpPacketExtensions( + const rtc::ArrayView 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 @@ TEST(RemoteEstimateEndToEnd, OfferedCapabilityIsInAnswer) { 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