1 // Copyright 2015 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef CAST_STREAMING_RTP_TIME_H_ 6 #define CAST_STREAMING_RTP_TIME_H_ 7 8 #include <stdint.h> 9 10 #include <chrono> 11 #include <cmath> 12 #include <limits> 13 #include <sstream> 14 #include <type_traits> 15 16 #include "cast/streaming/expanded_value_base.h" 17 #include "platform/api/time.h" 18 #include "util/saturate_cast.h" 19 20 namespace openscreen { 21 namespace cast { 22 23 // Forward declarations (see below). 24 class RtpTimeDelta; 25 class RtpTimeTicks; 26 27 // Convenience operator overloads for logging. 28 std::ostream& operator<<(std::ostream& out, const RtpTimeDelta rhs); 29 std::ostream& operator<<(std::ostream& out, const RtpTimeTicks rhs); 30 31 // The difference between two RtpTimeTicks values. This data type is modeled 32 // off of Chromium's base::TimeDelta, and used for performing compiler-checked 33 // arithmetic with RtpTimeTicks. 34 // 35 // This data type wraps a value, providing only the meaningful set of math 36 // operations that may be performed on the value. RtpTimeDeltas may be 37 // added/subtracted with other RtpTimeDeltas to produce a RtpTimeDelta holding 38 // the sum/difference. RtpTimeDeltas may also be multiplied or divided by 39 // integer amounts. Finally, RtpTimeDeltas may be divided by other 40 // RtpTimeDeltas to compute a number of periods (trunc'ed to an integer), or 41 // modulo each other to determine a time period remainder. 42 // 43 // The base class provides bit truncation/extension features for 44 // wire-formatting, and also the comparison operators. 45 // 46 // Usage example: 47 // 48 // // Time math. 49 // RtpTimeDelta zero; 50 // RtpTimeDelta one_second_later = 51 // zero + RtpTimeDelta::FromTicks(kAudioSamplingRate); 52 // RtpTimeDelta ten_seconds_later = one_second_later * 10; 53 // int64_t ten_periods = ten_seconds_later / one_second_later; 54 // 55 // // Logging convenience. 56 // OSP_DLOG_INFO << "The RTP time offset is " << ten_seconds_later; 57 // 58 // // Convert (approximately!) between RTP timebase and microsecond timebase: 59 // RtpTimeDelta nine_seconds_in_rtp = ten_seconds_later - one_second_later; 60 // using std::chrono::microseconds; 61 // microseconds nine_seconds_duration = 62 // nine_seconds_in_rtp.ToDuration<microseconds>(kAudioSamplingRate); 63 // RtpTimeDelta two_seconds_in_rtp = 64 // RtpTimeDelta::FromDuration(std::chrono::seconds(2), 65 // kAudioSamplingRate); 66 class RtpTimeDelta : public ExpandedValueBase<int64_t, RtpTimeDelta> { 67 public: RtpTimeDelta()68 constexpr RtpTimeDelta() : ExpandedValueBase(0) {} 69 70 // Arithmetic operators (with other deltas). 71 constexpr RtpTimeDelta operator+(RtpTimeDelta rhs) const { 72 return RtpTimeDelta(value_ + rhs.value_); 73 } 74 constexpr RtpTimeDelta operator-(RtpTimeDelta rhs) const { 75 return RtpTimeDelta(value_ - rhs.value_); 76 } 77 constexpr RtpTimeDelta& operator+=(RtpTimeDelta rhs) { 78 return (*this = (*this + rhs)); 79 } 80 constexpr RtpTimeDelta& operator-=(RtpTimeDelta rhs) { 81 return (*this = (*this - rhs)); 82 } 83 constexpr RtpTimeDelta operator-() const { return RtpTimeDelta(-value_); } 84 85 // Multiplicative operators (with other deltas). 86 constexpr int64_t operator/(RtpTimeDelta rhs) const { 87 return value_ / rhs.value_; 88 } 89 constexpr RtpTimeDelta operator%(RtpTimeDelta rhs) const { 90 return RtpTimeDelta(value_ % rhs.value_); 91 } 92 constexpr RtpTimeDelta& operator%=(RtpTimeDelta rhs) { 93 return (*this = (*this % rhs)); 94 } 95 96 // Multiplicative operators (with integer types). 97 template <typename IntType> 98 constexpr RtpTimeDelta operator*(IntType rhs) const { 99 static_assert(std::numeric_limits<IntType>::is_integer, 100 "|rhs| must be a POD integer type"); 101 return RtpTimeDelta(value_ * rhs); 102 } 103 template <typename IntType> 104 constexpr RtpTimeDelta operator/(IntType rhs) const { 105 static_assert(std::numeric_limits<IntType>::is_integer, 106 "|rhs| must be a POD integer type"); 107 return RtpTimeDelta(value_ / rhs); 108 } 109 template <typename IntType> 110 constexpr RtpTimeDelta& operator*=(IntType rhs) { 111 return (*this = (*this * rhs)); 112 } 113 template <typename IntType> 114 constexpr RtpTimeDelta& operator/=(IntType rhs) { 115 return (*this = (*this / rhs)); 116 } 117 118 // Maps this RtpTimeDelta to an approximate std::chrono::duration using the 119 // given RTP timebase. Assumes a zero-valued Duration corresponds to a 120 // zero-valued RtpTimeDelta. 121 template <typename Duration> ToDuration(int rtp_timebase)122 Duration ToDuration(int rtp_timebase) const { 123 OSP_DCHECK_GT(rtp_timebase, 0); 124 constexpr Duration kOneSecond = 125 std::chrono::duration_cast<Duration>(std::chrono::seconds(1)); 126 return Duration(ToNearestRepresentativeValue<typename Duration::rep>( 127 static_cast<double>(value_) / rtp_timebase * kOneSecond.count())); 128 } 129 130 // Maps the |duration| to an approximate RtpTimeDelta using the given RTP 131 // timebase. Assumes a zero-valued Duration corresponds to a zero-valued 132 // RtpTimeDelta. 133 template <typename Duration> FromDuration(Duration duration,int rtp_timebase)134 static constexpr RtpTimeDelta FromDuration(Duration duration, 135 int rtp_timebase) { 136 constexpr Duration kOneSecond = 137 std::chrono::duration_cast<Duration>(std::chrono::seconds(1)); 138 static_assert(kOneSecond > Duration::zero(), 139 "Duration is too coarse-grained to represent one second."); 140 return RtpTimeDelta(ToNearestRepresentativeValue<int64_t>( 141 static_cast<double>(duration.count()) / kOneSecond.count() * 142 rtp_timebase)); 143 } 144 145 // Construct a RtpTimeDelta from an exact number of ticks. FromTicks(int64_t ticks)146 static constexpr RtpTimeDelta FromTicks(int64_t ticks) { 147 return RtpTimeDelta(ticks); 148 } 149 150 private: 151 friend class ExpandedValueBase<int64_t, RtpTimeDelta>; 152 friend class RtpTimeTicks; 153 friend std::ostream& operator<<(std::ostream& out, const RtpTimeDelta rhs); 154 RtpTimeDelta(int64_t ticks)155 constexpr explicit RtpTimeDelta(int64_t ticks) : ExpandedValueBase(ticks) {} 156 value()157 constexpr int64_t value() const { return value_; } 158 159 template <typename Rep> 160 static std::enable_if_t<std::is_floating_point<Rep>::value, Rep> ToNearestRepresentativeValue(double ticks)161 ToNearestRepresentativeValue(double ticks) { 162 return Rep(ticks); 163 } 164 165 template <typename Rep> 166 static std::enable_if_t<std::is_integral<Rep>::value, Rep> ToNearestRepresentativeValue(double ticks)167 ToNearestRepresentativeValue(double ticks) { 168 return rounded_saturate_cast<Rep>(ticks); 169 } 170 }; 171 172 // A media timestamp whose timebase matches the periodicity of the content 173 // (e.g., for audio, the timebase would be the sampling frequency). This data 174 // type is modeled off of Chromium's base::TimeTicks. 175 // 176 // This data type wraps a value, providing only the meaningful set of math 177 // operations that may be performed on the value. The difference between two 178 // RtpTimeTicks is a RtpTimeDelta. Likewise, adding or subtracting a 179 // RtpTimeTicks with a RtpTimeDelta produces an off-set RtpTimeTicks. 180 // 181 // The base class provides bit truncation/extension features for 182 // wire-formatting, and also the comparison operators. 183 // 184 // Usage example: 185 // 186 // // Time math. 187 // RtpTimeTicks origin; 188 // RtpTimeTicks at_one_second = 189 // origin + RtpTimeDelta::FromTicks(kAudioSamplingRate); 190 // RtpTimeTicks at_two_seconds = 191 // at_one_second + RtpTimeDelta::FromTicks(kAudioSamplingRate); 192 // RtpTimeDelta elasped_in_between = at_two_seconds - at_one_second; 193 // RtpTimeDelta thrice_as_much_elasped = elasped_in_between * 3; 194 // RtpTimeTicks at_four_seconds = at_one_second + thrice_as_much_elasped; 195 // 196 // // Logging convenience. 197 // OSP_DLOG_INFO << "The RTP timestamp is " << at_four_seconds; 198 // 199 // // Convert (approximately!) between RTP timebase and stream time offsets in 200 // // microsecond timebase: 201 // using std::chrono::microseconds; 202 // microseconds four_seconds_since_stream_start = 203 // at_four_seconds.ToTimeSinceOrigin<microseconds>(kAudioSamplingRate); 204 // RtpTimeTicks at_three_seconds = RtpTimeDelta::FromTimeSinceOrigin( 205 // std::chrono::seconds(3), kAudioSamplingRate); 206 class RtpTimeTicks : public ExpandedValueBase<int64_t, RtpTimeTicks> { 207 public: RtpTimeTicks()208 constexpr RtpTimeTicks() : ExpandedValueBase(0) {} 209 210 // Compute the difference between two RtpTimeTickses. 211 constexpr RtpTimeDelta operator-(RtpTimeTicks rhs) const { 212 return RtpTimeDelta(value_ - rhs.value_); 213 } 214 215 // Return a new RtpTimeTicks before or after this one. 216 constexpr RtpTimeTicks operator+(RtpTimeDelta rhs) const { 217 return RtpTimeTicks(value_ + rhs.value()); 218 } 219 constexpr RtpTimeTicks operator-(RtpTimeDelta rhs) const { 220 return RtpTimeTicks(value_ - rhs.value()); 221 } 222 constexpr RtpTimeTicks& operator+=(RtpTimeDelta rhs) { 223 return (*this = (*this + rhs)); 224 } 225 constexpr RtpTimeTicks& operator-=(RtpTimeDelta rhs) { 226 return (*this = (*this - rhs)); 227 } 228 229 // Maps this RtpTimeTicks to an approximate std::chrono::duration representing 230 // the amount of time since the origin point (e.g., the start of a stream) 231 // using the given |rtp_timebase|. Assumes a zero-valued Duration corresponds 232 // to a zero-valued RtpTimeTicks. 233 template <typename Duration> ToTimeSinceOrigin(int rtp_timebase)234 Duration ToTimeSinceOrigin(int rtp_timebase) const { 235 return (*this - RtpTimeTicks()).ToDuration<Duration>(rtp_timebase); 236 } 237 238 // Maps the |time_since_origin| to an approximate RtpTimeTicks using the given 239 // RTP timebase. Assumes a zero-valued Duration corresponds to a zero-valued 240 // RtpTimeTicks. 241 template <typename Duration> FromTimeSinceOrigin(Duration time_since_origin,int rtp_timebase)242 static constexpr RtpTimeTicks FromTimeSinceOrigin(Duration time_since_origin, 243 int rtp_timebase) { 244 return RtpTimeTicks() + 245 RtpTimeDelta::FromDuration(time_since_origin, rtp_timebase); 246 } 247 248 private: 249 friend class ExpandedValueBase<int64_t, RtpTimeTicks>; 250 friend std::ostream& operator<<(std::ostream& out, const RtpTimeTicks rhs); 251 RtpTimeTicks(int64_t value)252 constexpr explicit RtpTimeTicks(int64_t value) : ExpandedValueBase(value) {} 253 value()254 constexpr int64_t value() const { return value_; } 255 }; 256 257 } // namespace cast 258 } // namespace openscreen 259 260 #endif // CAST_STREAMING_RTP_TIME_H_ 261