aboutsummaryrefslogtreecommitdiff
path: root/cast/streaming/rtp_time.h
blob: 0a7f5d86458a8066a90ac5eb76570d1c1323f242 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CAST_STREAMING_RTP_TIME_H_
#define CAST_STREAMING_RTP_TIME_H_

#include <stdint.h>

#include <chrono>
#include <cmath>
#include <limits>
#include <sstream>

#include "cast/streaming/expanded_value_base.h"
#include "platform/api/time.h"

namespace cast {
namespace streaming {

// Forward declarations (see below).
class RtpTimeDelta;
class RtpTimeTicks;

// Convenience operator overloads for logging.
std::ostream& operator<<(std::ostream& out, const RtpTimeDelta rhs);
std::ostream& operator<<(std::ostream& out, const RtpTimeTicks rhs);

// The difference between two RtpTimeTicks values.  This data type is modeled
// off of Chromium's base::TimeDelta, and used for performing compiler-checked
// arithmetic with RtpTimeTicks.
//
// This data type wraps a value, providing only the meaningful set of math
// operations that may be performed on the value.  RtpTimeDeltas may be
// added/subtracted with other RtpTimeDeltas to produce a RtpTimeDelta holding
// the sum/difference.  RtpTimeDeltas may also be multiplied or divided by
// integer amounts.  Finally, RtpTimeDeltas may be divided by other
// RtpTimeDeltas to compute a number of periods (trunc'ed to an integer), or
// modulo each other to determine a time period remainder.
//
// The base class provides bit truncation/extension features for
// wire-formatting, and also the comparison operators.
//
// Usage example:
//
//   // Time math.
//   RtpTimeDelta zero;
//   RtpTimeDelta one_second_later =
//       zero + RtpTimeDelta::FromTicks(kAudioSamplingRate);
//   RtpTimeDelta ten_seconds_later = one_second_later * 10;
//   int64_t ten_periods = ten_seconds_later / one_second_later;
//
//   // Logging convenience.
//   OSP_DLOG_INFO << "The RTP time offset is " << ten_seconds_later;
//
//   // Convert (approximately!) between RTP timebase and microsecond timebase:
//   RtpTimeDelta nine_seconds_in_rtp = ten_seconds_later - one_second_later;
//   using std::chrono::microseconds;
//   microseconds nine_seconds_duration =
//       nine_seconds_in_rtp.ToDuration<microseconds>(kAudioSamplingRate);
//   RtpTimeDelta two_seconds_in_rtp =
//       RtpTimeDelta::FromDuration(std::chrono::seconds(2),
//                                  kAudioSamplingRate);
class RtpTimeDelta : public ExpandedValueBase<int64_t, RtpTimeDelta> {
 public:
  constexpr RtpTimeDelta() : ExpandedValueBase(0) {}

  // Arithmetic operators (with other deltas).
  constexpr RtpTimeDelta operator+(RtpTimeDelta rhs) const {
    return RtpTimeDelta(value_ + rhs.value_);
  }
  constexpr RtpTimeDelta operator-(RtpTimeDelta rhs) const {
    return RtpTimeDelta(value_ - rhs.value_);
  }
  constexpr RtpTimeDelta& operator+=(RtpTimeDelta rhs) {
    return (*this = (*this + rhs));
  }
  constexpr RtpTimeDelta& operator-=(RtpTimeDelta rhs) {
    return (*this = (*this - rhs));
  }
  constexpr RtpTimeDelta operator-() const { return RtpTimeDelta(-value_); }

  // Multiplicative operators (with other deltas).
  constexpr int64_t operator/(RtpTimeDelta rhs) const {
    return value_ / rhs.value_;
  }
  constexpr RtpTimeDelta operator%(RtpTimeDelta rhs) const {
    return RtpTimeDelta(value_ % rhs.value_);
  }
  constexpr RtpTimeDelta& operator%=(RtpTimeDelta rhs) {
    return (*this = (*this % rhs));
  }

  // Multiplicative operators (with integer types).
  template <typename IntType>
  constexpr RtpTimeDelta operator*(IntType rhs) const {
    static_assert(std::numeric_limits<IntType>::is_integer,
                  "|rhs| must be a POD integer type");
    return RtpTimeDelta(value_ * rhs);
  }
  template <typename IntType>
  constexpr RtpTimeDelta operator/(IntType rhs) const {
    static_assert(std::numeric_limits<IntType>::is_integer,
                  "|rhs| must be a POD integer type");
    return RtpTimeDelta(value_ / rhs);
  }
  template <typename IntType>
  constexpr RtpTimeDelta& operator*=(IntType rhs) {
    return (*this = (*this * rhs));
  }
  template <typename IntType>
  constexpr RtpTimeDelta& operator/=(IntType rhs) {
    return (*this = (*this / rhs));
  }

  // Maps this RtpTimeDelta to an approximate std::chrono::duration using the
  // given RTP timebase.  Assumes a zero-valued Duration corresponds to a
  // zero-valued RtpTimeDelta.
  template <typename Duration>
  Duration ToDuration(int rtp_timebase) const {
    OSP_DCHECK_GT(rtp_timebase, 0);
    constexpr Duration kOneSecond =
        std::chrono::duration_cast<Duration>(std::chrono::seconds(1));
    return Duration(ToNearestRepresentativeValue<typename Duration::rep>(
        static_cast<double>(value_) / rtp_timebase * kOneSecond.count()));
  }

  // Maps the |duration| to an approximate RtpTimeDelta using the given RTP
  // timebase.  Assumes a zero-valued Duration corresponds to a zero-valued
  // RtpTimeDelta.
  template <typename Duration>
  static constexpr RtpTimeDelta FromDuration(Duration duration,
                                             int rtp_timebase) {
    constexpr Duration kOneSecond =
        std::chrono::duration_cast<Duration>(std::chrono::seconds(1));
    static_assert(kOneSecond > Duration::zero(),
                  "Duration is too coarse-grained to represent one second.");
    return RtpTimeDelta(ToNearestRepresentativeValue<int64_t>(
        static_cast<double>(duration.count()) / kOneSecond.count() *
        rtp_timebase));
  }

  // Construct a RtpTimeDelta from an exact number of ticks.
  static constexpr RtpTimeDelta FromTicks(int64_t ticks) {
    return RtpTimeDelta(ticks);
  }

 private:
  friend class ExpandedValueBase<int64_t, RtpTimeDelta>;
  friend class RtpTimeTicks;
  friend std::ostream& operator<<(std::ostream& out, const RtpTimeDelta rhs);

  constexpr explicit RtpTimeDelta(int64_t ticks) : ExpandedValueBase(ticks) {}

  constexpr int64_t value() const { return value_; }

  template <typename Rep>
  static Rep ToNearestRepresentativeValue(double ticks) {
    if (ticks <= std::numeric_limits<Rep>::min()) {
      return std::numeric_limits<Rep>::min();
    } else if (ticks >= std::numeric_limits<Rep>::max()) {
      return std::numeric_limits<Rep>::max();
    }

    static_assert(
        std::is_floating_point<Rep>::value ||
            (std::is_integral<Rep>::value &&
             sizeof(Rep) <= sizeof(decltype(llround(ticks)))),
        "Rep must be an integer (<= 64 bits) or a floating-point type.");
    if (std::is_floating_point<Rep>::value) {
      return Rep(ticks);
    }
    if (sizeof(Rep) <= sizeof(decltype(lround(ticks)))) {
      return Rep(lround(ticks));
    }
    return Rep(llround(ticks));
  }
};

// A media timestamp whose timebase matches the periodicity of the content
// (e.g., for audio, the timebase would be the sampling frequency).  This data
// type is modeled off of Chromium's base::TimeTicks.
//
// This data type wraps a value, providing only the meaningful set of math
// operations that may be performed on the value.  The difference between two
// RtpTimeTicks is a RtpTimeDelta.  Likewise, adding or subtracting a
// RtpTimeTicks with a RtpTimeDelta produces an off-set RtpTimeTicks.
//
// The base class provides bit truncation/extension features for
// wire-formatting, and also the comparison operators.
//
// Usage example:
//
//   // Time math.
//   RtpTimeTicks origin;
//   RtpTimeTicks at_one_second =
//       origin + RtpTimeDelta::FromTicks(kAudioSamplingRate);
//   RtpTimeTicks at_two_seconds =
//       at_one_second + RtpTimeDelta::FromTicks(kAudioSamplingRate);
//   RtpTimeDelta elasped_in_between = at_two_seconds - at_one_second;
//   RtpTimeDelta thrice_as_much_elasped = elasped_in_between * 3;
//   RtpTimeTicks at_four_seconds = at_one_second + thrice_as_much_elasped;
//
//   // Logging convenience.
//   OSP_DLOG_INFO << "The RTP timestamp is " << at_four_seconds;
//
//   // Convert (approximately!) between RTP timebase and stream time offsets in
//   // microsecond timebase:
//   using std::chrono::microseconds;
//   microseconds four_seconds_since_stream_start =
//       at_four_seconds.ToTimeSinceOrigin<microseconds>(kAudioSamplingRate);
//   RtpTimeTicks at_three_seconds = RtpTimeDelta::FromTimeSinceOrigin(
//       std::chrono::seconds(3), kAudioSamplingRate);
class RtpTimeTicks : public ExpandedValueBase<int64_t, RtpTimeTicks> {
 public:
  constexpr RtpTimeTicks() : ExpandedValueBase(0) {}

  // Compute the difference between two RtpTimeTickses.
  constexpr RtpTimeDelta operator-(RtpTimeTicks rhs) const {
    return RtpTimeDelta(value_ - rhs.value_);
  }

  // Return a new RtpTimeTicks before or after this one.
  constexpr RtpTimeTicks operator+(RtpTimeDelta rhs) const {
    return RtpTimeTicks(value_ + rhs.value());
  }
  constexpr RtpTimeTicks operator-(RtpTimeDelta rhs) const {
    return RtpTimeTicks(value_ - rhs.value());
  }
  constexpr RtpTimeTicks& operator+=(RtpTimeDelta rhs) {
    return (*this = (*this + rhs));
  }
  constexpr RtpTimeTicks& operator-=(RtpTimeDelta rhs) {
    return (*this = (*this - rhs));
  }

  // Maps this RtpTimeTicks to an approximate std::chrono::duration representing
  // the amount of time since the origin point (e.g., the start of a stream)
  // using the given |rtp_timebase|.  Assumes a zero-valued Duration corresponds
  // to a zero-valued RtpTimeTicks.
  template <typename Duration>
  Duration ToTimeSinceOrigin(int rtp_timebase) const {
    return (*this - RtpTimeTicks()).ToDuration<Duration>(rtp_timebase);
  }

  // Maps the |time_since_origin| to an approximate RtpTimeTicks using the given
  // RTP timebase.  Assumes a zero-valued Duration corresponds to a zero-valued
  // RtpTimeTicks.
  template <typename Duration>
  static constexpr RtpTimeTicks FromTimeSinceOrigin(Duration time_since_origin,
                                                    int rtp_timebase) {
    return RtpTimeTicks() +
           RtpTimeDelta::FromDuration(time_since_origin, rtp_timebase);
  }

 private:
  friend class ExpandedValueBase<int64_t, RtpTimeTicks>;
  friend std::ostream& operator<<(std::ostream& out, const RtpTimeTicks rhs);

  constexpr explicit RtpTimeTicks(int64_t value) : ExpandedValueBase(value) {}

  constexpr int64_t value() const { return value_; }
};

}  // namespace streaming
}  // namespace cast

#endif  // CAST_STREAMING_RTP_TIME_H_