1- #pragma once
1+ #pragma once
22
33#include < chrono>
44
@@ -9,6 +9,10 @@ namespace av {
99
1010/* *
1111 * @brief The Timestamp class represents timestamp value and it timebase
12+ *
13+ * Timestamp class can be treated as Fixed Point time representation where timestamp itself is a value and Time Base
14+ * is a multiplicator. Be careful with construct Timestamp from the std::chrono::duration with floating point @a rep
15+ *
1216 */
1317class Timestamp
1418{
@@ -18,10 +22,16 @@ class Timestamp
1822
1923 /* *
2024 * @brief Create AvCpp/FFmpeg compatible timestamp value from the std::chrono::duration/boost::chrono::duration
25+ *
26+ * Duration class must not be a floating point to avoid lost precision: Timestamp in the FFmpeg internally are
27+ * int64_t with integer AVRAtional as timebase that also uses int64_t internally to store numerator and denominator.
28+ *
2129 */
22- template <typename Duration, typename = typename Duration::period>
23- Timestamp (const Duration& duration)
24- {
30+ template <typename Duration,
31+ typename = typename Duration::period,
32+ typename = std::enable_if_t <std::is_integral_v<typename Duration::rep>>>
33+ constexpr Timestamp (const Duration& duration)
34+ {
2535 using Ratio = typename Duration::period;
2636
2737 static_assert (Ratio::num <= INT_MAX , " To prevent precision lost, ratio numerator must be less then INT_MAX" );
@@ -32,6 +42,53 @@ class Timestamp
3242 static_cast <int >(Ratio::den));
3343 }
3444
45+ /* *
46+ * @brief Create AvCpp/FFmpeg compatible timestamp value from the floating point
47+ * std::chrono::duration/boost::chrono::duration with given precision.
48+ *
49+ * PrecisionPeriod defines holded TimeBase
50+ *
51+ */
52+ template <typename Duration,
53+ typename PrecisionPeriod,
54+ typename = typename Duration::period,
55+ typename = std::enable_if_t <std::is_floating_point_v<typename Duration::rep>>>
56+ constexpr Timestamp (const Duration& duration, PrecisionPeriod)
57+ : Timestamp(duration, Rational{static_cast <int >(PrecisionPeriod::num), static_cast <int >(PrecisionPeriod::den)})
58+ {
59+ using Ratio = typename Duration::period;
60+ static_assert (Ratio::num <= INT_MAX , " To prevent precision lost, ratio numerator must be less then INT_MAX" );
61+ static_assert (Ratio::den <= INT_MAX , " To prevent precision lost, ratio denominator must be less then INT_MAX" );
62+ static_assert (PrecisionPeriod::num <= INT_MAX , " To prevent precision lost, ratio numerator must be less then INT_MAX" );
63+ static_assert (PrecisionPeriod::den <= INT_MAX , " To prevent precision lost, ratio denominator must be less then INT_MAX" );
64+ }
65+
66+ /* *
67+ * @brief Create AvCpp/FFmpeg compatible timestamp value from the floating point
68+ * std::chrono::duration/boost::chrono::duration with given precision.
69+ *
70+ */
71+ template <typename Duration,
72+ typename = typename Duration::period,
73+ typename = std::enable_if_t <std::is_floating_point_v<typename Duration::rep>>>
74+ constexpr Timestamp (const Duration& duration, const Rational& timebase)
75+ : m_timebase(timebase)
76+ {
77+ using ValueType = typename Duration::rep;
78+ using Ratio = typename Duration::period;
79+
80+ static_assert (Ratio::num <= INT_MAX , " To prevent precision lost, ratio numerator must be less then INT_MAX" );
81+ static_assert (Ratio::den <= INT_MAX , " To prevent precision lost, ratio denominator must be less then INT_MAX" );
82+
83+ // rescale input ticks into integer one
84+ // m_timestamp = ts * Raio / m_timebase
85+ ValueType const b = static_cast <ValueType>(Ratio::num) * m_timebase.getDenominator ();
86+ ValueType const c = static_cast <ValueType>(Ratio::den) * m_timebase.getNumerator ();
87+
88+ m_timestamp = static_cast <int64_t >(duration.count () * b / c);
89+ }
90+
91+
3592 int64_t timestamp () const noexcept ;
3693 int64_t timestamp (const Rational &timebase) const noexcept ;
3794 const Rational& timebase () const noexcept ;
@@ -45,19 +102,34 @@ class Timestamp
45102
46103 /* *
47104 * @brief Convert to the std::chrono::duration compatible value
105+ *
106+ * It possible to convert to the floating point duration without additional casts.
107+ *
48108 */
49- template <typename Duration>
50- Duration toDuration () const
109+ template <typename Duration,
110+ typename = typename Duration::period,
111+ typename = typename Duration::rep>
112+ constexpr Duration toDuration () const
51113 {
114+ using ValueType = typename Duration::rep;
52115 using Ratio = typename Duration::period;
53116
54117 static_assert (Ratio::num <= INT_MAX , " To prevent precision lost, ratio numerator must be less then INT_MAX" );
55118 static_assert (Ratio::den <= INT_MAX , " To prevent precision lost, ratio denominator must be less then INT_MAX" );
56119
57- Rational dstTimebase (static_cast <int >(Ratio::num),
58- static_cast <int >(Ratio::den));
59- auto ts = m_timebase.rescale (m_timestamp, dstTimebase);
60- return Duration (ts);
120+ if constexpr (std::is_integral_v<ValueType>) {
121+ Rational dstTimebase (static_cast <int >(Ratio::num),
122+ static_cast <int >(Ratio::den));
123+ auto ts = m_timebase.rescale (m_timestamp, dstTimebase);
124+ return Duration (ts);
125+ } else {
126+ namespace dt = std::chrono;
127+ // ts = m_timestamp * m_timebase / dstTimebase
128+ ValueType const b = m_timebase.getNumerator () * static_cast <ValueType>(Ratio::den);
129+ ValueType const c = m_timebase.getDenominator () * static_cast <ValueType>(Ratio::num);
130+ ValueType const ts = static_cast <ValueType>(m_timestamp) * b / c;
131+ return Duration{ts};
132+ }
61133 }
62134
63135 Timestamp& operator +=(const Timestamp &other);
0 commit comments