diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer.cc b/rtc_tools/rtc_event_log_visualizer/analyzer.cc index d78fea4899..01adfc569e 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer.cc +++ b/rtc_tools/rtc_event_log_visualizer/analyzer.cc @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -406,6 +407,12 @@ RtpPacketReceived RtpPacketForBWEFromHeader(const RTPHeader& header) { return rtp_packet; } +struct PacketLossSummary { + size_t num_packets = 0; + size_t num_lost_packets = 0; + Timestamp base_time = Timestamp::MinusInfinity(); +}; + } // namespace EventLogAnalyzer::EventLogAnalyzer(const ParsedRtcEventLog& log, @@ -1298,6 +1305,100 @@ void EventLogAnalyzer::CreateGoogCcSimulationGraph(Plot* plot) { plot->SetTitle("Simulated BWE behavior"); } +void EventLogAnalyzer::CreateOutgoingTWCCLossRateGraph(Plot* plot) { + TimeSeries loss_rate_series("Loss rate (from packet feedback)", + LineStyle::kLine, PointStyle::kHighlight); + TimeSeries average_loss_rate_series("Average loss rate last 5s", + LineStyle::kLine, PointStyle::kHighlight); + TimeSeries missing_feedback_series("Missing feedback", LineStyle::kNone, + PointStyle::kHighlight); + PacketLossSummary window_summary; + Timestamp last_observation_receive_time = Timestamp::Zero(); + + // Use loss based bwe 2 observation duration and observation window size. + constexpr TimeDelta kObservationDuration = TimeDelta::Millis(250); + constexpr uint32_t kObservationWindowSize = 20; + std::deque observations; + SeqNumUnwrapper unwrapper; + int64_t last_acked = 1; + if (!parsed_log_.transport_feedbacks(kIncomingPacket).empty()) { + last_acked = + unwrapper.Unwrap(parsed_log_.transport_feedbacks(kIncomingPacket)[0] + .transport_feedback.GetBaseSequence()); + } + for (auto& feedback : parsed_log_.transport_feedbacks(kIncomingPacket)) { + const rtcp::TransportFeedback& transport_feedback = + feedback.transport_feedback; + size_t base_seq_num = + unwrapper.Unwrap(transport_feedback.GetBaseSequence()); + // Collect packets that do not have feedback, which are from the last acked + // packet, to the current base packet. + for (size_t seq_num = last_acked; seq_num < base_seq_num; ++seq_num) { + missing_feedback_series.points.emplace_back( + config_.GetCallTimeSec(feedback.timestamp), + 100 + seq_num - last_acked); + } + last_acked = base_seq_num + transport_feedback.GetPacketStatusCount(); + + // Compute loss rate from the transport feedback. + auto loss_rate = + static_cast((transport_feedback.GetPacketStatusCount() - + transport_feedback.GetReceivedPackets().size()) * + 100.0 / transport_feedback.GetPacketStatusCount()); + loss_rate_series.points.emplace_back( + config_.GetCallTimeSec(feedback.timestamp), loss_rate); + + // Compute loss rate in a window of kObservationWindowSize. + if (window_summary.num_packets == 0) { + window_summary.base_time = feedback.log_time(); + } + window_summary.num_packets += transport_feedback.GetPacketStatusCount(); + window_summary.num_lost_packets += + transport_feedback.GetPacketStatusCount() - + transport_feedback.GetReceivedPackets().size(); + + const Timestamp last_received_time = feedback.log_time(); + const TimeDelta observation_duration = + window_summary.base_time == Timestamp::Zero() + ? TimeDelta::Zero() + : last_received_time - window_summary.base_time; + if (observation_duration > kObservationDuration) { + last_observation_receive_time = last_received_time; + observations.push_back(window_summary); + if (observations.size() > kObservationWindowSize) { + observations.pop_front(); + } + + // Compute average loss rate in a number of windows. + int total_packets = 0; + int total_loss = 0; + for (const auto& observation : observations) { + total_loss += observation.num_lost_packets; + total_packets += observation.num_packets; + } + if (total_packets > 0) { + float average_loss_rate = total_loss * 100.0 / total_packets; + average_loss_rate_series.points.emplace_back( + config_.GetCallTimeSec(feedback.timestamp), average_loss_rate); + } else { + average_loss_rate_series.points.emplace_back( + config_.GetCallTimeSec(feedback.timestamp), 0); + } + window_summary = PacketLossSummary(); + } + } + // Add the data set to the plot. + plot->AppendTimeSeriesIfNotEmpty(std::move(loss_rate_series)); + plot->AppendTimeSeriesIfNotEmpty(std::move(average_loss_rate_series)); + plot->AppendTimeSeriesIfNotEmpty(std::move(missing_feedback_series)); + + plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), + "Time (s)", kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 100, "Loss rate (percent)", kBottomMargin, + kTopMargin); + plot->SetTitle("Outgoing loss rate (from TWCC feedback)"); +} + void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { using RtpPacketType = LoggedRtpPacketOutgoing; using TransportFeedbackType = LoggedRtcpPacketTransportFeedback; diff --git a/rtc_tools/rtc_event_log_visualizer/analyzer.h b/rtc_tools/rtc_event_log_visualizer/analyzer.h index e58a4823f7..f63e9a68d6 100644 --- a/rtc_tools/rtc_event_log_visualizer/analyzer.h +++ b/rtc_tools/rtc_event_log_visualizer/analyzer.h @@ -67,6 +67,7 @@ class EventLogAnalyzer { void CreateStreamBitrateGraph(PacketDirection direction, Plot* plot); void CreateBitrateAllocationGraph(PacketDirection direction, Plot* plot); + void CreateOutgoingTWCCLossRateGraph(Plot* plot); void CreateGoogCcSimulationGraph(Plot* plot); void CreateSendSideBweSimulationGraph(Plot* plot); void CreateReceiveSideBweSimulationGraph(Plot* plot); diff --git a/rtc_tools/rtc_event_log_visualizer/main.cc b/rtc_tools/rtc_event_log_visualizer/main.cc index aa52527903..3e4f3e482d 100644 --- a/rtc_tools/rtc_event_log_visualizer/main.cc +++ b/rtc_tools/rtc_event_log_visualizer/main.cc @@ -227,7 +227,7 @@ int main(int argc, char* argv[]) { {"sendside_bwe", {"outgoing_packet_sizes", "outgoing_bitrate", "outgoing_stream_bitrate", "simulated_sendside_bwe", "network_delay_feedback", - "fraction_loss_feedback"}}, + "fraction_loss_feedback", "outgoing_twcc_loss"}}, {"receiveside_bwe", {"incoming_packet_sizes", "incoming_delay", "incoming_loss_rate", "incoming_bitrate", "incoming_stream_bitrate", @@ -377,6 +377,9 @@ int main(int argc, char* argv[]) { plots.RegisterPlot("simulated_goog_cc", [&](Plot* plot) { analyzer.CreateGoogCcSimulationGraph(plot); }); + plots.RegisterPlot("outgoing_twcc_loss", [&](Plot* plot) { + analyzer.CreateOutgoingTWCCLossRateGraph(plot); + }); plots.RegisterPlot("network_delay_feedback", [&](Plot* plot) { analyzer.CreateNetworkDelayFeedbackGraph(plot); });