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/. */
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
{
23 } // namespace mozilla::media
24 // CopyChooser specialization for nsTArray
26 struct nsTArray_RelocationStrategy
<mozilla::media::TimeIntervals
> {
28 nsTArray_RelocateUsingMoveConstructor
<mozilla::media::TimeIntervals
>;
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;
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
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
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
71 // If not specified, the base is 1e6, representing microseconds, for historical
72 // reasons. Users can gradually move to more precise representations when
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
{
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 // Only to be used in release assertions or unit testing, returns true if this
122 // TimeUnit has base aBase
123 bool IsBase(int64_t aBase
) const;
124 double ToSeconds() const;
125 nsCString
ToString() const;
126 TimeDuration
ToTimeDuration() const;
127 bool IsInfinite() const;
128 bool IsPositive() const;
129 bool IsPositiveOrZero() const;
131 bool IsNegative() const;
133 // Returns true if the fractions are equal when converted to the smallest
135 bool EqualsAtLowestResolution(const TimeUnit
& aOther
) const;
136 // Strict equality -- the fractions must be exactly equal
137 bool operator==(const TimeUnit
& aOther
) const;
138 bool operator!=(const TimeUnit
& aOther
) const;
139 bool operator>=(const TimeUnit
& aOther
) const;
140 bool operator>(const TimeUnit
& aOther
) const;
141 bool operator<=(const TimeUnit
& aOther
) const;
142 bool operator<(const TimeUnit
& aOther
) const;
143 TimeUnit
operator%(const TimeUnit
& aOther
) const;
144 TimeUnit
operator+(const TimeUnit
& aOther
) const;
145 TimeUnit
operator-(const TimeUnit
& aOther
) const;
146 TimeUnit
& operator+=(const TimeUnit
& aOther
);
147 TimeUnit
& operator-=(const TimeUnit
& aOther
);
148 template <typename T
>
149 TimeUnit
operator*(T aVal
) const {
150 // See bug 853398 for the reason to block double multiplier.
151 // If required, use MultDouble below and with caution.
152 static_assert(std::is_integral_v
<T
>, "Must be an integral type");
153 return TimeUnit(mTicks
* aVal
, mBase
);
155 TimeUnit
MultDouble(double aVal
) const;
156 friend TimeUnit
operator/(const TimeUnit
& aUnit
, int64_t aVal
) {
157 MOZ_DIAGNOSTIC_ASSERT(0 <= aVal
&& aVal
<= UINT32_MAX
);
158 return TimeUnit(aUnit
.mTicks
/ aVal
, aUnit
.mBase
);
160 friend TimeUnit
operator%(const TimeUnit
& aUnit
, int64_t aVal
) {
161 MOZ_DIAGNOSTIC_ASSERT(0 <= aVal
&& aVal
<= UINT32_MAX
);
162 return TimeUnit(aUnit
.mTicks
% aVal
, aUnit
.mBase
);
165 struct TruncatePolicy
{
166 template <typename T
>
167 static T
policy(T
& aValue
) {
168 return static_cast<T
>(aValue
);
173 template <typename T
>
174 static T
policy(T
& aValue
) {
175 return std::round(aValue
);
179 struct CeilingPolicy
{
180 template <typename T
>
181 static T
policy(T
& aValue
) {
182 return std::ceil(aValue
);
186 template <class RoundingPolicy
= TruncatePolicy
>
187 TimeUnit
ToBase(int64_t aTargetBase
) const {
189 return ToBase
<RoundingPolicy
>(aTargetBase
, dummy
);
192 template <class RoundingPolicy
= TruncatePolicy
>
193 TimeUnit
ToBase(const TimeUnit
& aTimeUnit
) const {
195 return ToBase
<RoundingPolicy
>(aTimeUnit
, dummy
);
198 // Allow returning the same value, in a base that matches another TimeUnit.
199 template <class RoundingPolicy
= TruncatePolicy
>
200 TimeUnit
ToBase(const TimeUnit
& aTimeUnit
, double& aOutError
) const {
201 int64_t targetBase
= aTimeUnit
.mBase
;
202 return ToBase
<RoundingPolicy
>(targetBase
, aOutError
);
205 template <class RoundingPolicy
= TruncatePolicy
>
206 TimeUnit
ToBase(int64_t aTargetBase
, double& aOutError
) const {
208 CheckedInt
<int64_t> ticks
= mTicks
* aTargetBase
;
209 if (ticks
.isValid()) {
210 imaxdiv_t rv
= imaxdiv(ticks
.value(), mBase
);
212 return TimeUnit(rv
.quot
, aTargetBase
);
215 double approx
= static_cast<double>(mTicks
.value()) *
216 static_cast<double>(aTargetBase
) /
217 static_cast<double>(mBase
);
219 aOutError
= modf(approx
, &integer
);
220 return TimeUnit(AssertedCast
<int64_t>(RoundingPolicy::policy(approx
)),
224 bool IsValid() const;
226 constexpr TimeUnit() = default;
228 TimeUnit(const TimeUnit
&) = default;
230 TimeUnit
& operator=(const TimeUnit
&) = default;
232 bool IsPosInf() const;
233 bool IsNegInf() const;
235 // Allow serializing a TimeUnit via IPC
236 friend IPC::ParamTraits
<mozilla::media::TimeUnit
>;
238 #ifndef VISIBLE_TIMEUNIT_INTERNALS
241 int64_t ToCommonUnit(int64_t aRatio
) const;
242 // Reduce a TimeUnit to the smallest possible ticks and base. This is useful
243 // to comparison with big time values that can otherwise overflow.
244 TimeUnit
Reduced() const;
246 CheckedInt64 mTicks
{0};
247 // Default base is microseconds.
248 int64_t mBase
{USECS_PER_S
};
251 using NullableTimeUnit
= Maybe
<TimeUnit
>;
253 using TimeInterval
= Interval
<TimeUnit
>;
255 // A set of intervals, containing TimeUnit.
256 class TimeIntervals
: public IntervalSet
<TimeUnit
> {
258 using BaseType
= IntervalSet
<TimeUnit
>;
259 using InnerType
= TimeUnit
;
261 // We can't use inherited constructors yet. So we have to duplicate all the
262 // constructors found in IntervalSet base class.
263 // all this could be later replaced with:
264 // using IntervalSet<TimeUnit>::IntervalSet;
266 // MOZ_IMPLICIT as we want to enable initialization in the form:
267 // TimeIntervals i = ... like we would do with IntervalSet<T> i = ...
268 MOZ_IMPLICIT
TimeIntervals(const BaseType
& aOther
) : BaseType(aOther
) {}
269 MOZ_IMPLICIT
TimeIntervals(BaseType
&& aOther
) : BaseType(std::move(aOther
)) {}
270 explicit TimeIntervals(const BaseType::ElemType
& aOther
) : BaseType(aOther
) {}
271 explicit TimeIntervals(BaseType::ElemType
&& aOther
)
272 : BaseType(std::move(aOther
)) {}
274 static TimeIntervals
Invalid() {
275 return TimeIntervals(TimeInterval(TimeUnit::FromNegativeInfinity(),
276 TimeUnit::FromNegativeInfinity()));
278 bool IsInvalid() const {
279 return Length() == 1 && Start(0).IsNegInf() && End(0).IsNegInf();
282 // Returns the same interval, with a given base resolution.
283 TimeIntervals
ToBase(const TimeUnit
& aBase
) const {
284 TimeIntervals output
;
285 for (const auto& interval
: mIntervals
) {
286 TimeInterval convertedInterval
{interval
.mStart
.ToBase(aBase
),
287 interval
.mEnd
.ToBase(aBase
),
288 interval
.mFuzz
.ToBase(aBase
)};
289 output
+= convertedInterval
;
294 // Returns the same interval, with a microsecond resolution. This is used to
295 // compare TimeUnits internal to demuxers (that use a base from the container)
296 // to floating point numbers in seconds from content.
297 TimeIntervals
ToMicrosecondResolution() const {
298 TimeIntervals output
;
300 for (const auto& interval
: mIntervals
) {
301 TimeInterval reducedPrecision
{interval
.mStart
.ToBase(USECS_PER_S
),
302 interval
.mEnd
.ToBase(USECS_PER_S
),
303 interval
.mFuzz
.ToBase(USECS_PER_S
)};
304 output
+= reducedPrecision
;
309 nsCString
ToString() const {
311 for (const auto& interval
: mIntervals
) {
312 dump
+= nsPrintfCString("[%s],", interval
.ToString().get());
317 TimeIntervals() = default;
320 using TimeRange
= Interval
<double>;
322 // A set of intervals, containing doubles that are seconds.
323 class TimeRanges
: public IntervalSet
<double> {
325 using BaseType
= IntervalSet
<double>;
326 using InnerType
= double;
327 using nld
= std::numeric_limits
<double>;
329 // We can't use inherited constructors yet. So we have to duplicate all the
330 // constructors found in IntervalSet base class.
331 // all this could be later replaced with:
332 // using IntervalSet<TimeUnit>::IntervalSet;
334 // MOZ_IMPLICIT as we want to enable initialization in the form:
335 // TimeIntervals i = ... like we would do with IntervalSet<T> i = ...
336 MOZ_IMPLICIT
TimeRanges(const BaseType
& aOther
) : BaseType(aOther
) {}
337 MOZ_IMPLICIT
TimeRanges(BaseType
&& aOther
) : BaseType(std::move(aOther
)) {}
338 explicit TimeRanges(const BaseType::ElemType
& aOther
) : BaseType(aOther
) {}
339 explicit TimeRanges(BaseType::ElemType
&& aOther
)
340 : BaseType(std::move(aOther
)) {}
342 static TimeRanges
Invalid() {
343 return TimeRanges(TimeRange(-nld::infinity(), nld::infinity()));
345 bool IsInvalid() const {
346 return Length() == 1 && Start(0) == -nld::infinity() &&
347 End(0) == nld::infinity();
349 // Convert from TimeUnit-based intervals to second-based TimeRanges.
350 explicit TimeRanges(const TimeIntervals
& aIntervals
) {
351 for (const auto& interval
: aIntervals
) {
352 Add(TimeRange(interval
.mStart
.ToSeconds(), interval
.mEnd
.ToSeconds()));
356 TimeRanges
ToMicrosecondResolution() const;
358 TimeRanges() = default;
362 } // namespace mozilla
364 #endif // TIME_UNITS_H