diff options
Diffstat (limited to 'libutil/Timecode.cpp')
-rw-r--r-- | libutil/Timecode.cpp | 591 |
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 |