| /* | |
| * Copyright 2019 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 "rtc_tools/rtc_event_log_visualizer/log_simulation.h" | |
| #include <algorithm> | |
| #include <cstdint> | |
| #include <functional> | |
| #include <memory> | |
| #include <utility> | |
| #include "api/transport/network_control.h" | |
| #include "api/transport/network_types.h" | |
| #include "api/units/data_rate.h" | |
| #include "api/units/time_delta.h" | |
| #include "api/units/timestamp.h" | |
| #include "logging/rtc_event_log/events/logged_rtp_rtcp.h" | |
| #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h" | |
| #include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h" | |
| #include "logging/rtc_event_log/rtc_event_log_parser.h" | |
| #include "logging/rtc_event_log/rtc_event_processor.h" | |
| #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" | |
| #include "modules/rtp_rtcp/source/time_util.h" | |
| #include "rtc_base/network/sent_packet.h" | |
| #include "system_wrappers/include/clock.h" | |
| namespace webrtc { | |
| LogBasedNetworkControllerSimulation::LogBasedNetworkControllerSimulation( | |
| std::unique_ptr<NetworkControllerFactoryInterface> factory, | |
| std::function<void(const NetworkControlUpdate&, Timestamp)> update_handler) | |
| : update_handler_(update_handler), factory_(std::move(factory)) {} | |
| LogBasedNetworkControllerSimulation::~LogBasedNetworkControllerSimulation() {} | |
| void LogBasedNetworkControllerSimulation::HandleStateUpdate( | |
| const NetworkControlUpdate& update) { | |
| update_handler_(update, current_time_); | |
| } | |
| void LogBasedNetworkControllerSimulation::ProcessUntil(Timestamp to_time) { | |
| if (last_process_.IsInfinite()) { | |
| NetworkControllerConfig config; | |
| config.constraints.at_time = to_time; | |
| config.constraints.min_data_rate = DataRate::KilobitsPerSec(30); | |
| config.constraints.starting_rate = DataRate::KilobitsPerSec(300); | |
| config.event_log = &null_event_log_; | |
| controller_ = factory_->Create(config); | |
| } | |
| if (last_process_.IsInfinite() || | |
| to_time - last_process_ > TimeDelta::Seconds(1)) { | |
| last_process_ = to_time; | |
| current_time_ = to_time; | |
| ProcessInterval msg; | |
| msg.at_time = to_time; | |
| HandleStateUpdate(controller_->OnProcessInterval(msg)); | |
| } else { | |
| while (last_process_ + factory_->GetProcessInterval() <= to_time) { | |
| last_process_ += factory_->GetProcessInterval(); | |
| current_time_ = last_process_; | |
| ProcessInterval msg; | |
| msg.at_time = current_time_; | |
| HandleStateUpdate(controller_->OnProcessInterval(msg)); | |
| } | |
| current_time_ = to_time; | |
| } | |
| } | |
| void LogBasedNetworkControllerSimulation::OnProbeCreated( | |
| const LoggedBweProbeClusterCreatedEvent& probe_cluster) { | |
| pending_probes_.push_back({probe_cluster, 0, 0}); | |
| } | |
| void LogBasedNetworkControllerSimulation::OnPacketSent( | |
| const LoggedPacketInfo& packet) { | |
| ProcessUntil(packet.log_packet_time); | |
| if (packet.has_transport_seq_no) { | |
| PacedPacketInfo probe_info; | |
| if (!pending_probes_.empty() && | |
| packet.media_type == LoggedMediaType::kVideo) { | |
| auto& probe = pending_probes_.front(); | |
| probe_info.probe_cluster_id = probe.event.id; | |
| probe_info.send_bitrate = DataRate::BitsPerSec(probe.event.bitrate_bps); | |
| probe_info.probe_cluster_min_bytes = probe.event.min_bytes; | |
| probe_info.probe_cluster_min_probes = probe.event.min_packets; | |
| probe.packets_sent++; | |
| probe.bytes_sent += packet.size + packet.overhead; | |
| if (probe.bytes_sent >= probe.event.min_bytes && | |
| probe.packets_sent >= probe.event.min_packets) { | |
| pending_probes_.pop_front(); | |
| } | |
| } | |
| RtpPacketSendInfo packet_info; | |
| packet_info.media_ssrc = packet.ssrc; | |
| packet_info.transport_sequence_number = packet.transport_seq_no; | |
| packet_info.rtp_sequence_number = packet.stream_seq_no; | |
| packet_info.length = packet.size; | |
| packet_info.pacing_info = probe_info; | |
| transport_feedback_.AddPacket(packet_info, packet.overhead, | |
| packet.log_packet_time); | |
| } | |
| rtc::SentPacket sent_packet; | |
| sent_packet.send_time_ms = packet.log_packet_time.ms(); | |
| sent_packet.info.included_in_allocation = true; | |
| sent_packet.info.packet_size_bytes = packet.size + packet.overhead; | |
| if (packet.has_transport_seq_no) { | |
| sent_packet.packet_id = packet.transport_seq_no; | |
| sent_packet.info.included_in_feedback = true; | |
| } | |
| auto msg = transport_feedback_.ProcessSentPacket(sent_packet); | |
| if (msg) | |
| HandleStateUpdate(controller_->OnSentPacket(*msg)); | |
| } | |
| void LogBasedNetworkControllerSimulation::OnFeedback( | |
| const LoggedRtcpPacketTransportFeedback& feedback) { | |
| auto feedback_time = Timestamp::Millis(feedback.log_time_ms()); | |
| ProcessUntil(feedback_time); | |
| auto msg = transport_feedback_.ProcessTransportFeedback( | |
| feedback.transport_feedback, feedback_time); | |
| if (msg) | |
| HandleStateUpdate(controller_->OnTransportPacketsFeedback(*msg)); | |
| } | |
| void LogBasedNetworkControllerSimulation::OnReceiverReport( | |
| const LoggedRtcpPacketReceiverReport& report) { | |
| if (report.rr.report_blocks().empty()) | |
| return; | |
| auto report_time = Timestamp::Millis(report.log_time_ms()); | |
| ProcessUntil(report_time); | |
| int packets_delta = 0; | |
| int lost_delta = 0; | |
| for (auto& block : report.rr.report_blocks()) { | |
| auto it = last_report_blocks_.find(block.source_ssrc()); | |
| if (it != last_report_blocks_.end()) { | |
| packets_delta += | |
| block.extended_high_seq_num() - it->second.extended_high_seq_num(); | |
| lost_delta += block.cumulative_lost() - it->second.cumulative_lost(); | |
| } | |
| last_report_blocks_[block.source_ssrc()] = block; | |
| } | |
| if (packets_delta > lost_delta) { | |
| TransportLossReport msg; | |
| msg.packets_lost_delta = lost_delta; | |
| msg.packets_received_delta = packets_delta - lost_delta; | |
| msg.receive_time = report_time; | |
| msg.start_time = last_report_block_time_; | |
| msg.end_time = report_time; | |
| last_report_block_time_ = report_time; | |
| HandleStateUpdate(controller_->OnTransportLossReport(msg)); | |
| } | |
| Clock* clock = Clock::GetRealTimeClock(); | |
| TimeDelta rtt = TimeDelta::PlusInfinity(); | |
| for (auto& rb : report.rr.report_blocks()) { | |
| if (rb.last_sr()) { | |
| Timestamp report_log_time = Timestamp::Micros(report.log_time_us()); | |
| uint32_t receive_time_ntp = | |
| CompactNtp(clock->ConvertTimestampToNtpTime(report_log_time)); | |
| uint32_t rtt_ntp = | |
| receive_time_ntp - rb.delay_since_last_sr() - rb.last_sr(); | |
| rtt = std::min(rtt, CompactNtpRttToTimeDelta(rtt_ntp)); | |
| } | |
| } | |
| if (rtt.IsFinite()) { | |
| RoundTripTimeUpdate msg; | |
| msg.receive_time = report_time; | |
| msg.round_trip_time = rtt; | |
| HandleStateUpdate(controller_->OnRoundTripTimeUpdate(msg)); | |
| } | |
| } | |
| void LogBasedNetworkControllerSimulation::OnIceConfig( | |
| const LoggedIceCandidatePairConfig& candidate) { | |
| if (candidate.type == IceCandidatePairConfigType::kSelected) { | |
| auto log_time = Timestamp::Micros(candidate.log_time_us()); | |
| ProcessUntil(log_time); | |
| NetworkRouteChange msg; | |
| msg.at_time = log_time; | |
| msg.constraints.min_data_rate = DataRate::KilobitsPerSec(30); | |
| msg.constraints.starting_rate = DataRate::KilobitsPerSec(300); | |
| msg.constraints.at_time = log_time; | |
| HandleStateUpdate(controller_->OnNetworkRouteChange(msg)); | |
| } | |
| } | |
| void LogBasedNetworkControllerSimulation::ProcessEventsInLog( | |
| const ParsedRtcEventLog& parsed_log_) { | |
| auto packet_infos = parsed_log_.GetOutgoingPacketInfos(); | |
| RtcEventProcessor processor; | |
| processor.AddEvents( | |
| parsed_log_.bwe_probe_cluster_created_events(), | |
| [this](const LoggedBweProbeClusterCreatedEvent& probe_cluster) { | |
| OnProbeCreated(probe_cluster); | |
| }); | |
| processor.AddEvents( | |
| packet_infos, | |
| [this](const LoggedPacketInfo& packet) { OnPacketSent(packet); }, | |
| PacketDirection::kOutgoingPacket); | |
| processor.AddEvents( | |
| parsed_log_.transport_feedbacks(PacketDirection::kIncomingPacket), | |
| [this](const LoggedRtcpPacketTransportFeedback& feedback) { | |
| OnFeedback(feedback); | |
| }, | |
| PacketDirection::kIncomingPacket); | |
| processor.AddEvents( | |
| parsed_log_.receiver_reports(PacketDirection::kIncomingPacket), | |
| [this](const LoggedRtcpPacketReceiverReport& report) { | |
| OnReceiverReport(report); | |
| }, | |
| PacketDirection::kIncomingPacket); | |
| processor.AddEvents(parsed_log_.ice_candidate_pair_configs(), | |
| [this](const LoggedIceCandidatePairConfig& candidate) { | |
| OnIceConfig(candidate); | |
| }); | |
| processor.ProcessEventsInOrder(); | |
| } | |
| } // namespace webrtc |