// Copyright 2023 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CAST_STREAMING_IMPL_STATISTICS_ANALYZER_H_ #define CAST_STREAMING_IMPL_STATISTICS_ANALYZER_H_ #include #include #include #include #include #include "cast/streaming/impl/clock_offset_estimator.h" #include "cast/streaming/impl/statistics_collector.h" #include "cast/streaming/public/statistics.h" #include "platform/api/time.h" #include "util/alarm.h" #include "util/raw_ptr.h" namespace openscreen::cast { class StatisticsAnalyzer { public: StatisticsAnalyzer(SenderStatsClient* stats_client, ClockNowFunctionPtr now, TaskRunner& task_runner, std::unique_ptr offset_estimator); ~StatisticsAnalyzer(); void ScheduleAnalysis(); // Get the statistics collector managed by this analyzer. StatisticsCollector* statistics_collector() { return statistics_collector_.get(); } private: struct FrameStatsAggregate { int event_counter; uint32_t sum_size; Clock::duration sum_delay; }; struct PacketStatsAggregate { int event_counter; uint32_t sum_size; }; struct LatencyStatsAggregate { int data_point_counter; Clock::duration sum_latency; }; struct FrameInfo { Clock::time_point capture_begin_time = Clock::time_point::min(); Clock::time_point capture_end_time = Clock::time_point::min(); Clock::time_point encode_end_time = Clock::time_point::min(); }; struct PacketInfo { Clock::time_point timestamp; StatisticsEvent::Type type; }; struct SessionStats { Clock::time_point first_event_time = Clock::time_point::max(); Clock::time_point last_event_time = Clock::time_point::min(); Clock::time_point last_response_received_time = Clock::time_point::min(); int late_frame_counter = 0; }; // Named std::pair equivalent for audio + video classes. template struct AVPair { T audio; T video; const T& Get(StatisticsEvent::MediaType media_type) const { if (media_type == StatisticsEvent::MediaType::kAudio) { return audio; } OSP_CHECK(media_type == StatisticsEvent::MediaType::kVideo); return video; } T& Get(StatisticsEvent::MediaType media_type) { return const_cast(const_cast(this)->Get(media_type)); } }; using FrameStatsMap = std::map; using PacketStatsMap = std::map; using LatencyStatsMap = std::map; using FrameInfoMap = std::map; using PacketKey = std::pair; using PacketInfoMap = std::map; // Initialize the stats histograms with the preferred min, max, and width. void InitHistograms(); // Takes the Frame and Packet events from the `collector_`, and processes them // into a form expected by `stats_client_`. Then sends the stats, and // schedules a future analysis. void AnalyzeStatistics(); // Constructs a stats list, and sends it to `stats_client_`; void SendStatistics(); // Handles incoming stat events, and adds their infos to all of the proper // stats maps / aggregates. void ProcessFrameEvents(const std::vector& frame_events); void ProcessPacketEvents(const std::vector& packet_events); void RecordFrameLatencies(const FrameEvent& frame_event); void RecordPacketLatencies(const PacketEvent& packet_event); void RecordEventTimes(const StatisticsEvent& event); void ErasePacketInfo(const PacketEvent& packet_event); void AddToLatencyAggregrate(StatisticType latency_stat, Clock::duration latency_delta, StatisticsEvent::MediaType media_type); void AddToHistogram(HistogramType histogram, StatisticsEvent::MediaType media_type, int64_t sample); // Creates a stats list, and populates the entries based on stored stats info // / aggregates for each stat field. SenderStats::StatisticsList ConstructStatisticsList( Clock::time_point end_time, StatisticsEvent::MediaType media_type); void PopulatePacketCountStat(StatisticsEvent::Type event, StatisticType stat, StatisticsEvent::MediaType media_type, SenderStats::StatisticsList& stats_list); void PopulateFrameCountStat(StatisticsEvent::Type event, StatisticType stat, StatisticsEvent::MediaType media_type, SenderStats::StatisticsList& stats_list); void PopulateFpsStat(StatisticsEvent::Type event, StatisticType stat, StatisticsEvent::MediaType media_type, Clock::time_point end_time, SenderStats::StatisticsList& stats_list); void PopulateAvgLatencyStat(StatisticType stat, StatisticsEvent::MediaType media_type, SenderStats::StatisticsList& stats_list); void PopulateFrameBitrateStat(StatisticsEvent::Type event, StatisticType stat, StatisticsEvent::MediaType media_type, Clock::time_point end_time, SenderStats::StatisticsList& stats_list); void PopulatePacketBitrateStat(StatisticsEvent::Type event, StatisticType stat, StatisticsEvent::MediaType media_type, Clock::time_point end_time, SenderStats::StatisticsList& stats_list); void PopulateSessionStats(StatisticsEvent::MediaType media_type, Clock::time_point end_time, SenderStats::StatisticsList& stats_list); // Calculates the offset between the sender and receiver clocks and returns // the sender-side version of this receiver timestamp, if possible. std::optional ToSenderTimestamp( Clock::time_point receiver_timestamp, StatisticsEvent::MediaType media_type) const; // The statistics client to which we report analyzed statistics. const raw_ptr stats_client_; // The statistics collector from which we take the un-analyzed stats packets. std::unique_ptr statistics_collector_; // Keeps track of the best-guess clock offset between the sender and receiver. std::unique_ptr offset_estimator_; // Keep track of time and events for this analyzer. ClockNowFunctionPtr now_; Alarm alarm_; Clock::time_point start_time_; // Maps of frame / packet infos used for stats that rely on seeing multiple // events. For example, network latency is the calculated time difference // between went a packet is sent, and when it is received. AVPair recent_frame_infos_; AVPair recent_packet_infos_; // Aggregate statistics. AVPair frame_stats_; AVPair packet_stats_; AVPair latency_stats_; // Stats that relate to the entirety of the session. For example, total late // frames, or time of last event. AVPair session_stats_; // Histograms. AVPair histograms_; }; } // namespace openscreen::cast #endif // CAST_STREAMING_IMPL_STATISTICS_ANALYZER_H_