/* * Copyright (c) 2024 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 "test/network/schedulable_network_behavior.h" #include #include "api/sequence_checker.h" #include "api/task_queue/task_queue_base.h" #include "api/test/network_emulation/network_config_schedule.pb.h" #include "api/test/simulated_network.h" #include "api/units/data_rate.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" #include "rtc_base/task_utils/repeating_task.h" #include "system_wrappers/include/clock.h" #include "test/network/simulated_network.h" namespace webrtc { namespace { using ::webrtc::BuiltInNetworkBehaviorConfig; void UpdateConfigFromSchedule( const network_behaviour::NetworkConfigScheduleItem& schedule_item, BuiltInNetworkBehaviorConfig& config) { if (schedule_item.has_queue_length_packets()) { config.queue_length_packets = schedule_item.queue_length_packets(); } if (schedule_item.has_queue_delay_ms()) { config.queue_delay_ms = schedule_item.queue_delay_ms(); } if (schedule_item.has_link_capacity_kbps()) { config.link_capacity = DataRate::KilobitsPerSec(schedule_item.link_capacity_kbps()); } if (schedule_item.has_loss_percent()) { config.loss_percent = schedule_item.loss_percent(); } if (schedule_item.has_delay_standard_deviation_ms()) { config.delay_standard_deviation_ms = schedule_item.delay_standard_deviation_ms(); } if (schedule_item.has_allow_reordering()) { config.allow_reordering = schedule_item.allow_reordering(); } if (schedule_item.has_avg_burst_loss_length()) { config.avg_burst_loss_length = schedule_item.avg_burst_loss_length(); } if (schedule_item.has_packet_overhead()) { config.packet_overhead = schedule_item.packet_overhead(); } } BuiltInNetworkBehaviorConfig GetInitialConfig( const network_behaviour::NetworkConfigSchedule& schedule) { BuiltInNetworkBehaviorConfig config; if (!schedule.item().empty()) { UpdateConfigFromSchedule(schedule.item(0), config); } return config; } } // namespace SchedulableNetworkBehavior::SchedulableNetworkBehavior( network_behaviour::NetworkConfigSchedule schedule, webrtc::Clock& clock) : SimulatedNetwork(GetInitialConfig(schedule)), schedule_(std::move(schedule)), clock_(clock), config_(GetInitialConfig(schedule_)) { if (schedule_.item().size() > 1) { next_schedule_index_ = 1; } sequence_checker_.Detach(); } bool SchedulableNetworkBehavior::EnqueuePacket( webrtc::PacketInFlightInfo packet_info) { RTC_DCHECK_RUN_ON(&sequence_checker_); if (first_send_time_.IsMinusInfinity()) { first_send_time_ = webrtc::Timestamp::Micros(packet_info.send_time_us); if (schedule_.item().size() > 1) { RTC_CHECK_LT(next_schedule_index_, schedule_.item().size()); webrtc::TimeDelta delay = webrtc::TimeDelta::Millis(schedule_.item()[next_schedule_index_] .time_since_first_sent_packet_ms()); schedule_task_ = RepeatingTaskHandle::DelayedStart( webrtc::TaskQueueBase::Current(), delay, [this] { return UpdateConfigAndReschedule(); }); } } return SimulatedNetwork::EnqueuePacket(packet_info); } TimeDelta SchedulableNetworkBehavior::UpdateConfigAndReschedule() { RTC_DCHECK_RUN_ON(&sequence_checker_); Timestamp reschedule_time = clock_.CurrentTime(); RTC_CHECK_LT(next_schedule_index_, schedule_.item().size()); auto next_config = schedule_.item()[next_schedule_index_]; UpdateConfigFromSchedule(next_config, config_); SimulatedNetwork::SetConfig(config_, reschedule_time); next_schedule_index_ = ++next_schedule_index_ % schedule_.item().size(); webrtc::TimeDelta delay = webrtc::TimeDelta::Zero(); webrtc::TimeDelta time_since_first_sent_packet = reschedule_time - first_send_time_; if (next_schedule_index_ != 0) { delay = webrtc::TimeDelta::Millis(schedule_.item()[next_schedule_index_] .time_since_first_sent_packet_ms()) - (time_since_first_sent_packet - wrap_time_delta_); } else if (!schedule_.has_repeat_schedule_after_last_ms()) { // No more schedule items. schedule_task_.Stop(); return TimeDelta::Zero(); // This is ignored. } else { // Wrap around to the first schedule item. wrap_time_delta_ += TimeDelta::Millis(schedule_.repeat_schedule_after_last_ms()) + TimeDelta::Millis(schedule_.item()[schedule_.item().size() - 1] .time_since_first_sent_packet_ms()); delay = webrtc::TimeDelta::Millis(schedule_.repeat_schedule_after_last_ms()); RTC_DCHECK_GE(delay, TimeDelta::Zero()); } return delay; } } // namespace webrtc