diff --git a/rtc_tools/event_log_visualizer/analyzer.cc b/rtc_tools/event_log_visualizer/analyzer.cc index 1b52c308a2..a4126bc921 100644 --- a/rtc_tools/event_log_visualizer/analyzer.cc +++ b/rtc_tools/event_log_visualizer/analyzer.cc @@ -1115,6 +1115,46 @@ void EventLogAnalyzer::CreateStreamBitrateGraph(PacketDirection direction, plot->SetTitle(GetDirectionAsString(direction) + " bitrate per stream"); } +// Plot the bitrate allocation for each temporal and spatial layer. +// Computed from RTCP XR target bitrate block, so the graph is only populated if +// those are sent. +void EventLogAnalyzer::CreateBitrateAllocationGraph(PacketDirection direction, + Plot* plot) { + std::map time_series; + const auto& xr_list = parsed_log_.extended_reports(direction); + for (const auto& rtcp : xr_list) { + const absl::optional& target_bitrate = + rtcp.xr.target_bitrate(); + if (!target_bitrate.has_value()) + continue; + for (const auto& bitrate_item : target_bitrate->GetTargetBitrates()) { + LayerDescription layer(rtcp.xr.sender_ssrc(), bitrate_item.spatial_layer, + bitrate_item.temporal_layer); + auto time_series_it = time_series.find(layer); + if (time_series_it == time_series.end()) { + std::string layer_name = GetLayerName(layer); + bool inserted; + std::tie(time_series_it, inserted) = time_series.insert( + std::make_pair(layer, TimeSeries(layer_name, LineStyle::kStep))); + RTC_DCHECK(inserted); + } + float x = config_.GetCallTimeSec(rtcp.log_time_us()); + float y = bitrate_item.target_bitrate_kbps; + time_series_it->second.points.emplace_back(x, y); + } + } + for (auto& layer : time_series) { + plot->AppendTimeSeries(std::move(layer.second)); + } + plot->SetXAxis(config_.CallBeginTimeSec(), config_.CallEndTimeSec(), + "Time (s)", kLeftMargin, kRightMargin); + plot->SetSuggestedYAxis(0, 1, "Bitrate (kbps)", kBottomMargin, kTopMargin); + if (direction == kIncomingPacket) + plot->SetTitle("Target bitrate per incoming layer"); + else + plot->SetTitle("Target bitrate per outgoing layer"); +} + void EventLogAnalyzer::CreateSendSideBweSimulationGraph(Plot* plot) { using RtpPacketType = LoggedRtpPacketOutgoing; using TransportFeedbackType = LoggedRtcpPacketTransportFeedback; diff --git a/rtc_tools/event_log_visualizer/analyzer.h b/rtc_tools/event_log_visualizer/analyzer.h index f8dd3de31d..e0ef0c3d61 100644 --- a/rtc_tools/event_log_visualizer/analyzer.h +++ b/rtc_tools/event_log_visualizer/analyzer.h @@ -81,6 +81,7 @@ class EventLogAnalyzer { bool show_alr_state = false); void CreateStreamBitrateGraph(PacketDirection direction, Plot* plot); + void CreateBitrateAllocationGraph(PacketDirection direction, Plot* plot); void CreateSendSideBweSimulationGraph(Plot* plot); void CreateReceiveSideBweSimulationGraph(Plot* plot); @@ -132,6 +133,25 @@ class EventLogAnalyzer { void PrintNotifications(FILE* file); private: + struct LayerDescription { + LayerDescription(uint32_t ssrc, + uint8_t spatial_layer, + uint8_t temporal_layer) + : ssrc(ssrc), + spatial_layer(spatial_layer), + temporal_layer(temporal_layer) {} + bool operator<(const LayerDescription& other) const { + if (ssrc != other.ssrc) + return ssrc < other.ssrc; + if (spatial_layer != other.spatial_layer) + return spatial_layer < other.spatial_layer; + return temporal_layer < other.temporal_layer; + } + uint32_t ssrc; + uint8_t spatial_layer; + uint8_t temporal_layer; + }; + bool IsRtxSsrc(PacketDirection direction, uint32_t ssrc) const { if (direction == kIncomingPacket) { return parsed_log_.incoming_rtx_ssrcs().find(ssrc) != @@ -200,6 +220,14 @@ class EventLogAnalyzer { return name.str(); } + std::string GetLayerName(LayerDescription layer) const { + char buffer[100]; + rtc::SimpleStringBuilder name(buffer); + name << "SSRC " << layer.ssrc << " sl " << layer.spatial_layer << ", tl " + << layer.temporal_layer; + return name.str(); + } + void Alert_RtpLogTimeGap(PacketDirection direction, float time_seconds, int64_t duration) { diff --git a/rtc_tools/event_log_visualizer/main.cc b/rtc_tools/event_log_visualizer/main.cc index c08b71b658..11824da5df 100644 --- a/rtc_tools/event_log_visualizer/main.cc +++ b/rtc_tools/event_log_visualizer/main.cc @@ -94,6 +94,14 @@ WEBRTC_DEFINE_bool(plot_incoming_stream_bitrate, WEBRTC_DEFINE_bool(plot_outgoing_stream_bitrate, true, "Plot the bitrate used by each outgoing stream."); +WEBRTC_DEFINE_bool(plot_incoming_layer_bitrate_allocation, + false, + "Plot the target bitrate for each incoming layer. Requires " + "incoming RTCP XR with target bitrate to be populated."); +WEBRTC_DEFINE_bool(plot_outgoing_layer_bitrate_allocation, + false, + "Plot the target bitrate for each outgoing layer. Requires " + "outgoing RTCP XR with target bitrate to be populated."); WEBRTC_DEFINE_bool( plot_simulated_receiveside_bwe, false, @@ -343,6 +351,14 @@ int main(int argc, char* argv[]) { analyzer.CreateStreamBitrateGraph(webrtc::kOutgoingPacket, collection->AppendNewPlot()); } + if (FLAG_plot_incoming_layer_bitrate_allocation) { + analyzer.CreateBitrateAllocationGraph(webrtc::kIncomingPacket, + collection->AppendNewPlot()); + } + if (FLAG_plot_outgoing_layer_bitrate_allocation) { + analyzer.CreateBitrateAllocationGraph(webrtc::kOutgoingPacket, + collection->AppendNewPlot()); + } if (FLAG_plot_simulated_receiveside_bwe) { analyzer.CreateReceiveSideBweSimulationGraph(collection->AppendNewPlot()); } @@ -522,6 +538,8 @@ void SetAllPlotFlags(bool setting) { FLAG_plot_outgoing_bitrate = setting; FLAG_plot_incoming_stream_bitrate = setting; FLAG_plot_outgoing_stream_bitrate = setting; + FLAG_plot_incoming_layer_bitrate_allocation = setting; + FLAG_plot_outgoing_layer_bitrate_allocation = setting; FLAG_plot_simulated_receiveside_bwe = setting; FLAG_plot_simulated_sendside_bwe = setting; FLAG_plot_network_delay_feedback = setting;