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