| // 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 <map> |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| #include <vector> |
| |
| #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" |
| |
| namespace openscreen::cast { |
| |
| class StatisticsAnalyzer { |
| public: |
| StatisticsAnalyzer(SenderStatsClient* stats_client, |
| ClockNowFunctionPtr now, |
| TaskRunner& task_runner, |
| std::unique_ptr<ClockOffsetEstimator> 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 <typename T> |
| 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<T&>(const_cast<const AVPair*>(this)->Get(media_type)); |
| } |
| }; |
| |
| using FrameStatsMap = std::map<StatisticsEvent::Type, FrameStatsAggregate>; |
| using PacketStatsMap = std::map<StatisticsEvent::Type, PacketStatsAggregate>; |
| using LatencyStatsMap = std::map<StatisticType, LatencyStatsAggregate>; |
| |
| using FrameInfoMap = std::map<RtpTimeTicks, FrameInfo>; |
| using PacketKey = std::pair<RtpTimeTicks, uint16_t>; |
| using PacketInfoMap = std::map<PacketKey, PacketInfo>; |
| |
| // 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<FrameEvent>& frame_events); |
| void ProcessPacketEvents(const std::vector<PacketEvent>& 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<Clock::time_point> ToSenderTimestamp( |
| Clock::time_point receiver_timestamp, |
| StatisticsEvent::MediaType media_type) const; |
| |
| // The statistics client to which we report analyzed statistics. |
| SenderStatsClient* const stats_client_; |
| |
| // The statistics collector from which we take the un-analyzed stats packets. |
| std::unique_ptr<StatisticsCollector> statistics_collector_; |
| |
| // Keeps track of the best-guess clock offset between the sender and receiver. |
| std::unique_ptr<ClockOffsetEstimator> 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<FrameInfoMap> recent_frame_infos_; |
| AVPair<PacketInfoMap> recent_packet_infos_; |
| |
| // Aggregate statistics. |
| AVPair<FrameStatsMap> frame_stats_; |
| AVPair<PacketStatsMap> packet_stats_; |
| AVPair<LatencyStatsMap> latency_stats_; |
| |
| // Stats that relate to the entirety of the session. For example, total late |
| // frames, or time of last event. |
| AVPair<SessionStats> session_stats_; |
| |
| // Histograms. |
| AVPair<SenderStats::HistogramsList> histograms_; |
| }; |
| |
| } // namespace openscreen::cast |
| |
| #endif // CAST_STREAMING_IMPL_STATISTICS_ANALYZER_H_ |