|  | /* | 
|  | *  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/event_log_visualizer/log_simulation.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <utility> | 
|  |  | 
|  | #include "logging/rtc_event_log/rtc_event_processor.h" | 
|  | #include "modules/rtp_rtcp/source/time_util.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::kbps(30); | 
|  | config.constraints.starting_rate = DataRate::kbps(300); | 
|  | 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_bps = 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.ssrc = packet.ssrc; | 
|  | packet_info.transport_sequence_number = packet.transport_seq_no; | 
|  | packet_info.rtp_sequence_number = packet.stream_seq_no; | 
|  | packet_info.has_rtp_sequence_number = true; | 
|  | 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::ms(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::ms(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)); | 
|  | } | 
|  |  | 
|  | TimeDelta rtt = TimeDelta::PlusInfinity(); | 
|  | for (auto& rb : report.rr.report_blocks()) { | 
|  | if (rb.last_sr()) { | 
|  | uint32_t receive_time_ntp = | 
|  | CompactNtp(TimeMicrosToNtp(report.log_time_us())); | 
|  | uint32_t rtt_ntp = | 
|  | receive_time_ntp - rb.delay_since_last_sr() - rb.last_sr(); | 
|  | rtt = std::min(rtt, TimeDelta::ms(CompactNtpRttToMs(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::us(candidate.log_time_us()); | 
|  | ProcessUntil(log_time); | 
|  | NetworkRouteChange msg; | 
|  | msg.at_time = log_time; | 
|  | msg.constraints.min_data_rate = DataRate::kbps(30); | 
|  | msg.constraints.starting_rate = DataRate::kbps(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); | 
|  | }); | 
|  | processor.AddEvents( | 
|  | parsed_log_.transport_feedbacks(PacketDirection::kIncomingPacket), | 
|  | [this](const LoggedRtcpPacketTransportFeedback& feedback) { | 
|  | OnFeedback(feedback); | 
|  | }); | 
|  | processor.AddEvents( | 
|  | parsed_log_.receiver_reports(PacketDirection::kIncomingPacket), | 
|  | [this](const LoggedRtcpPacketReceiverReport& report) { | 
|  | OnReceiverReport(report); | 
|  | }); | 
|  | processor.AddEvents(parsed_log_.ice_candidate_pair_configs(), | 
|  | [this](const LoggedIceCandidatePairConfig& candidate) { | 
|  | OnIceConfig(candidate); | 
|  | }); | 
|  | processor.ProcessEventsInOrder(); | 
|  | } | 
|  |  | 
|  | }  // namespace webrtc |