aboutsummaryrefslogtreecommitdiff
path: root/libutil/Timecode.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libutil/Timecode.cpp')
-rw-r--r--libutil/Timecode.cpp591
1 files changed, 591 insertions, 0 deletions
diff --git a/libutil/Timecode.cpp b/libutil/Timecode.cpp
new file mode 100644
index 0000000..195a009
--- /dev/null
+++ b/libutil/Timecode.cpp
@@ -0,0 +1,591 @@
+///////////////////////////////////////////////////////////////////////////////
+//
+// The contents of this file are subject to the Mozilla Public License
+// Version 1.1 (the "License"); you may not use this file except in
+// compliance with the License. You may obtain a copy of the License at
+// http://www.mozilla.org/MPL/
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
+// License for the specific language governing rights and limitations
+// under the License.
+//
+// The Original Code is MP4v2.
+//
+// The Initial Developer of the Original Code is Kona Blend.
+// Portions created by Kona Blend are Copyright (C) 2008.
+// All Rights Reserved.
+//
+// Contributors:
+// Kona Blend, kona8lend@@gmail.com
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "libutil/impl.h"
+
+namespace mp4v2 { namespace util {
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode::Timecode( const Timecode& obj )
+ : _scale ( 1.0 )
+ , _duration ( 0 )
+ , _format ( FRAME )
+ , _svalue ( "" )
+ , _hours ( 0 )
+ , _minutes ( 0 )
+ , _seconds ( 0 )
+ , _subseconds ( 0 )
+ , scale ( _scale )
+ , duration ( _duration )
+ , format ( _format )
+ , svalue ( _svalue )
+ , hours ( _hours )
+ , minutes ( _minutes )
+ , seconds ( _seconds )
+ , subseconds ( _subseconds )
+{
+ operator=( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode::Timecode( const string& time_, double scale_ )
+ : _scale ( scale_ < 1.0 ? 1.0 : scale_ )
+ , _duration ( 0 )
+ , _format ( FRAME )
+ , _svalue ( "" )
+ , _hours ( 0 )
+ , _minutes ( 0 )
+ , _seconds ( 0 )
+ , _subseconds ( 0 )
+ , scale ( _scale )
+ , duration ( _duration )
+ , format ( _format )
+ , svalue ( _svalue )
+ , hours ( _hours )
+ , minutes ( _minutes )
+ , seconds ( _seconds )
+ , subseconds ( _subseconds )
+{
+ parse( time_ );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode::Timecode( uint64_t duration_, double scale_ )
+ : _scale ( scale_ < 1.0 ? 1.0 : scale_ )
+ , _duration ( 0 )
+ , _format ( FRAME )
+ , _svalue ( "" )
+ , _hours ( 0 )
+ , _minutes ( 0 )
+ , _seconds ( 0 )
+ , _subseconds ( 0 )
+ , scale ( _scale )
+ , duration ( _duration )
+ , format ( _format )
+ , svalue ( _svalue )
+ , hours ( _hours )
+ , minutes ( _minutes )
+ , seconds ( _seconds )
+ , subseconds ( _subseconds )
+{
+ setDuration( duration_ );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint64_t
+Timecode::convertDuration( const Timecode& obj ) const
+{
+ if( _scale == obj._scale )
+ return obj._duration;
+
+ return static_cast<uint64_t>( ( _scale / obj._scale ) * obj._duration );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode&
+Timecode::operator=( const Timecode& rhs )
+{
+ _scale = rhs._scale;
+ _duration = rhs._duration;
+ _format = FRAME;
+ _svalue = rhs._svalue;
+
+ _hours = rhs._hours;
+ _minutes = rhs._minutes;
+ _seconds = rhs._seconds;
+ _subseconds = rhs._subseconds;
+
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode&
+Timecode::operator+=( const Timecode& rhs )
+{
+ uint64_t dur = _duration + convertDuration( rhs );
+ // overflow check
+ if( dur < _duration )
+ dur = numeric_limits<long long>::max();
+
+ setDuration( dur );
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode&
+Timecode::operator-=( const Timecode& rhs )
+{
+ uint64_t dur = _duration - convertDuration( rhs );
+ // underflow check
+ if( dur > _duration )
+ dur = 0;
+
+ setDuration( dur );
+ return *this;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator<( const Timecode& obj ) const
+{
+ return _duration < convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator<=( const Timecode& obj ) const
+{
+ return _duration <= convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator>( const Timecode& obj ) const
+{
+ return _duration < convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator>=( const Timecode& obj ) const
+{
+ return _duration < convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator!=( const Timecode& obj ) const
+{
+ return _duration != convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::operator==( const Timecode& obj ) const
+{
+ return _duration == convertDuration( obj );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode
+Timecode::operator+( const Timecode& obj ) const
+{
+ Timecode t( *this );
+ t += obj;
+ return t;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Timecode
+Timecode::operator-( const Timecode& obj ) const
+{
+ Timecode t( *this );
+ t -= obj;
+ return t;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool
+Timecode::parse( const string& time, string* outError )
+{
+ string outErrorPlacebo;
+ string& error = outError ? *outError : outErrorPlacebo;
+ error.clear();
+
+ _format = FRAME;
+ _hours = 0;
+ _minutes = 0;
+ _seconds = 0;
+ _subseconds = 0;
+
+ // bail if empty
+ if( time.empty() ) {
+ recompute();
+ return false;
+ }
+
+ // count number of ':'
+ int nsect = 0;
+ int nsemi = 0;
+ int ndot = 0;
+
+ const string::size_type max = time.length();
+ for( string::size_type i = 0; i < max; i++ ) {
+ switch( time[i] ) {
+ case ':':
+ nsect++;
+ break;
+
+ case ';':
+ if( nsemi++ ) {
+ error = "too many semicolons";
+ return true;
+ }
+ nsect++;
+ break;
+
+ case '.':
+ if( ndot++ ) {
+ error = "too many periods";
+ return true;
+ }
+ nsect++;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ // bail if impossible number of sections
+ if( nsect > 3 ) {
+ recompute();
+ error = "too many sections";
+ return true;
+ }
+
+ enum Target {
+ HOURS,
+ MINUTES,
+ SECONDS,
+ SUBSECONDS,
+ };
+
+ // setup target before parsing
+ Target target;
+ uint64_t* tvalue;
+ switch( nsect ) {
+ default:
+ case 0:
+ target = SUBSECONDS;
+ tvalue = &_subseconds;
+ break;
+
+ case 1:
+ target = SECONDS;
+ tvalue = &_seconds;
+ break;
+
+ case 2:
+ target = MINUTES;
+ tvalue = &_minutes;
+ break;
+
+ case 3:
+ target = HOURS;
+ tvalue = &_hours;
+ break;
+ }
+
+ istringstream convert;
+ string tbuffer;
+ for( string::size_type i = 0; i < max; i++ ) {
+ const char c = time[i];
+ switch( c ) {
+ case ':':
+ switch( target ) {
+ case HOURS:
+ convert.clear();
+ convert.str( tbuffer );
+ if( !tbuffer.empty() && !(convert >> *tvalue) ) {
+ error = "failed to convert integer";
+ return true;
+ }
+ tbuffer.clear();
+ target = MINUTES;
+ tvalue = &_minutes;
+ break;
+
+ case MINUTES:
+ convert.clear();
+ convert.str( tbuffer );
+ if( !tbuffer.empty() && !(convert >> *tvalue) ) {
+ error = "failed to convert integer";
+ return true;
+ }
+ tbuffer.clear();
+ target = SECONDS;
+ tvalue = &_seconds;
+ break;
+
+ case SECONDS:
+ convert.clear();
+ convert.str( tbuffer );
+ if( !tbuffer.empty() && !(convert >> *tvalue) ) {
+ error = "failed to convert integer";
+ return true;
+ }
+ tbuffer.clear();
+ target = SUBSECONDS;
+ tvalue = &_subseconds;
+ break;
+
+ default:
+ case SUBSECONDS:
+ error = "unexpected char ':'";
+ return true;
+ }
+ break;
+
+ case '.':
+ {
+ if( target != SECONDS ) {
+ error = "unexpected char '.'";
+ return true;
+ }
+ _format = DECIMAL;
+ convert.clear();
+ convert.str( tbuffer );
+ if( !tbuffer.empty() && !(convert >> *tvalue) ) {
+ error = "failed to convert integer";
+ return true;
+ }
+ tbuffer.clear();
+ target = SUBSECONDS;
+ tvalue = &_subseconds;
+ break;
+ }
+
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ tbuffer += c;
+ if( tbuffer.length() > 16 ) {
+ error = "overflow";
+ return true;
+ }
+ break;
+
+ default:
+ error = "unexpected char '";
+ error += c;
+ error += "'";
+ return true;
+ }
+ }
+
+ // apply final section
+ if( !tbuffer.empty() ) {
+ convert.clear();
+ convert.str( tbuffer );
+ if( !tbuffer.empty() && !(convert >> *tvalue) ) {
+ error = "failed to convert integer";
+ return true;
+ }
+ }
+
+ // special post processing
+ switch( _format ) {
+ case FRAME:
+ default:
+ break;
+
+ case DECIMAL:
+ {
+ double div = std::pow( 10.0, static_cast<double>(tbuffer.length()) );
+ if( div < 1.0 )
+ div = 1.0;
+ *tvalue = static_cast<uint64_t>( static_cast<double>(*tvalue) / div * std::ceil( _scale ));
+ break;
+ }
+ }
+
+ recompute();
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::recompute()
+{
+ // case: 29.97 becomes 30.0
+ // case: 30.0 becomes 30.0
+ const uint64_t iscale = uint64_t( std::ceil( _scale ));
+
+ if( _subseconds > iscale - 1 ) {
+ uint64_t n = _subseconds / iscale;
+ _seconds += n;
+ _subseconds -= n * iscale;
+ }
+
+ if( _seconds > 59 ) {
+ uint64_t n = _seconds / 60;
+ _minutes += n;
+ _seconds -= n * 60;
+ }
+
+ if( _minutes > 59 ) {
+ uint64_t n = _minutes / 60;
+ _hours += n;
+ _minutes -= n * 60;
+ }
+
+ _duration = _subseconds + (iscale * _seconds) + (iscale * _minutes * 60) + (iscale * _hours * 3600);
+
+ ostringstream oss;
+ oss << setfill('0') << right
+ << setw(2) << _hours
+ << ':'
+ << setw(2) << _minutes
+ << ':'
+ << setw(2) << _seconds;
+
+ switch( _format ) {
+ case FRAME:
+ oss << ':' << setw(2) << setfill( '0' ) << _subseconds;
+ break;
+
+ case DECIMAL:
+ {
+ oss << '.' << setw(3) << setfill( '0' ) << static_cast<uint64_t>(_subseconds / _scale * 1000.0 + 0.5);
+ break;
+ }
+ }
+
+ _svalue = oss.str();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::reset()
+{
+ setDuration( 0 );
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setDuration( uint64_t duration_, double scale_ )
+{
+ if( scale_ != 0.0 ) {
+ _scale = scale_;
+ if( _scale < 1.0 )
+ _scale = 1.0;
+ }
+
+ _duration = duration_;
+
+ const uint64_t iscale = uint64_t( std::ceil( _scale ));
+ uint64_t i = _duration;
+
+ _hours = i / (iscale * 3600);
+ i -= (iscale * 3600 * _hours);
+
+ _minutes = i / (iscale * 60);
+ i -= (iscale * 60 * _minutes);
+
+ _seconds = i / iscale;
+ i -= (iscale * _seconds);
+
+ _subseconds = i;
+
+ recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setFormat( Format format_ )
+{
+ _format = format_;
+ recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setHours( uint64_t hours_ )
+{
+ _hours = hours_;
+ recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setMinutes( uint64_t minutes_ )
+{
+ _minutes = minutes_;
+ recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setScale( double scale_ )
+{
+ const double oldscale = _scale;
+ _scale = scale_;
+ if( _scale < 1.0 )
+ _scale = 1.0;
+
+ _subseconds = static_cast<uint64_t>( (_scale / oldscale) * _subseconds );
+ recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setSeconds( uint64_t seconds_ )
+{
+ _seconds = seconds_;
+ recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void
+Timecode::setSubseconds( uint64_t subseconds_ )
+{
+ _subseconds = subseconds_;
+ recompute();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+}} // namespace mp4v2::util