You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
592 lines
15 KiB
592 lines
15 KiB
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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
|