/* * Copyright (c) 2026 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. */ #ifndef VIDEO_TIMING_SIMULATOR_FRAME_BASE_H_ #define VIDEO_TIMING_SIMULATOR_FRAME_BASE_H_ #include #include #include #include "absl/algorithm/container.h" #include "api/units/time_delta.h" #include "api/units/timestamp.h" namespace webrtc::video_timing_simulator { // CRTP base struct for code reuse of departure and arrival timestamp functions. template struct FrameBase { // Data members are defined in derived struct. // -- CRTP accessor -- const FrameT& self() const { return static_cast(*this); } // -- Value accessors -- // Departure time (possibly offset), as determined by RTP timestamp from // the derived class. Timestamp DepartureTimestamp(Timestamp offset = Timestamp::Zero()) const { int64_t unwrapped_rtp_timestamp = self().unwrapped_rtp_timestamp; if (unwrapped_rtp_timestamp < 0) { return Timestamp::PlusInfinity(); } constexpr int64_t kMicrosPerMillis = 1'000; constexpr int64_t kRtpVideoTicksPerMillis = 90; // Convert from RTP ticks to microseconds using integer division with // truncation. Note that this introduces an error of up to 1us. That is fine // for our purposes however: the arrival timestamp is logged in ms and the // expected frame delay variation caused by the network is also on the order // of ms. int64_t departure_timestamp_us = (unwrapped_rtp_timestamp * kMicrosPerMillis) / kRtpVideoTicksPerMillis; return Timestamp::Micros(departure_timestamp_us - offset.us()); } // Arrival time (possibly offset), as determined by // `ArrivalTimestampInternal()` from the derived class. This allows derived // classes to define themselves the meaning of "arrival": typically decodable // or rendered, but could be assembled or decoded as well. Timestamp ArrivalTimestamp(Timestamp offset = Timestamp::Zero()) const { Timestamp arrival_timestamp = self().ArrivalTimestampInternal(); if (!arrival_timestamp.IsFinite()) { return arrival_timestamp; } return Timestamp::Micros(arrival_timestamp.us() - offset.us()); } // -- Per-frame metrics -- // One way delay with required timestamp offset normalization. TimeDelta OneWayDelay(Timestamp arrival_offset, Timestamp departure_offset) const { return ArrivalTimestamp(arrival_offset) - DepartureTimestamp(departure_offset); } }; // -- Comparators and sorting -- template bool DepartureOrder(const FrameT& a, const FrameT& b) { return a.DepartureTimestamp() < b.DepartureTimestamp(); } template void SortByDepartureOrder(std::vector& frames) { absl::c_stable_sort(frames, DepartureOrder); } template bool ArrivalOrder(const FrameT& a, const FrameT& b) { return a.ArrivalTimestamp() < b.ArrivalTimestamp(); } template void SortByArrivalOrder(std::vector& frames) { absl::c_stable_sort(frames, ArrivalOrder); } template inline bool AssembledOrder(const FrameT& a, const FrameT& b) { return a.assembled_timestamp < b.assembled_timestamp; } template inline void SortByAssembledOrder(std::vector& frames) { absl::c_stable_sort(frames, AssembledOrder); } // --- Inter-frame metrics --- // Difference in packet counts between two frames. template std::optional InterPacketCount(const FrameT& cur, const FrameT& prev) { if (cur.num_packets <= 0 || prev.num_packets <= 0) { return std::nullopt; } return cur.num_packets - prev.num_packets; } // Difference in frame size (bytes) between two frames. template std::optional InterFrameSizeBytes(const FrameT& cur, const FrameT& prev) { if (cur.size.IsZero() || prev.size.IsZero()) { return std::nullopt; } return cur.size.bytes() - prev.size.bytes(); } // Difference in departure timestamp between two frames. template TimeDelta InterDepartureTime(const FrameT& cur, const FrameT& prev) { return cur.DepartureTimestamp() - prev.DepartureTimestamp(); } // Difference in arrival timestamp between two frames. template TimeDelta InterArrivalTime(const FrameT& cur, const FrameT& prev) { return cur.ArrivalTimestamp() - prev.ArrivalTimestamp(); } // https://datatracker.ietf.org/doc/html/rfc5481#section-1 template TimeDelta InterFrameDelayVariation(const FrameT& cur, const FrameT& prev) { return InterArrivalTime(cur, prev) - InterDepartureTime(cur, prev); } // Difference in assembled timestamp between two frames. template TimeDelta InterAssembledTime(const FrameT& cur, const FrameT& prev) { return cur.assembled_timestamp - prev.assembled_timestamp; } } // namespace webrtc::video_timing_simulator #endif // VIDEO_TIMING_SIMULATOR_FRAME_BASE_H_