Skip to content

Commit 64364d8

Browse files
committed
Initial implementation of MediaTime
1 parent 26c958b commit 64364d8

File tree

2 files changed

+306
-0
lines changed

2 files changed

+306
-0
lines changed

include/cinder/MediaTime.h

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
/*
2+
Copyright (c) 2020, The Cinder Project: http://libcinder.org All rights reserved.
3+
This code is intended for use with the Cinder C++ library: http://libcinder.org
4+
5+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that
6+
the following conditions are met:
7+
8+
* Redistributions of source code must retain the above copyright notice, this list of conditions and
9+
the following disclaimer.
10+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
11+
the following disclaimer in the documentation and/or other materials provided with the distribution.
12+
13+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
14+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
15+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
16+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
17+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
19+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
20+
POSSIBILITY OF SUCH DAMAGE.
21+
*/
22+
23+
#pragma once
24+
25+
#include "cinder/Cinder.h"
26+
#include <iosfwd>
27+
28+
namespace cinder {
29+
30+
//! Does not allow negative or zero base
31+
struct MediaTime {
32+
public:
33+
static constexpr int32_t DEFAULT_TIME_BASE = 1000000; // microseconds
34+
static constexpr int64_t MAX_TIME_BASE = 2147483647;
35+
36+
MediaTime() : value( 0 ), base( 1 ), epoch( 0 ) {} // zero
37+
MediaTime( int64_t value, int32_t base, int64_t epoch = 0 );
38+
39+
explicit MediaTime( int value ) : value( value ), base( 1 ), epoch( 0 ) {}
40+
explicit MediaTime( int64_t value ) : value( value ), base( 1 ), epoch( 0 ) {}
41+
explicit MediaTime( double seconds )
42+
: value( (int64_t)( seconds * DEFAULT_TIME_BASE ) ), base( DEFAULT_TIME_BASE ), epoch( 0 )
43+
{}
44+
45+
int64_t getValue() const { return value; }
46+
int32_t getBase() const { return base; }
47+
int64_t getEpoch() const { return epoch; }
48+
double getSeconds() const { return value / (double)base; }
49+
50+
//! Scale value to 'newBase'. value is set to floor if necessary.
51+
void setBase( int32_t newBase );
52+
53+
//! Returns value as scaled to \a otherBase
54+
int64_t getValueAsBase( int32_t otherBase ) const { return ( base == otherBase ) ? value : ( value * otherBase / base ); }
55+
56+
bool operator<( const MediaTime &rhs ) const {
57+
if( epoch != rhs.epoch ) return epoch < rhs.epoch;
58+
if( base == rhs.base ) return value < rhs.value;
59+
else return value * rhs.base < rhs.value * base;
60+
}
61+
62+
bool operator>( const MediaTime &rhs ) const {
63+
if( epoch != rhs.epoch ) return epoch > rhs.epoch;
64+
if( base == rhs.base ) return value > rhs.value;
65+
else return value * rhs.base > rhs.value * base;
66+
}
67+
68+
bool operator<=( const MediaTime &rhs ) const {
69+
if( epoch != rhs.epoch ) return epoch <= rhs.epoch;
70+
if( base == rhs.base ) return value <= rhs.value;
71+
else return value * rhs.base <= rhs.value * base;
72+
}
73+
74+
bool operator>=( const MediaTime &rhs ) const {
75+
if( epoch != rhs.epoch ) return epoch >= rhs.epoch;
76+
if( base == rhs.base ) return value >= rhs.value;
77+
else return value * rhs.base >= rhs.value * base;
78+
}
79+
80+
//! Tests for mathematical equivalence, not bitwise equivalence
81+
bool operator==( const MediaTime &rhs ) const {
82+
if( epoch != rhs.epoch ) return false;
83+
else if( base == rhs.base ) return value == rhs.value;
84+
else return value * rhs.base == rhs.value * base;
85+
}
86+
87+
bool operator!=( const MediaTime &rhs ) const {
88+
if( epoch != rhs.epoch ) return true;
89+
else if( base == rhs.base ) return value != rhs.value;
90+
else return value * rhs.base != rhs.value * base;
91+
}
92+
93+
//! Epoch is max of epochs. If base overflows, DEFAULT_TIME_BASE is selected
94+
MediaTime operator-( const MediaTime &rhs ) const {
95+
if( rhs.base == base ) return MediaTime( value - rhs.value, base, epoch > rhs.epoch ? epoch : rhs.epoch );
96+
else {
97+
int64_t lhsValue = value, rhsValue = rhs.value;
98+
int32_t newBase = simplifyBases( &lhsValue, base, &rhsValue, rhs.base );
99+
return MediaTime( lhsValue - rhsValue, newBase, epoch > rhs.epoch ? epoch : rhs.epoch );
100+
}
101+
}
102+
103+
//! Epoch is max of epochs. If base overflows, DEFAULT_TIME_BASE is selected
104+
MediaTime operator+( const MediaTime &rhs ) const {
105+
if( rhs.base == base ) return MediaTime( value + rhs.value, base, epoch > rhs.epoch ? epoch : rhs.epoch );
106+
else {
107+
int64_t lhsValue = value, rhsValue = rhs.value;
108+
int32_t newBase = simplifyBases( &lhsValue, base, &rhsValue, rhs.base );
109+
return MediaTime( lhsValue + rhsValue, newBase, epoch > rhs.epoch ? epoch : rhs.epoch );
110+
}
111+
}
112+
113+
//! Epoch is max of epochs. If base overflows, DEFAULT_TIME_BASE is selected
114+
MediaTime operator*( const MediaTime &rhs ) const {
115+
if( base * rhs.base <= DEFAULT_TIME_BASE ) return MediaTime( value * rhs.value, base * rhs.base, epoch > rhs.epoch ? epoch : rhs.epoch );
116+
else {
117+
int64_t lhsValue = value * DEFAULT_TIME_BASE / base, rhsValue = rhs.value * DEFAULT_TIME_BASE / rhs.base;
118+
return MediaTime( lhsValue * rhsValue / DEFAULT_TIME_BASE, DEFAULT_TIME_BASE, epoch > rhs.epoch ? epoch : rhs.epoch );
119+
}
120+
}
121+
122+
//! Epoch is max of epochs. If base overflows, DEFAULT_TIME_BASE is selected
123+
MediaTime operator/( const MediaTime &rhs ) const {
124+
// exchanges 'value' and 'base' of 'rhs' and multiplies
125+
if( base * rhs.value <= (int64_t)DEFAULT_TIME_BASE ) return MediaTime( value * rhs.base, (int32_t)(base * rhs.value), epoch > rhs.epoch ? epoch : rhs.epoch );
126+
else {
127+
int64_t lhsValue = value * DEFAULT_TIME_BASE / base, rhsValue = (int64_t)rhs.base * DEFAULT_TIME_BASE / rhs.value;
128+
return MediaTime( lhsValue * rhsValue / DEFAULT_TIME_BASE, DEFAULT_TIME_BASE, epoch > rhs.epoch ? epoch : rhs.epoch );
129+
}
130+
}
131+
132+
//! Does not affect epoch. If base overflows, DEFAULT_TIME_BASE is selected
133+
MediaTime& operator-=( const MediaTime &rhs ) {
134+
MediaTime newTime = *this - rhs;
135+
value = newTime.value;
136+
base = newTime.base;
137+
return *this;
138+
}
139+
140+
//! Does not affect epoch. If base overflows, DEFAULT_TIME_BASE is selected
141+
MediaTime& operator+=( const MediaTime &rhs ) {
142+
MediaTime newTime = *this + rhs;
143+
value = newTime.value;
144+
base = newTime.base;
145+
return *this;
146+
}
147+
148+
//! Does not affect epoch. If base overflows, DEFAULT_TIME_BASE is selected
149+
MediaTime& operator*=( const MediaTime &rhs ) {
150+
MediaTime newTime = *this * rhs;
151+
value = newTime.value;
152+
base = newTime.base;
153+
return *this;
154+
}
155+
156+
//! Does not affect epoch. If base overflows, DEFAULT_TIME_BASE is selected
157+
MediaTime& operator/=( const MediaTime &rhs ) {
158+
MediaTime newTime = *this / MediaTime( rhs.value, rhs.base, rhs.epoch );
159+
value = newTime.value;
160+
base = newTime.base;
161+
return *this;
162+
}
163+
164+
MediaTime operator-() const {
165+
return MediaTime( -value, base, epoch );
166+
}
167+
168+
friend std::ostream& operator<<( std::ostream &os, const MediaTime &mt );
169+
170+
//! Divides the value and base by the greatest common common divisor of both. Does not affect epoch
171+
void simplify();
172+
173+
//! returns a new common base, and re-bases 'lhsValue' and 'rhsValue' to this new base. If no legal common base, then uses DEFAULT_TIME_BASE
174+
static int32_t simplifyBases( int64_t *lhsValue, int32_t lhsBase, int64_t *rhsValue, int32_t rhsBase );
175+
176+
int64_t value;
177+
int32_t base;
178+
int64_t epoch;
179+
};
180+
181+
inline MediaTime operator*( int lhs, const MediaTime &rhs ) { return MediaTime( rhs.value * lhs, rhs.base, rhs.epoch ); }
182+
inline MediaTime operator*( const MediaTime &lhs, int rhs ) { return MediaTime( lhs.value * rhs, lhs.base, lhs.epoch ); }
183+
inline MediaTime operator*( double lhs, const MediaTime &rhs ) { return MediaTime( lhs ) * rhs; }
184+
inline MediaTime operator*( const MediaTime &lhs, double rhs ) { return lhs * MediaTime( rhs ); }
185+
inline MediaTime operator*( float lhs, const MediaTime &rhs ) { return MediaTime( lhs ) * rhs; }
186+
inline MediaTime operator*( const MediaTime &lhs, float rhs ) { return lhs * MediaTime( rhs ); }
187+
188+
inline MediaTime operator/( int lhs, const MediaTime &rhs ) { return MediaTime( lhs ) / rhs; }
189+
inline MediaTime operator/( const MediaTime &lhs, int rhs ) { return lhs / MediaTime( rhs ); }
190+
inline MediaTime operator/( double lhs, const MediaTime &rhs ) { return MediaTime( lhs ) / rhs; }
191+
inline MediaTime operator/( const MediaTime &lhs, double rhs ) { return lhs / MediaTime( rhs ); }
192+
inline MediaTime operator/( float lhs, const MediaTime &rhs ) { return MediaTime( lhs ) / rhs; }
193+
inline MediaTime operator/( const MediaTime &lhs, float rhs ) { return lhs / MediaTime( rhs ); }
194+
195+
inline MediaTime operator"" _sec( long double seconds )
196+
{
197+
return MediaTime( (int64_t)(seconds * MediaTime::DEFAULT_TIME_BASE ), MediaTime::DEFAULT_TIME_BASE );
198+
}
199+
200+
inline MediaTime operator"" _sec( unsigned long long seconds )
201+
{
202+
return MediaTime( (int64_t)(seconds * MediaTime::DEFAULT_TIME_BASE ), MediaTime::DEFAULT_TIME_BASE );
203+
}
204+
205+
} // namespace cinder

src/cinder/MediaTime.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
Copyright (c) 2020, The Cinder Project: http://libcinder.org All rights reserved.
3+
This code is intended for use with the Cinder C++ library: http://libcinder.org
4+
5+
Redistribution and use in source and binary forms, with or without modification, are permitted provided that
6+
the following conditions are met:
7+
8+
* Redistributions of source code must retain the above copyright notice, this list of conditions and
9+
the following disclaimer.
10+
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
11+
the following disclaimer in the documentation and/or other materials provided with the distribution.
12+
13+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
14+
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
15+
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
16+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
17+
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
19+
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
20+
POSSIBILITY OF SUCH DAMAGE.
21+
*/
22+
23+
#include "cinder/MediaTime.h"
24+
#include "cinder/CinderAssert.h"
25+
#include <algorithm>
26+
#include <iostream>
27+
28+
namespace cinder {
29+
30+
MediaTime::MediaTime( int64_t value, int32_t base, int64_t epoch )
31+
: value( value ), base( base ), epoch( epoch )
32+
{
33+
if( base < 0 ) {
34+
value = -value;
35+
base = -base;
36+
}
37+
CI_ASSERT( base > 0 );
38+
}
39+
40+
void MediaTime::setBase( int32_t newBase )
41+
{
42+
CI_ASSERT( newBase > 0 ); value = value * newBase / base; base = newBase;
43+
}
44+
45+
// Divides the value and base by the greatest common common divisor of both. Does not affect epoch
46+
void MediaTime::simplify()
47+
{
48+
CI_ASSERT( base > 0 );
49+
50+
if( value == 0 ) {
51+
base = 1;
52+
return;
53+
}
54+
55+
int64_t u = value, v = base;
56+
while( v != 0 ) {
57+
int64_t r = u % v;
58+
u = v;
59+
v = r;
60+
}
61+
value /= u;
62+
base /= (int32_t)u;
63+
}
64+
65+
//! returns a new common base, and re-bases 'lhsValue' and 'rhsValue' to this new base. If no legal common base, then uses DEFAULT_TIME_BASE
66+
int32_t MediaTime::simplifyBases( int64_t *lhsValue, int32_t lhsBase, int64_t *rhsValue, int32_t rhsBase )
67+
{
68+
CI_ASSERT( lhsBase > 0 && rhsBase > 0 );
69+
70+
// calculate gcdV = gcd( lhsBase, rhsBase )
71+
int64_t a = (int64_t)lhsBase, b = (int64_t)rhsBase;
72+
while( b != 0 ) {
73+
int64_t t = b;
74+
b = a % b;
75+
a = t;
76+
}
77+
const int64_t gcdV = a;
78+
79+
// lcm(a,b) = a / gcd(a,b) * b
80+
int64_t lcmV = (int64_t)lhsBase / gcdV * rhsBase;
81+
// if lcm is acceptably small, use that
82+
if( lcmV <= DEFAULT_TIME_BASE ) {
83+
*lhsValue = (int64_t)(*lhsValue) * lcmV / lhsBase;
84+
*rhsValue = (int64_t)(*rhsValue) * lcmV / rhsBase;
85+
return (int32_t)lcmV;
86+
}
87+
// we're going to have to rebase using DEFAULT_BASE
88+
*lhsValue = (int64_t)(*lhsValue) * DEFAULT_TIME_BASE / lhsBase;
89+
*rhsValue = (int64_t)(*rhsValue) * DEFAULT_TIME_BASE / rhsBase;
90+
return DEFAULT_TIME_BASE;
91+
}
92+
93+
std::ostream& operator<<( std::ostream &os, const MediaTime &mt )
94+
{
95+
if( mt.epoch == 0 )
96+
return os << mt.value << "/" << mt.base;
97+
else
98+
return os << mt.value << "/" << mt.base << "[" << mt.epoch << "]";
99+
}
100+
101+
} // namespace cinder

0 commit comments

Comments
 (0)