/* * Copyright 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "pc/test/integration_test_helpers.h" namespace webrtc { PeerConnectionInterface::RTCOfferAnswerOptions IceRestartOfferAnswerOptions() { PeerConnectionInterface::RTCOfferAnswerOptions options; options.ice_restart = true; return options; } void RemoveSsrcsAndMsids(std::unique_ptr& sdp) { for (ContentInfo& content : sdp->description()->contents()) { content.media_description()->mutable_streams().clear(); } sdp->description()->set_msid_signaling(0); } void RemoveSsrcsAndKeepMsids( std::unique_ptr& sdp) { for (ContentInfo& content : sdp->description()->contents()) { std::string track_id; std::vector stream_ids; if (!content.media_description()->streams().empty()) { const StreamParams& first_stream = content.media_description()->streams()[0]; track_id = first_stream.id; stream_ids = first_stream.stream_ids(); } content.media_description()->mutable_streams().clear(); StreamParams new_stream; new_stream.id = track_id; new_stream.set_stream_ids(stream_ids); content.media_description()->AddStream(new_stream); } } void SetSdpType(std::unique_ptr& sdp, SdpType sdpType) { std::string str; sdp->ToString(&str); sdp = CreateSessionDescription(sdpType, str); } int FindFirstMediaStatsIndexByKind( const std::string& kind, const std::vector& inbound_rtps) { for (size_t i = 0; i < inbound_rtps.size(); i++) { if (*inbound_rtps[i]->kind == kind) { return i; } } return -1; } void ReplaceFirstSsrc(StreamParams& stream, uint32_t ssrc) { stream.ssrcs[0] = ssrc; for (auto& group : stream.ssrc_groups) { group.ssrcs[0] = ssrc; } } TaskQueueMetronome::TaskQueueMetronome(TimeDelta tick_period) : tick_period_(tick_period) {} TaskQueueMetronome::~TaskQueueMetronome() { RTC_DCHECK_RUN_ON(&sequence_checker_); } void TaskQueueMetronome::RequestCallOnNextTick( absl::AnyInvocable callback) { RTC_DCHECK_RUN_ON(&sequence_checker_); callbacks_.push_back(std::move(callback)); // Only schedule a tick callback for the first `callback` addition. // Schedule on the current task queue to comply with RequestCallOnNextTick // requirements. if (callbacks_.size() == 1) { TaskQueueBase::Current()->PostDelayedTask( SafeTask(safety_.flag(), [this] { RTC_DCHECK_RUN_ON(&sequence_checker_); std::vector> callbacks; callbacks_.swap(callbacks); for (auto& callback : callbacks) std::move(callback)(); }), tick_period_); } } TimeDelta TaskQueueMetronome::TickPeriod() const { RTC_DCHECK_RUN_ON(&sequence_checker_); return tick_period_; } // Implementation of PeerConnectionIntegrationWrapper functions void PeerConnectionIntegrationWrapper::StartWatchingDelayStats() { // Get the baseline numbers for audio_packets and audio_delay. auto received_stats = NewGetStats(); auto rtp_stats = received_stats->GetStatsOfType()[0]; ASSERT_TRUE(rtp_stats->relative_packet_arrival_delay.has_value()); ASSERT_TRUE(rtp_stats->packets_received.has_value()); rtp_stats_id_ = rtp_stats->id(); audio_packets_stat_ = *rtp_stats->packets_received; audio_delay_stat_ = *rtp_stats->relative_packet_arrival_delay; audio_samples_stat_ = *rtp_stats->total_samples_received; audio_concealed_stat_ = *rtp_stats->concealed_samples; } void PeerConnectionIntegrationWrapper::UpdateDelayStats(std::string tag, int desc_size) { auto report = NewGetStats(); auto rtp_stats = report->GetAs(rtp_stats_id_); ASSERT_TRUE(rtp_stats); auto delta_packets = *rtp_stats->packets_received - audio_packets_stat_; auto delta_rpad = *rtp_stats->relative_packet_arrival_delay - audio_delay_stat_; auto recent_delay = delta_packets > 0 ? delta_rpad / delta_packets : -1; // The purpose of these checks is to sound the alarm early if we introduce // serious regressions. The numbers are not acceptable for production, but // occur on slow bots. // // An average relative packet arrival delay over the renegotiation of // > 100 ms indicates that something is dramatically wrong, and will impact // quality for sure. // Worst bots: // linux_x86_dbg at 0.206 #if !defined(NDEBUG) EXPECT_GT(0.25, recent_delay) << tag << " size " << desc_size; #else EXPECT_GT(0.1, recent_delay) << tag << " size " << desc_size; #endif auto delta_samples = *rtp_stats->total_samples_received - audio_samples_stat_; auto delta_concealed = *rtp_stats->concealed_samples - audio_concealed_stat_; // These limits should be adjusted down as we improve: // // Concealing more than 4000 samples during a renegotiation is unacceptable. // But some bots are slow. // Worst bots: // linux_more_configs bot at conceal count 5184 // android_arm_rel at conceal count 9241 // linux_x86_dbg at 15174 #if !defined(NDEBUG) EXPECT_GT(18000U, delta_concealed) << "Concealed " << delta_concealed << " of " << delta_samples << " samples"; #else EXPECT_GT(15000U, delta_concealed) << "Concealed " << delta_concealed << " of " << delta_samples << " samples"; #endif // Concealing more than 20% of samples during a renegotiation is // unacceptable. // Worst bots: // Nondebug: Linux32 Release at conceal rate 0.606597 (CI run) // Debug: linux_x86_dbg bot at conceal rate 0.854 // internal bot at conceal rate 0.967 (b/294020344) // TODO(https://crbug.com/webrtc/15393): Improve audio quality during // renegotiation so that we can reduce these thresholds, 99% is not even // close to the 20% deemed unacceptable above or the 0% that would be ideal. if (delta_samples > 0) { #if !defined(NDEBUG) EXPECT_LT(1.0 * delta_concealed / delta_samples, 0.99) << "Concealed " << delta_concealed << " of " << delta_samples << " samples"; #else EXPECT_LT(1.0 * delta_concealed / delta_samples, 0.7) << "Concealed " << delta_concealed << " of " << delta_samples << " samples"; #endif } // Increment trailing counters audio_packets_stat_ = *rtp_stats->packets_received; audio_delay_stat_ = *rtp_stats->relative_packet_arrival_delay; audio_samples_stat_ = *rtp_stats->total_samples_received; audio_concealed_stat_ = *rtp_stats->concealed_samples; } bool PeerConnectionIntegrationWrapper::Init( const PeerConnectionFactory::Options* options, const PeerConnectionInterface::RTCConfiguration* config, PeerConnectionDependencies dependencies, rtc::SocketServer* socket_server, rtc::Thread* network_thread, rtc::Thread* worker_thread, std::unique_ptr event_log_factory, bool reset_encoder_factory, bool reset_decoder_factory, bool create_media_engine) { // There's an error in this test code if Init ends up being called twice. RTC_DCHECK(!peer_connection_); RTC_DCHECK(!peer_connection_factory_); fake_network_manager_.reset(new rtc::FakeNetworkManager()); fake_network_manager_->AddInterface(kDefaultLocalAddress); socket_factory_.reset(new rtc::BasicPacketSocketFactory(socket_server)); std::unique_ptr port_allocator( new cricket::BasicPortAllocator(fake_network_manager_.get(), socket_factory_.get())); port_allocator_ = port_allocator.get(); fake_audio_capture_module_ = FakeAudioCaptureModule::Create(); if (!fake_audio_capture_module_) { return false; } rtc::Thread* const signaling_thread = rtc::Thread::Current(); PeerConnectionFactoryDependencies pc_factory_dependencies; pc_factory_dependencies.network_thread = network_thread; pc_factory_dependencies.worker_thread = worker_thread; pc_factory_dependencies.signaling_thread = signaling_thread; pc_factory_dependencies.task_queue_factory = CreateDefaultTaskQueueFactory(); pc_factory_dependencies.trials = std::make_unique(); pc_factory_dependencies.decode_metronome = std::make_unique(TimeDelta::Millis(8)); pc_factory_dependencies.adm = fake_audio_capture_module_; if (create_media_engine) { EnableMediaWithDefaults(pc_factory_dependencies); } if (reset_encoder_factory) { pc_factory_dependencies.video_encoder_factory.reset(); } if (reset_decoder_factory) { pc_factory_dependencies.video_decoder_factory.reset(); } if (!pc_factory_dependencies.audio_processing) { // If the standard Creation method for APM returns a null pointer, instead // use the builder for testing to create an APM object. pc_factory_dependencies.audio_processing = AudioProcessingBuilderForTesting().Create(); } if (event_log_factory) { event_log_factory_ = event_log_factory.get(); pc_factory_dependencies.event_log_factory = std::move(event_log_factory); } else { pc_factory_dependencies.event_log_factory = std::make_unique(); } peer_connection_factory_ = CreateModularPeerConnectionFactory(std::move(pc_factory_dependencies)); if (!peer_connection_factory_) { return false; } if (options) { peer_connection_factory_->SetOptions(*options); } if (config) { sdp_semantics_ = config->sdp_semantics; } dependencies.allocator = std::move(port_allocator); peer_connection_ = CreatePeerConnection(config, std::move(dependencies)); return peer_connection_.get() != nullptr; } } // namespace webrtc