|  | /* | 
|  | *  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(cricket::SessionDescription* desc) { | 
|  | for (ContentInfo& content : desc->contents()) { | 
|  | content.media_description()->mutable_streams().clear(); | 
|  | } | 
|  | desc->set_msid_signaling(0); | 
|  | } | 
|  |  | 
|  | void RemoveSsrcsAndKeepMsids(cricket::SessionDescription* desc) { | 
|  | for (ContentInfo& content : desc->contents()) { | 
|  | std::string track_id; | 
|  | std::vector<std::string> 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); | 
|  | } | 
|  | } | 
|  |  | 
|  | int FindFirstMediaStatsIndexByKind( | 
|  | const std::string& kind, | 
|  | const std::vector<const RTCInboundRtpStreamStats*>& 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<void() &&> 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<absl::AnyInvocable<void() &&>> 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<RTCInboundRtpStreamStats>()[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<RTCInboundRtpStreamStats>(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<FakeRtcEventLogFactory> 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<cricket::PortAllocator> 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<FieldTrialBasedConfig>(); | 
|  | pc_factory_dependencies.decode_metronome = | 
|  | std::make_unique<TaskQueueMetronome>(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<RtcEventLogFactory>(); | 
|  | } | 
|  | 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 |