Bug 1858921 - Part 6: Remove unused default template arguments r=sfink
[gecko.git] / dom / media / TimeUnits.h
blob05d81c41c9b88a74dab3ae29a91c104c427bf59d
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef TIME_UNITS_H
8 #define TIME_UNITS_H
10 #include <limits>
11 #include <type_traits>
13 #include "Intervals.h"
14 #include "mozilla/CheckedInt.h"
15 #include "mozilla/FloatingPoint.h"
16 #include "mozilla/Maybe.h"
17 #include "mozilla/TimeStamp.h"
18 #include "mozilla/IntegerPrintfMacros.h"
19 #include "nsPrintfCString.h"
21 namespace mozilla::media {
22 class TimeIntervals;
23 } // namespace mozilla::media
24 // CopyChooser specialization for nsTArray
25 template <>
26 struct nsTArray_RelocationStrategy<mozilla::media::TimeIntervals> {
27 using Type =
28 nsTArray_RelocateUsingMoveConstructor<mozilla::media::TimeIntervals>;
31 namespace mozilla {
33 // Number of milliseconds per second. 1e3.
34 static const int64_t MSECS_PER_S = 1000;
36 // Number of microseconds per second. 1e6.
37 static const int64_t USECS_PER_S = 1000000;
39 // Number of nanoseconds per second. 1e9.
40 static const int64_t NSECS_PER_S = 1000000000;
42 namespace media {
44 #ifndef PROCESS_DECODE_LOG
45 # define PROCESS_DECODE_LOG(sample) \
46 MOZ_LOG(sPDMLog, mozilla::LogLevel::Verbose, \
47 ("ProcessDecode: mDuration=%" PRIu64 "µs ; mTime=%" PRIu64 \
48 "µs ; mTimecode=%" PRIu64 "µs", \
49 (sample)->mDuration.ToMicroseconds(), \
50 (sample)->mTime.ToMicroseconds(), \
51 (sample)->mTimecode.ToMicroseconds()))
52 #endif // PROCESS_DECODE_LOG
54 // TimeUnit is a class that represents a time value, that can be negative or
55 // positive.
57 // Internally, it works by storing a numerator (the tick numbers), that uses
58 // checked arithmetics, and a denominator (the base), that is a regular integer
59 // on which arithmetics is never performed, and is only set at construction, or
60 // replaced.
62 // Dividing the tick count by the base always yields a value in seconds,
63 // but it's very useful to have a base that is dependant on the context: it can
64 // be the sample-rate of an audio stream, the time base of an mp4, that's often
65 // 90000 because it's divisible by 24, 25 and 30.
67 // Keeping time like this allows performing calculations on time values with
68 // maximum precision, without having to have to care about rounding errors or
69 // precision loss.
71 // If not specified, the base is 1e6, representing microseconds, for historical
72 // reasons. Users can gradually move to more precise representations when
73 // needed.
75 // INT64_MAX has the special meaning of being +∞, and INT64_MIN means -∞. Any
76 // other value corresponds to a valid time.
78 // If an overflow or other problem occurs, the underlying CheckedInt<int64_t> is
79 // invalid and a crash is triggered.
80 class TimeUnit final {
81 public:
82 constexpr TimeUnit(CheckedInt64 aTicks, int64_t aBase)
83 : mTicks(aTicks), mBase(aBase) {
84 MOZ_RELEASE_ASSERT(mBase > 0);
87 explicit constexpr TimeUnit(CheckedInt64 aTicks)
88 : mTicks(aTicks), mBase(USECS_PER_S) {}
90 // Return the maximum number of ticks that a TimeUnit can contain.
91 static constexpr int64_t MaxTicks() {
92 return std::numeric_limits<int64_t>::max() - 1;
95 // This is only precise up to a point, which is aValue * aBase <= 2^53 - 1
96 static TimeUnit FromSeconds(double aValue, int64_t aBase = USECS_PER_S);
97 static constexpr TimeUnit FromMicroseconds(int64_t aValue) {
98 return TimeUnit(aValue, USECS_PER_S);
100 static TimeUnit FromHns(int64_t aValue, int64_t aBase) {
101 // Truncating here would mean a loss of precision.
102 return TimeUnit::FromNanoseconds(aValue * 100).ToBase<RoundPolicy>(aBase);
104 static constexpr TimeUnit FromNanoseconds(int64_t aValue) {
105 return TimeUnit(aValue, NSECS_PER_S);
107 static TimeUnit FromInfinity();
108 static TimeUnit FromNegativeInfinity();
109 static TimeUnit FromTimeDuration(const TimeDuration& aDuration);
110 static constexpr TimeUnit Zero(int64_t aBase = USECS_PER_S) {
111 return TimeUnit(0, aBase);
113 static constexpr TimeUnit Zero(const TimeUnit& aOther) {
114 return TimeUnit(0, aOther.mBase);
116 static TimeUnit Invalid();
117 int64_t ToMilliseconds() const;
118 int64_t ToMicroseconds() const;
119 int64_t ToNanoseconds() const;
120 int64_t ToTicksAtRate(int64_t aRate) const;
121 double ToSeconds() const;
122 nsCString ToString() const;
123 TimeDuration ToTimeDuration() const;
124 bool IsInfinite() const;
125 bool IsPositive() const;
126 bool IsPositiveOrZero() const;
127 bool IsZero() const;
128 bool IsNegative() const;
130 // Returns true if the fractions are equal when converted to the smallest
131 // base.
132 bool EqualsAtLowestResolution(const TimeUnit& aOther) const;
133 // Strict equality -- the fractions must be exactly equal
134 bool operator==(const TimeUnit& aOther) const;
135 bool operator!=(const TimeUnit& aOther) const;
136 bool operator>=(const TimeUnit& aOther) const;
137 bool operator>(const TimeUnit& aOther) const;
138 bool operator<=(const TimeUnit& aOther) const;
139 bool operator<(const TimeUnit& aOther) const;
140 TimeUnit operator%(const TimeUnit& aOther) const;
141 TimeUnit operator+(const TimeUnit& aOther) const;
142 TimeUnit operator-(const TimeUnit& aOther) const;
143 TimeUnit& operator+=(const TimeUnit& aOther);
144 TimeUnit& operator-=(const TimeUnit& aOther);
145 template <typename T>
146 TimeUnit operator*(T aVal) const {
147 // See bug 853398 for the reason to block double multiplier.
148 // If required, use MultDouble below and with caution.
149 static_assert(std::is_integral_v<T>, "Must be an integral type");
150 return TimeUnit(mTicks * aVal, mBase);
152 TimeUnit MultDouble(double aVal) const;
153 friend TimeUnit operator/(const TimeUnit& aUnit, int64_t aVal) {
154 MOZ_DIAGNOSTIC_ASSERT(0 <= aVal && aVal <= UINT32_MAX);
155 return TimeUnit(aUnit.mTicks / aVal, aUnit.mBase);
157 friend TimeUnit operator%(const TimeUnit& aUnit, int64_t aVal) {
158 MOZ_DIAGNOSTIC_ASSERT(0 <= aVal && aVal <= UINT32_MAX);
159 return TimeUnit(aUnit.mTicks % aVal, aUnit.mBase);
162 struct TruncatePolicy {
163 template <typename T>
164 static T policy(T& aValue) {
165 return static_cast<T>(aValue);
169 struct RoundPolicy {
170 template <typename T>
171 static T policy(T& aValue) {
172 return std::round(aValue);
176 struct CeilingPolicy {
177 template <typename T>
178 static T policy(T& aValue) {
179 return std::ceil(aValue);
183 template <class RoundingPolicy = TruncatePolicy>
184 TimeUnit ToBase(int64_t aTargetBase) const {
185 double dummy = 0.0;
186 return ToBase<RoundingPolicy>(aTargetBase, dummy);
189 template <class RoundingPolicy = TruncatePolicy>
190 TimeUnit ToBase(const TimeUnit& aTimeUnit) const {
191 double dummy = 0.0;
192 return ToBase<RoundingPolicy>(aTimeUnit, dummy);
195 // Allow returning the same value, in a base that matches another TimeUnit.
196 template <class RoundingPolicy = TruncatePolicy>
197 TimeUnit ToBase(const TimeUnit& aTimeUnit, double& aOutError) const {
198 int64_t targetBase = aTimeUnit.mBase;
199 return ToBase<RoundingPolicy>(targetBase, aOutError);
202 template <class RoundingPolicy = TruncatePolicy>
203 TimeUnit ToBase(int64_t aTargetBase, double& aOutError) const {
204 aOutError = 0.0;
205 CheckedInt<int64_t> ticks = mTicks * aTargetBase;
206 if (ticks.isValid()) {
207 imaxdiv_t rv = imaxdiv(ticks.value(), mBase);
208 if (!rv.rem) {
209 return TimeUnit(rv.quot, aTargetBase);
212 double approx = static_cast<double>(mTicks.value()) *
213 static_cast<double>(aTargetBase) /
214 static_cast<double>(mBase);
215 double integer;
216 aOutError = modf(approx, &integer);
217 return TimeUnit(AssertedCast<int64_t>(RoundingPolicy::policy(approx)),
218 aTargetBase);
221 bool IsValid() const;
223 constexpr TimeUnit() = default;
225 TimeUnit(const TimeUnit&) = default;
227 TimeUnit& operator=(const TimeUnit&) = default;
229 bool IsPosInf() const;
230 bool IsNegInf() const;
232 // Allow serializing a TimeUnit via IPC
233 friend IPC::ParamTraits<mozilla::media::TimeUnit>;
235 #ifndef VISIBLE_TIMEUNIT_INTERNALS
236 private:
237 #endif
238 int64_t ToCommonUnit(int64_t aRatio) const;
239 // Reduce a TimeUnit to the smallest possible ticks and base. This is useful
240 // to comparison with big time values that can otherwise overflow.
241 TimeUnit Reduced() const;
243 CheckedInt64 mTicks{0};
244 // Default base is microseconds.
245 int64_t mBase{USECS_PER_S};
248 using NullableTimeUnit = Maybe<TimeUnit>;
250 using TimeInterval = Interval<TimeUnit>;
252 // A set of intervals, containing TimeUnit.
253 class TimeIntervals : public IntervalSet<TimeUnit> {
254 public:
255 using BaseType = IntervalSet<TimeUnit>;
256 using InnerType = TimeUnit;
258 // We can't use inherited constructors yet. So we have to duplicate all the
259 // constructors found in IntervalSet base class.
260 // all this could be later replaced with:
261 // using IntervalSet<TimeUnit>::IntervalSet;
263 // MOZ_IMPLICIT as we want to enable initialization in the form:
264 // TimeIntervals i = ... like we would do with IntervalSet<T> i = ...
265 MOZ_IMPLICIT TimeIntervals(const BaseType& aOther) : BaseType(aOther) {}
266 MOZ_IMPLICIT TimeIntervals(BaseType&& aOther) : BaseType(std::move(aOther)) {}
267 explicit TimeIntervals(const BaseType::ElemType& aOther) : BaseType(aOther) {}
268 explicit TimeIntervals(BaseType::ElemType&& aOther)
269 : BaseType(std::move(aOther)) {}
271 static TimeIntervals Invalid() {
272 return TimeIntervals(TimeInterval(TimeUnit::FromNegativeInfinity(),
273 TimeUnit::FromNegativeInfinity()));
275 bool IsInvalid() const {
276 return Length() == 1 && Start(0).IsNegInf() && End(0).IsNegInf();
279 // Returns the same interval, with a given base resolution.
280 TimeIntervals ToBase(const TimeUnit& aBase) const {
281 TimeIntervals output;
282 for (const auto& interval : mIntervals) {
283 TimeInterval convertedInterval{interval.mStart.ToBase(aBase),
284 interval.mEnd.ToBase(aBase),
285 interval.mFuzz.ToBase(aBase)};
286 output += convertedInterval;
288 return output;
291 // Returns the same interval, with a microsecond resolution. This is used to
292 // compare TimeUnits internal to demuxers (that use a base from the container)
293 // to floating point numbers in seconds from content.
294 TimeIntervals ToMicrosecondResolution() const {
295 TimeIntervals output;
297 for (const auto& interval : mIntervals) {
298 TimeInterval reducedPrecision{interval.mStart.ToBase(USECS_PER_S),
299 interval.mEnd.ToBase(USECS_PER_S),
300 interval.mFuzz.ToBase(USECS_PER_S)};
301 output += reducedPrecision;
303 return output;
306 nsCString ToString() const {
307 nsCString dump;
308 for (const auto& interval : mIntervals) {
309 dump += nsPrintfCString("[%s],", interval.ToString().get());
311 return dump;
314 TimeIntervals() = default;
317 using TimeRange = Interval<double>;
319 // A set of intervals, containing doubles that are seconds.
320 class TimeRanges : public IntervalSet<double> {
321 public:
322 using BaseType = IntervalSet<double>;
323 using InnerType = double;
324 using nld = std::numeric_limits<double>;
326 // We can't use inherited constructors yet. So we have to duplicate all the
327 // constructors found in IntervalSet base class.
328 // all this could be later replaced with:
329 // using IntervalSet<TimeUnit>::IntervalSet;
331 // MOZ_IMPLICIT as we want to enable initialization in the form:
332 // TimeIntervals i = ... like we would do with IntervalSet<T> i = ...
333 MOZ_IMPLICIT TimeRanges(const BaseType& aOther) : BaseType(aOther) {}
334 MOZ_IMPLICIT TimeRanges(BaseType&& aOther) : BaseType(std::move(aOther)) {}
335 explicit TimeRanges(const BaseType::ElemType& aOther) : BaseType(aOther) {}
336 explicit TimeRanges(BaseType::ElemType&& aOther)
337 : BaseType(std::move(aOther)) {}
339 static TimeRanges Invalid() {
340 return TimeRanges(TimeRange(-nld::infinity(), nld::infinity()));
342 bool IsInvalid() const {
343 return Length() == 1 && Start(0) == -nld::infinity() &&
344 End(0) == nld::infinity();
346 // Convert from TimeUnit-based intervals to second-based TimeRanges.
347 explicit TimeRanges(const TimeIntervals& aIntervals) {
348 for (const auto& interval : aIntervals) {
349 Add(TimeRange(interval.mStart.ToSeconds(), interval.mEnd.ToSeconds()));
353 TimeRanges ToMicrosecondResolution() const;
355 TimeRanges() = default;
358 } // namespace media
359 } // namespace mozilla
361 #endif // TIME_UNITS_H