| // Copyright 2016 The Fuchsia 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 LIB_MEDIA_CPP_TIMELINE_RATE_H_ |
| #define LIB_MEDIA_CPP_TIMELINE_RATE_H_ |
| |
| #include <stdint.h> |
| #include <zircon/assert.h> |
| |
| #include <limits> |
| |
| namespace media { |
| |
| // TODO(dalesat): Consider always allowing inexact results. |
| |
| // Expresses the relative rate of a timeline as the ratio between two uint32_t |
| // values subject_delta / reference_delta. "subject" refers to the timeline |
| // whose rate is being represented, and "reference" refers to the timeline |
| // relative to which the rate is expressed. |
| class TimelineRate final { |
| public: |
| // Used to indicate overflow of scaling operations. |
| static constexpr int64_t kOverflow = std::numeric_limits<int64_t>::max(); |
| |
| // Zero as a TimelineRate. |
| static const TimelineRate Zero; |
| |
| // Nanoseconds (subject) per second (reference) as a TimelineRate. |
| static const TimelineRate NsPerSecond; |
| |
| // Reduces the ratio of *subject_delta and *reference_delta. |
| static void Reduce(uint32_t* subject_delta, uint32_t* reference_delta); |
| |
| // Produces the product of the rates. If exact is true, DCHECKs on loss of |
| // precision. |
| static void Product(uint32_t a_subject_delta, uint32_t a_reference_delta, |
| uint32_t b_subject_delta, uint32_t b_reference_delta, |
| uint32_t* product_subject_delta, uint32_t* product_reference_delta, |
| bool exact = true); |
| |
| // Produces the product of the rates and the int64_t as an int64_t. Returns |
| // kOverflow on overflow. |
| static int64_t Scale(int64_t value, uint32_t subject_delta, uint32_t reference_delta); |
| |
| // Returns the product of the rates. If exact is true, DCHECKs on loss of |
| // precision. |
| static TimelineRate Product(TimelineRate a, TimelineRate b, bool exact = true) { |
| uint32_t result_subject_delta; |
| uint32_t result_reference_delta; |
| Product(a.subject_delta(), a.reference_delta(), b.subject_delta(), b.reference_delta(), |
| &result_subject_delta, &result_reference_delta, exact); |
| return TimelineRate(result_subject_delta, result_reference_delta); |
| } |
| |
| TimelineRate() : subject_delta_(0), reference_delta_(1) {} |
| |
| explicit TimelineRate(uint32_t subject_delta) |
| : subject_delta_(subject_delta), reference_delta_(1) {} |
| |
| explicit TimelineRate(float rate_as_float) |
| : subject_delta_(rate_as_float > 1.0f ? kFloatFactor |
| : static_cast<uint32_t>(kFloatFactor * rate_as_float)), |
| reference_delta_(rate_as_float > 1.0f ? static_cast<uint32_t>(kFloatFactor / rate_as_float) |
| : kFloatFactor) { |
| // The expressions above are intended to provide good precision for |
| // 'reasonable' playback rate values (say in the range 0.0 to 4.0). The |
| // expressions always produce a ratio of kFloatFactor and a number smaller |
| // than kFloatFactor. kFloatFactor's value was chosen because floats have |
| // a 23-bit mantissa, and operations with a larger factor would sacrifice |
| // precision. |
| ZX_DEBUG_ASSERT(rate_as_float >= 0.0f); |
| Reduce(&subject_delta_, &reference_delta_); |
| } |
| |
| TimelineRate(uint32_t subject_delta, uint32_t reference_delta) |
| : subject_delta_(subject_delta), reference_delta_(reference_delta) { |
| ZX_DEBUG_ASSERT(reference_delta != 0); |
| Reduce(&subject_delta_, &reference_delta_); |
| } |
| |
| // Determines whether this |TimelineRate| is invertible. |
| bool invertible() const { return subject_delta_ != 0; } |
| |
| // Returns the inverse of the rate. DCHECKs if the subject_delta of this |
| // rate is zero. |
| TimelineRate Inverse() const { |
| ZX_DEBUG_ASSERT(subject_delta_ != 0); |
| |
| // Note: TimelineRates should be always be in their reduced form. Because |
| // of this, we do not want to invoke the subject/reference constructor |
| // (which will attempt to reduce the ratio). Instead, use the default |
| // constructor and just swap subject/reference. |
| TimelineRate ret; |
| ret.subject_delta_ = reference_delta_; |
| ret.reference_delta_ = subject_delta_; |
| return ret; |
| } |
| |
| // Scales the value by this rate. Returns kOverflow on overflow. |
| int64_t Scale(int64_t value) const { return Scale(value, subject_delta_, reference_delta_); } |
| |
| // Scales the value by the inverse of this rate. |
| int64_t ScaleInverse(int64_t value) const { |
| return Scale(value, reference_delta_, subject_delta_); |
| } |
| |
| uint32_t subject_delta() const { return subject_delta_; } |
| uint32_t reference_delta() const { return reference_delta_; } |
| |
| private: |
| // A multiplier for float-to-TimelineRate conversions chosen because floats |
| // have a 23-bit mantissa. |
| static constexpr uint32_t kFloatFactor = 1ul << 23; |
| |
| uint32_t subject_delta_; |
| uint32_t reference_delta_; |
| }; |
| |
| // Tests two rates for equality. |
| inline bool operator==(TimelineRate a, TimelineRate b) { |
| return a.subject_delta() == b.subject_delta() && a.reference_delta() == b.reference_delta(); |
| } |
| |
| // Tests two rates for inequality. |
| inline bool operator!=(TimelineRate a, TimelineRate b) { return !(a == b); } |
| |
| // Returns the ratio of the two rates. DCHECKs on loss of precision. |
| inline TimelineRate operator/(TimelineRate a, TimelineRate b) { |
| return TimelineRate::Product(a, b.Inverse()); |
| } |
| |
| // Returns the product of the two rates. DCHECKs on loss of precision. |
| inline TimelineRate operator*(TimelineRate a, TimelineRate b) { |
| return TimelineRate::Product(a, b); |
| } |
| |
| // Returns the product of the rate and the int64_t. Returns kOverflow on |
| // overflow. |
| inline int64_t operator*(TimelineRate a, int64_t b) { return a.Scale(b); } |
| |
| // Returns the product of the rate and the int64_t. Returns kOverflow on |
| // overflow. |
| inline int64_t operator*(int64_t a, TimelineRate b) { return b.Scale(a); } |
| |
| // Returns the the int64_t divided by the rate. Returns kOverflow on |
| // overflow. |
| inline int64_t operator/(int64_t a, TimelineRate b) { return b.ScaleInverse(a); } |
| |
| } // namespace media |
| |
| #endif // LIB_MEDIA_CPP_TIMELINE_RATE_H_ |