diff options
Diffstat (limited to 'libutil')
-rw-r--r-- | libutil/Database.cpp | 225 | ||||
-rw-r--r-- | libutil/Database.h | 101 | ||||
-rw-r--r-- | libutil/Timecode.cpp | 591 | ||||
-rw-r--r-- | libutil/Timecode.h | 119 | ||||
-rw-r--r-- | libutil/TrackModifier.cpp | 503 | ||||
-rw-r--r-- | libutil/TrackModifier.h | 160 | ||||
-rw-r--r-- | libutil/Utility.cpp | 764 | ||||
-rw-r--r-- | libutil/Utility.h | 206 | ||||
-rw-r--r-- | libutil/crc.cpp | 110 | ||||
-rw-r--r-- | libutil/crc.h | 38 | ||||
-rw-r--r-- | libutil/impl.h | 10 | ||||
-rw-r--r-- | libutil/other.cpp | 106 | ||||
-rw-r--r-- | libutil/other.h | 40 | ||||
-rw-r--r-- | libutil/util.h | 34 |
14 files changed, 3007 insertions, 0 deletions
diff --git a/libutil/Database.cpp b/libutil/Database.cpp new file mode 100644 index 0000000..4b99b07 --- /dev/null +++ b/libutil/Database.cpp @@ -0,0 +1,225 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 { + +/////////////////////////////////////////////////////////////////////////////// + +Database::Database( const string& filename, const string& key ) + : _filename ( filename ) + , _key ( key ) +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +Database::~Database() +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Database::close() +{ + _stream.close(); + _stream.clear(); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +Database::open( bool write, string& fname ) +{ + _currentKeyValue.clear(); + + _stream.clear(); + _stream.open( fname.c_str(), write ? ios::out : ios::in ); + return _stream.rdstate(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Database::parseData( map<string,string>& data ) +{ + data.clear(); + + string name; + string value; + + if ( _currentKeyValue.length() ) { + data[ _key ] = _currentKeyValue; + _currentKeyValue.clear(); + } + + while ( !parsePair( name, value ) ) { + if ( name == _key ) { + _currentKeyValue = value; + break; + } + data[ name ] = value; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +Database::parsePair( string& name, string& value ) +{ + enum Mode { LTRIM, COMMENT, NAME, DELIM, VALUE }; + Mode mode = LTRIM; + bool delimVisible = false; + + int visibleLength = 0; + + for ( char c; !_stream.get( c ).rdstate(); ) { + switch( mode ) { + case LTRIM: + switch( c ) { + case '\0': // NULL + case '\t': // TAB + case ' ': // SPACE + case '\n': // NEWLINE + case '\r': // CARRIAGE-RETURN + break; + + case '#': // COMMENT + mode = COMMENT; + break; + + default: + mode = NAME; + name = tolower( c ); + break; + } + break; + + case COMMENT: + switch( c ) { + case '\n': // NEWLINE + case '\r': // CARRIAGE-RETURN + mode = LTRIM; + name.clear(); + break; + + default: + break; + } + break; + + case NAME: + switch( c ) { + case '\0': // NULL + break; + + case '\n': // NEWLINE + case '\r': // CARRIAGE-RETURN + mode = LTRIM; + break; + + case '\t': // TAB + case ' ': // SPACE + case '=': // DELIMITER + mode = DELIM; + delimVisible = false; + break; + + default: + name += tolower( c ); + break; + } + break; + + case DELIM: + switch( c ) { + case '\n': // NEWLINE + case '\r': // CARRIAGE-RETURN + mode = LTRIM; + name.clear(); + break; + + case '\0': // NULL + case '\t': // TAB + case ' ': // SPACE + break; + + case '=': // DELIMITER + if( delimVisible ) { + mode = VALUE; + value = c; + visibleLength = value.length(); + } + delimVisible = true; + break; + + default: + mode = VALUE; + value = c; + visibleLength = value.length(); + break; + } + break; + + case VALUE: + switch (c) { + case '\0': // NULL + break; + + case '\n': // NEWLINE + case '\r': // CARRIAGE-RETURN + if( visibleLength ) + value.resize( visibleLength ); + return false; + + case '\t': // TAB + case ' ': // SPACE + value += ' '; + break; + + default: + value += c; + visibleLength = value.length(); + break; + } + break; + + default: + break; + } + } + + if( mode != VALUE ) + return true; + + if( visibleLength ) + value.resize( visibleLength ); + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +}} // namespace mp4v2::util diff --git a/libutil/Database.h b/libutil/Database.h new file mode 100644 index 0000000..43831a6 --- /dev/null +++ b/libutil/Database.h @@ -0,0 +1,101 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef MP4V2_UTIL_DATABASE_H +#define MP4V2_UTIL_DATABASE_H + +namespace mp4v2 { namespace util { + +/////////////////////////////////////////////////////////////////////////////// +/// +/// Database class is the base implementation for persistent databases. +/// +/// All databases use an ASCII file format: +/// @li leading/trailing spaces on any line are trimmed. +/// @li lines beginning with '#' are considered comments. +/// @li lines of { <NAME><DELIMITER><VALUE><EOL> } form NAME/VALUEs pairs. +/// @li <NAME> cannot contain any <DELIMITER> characters. +/// @li <DELIMITER> is any combination of { ' ', '=', '\t' }. +/// @li <VALUE> continues until <EOL>. +/// @li <EOL> is optional for last line. +/// @li <NAME> of value this._key marks the beginning of a new record. +/// @li <NAME> is case-insensitive. +/// @li subsequent lines of NAME/VALUE pairs are part of the same record. +/// +/////////////////////////////////////////////////////////////////////////////// +class Database { +public: + virtual ~Database(); + +protected: + /// Constructor. + /// + /// @param file specifies filename for IO operations. + /// @param key specifies the name of primary key. + /// + Database( const string& file, const string& key ); + + /// Close database file. + void close(); + + /// Open database file. + /// + /// @param write <b>true</b> to open file for writing, <b>false</b> for reading. + /// @param fname filename to open. + /// + /// @return <b>true</b> on error. + /// + bool open( bool write, string& fname ); + + /// Parse a record-data from open intput stream. + /// + /// @param data is populated with NAME/VALUE pairs if any. + /// + void parseData( map<string,string>& data ); + + /////////////////////////////////////////////////////////////////////////// + + const string _filename; // filename (basename only) used for database + const string _key; // name of key for record boundries + fstream _stream; // // IO object + +private: + /// parse a name/value pair from open input stream. + /// + /// @param name stores the parsed name. + /// @param value stores the parsed value. + /// + /// @return <b>true</b> on error (no name/value pair was parised). + /// + bool parsePair( string& name, string& value ); + + /*************************************************************************/ + + string _currentKeyValue; +}; + +/////////////////////////////////////////////////////////////////////////////// + +}} // namespace mp4v2::util + +#endif // MP4V2_UTIL_DATABASE_H 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 diff --git a/libutil/Timecode.h b/libutil/Timecode.h new file mode 100644 index 0000000..dd164e6 --- /dev/null +++ b/libutil/Timecode.h @@ -0,0 +1,119 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef MP4V2_UTIL_TIMECODE_H +#define MP4V2_UTIL_TIMECODE_H + +namespace mp4v2 { namespace util { + +/////////////////////////////////////////////////////////////////////////////// + +/// Class representing SMPTE timecode. +/// +/// Objects of this class represent a specific time or duration and can +/// be converted to/from string values, and various epochs. +/// +/// The standard pattern for string representation is as follows: +/// @li HH:MM:SS:FF +/// @li HH:MM:SS.DDD +/// +/// where: +/// @li <b>HH</b> specifies hours +/// @li <b>MM</b> specifies minutes +/// @li <b>SS</b> specifies seconds +/// @li <b>:</b> specifies normal timecode +/// @li <b>FF</b> specifies the frame number +/// @li <b>.</b> specifies decimal fractions of a second follow +/// @li <b>DDD</b> specifies decimal fractions of a second, rounded down to closest scale +/// +class MP4V2_EXPORT Timecode { +public: + enum Format { + FRAME, + DECIMAL, + }; + +private: + double _scale; + uint64_t _duration; + Format _format; + string _svalue; + + uint64_t _hours; + uint64_t _minutes; + uint64_t _seconds; + uint64_t _subseconds; + +public: + const double& scale; + const uint64_t& duration; + const Format& format; + const string& svalue; + + const uint64_t& hours; + const uint64_t& minutes; + const uint64_t& seconds; + const uint64_t& subseconds; + +public: + Timecode( const Timecode& ); + explicit Timecode( const string&, double = 1.0 ); + explicit Timecode( uint64_t = 0, double = 1.0 ); + + Timecode& operator= ( const Timecode& ); + Timecode& operator+= ( const Timecode& ); + Timecode& operator-= ( const Timecode& ); + + bool operator< ( const Timecode& ) const; + bool operator<= ( const Timecode& ) const; + bool operator> ( const Timecode& ) const; + bool operator>= ( const Timecode& ) const; + bool operator!= ( const Timecode& ) const; + bool operator== ( const Timecode& ) const; + + Timecode operator+ ( const Timecode& ) const; + Timecode operator- ( const Timecode& ) const; + + bool parse( const string&, string* = NULL ); + + void reset(); + + void setScale ( double ); + void setDuration ( uint64_t, double = 0.0 ); + void setFormat ( Format ); + + void setHours ( uint64_t ); + void setMinutes ( uint64_t ); + void setSeconds ( uint64_t ); + void setSubseconds ( uint64_t ); + +private: + uint64_t convertDuration( const Timecode& ) const; + void recompute(); +}; + +/////////////////////////////////////////////////////////////////////////////// + +}} // namespace mp4v2::util + +#endif // MP4V2_UTIL_TIMECODE_H diff --git a/libutil/TrackModifier.cpp b/libutil/TrackModifier.cpp new file mode 100644 index 0000000..6e3ccc4 --- /dev/null +++ b/libutil/TrackModifier.cpp @@ -0,0 +1,503 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 { + +/////////////////////////////////////////////////////////////////////////////// + +TrackModifier::TrackModifier( MP4FileHandle file_, uint16_t trackIndex_ ) + : _track ( refTrackAtom( *static_cast<MP4File*>(file_), trackIndex_ )) + , _props ( *this ) // must come after _track is initialized + , _enabled ( false ) + , _inMovie ( false ) + , _inPreview ( false ) + , _layer ( 0 ) + , _alternateGroup ( 0 ) + , _volume ( 1.0f ) + , _width ( 0.0f ) + , _height ( 0.0f ) + , _language ( bmff::ILC_UND ) + , _handlerType ( "" ) + , _handlerName ( "" ) + , _userDataName ( "" ) + , file ( *static_cast<MP4File*>(file_) ) + , trackIndex ( trackIndex_ ) + , trackId ( MP4FindTrackId( file_, trackIndex_ )) + , enabled ( _enabled ) + , inMovie ( _inMovie ) + , inPreview ( _inPreview ) + , layer ( _layer ) + , alternateGroup ( _alternateGroup ) + , volume ( _volume ) + , width ( _width ) + , height ( _height ) + , language ( _language ) + , handlerType ( _handlerType ) + , handlerName ( _handlerName ) + , userDataName ( _userDataName ) +{ + fetch(); +} + +/////////////////////////////////////////////////////////////////////////////// + +TrackModifier::~TrackModifier() +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::dump( ostream& out, const string& xind ) +{ + const uint32_t w = 14; + const string eq = " = "; + const string ind = " "; + + out << left << xind << "track[" << trackIndex << "] id=" << trackId + << '\n' << xind << ind << setw( w ) << "type" << eq << toStringTrackType( handlerType ) + << '\n' << xind << ind << setw( w ) << "enabled" << eq << toString( enabled ) + << '\n' << xind << ind << setw( w ) << "inMovie" << eq << toString( inMovie ) + << '\n' << xind << ind << setw( w ) << "inPreview" << eq << toString( inPreview ) + << '\n' << xind << ind << setw( w ) << "layer" << eq << layer + << '\n' << xind << ind << setw( w ) << "alternateGroup" << eq << alternateGroup + << '\n' << xind << ind << setw( w ) << "volume" << eq << toString( volume, 8, 8 ) + << '\n' << xind << ind << setw( w ) << "width" << eq << toString( width, 16, 16 ) + << '\n' << xind << ind << setw( w ) << "height" << eq << toString( height, 16, 16 ) + << '\n' << xind << ind << setw( w ) << "language" << eq << bmff::enumLanguageCode.toString( language, true ) + << '\n' << xind << ind << setw( w ) << "handlerName" << eq << handlerName; + + out << '\n' << xind << ind << setw( w ) << "userDataName" << eq + << ( _props.userDataName ? userDataName : "<absent>" ); + + out << '\n'; +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::fetch() +{ + _props.update(); + + const uint32_t flags = _props.flags.GetValue(); + _enabled = flags & 0x01; + _inMovie = flags & 0x02; + _inPreview = flags & 0x04; + + _layer = _props.layer.GetValue(); + _alternateGroup = _props.alternateGroup.GetValue(); + _volume = _props.volume.GetValue(); + _width = _props.width.GetValue(); + _height = _props.height.GetValue(); + + _language = _props.language.GetValue(); + _handlerType = _props.handlerType.GetValue(); + _handlerName = _props.handlerName.GetValue(); + + if( _props.userDataName ) { + uint8_t* buffer; + uint32_t size; + _props.userDataName->GetValue( &buffer, &size ); + _userDataName = string( reinterpret_cast<char*>(buffer), size ); + } + else { + _userDataName.clear(); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool& +TrackModifier::fromString( const string& src, bool& dst ) +{ + if( src == "true" ) + dst = true; + else if ( src == "false" ) + dst = false; + else { + istringstream iss( src ); + iss >> dst; + if( iss.rdstate() != ios::eofbit ) { + ostringstream oss; + oss << "invalid value: " << src; + throw new MP4Exception( oss ); + } + } + + return dst; +} + +/////////////////////////////////////////////////////////////////////////////// + +float& +TrackModifier::fromString( const string& src, float& dst ) +{ + istringstream iss( src ); + iss >> dst; + if( iss.rdstate() != ios::eofbit ) { + ostringstream oss; + oss << "invalid value: " << src; + throw new MP4Exception( oss ); + } + + return dst; +} + +/////////////////////////////////////////////////////////////////////////////// + +uint16_t& +TrackModifier::fromString( const string& src, uint16_t& dst ) +{ + istringstream iss( src ); + iss >> dst; + if( iss.rdstate() != ios::eofbit ) { + ostringstream oss; + oss << "invalid value: " << src; + throw new MP4Exception( oss ); + } + + return dst; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +TrackModifier::hasUserDataName() const +{ + return _props.userDataName != NULL; +} + +/////////////////////////////////////////////////////////////////////////////// + +MP4Atom& +TrackModifier::refTrackAtom( MP4File& file, uint16_t index ) +{ + MP4Atom& root = *file.FindAtom( NULL ); + + ostringstream oss; + oss << "moov.trak[" << index << "]"; + MP4Atom* trak = root.FindAtom( oss.str().c_str() ); + if( !trak ) { + oss.str( "" ); + oss << "trackIndex " << index << " not found"; + throw new MP4Exception( oss ); + } + + return *trak; +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::removeUserDataName() +{ + MP4Atom* name = _track.FindAtom( "trak.udta.name" ); + if( name ) + name->GetParentAtom()->DeleteChildAtom( name ); + + MP4Atom* udta = _track.FindAtom( "trak.udta" ); + if( udta && !udta->GetNumberOfChildAtoms() ) + udta->GetParentAtom()->DeleteChildAtom( udta ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::setAlternateGroup( uint16_t value ) +{ + _props.alternateGroup.SetValue( value ); + fetch(); +} + +void +TrackModifier::setAlternateGroup( const string& value ) +{ + uint16_t tmp; + setAlternateGroup( fromString( value, tmp )); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::setEnabled( bool value ) +{ + _enabled = value; + _props.flags.SetValue( (_enabled ? 0x01 : 0) | (_inMovie ? 0x02 : 0) | (_inPreview ? 0x04 : 0) ); + fetch(); +} + +void +TrackModifier::setEnabled( const string& value ) +{ + bool tmp; + setEnabled( fromString( value, tmp )); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::setHandlerName( const string& value ) +{ + _props.handlerName.SetValue( value.c_str() ); + fetch(); +} +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::setHeight( float value ) +{ + _props.height.SetValue( value ); + fetch(); +} + +void +TrackModifier::setHeight( const string& value ) +{ + float tmp; + setHeight( fromString( value, tmp )); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::setInMovie( bool value ) +{ + _inMovie = value; + _props.flags.SetValue( (_enabled ? 0x01 : 0) | (_inMovie ? 0x02 : 0) | (_inPreview ? 0x04 : 0) ); + fetch(); +} + +void +TrackModifier::setInMovie( const string& value ) +{ + bool tmp; + setInMovie( fromString( value, tmp )); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::setInPreview( bool value ) +{ + _inPreview = value; + _props.flags.SetValue( (_enabled ? 0x01 : 0) | (_inMovie ? 0x02 : 0) | (_inPreview ? 0x04 : 0) ); + fetch(); +} + +void +TrackModifier::setInPreview( const string& value ) +{ + bool tmp; + setInPreview( fromString( value, tmp )); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::setLanguage( bmff::LanguageCode value ) +{ + _props.language.SetValue( value ); + fetch(); +} + +void +TrackModifier::setLanguage( const string& value ) +{ + setLanguage( bmff::enumLanguageCode.toType( value )); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::setLayer( uint16_t value ) +{ + _props.layer.SetValue( value ); + fetch(); +} + +void +TrackModifier::setLayer( const string& value ) +{ + uint16_t tmp; + setLayer( fromString( value, tmp )); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::setUserDataName( const string& value ) +{ + if( !_props.userDataName ) { + ostringstream oss; + oss << "moov.trak[" << trackIndex << "]"; + file.AddDescendantAtoms( oss.str().c_str(), "udta.name" ); + _props.update(); + } + + _props.userDataName->SetValue( reinterpret_cast<const uint8_t*>(value.c_str()), value.size() ); + fetch(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::setVolume( float value ) +{ + _props.volume.SetValue( value ); + fetch(); +} + +void +TrackModifier::setVolume( const string& value ) +{ + float tmp; + setVolume( fromString( value, tmp )); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::setWidth( float value ) +{ + _props.width.SetValue( value ); + fetch(); +} + +void +TrackModifier::setWidth( const string& value ) +{ + float tmp; + setWidth( fromString( value, tmp )); +} + +/////////////////////////////////////////////////////////////////////////////// + +string +TrackModifier::toString( bool value ) +{ + ostringstream oss; + oss << (value ? "true" : "false"); + return oss.str(); +} + +/////////////////////////////////////////////////////////////////////////////// + +string +TrackModifier::toString( float value, uint8_t i, uint8_t f ) +{ + ostringstream oss; + oss << fixed << setprecision(f <= 8 ? 4 : 8) << value; + return oss.str(); +} + +/////////////////////////////////////////////////////////////////////////////// + +string +TrackModifier::toStringTrackType( const string& code ) +{ + if( !code.compare( "vide" )) // 14496-12 + return "video"; + if( !code.compare( "soun" )) // 14496-12 + return "audio"; + if( !code.compare( "hint" )) // 14496-12 + return "hint"; + + if( !code.compare( "text" )) // QTFF + return "text"; + if( !code.compare( "tmcd" )) // QTFF + return "timecode"; + + if( !code.compare( "subt" )) // QTFF + return "subtitle"; + + return string( "(" ) + code + ")"; +} + +/////////////////////////////////////////////////////////////////////////////// + +TrackModifier::Properties::Properties( TrackModifier& trackModifier_ ) + : _trackModifier ( trackModifier_ ) + , flags ( static_cast<MP4Integer24Property&> ( refProperty( "trak.tkhd.flags" ))) + , layer ( static_cast<MP4Integer16Property&> ( refProperty( "trak.tkhd.layer" ))) + , alternateGroup ( static_cast<MP4Integer16Property&> ( refProperty( "trak.tkhd.alternate_group" ))) + , volume ( static_cast<MP4Float32Property&> ( refProperty( "trak.tkhd.volume" ))) + , width ( static_cast<MP4Float32Property&> ( refProperty( "trak.tkhd.width" ))) + , height ( static_cast<MP4Float32Property&> ( refProperty( "trak.tkhd.height" ))) + , language ( static_cast<MP4LanguageCodeProperty&>( refProperty( "trak.mdia.mdhd.language" ))) + , handlerType ( static_cast<MP4StringProperty&> ( refProperty( "trak.mdia.hdlr.handlerType" ))) + , handlerName ( static_cast<MP4StringProperty&> ( refProperty( "trak.mdia.hdlr.name" ))) + , userDataName ( static_cast<MP4BytesProperty*> ( findProperty( "trak.udta.name.value" ))) +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +MP4Property* +TrackModifier::Properties::findProperty( const char* name ) +{ + MP4Property* property; + if( !_trackModifier._track.FindProperty( name, &property )) + return NULL; + + return property; +} + +/////////////////////////////////////////////////////////////////////////////// + +MP4Property& +TrackModifier::Properties::refProperty( const char* name ) +{ + MP4Property* property; + if( !_trackModifier._track.FindProperty( name, &property )) { + ostringstream oss; + oss << "trackId " << _trackModifier.trackId << " property '" << name << "' not found"; + throw new MP4Exception( oss ); + } + + return *property; +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::Properties::update() +{ + // update optional properties + updateProperty( "trak.udta.name.value", reinterpret_cast<MP4Property**>( &userDataName )); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +TrackModifier::Properties::updateProperty( const char* name, MP4Property** pp ) +{ + *pp = NULL; + _trackModifier._track.FindProperty( name, pp ); +} + +/////////////////////////////////////////////////////////////////////////////// + +}} // namespace mp4v2::util diff --git a/libutil/TrackModifier.h b/libutil/TrackModifier.h new file mode 100644 index 0000000..798a061 --- /dev/null +++ b/libutil/TrackModifier.h @@ -0,0 +1,160 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef MP4V2_UTIL_TRACKMODIFIER_H +#define MP4V2_UTIL_TRACKMODIFIER_H + +namespace mp4v2 { namespace util { + +/////////////////////////////////////////////////////////////////////////////// + +class MP4V2_EXPORT TrackModifier +{ +private: + class Properties + { + private: + TrackModifier& _trackModifier; + + public: + Properties( TrackModifier& ); + + void update(); + + MP4Integer24Property& flags; + MP4Integer16Property& layer; + MP4Integer16Property& alternateGroup; + MP4Float32Property& volume; + MP4Float32Property& width; + MP4Float32Property& height; + MP4LanguageCodeProperty& language; + MP4StringProperty& handlerType; + MP4StringProperty& handlerName; + MP4BytesProperty* userDataName; + + private: + MP4Property& refProperty( const char* ); + MP4Property* findProperty( const char* ); + void updateProperty( const char*, MP4Property** ); + }; + + friend class Properties; + +private: + static MP4Atom& refTrackAtom( MP4File&, uint16_t ); + +private: + MP4Atom& _track; + Properties _props; + + // Track Header + bool _enabled; + bool _inMovie; + bool _inPreview; + uint16_t _layer; + uint16_t _alternateGroup; + float _volume; + float _width; + float _height; + + // Media Header + bmff::LanguageCode _language; + + // Handler Reference + string _handlerType; + string _handlerName; + + // User Data name + string _userDataName; + +public: + MP4File& file; + const uint16_t trackIndex; + const MP4TrackId trackId; + + const bool& enabled; + const bool& inMovie; + const bool& inPreview; + const uint16_t& layer; + const uint16_t& alternateGroup; + const float& volume; + const float& width; + const float& height; + + const bmff::LanguageCode& language; + + const string& handlerType; + const string& handlerName; + + const string& userDataName; + +public: + TrackModifier( MP4FileHandle, uint16_t ); + ~TrackModifier(); + + void setEnabled ( bool ); + void setInMovie ( bool ); + void setInPreview ( bool ); + void setLayer ( uint16_t ); + void setAlternateGroup ( uint16_t ); + void setVolume ( float ); + void setWidth ( float ); + void setHeight ( float ); + void setLanguage ( bmff::LanguageCode ); + void setHandlerName ( const string& ); + void setUserDataName ( const string& ); + + // set by string + void setEnabled ( const string& ); + void setInMovie ( const string& ); + void setInPreview ( const string& ); + void setLayer ( const string& ); + void setAlternateGroup ( const string& ); + void setVolume ( const string& ); + void setWidth ( const string& ); + void setHeight ( const string& ); + void setLanguage ( const string& ); + + bool hasUserDataName() const; + void removeUserDataName(); + + void dump( ostream&, const string& ); + +private: + void fetch(); + + static string toString( bool ); + static string toString( float, uint8_t, uint8_t ); + + static bool& fromString( const string&, bool& ); + static float& fromString( const string&, float& ); + static uint16_t& fromString( const string&, uint16_t& ); + + static string toStringTrackType( const string& ); +}; + +/////////////////////////////////////////////////////////////////////////////// + +}} // namespace mp4v2::util + +#endif // MP4V2_UTIL_TRACKMODIFIER_H diff --git a/libutil/Utility.cpp b/libutil/Utility.cpp new file mode 100644 index 0000000..43712da --- /dev/null +++ b/libutil/Utility.cpp @@ -0,0 +1,764 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 { + +/////////////////////////////////////////////////////////////////////////////// + +Utility::Utility( string name_, int argc_, char** argv_ ) + : _longOptions ( NULL ) + , _name ( name_ ) + , _argc ( argc_ ) + , _argv ( argv_ ) + , _optimize ( false ) + , _dryrun ( false ) + , _keepgoing ( false ) + , _overwrite ( false ) + , _force ( false ) + , _debug ( 0 ) + , _verbosity ( 1 ) + , _jobCount ( 0 ) + , _debugVerbosity ( 0 ) + , _debugImplicits ( false ) + , _group ( "OPTIONS" ) + +,STD_OPTIMIZE( 'z', false, "optimize", false, LC_NONE, "optimize mp4 file after modification" ) +,STD_DRYRUN( 'y', false, "dryrun", false, LC_NONE, "do not actually create or modify any files" ) +,STD_KEEPGOING( 'k', false, "keepgoing", false, LC_NONE, "continue batch processing even after errors" ) +,STD_OVERWRITE( 'o', false, "overwrite", false, LC_NONE, "overwrite existing files when creating" ) +,STD_FORCE( 'f', false, "force", false, LC_NONE, "force overwrite even if file is read-only" ) +,STD_QUIET( 'q', false, "quiet", false, LC_NONE, "equivalent to --verbose 0" ) +,STD_DEBUG( 'd', false, "debug", true, LC_DEBUG, "increase debug or long-option to set NUM", "NUM", + // 79-cols, inclusive, max desired width + // |----------------------------------------------------------------------------| + "\nDEBUG LEVELS (for raw mp4 file I/O)" + "\n 0 supressed" + "\n 1 add warnings and errors (default)" + "\n 2 add table details" + "\n 3 add implicits" + "\n 4 everything" ) +,STD_VERBOSE( 'v', false, "verbose", true, LC_VERBOSE, "increase verbosity or long-option to set NUM", "NUM", + // 79-cols, inclusive, max desired width + // |----------------------------------------------------------------------------| + "\nVERBOSE LEVELS" + "\n 0 warnings and errors" + "\n 1 normal informative messages (default)" + "\n 2 more informative messages" + "\n 3 everything" ) +,STD_HELP( 'h', false, "help", false, LC_HELP, "print brief help or long-option for extended help" ) +,STD_VERSION( 0, false, "version", false, LC_VERSION, "print version information and exit" ) +,STD_VERSIONX( 0, false, "versionx", false, LC_VERSIONX, "print extended version information", "ARG", "", true ) + +{ + debugUpdate( 1 ); + + _usage = "<UNDEFINED>"; + _description = "<UNDEFINED>"; + _groups.push_back( &_group ); +} + +/////////////////////////////////////////////////////////////////////////////// + +Utility::~Utility() +{ + delete[] _longOptions; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +Utility::batch( int argi ) +{ + _jobCount = 0; + _jobTotal = _argc - argi; + + // nothing to be done + if( !_jobTotal ) + return SUCCESS; + + bool batchResult = FAILURE; + for( int i = argi; i < _argc; i++ ) { + bool subResult = FAILURE; + try { + if( !job( _argv[i] )) { + batchResult = SUCCESS; + subResult = SUCCESS; + } + } + catch( mp4v2::impl::MP4Error* x ) { + x->Print( stderr ); + delete x; + } + catch( MP4Exception* x ) { + herrf( "%s\n", x->what.c_str() ); + delete x; + } + + if( !_keepgoing && subResult == FAILURE ) + return FAILURE; + } + + return batchResult; +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::debugUpdate( uint32_t debug ) +{ + _debug = debug; + verbose2f( "debug level: %u\n", _debug ); + + switch( _debug ) { + case 0: + _debugVerbosity = 0; + _debugImplicits = false; + break; + + case 1: + _debugVerbosity = MP4_DETAILS_ERROR; + _debugImplicits = false; + break; + + case 2: + _debugVerbosity = MP4_DETAILS_ERROR | MP4_DETAILS_TABLE; + _debugImplicits = false; + break; + + case 3: + _debugVerbosity = MP4_DETAILS_ERROR | MP4_DETAILS_TABLE; + _debugImplicits = true; + break; + + case 4: + default: + _debugVerbosity = MP4_DETAILS_ALL; + _debugImplicits = true; + break; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +Utility::dryrunAbort() +{ + if( !_dryrun ) + return false; + + verbose2f( "skipping: dry-run mode enabled\n" ); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::errf( const char* format, ... ) +{ + va_list ap; + va_start( ap, format ); + vfprintf( stderr, format, ap ); + va_end( ap ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::formatGroups() +{ + // determine longest long-option [+space +argname] + int longMax = 0; + list<Group*>::reverse_iterator ie = _groups.rend(); + for( list<Group*>::reverse_iterator it = _groups.rbegin(); it != ie; it++ ) { + Group& group = **it; + const Group::List::const_iterator ieo = group.options.end(); + for( Group::List::const_iterator ito = group.options.begin(); ito != ieo; ito++ ) { + const Option& option = **ito; + if( option.hidden ) + continue; + + int len = option.lname.length(); + if( option.lhasarg ) + len += 1 + option.argname.length(); + if( len > longMax ) + longMax = len; + } + } + + // format help output (no line-wrapping yet) + ostringstream oss; + + int groupCount = 0; + int optionCount = 0; + ie = _groups.rend(); + for( list<Group*>::reverse_iterator it = _groups.rbegin(); it != ie; it++, groupCount++ ) { + if( groupCount ) + oss << '\n'; + Group& group = **it; + oss << '\n' << group.name; + const Group::List::const_iterator ieo = group.options.end(); + for( Group::List::const_iterator ito = group.options.begin(); ito != ieo; ito++, optionCount++ ) { + const Option& option = **ito; + if( option.hidden ) + continue; + + oss << "\n "; + + if( option.scode == 0 ) + oss << " --"; + else + oss << '-' << option.scode << ", --"; + + if( option.lhasarg ) { + oss << option.lname << ' ' << option.argname; + oss << setw( longMax - option.lname.length() - 1 - option.argname.length() ) << ""; + } + else { + oss << setw( longMax ) << left << option.lname; + } + + oss << " "; + + const string::size_type imax = option.descr.length(); + for( string::size_type i = 0; i < imax; i++ ) + oss << option.descr[i]; + } + } + + _help = oss.str(); + + // allocate and populate C-style options + delete[] _longOptions; + _longOptions = new prog::Option[optionCount + 1]; + + // fill EOL marker + _longOptions[optionCount].name = NULL; + _longOptions[optionCount].type = prog::Option::NO_ARG; + _longOptions[optionCount].flag = 0; + _longOptions[optionCount].val = 0; + + _shortOptions.clear(); + + int optionIndex = 0; + ie = _groups.rend(); + for( list<Group*>::reverse_iterator it = _groups.rbegin(); it != ie; it++ ) { + Group& group = **it; + const Group::List::const_iterator ieo = group.options.end(); + for( Group::List::const_iterator ito = group.options.begin(); ito != ieo; ito++, optionIndex++ ) { + const Option& a = **ito; + prog::Option& b = _longOptions[optionIndex]; + + b.name = const_cast<char*>(a.lname.c_str()); + b.type = a.lhasarg ? prog::Option::REQUIRED_ARG : prog::Option::NO_ARG; + b.flag = 0; + b.val = (a.lcode == LC_NONE) ? a.scode : a.lcode; + + if( a.scode != 0 ) { + _shortOptions += a.scode; + if( a.shasarg ) + _shortOptions += ':'; + } + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +Utility::job( string arg ) +{ + verbose2f( "job begin: %s\n", arg.c_str() ); + + // perform job + JobContext job( arg ); + bool result = FAILURE; + try { + result = utility_job( job ); + } + catch( mp4v2::impl::MP4Error* x ) { + x->Print( stderr ); + delete x; + } + catch( MP4Exception* x ) { + herrf( "%s\n", x->what.c_str() ); + delete x; + } + + // close file handle flagged with job + if( job.fileHandle != MP4_INVALID_FILE_HANDLE ) { + verbose2f( "closing %s\n", job.file.c_str() ); + MP4Close( job.fileHandle ); + + // invoke optimize if flagged + if( _optimize && job.optimizeApplicable ) { + verbose1f( "optimizing %s\n", job.file.c_str() ); + if( !MP4Optimize( job.file.c_str(), NULL )) + hwarnf( "optimize failed: %s\n", job.file.c_str() ); + } + } + + // free data flagged with job + list<void*>::iterator ie = job.tofree.end(); + for( list<void*>::iterator it = job.tofree.begin(); it != ie; it++ ) + free( *it ); + + + verbose2f( "job end\n" ); + _jobCount++; + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +Utility::herrf( const char* format, ... ) +{ + va_list ap; + va_start( ap, format ); + + if( _keepgoing ) { + fprintf( stdout, "WARNING: " ); + vfprintf( stdout, format, ap ); + } + else { + fprintf( stderr, "ERROR: " ); + vfprintf( stderr, format, ap ); + } + + va_end( ap ); + return FAILURE; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +Utility::hwarnf( const char* format, ... ) +{ + fprintf( stdout, "WARNING: " ); + va_list ap; + va_start( ap, format ); + vfprintf( stdout, format, ap ); + va_end( ap ); + return FAILURE; +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::outf( const char* format, ... ) +{ + va_list ap; + va_start( ap, format ); + vfprintf( stdout, format, ap ); + va_end( ap ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::printHelp( bool extended, bool toerr ) +{ + ostringstream oss; + oss << "Usage: " << _name << " " << _usage << '\n' << _description << '\n' << _help; + + if( extended ) { + const list<Group*>::reverse_iterator ie = _groups.rend(); + for( list<Group*>::reverse_iterator it = _groups.rbegin(); it != ie; it++ ) { + Group& group = **it; + const Group::List::const_iterator ieo = group.options.end(); + for( Group::List::const_iterator ito = group.options.begin(); ito != ieo; ito++ ) { + const Option& option = **ito; + if( option.help.empty() ) + continue; + + oss << '\n' << option.help; + } + } + } + + if( toerr ) + errf( "%s\n\n", oss.str().c_str() ); + else + outf( "%s\n\n", oss.str().c_str() ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::printUsage( bool toerr ) +{ + ostringstream oss; + oss << "Usage: " << _name << " " << _usage + << "\nTry -h for brief help or --help for extended help"; + + if( toerr ) + errf( "%s\n", oss.str().c_str() ); + else + outf( "%s\n", oss.str().c_str() ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::printVersion( bool extended ) +{ + ostringstream oss; + oss << left; + + if( extended ) { + oss << setw(13) << "utility:" << _name + << '\n' << setw(13) << "product:" << MP4V2_PROJECT_name + << '\n' << setw(13) << "version:" << MP4V2_PROJECT_version + << '\n' << setw(13) << "build date:" << MP4V2_PROJECT_build + << '\n' + << '\n' << setw(18) << "repository URL:" << MP4V2_PROJECT_repo_url + << '\n' << setw(18) << "repository root:" << MP4V2_PROJECT_repo_root + << '\n' << setw(18) << "repository UUID:" << MP4V2_PROJECT_repo_uuid + << '\n' << setw(18) << "repository rev:" << MP4V2_PROJECT_repo_rev + << '\n' << setw(18) << "repository date:" << MP4V2_PROJECT_repo_date + << '\n' << setw(18) << "repository type:" << MP4V2_PROJECT_repo_type; + } + else { + oss << _name << " - " << MP4V2_PROJECT_name_formal; + } + + outf( "%s\n", oss.str().c_str() ); +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +Utility::process() +{ + bool rv = true; + + try { + rv = process_impl(); + } + catch( MP4Exception* x ) { + // rare usage of herrf, make sure its not a warning header. + _keepgoing = false; + herrf( "%s\n", x->what.c_str() ); + delete x; + } + + return rv; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +Utility::process_impl() +{ + formatGroups(); + + // populate code lookup set + set<int> codes; + const Group::List::const_iterator ie = _group.options.end(); + for( Group::List::const_iterator it = _group.options.begin(); it != ie; it++ ) { + const Option& option = **it; + if( option.scode != 0 ) + codes.insert( option.scode ); + if( option.lcode != LC_NONE ) + codes.insert( option.lcode ); + } + + for( ;; ) { + const int code = prog::getOption( _argc, _argv, _shortOptions.c_str(), _longOptions, NULL ); + if( code == -1 ) + break; + + bool handled = false; + if( utility_option( code, handled )) + return FAILURE; + if( handled ) + continue; + + if( codes.find( code ) == codes.end() ) + continue; + + switch( code ) { + case 'z': + _optimize = true; + break; + + case 'y': + _dryrun = true; + break; + + case 'k': + _keepgoing = true; + break; + + case 'o': + _overwrite = true; + break; + + case 'f': + _force = true; + break; + + case 'q': + _verbosity = 0; + debugUpdate( 0 ); + break; + + case 'v': + _verbosity++; + break; + + case 'd': + debugUpdate( _debug + 1 ); + break; + + case 'h': + printHelp( false, false ); + return SUCCESS; + + case LC_DEBUG: + debugUpdate( std::strtoul( prog::optarg, NULL, 0 ) ); + break; + + case LC_VERBOSE: + { + const uint32_t level = std::strtoul( prog::optarg, NULL, 0 ); + _verbosity = ( level < 4 ) ? level : 3; + break; + } + + case LC_HELP: + printHelp( true, false ); + return SUCCESS; + + case LC_VERSION: + printVersion( false ); + return SUCCESS; + + case LC_VERSIONX: + printVersion( true ); + return SUCCESS; + + default: + printUsage( true ); + return FAILURE; + } + } + + if( !(prog::optind < _argc) ) { + printUsage( true ); + return FAILURE; + } + + const bool result = batch( prog::optind ); + verbose2f( "exit code %d\n", result ); + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +Utility::openFileForWriting( io::File& file ) +{ + // simple case is file does not exist + if( !io::FileSystem::exists( file.name )) { + if( file.open() ) + return herrf( "unable to open %s for write: %s\n", file.name.c_str(), sys::getLastErrorStr() ); + return SUCCESS; + } + + // fail if overwrite is not enabled + if( !_overwrite ) + return herrf( "file already exists: %s\n", file.name.c_str() ); + + // only overwrite if it is a file + if( !io::FileSystem::isFile( file.name )) + return herrf( "cannot overwrite non-file: %s\n", file.name.c_str() ); + + // first attemp to re-open/truncate so as to keep any file perms + if( !file.open() ) + return SUCCESS; + + // fail if force is not enabled + if( !_force ) + return herrf( "unable to overwrite file: %s\n", file.name.c_str() ); + + // first attempt to open, truncating file + if( !file.open() ) + return SUCCESS; + + // nuke file + if( ::remove( file.name.c_str() )) + return herrf( "unable to remove %s: %s\n", file.name.c_str(), sys::getLastErrorStr() ); + + // final effort + if( !file.open() ) + return SUCCESS; + + return herrf( "unable to open %s for write: %s\n", file.name.c_str(), sys::getLastErrorStr() ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::verbose( uint32_t level, const char* format, va_list ap ) +{ + if( level > _verbosity ) + return; + vfprintf( stdout, format, ap ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::verbose1f( const char* format, ... ) +{ + va_list ap; + va_start( ap, format ); + verbose( 1, format, ap ); + va_end( ap ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::verbose2f( const char* format, ... ) +{ + va_list ap; + va_start( ap, format ); + verbose( 2, format, ap ); + va_end( ap ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::verbose3f( const char* format, ... ) +{ + va_list ap; + va_start( ap, format ); + verbose( 3, format, ap ); + va_end( ap ); +} + +/////////////////////////////////////////////////////////////////////////////// + +const bool Utility::SUCCESS = false; +const bool Utility::FAILURE = true; + +/////////////////////////////////////////////////////////////////////////////// + +Utility::Group::Group( string name_ ) + : name ( name_ ) + , options ( _options ) +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +Utility::Group::~Group() +{ + const List::iterator ie = _optionsDelete.end(); + for( List::iterator it = _optionsDelete.begin(); it != ie; it++ ) + delete *it; +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::Group::add( const Option& option ) +{ + _options.push_back( &option ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::Group::add( + char scode, + bool shasarg, + string lname, + bool lhasarg, + uint32_t lcode, + string descr, + string argname, + string help, + bool hidden ) +{ + Option* o = new Option( scode, shasarg, lname, lhasarg, lcode, descr, argname, help, hidden ); + _options.push_back( o ); + _optionsDelete.push_back( o ); +} + +/////////////////////////////////////////////////////////////////////////////// + +void +Utility::Group::add( + string lname, + bool lhasarg, + uint32_t lcode, + string descr, + string argname, + string help, + bool hidden ) +{ + add( 0, false, lname, lhasarg, lcode, descr, argname, help, hidden ); +} + +/////////////////////////////////////////////////////////////////////////////// + +Utility::Option::Option( + char scode_, + bool shasarg_, + string lname_, + bool lhasarg_, + uint32_t lcode_, + string descr_, + string argname_, + string help_, + bool hidden_ ) + : scode ( scode_ ) + , shasarg ( shasarg_ ) + , lname ( lname_ ) + , lhasarg ( lhasarg_ ) + , lcode ( lcode_ ) + , descr ( descr_ ) + , argname ( argname_ ) + , help ( help_ ) + , hidden ( hidden_ ) +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +Utility::JobContext::JobContext( string file_ ) + : file ( file_ ) + , fileHandle ( MP4_INVALID_FILE_HANDLE ) + , optimizeApplicable ( false ) +{ +} + +/////////////////////////////////////////////////////////////////////////////// + +}} // namespace mp4v2::util diff --git a/libutil/Utility.h b/libutil/Utility.h new file mode 100644 index 0000000..c8c7caf --- /dev/null +++ b/libutil/Utility.h @@ -0,0 +1,206 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef MP4V2_UTIL_UTILITY_H +#define MP4V2_UTIL_UTILITY_H + +namespace mp4v2 { namespace util { + +/////////////////////////////////////////////////////////////////////////////// +/// +/// Utility general program class. +/// +/// This class provides a base implementation for a general utility which +/// helps meet behavioral criteria for command-line executables. +/// +/// Inherit batch processing ability is also provided and is used optionally +/// by the concrete implementation. +/// +/// Criteria and guidelines for utility behavior in MP4v2 are as follows: +/// @li exit with 0 when the utility succeeds at its main task, 1 for failure. +/// @li print brief-usage when no arguments are supplied and exit with 1. +/// @li print brief-usage when too many arguments are supplied and exit with 1. +/// @li issue brief-usage when invalid argument is supplied and exit with 1. +/// @li support --help option and exit with 0. +/// @li support --version option and exit with 0. +/// @li utilities which <b>create</b> new output files should never +/// over-write an existing file unless given the project's universal +/// '-o' or '--overwrite' option. +/// +/////////////////////////////////////////////////////////////////////////////// +class MP4V2_EXPORT Utility +{ +protected: + enum LongCode { + LC_NONE = 0xf0000000, // safe (cannot conflict with char values) + LC_DEBUG, + LC_VERBOSE, + LC_HELP, + LC_VERSION, + LC_VERSIONX, + _LC_MAX // will be used to seeed derived-class long-codes enum + }; + + class MP4V2_EXPORT Option { + public: + Option( char, bool, string, bool, uint32_t, string, string = "ARG", string = "", bool = false ); + + const char scode; + const bool shasarg; + const string lname; + const bool lhasarg; + const uint32_t lcode; + const string descr; + const string argname; + const string help; + const bool hidden; + }; + + class MP4V2_EXPORT Group { + public: + explicit Group( string ); + ~Group(); + + void add( const Option& ); // options added this way will not be deleted + void add( char, bool, string, bool, uint32_t, string, string = "ARG", string = "", bool = false ); + void add( string, bool, uint32_t, string, string = "ARG", string = "", bool = false ); + + const string name; + + public: + typedef list<const Option*> List; + + private: + List _options; + List _optionsDelete; + + public: + const List& options; + }; + + //! structure passed as argument to each job during batch processing + class MP4V2_EXPORT JobContext + { + public: + JobContext( string file_ ); + + const string file; //!< file job is working on + MP4FileHandle fileHandle; //!< handle of file, if applicable to job + bool optimizeApplicable; //!< indicate file optimization is applicable + list<void*> tofree; //!< memory to free at end of job + }; + +public: + virtual ~Utility(); + + bool process(); + +protected: + Utility( string, int, char** ); + + void printUsage ( bool ); //!< print usage + void printHelp ( bool, bool ); //!< print help + void printVersion ( bool ); //!< print utility version + + void errf ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3); //!< print to stderr + void outf ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3); //!< print to stdout + + bool herrf ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3); //!< print to stderr indicating error + bool hwarnf ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3); //!< print to stderr indicating warning + + void verbose1f ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3); + void verbose2f ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3); + void verbose3f ( const char*, ... ) MP4V2_WFORMAT_PRINTF(2,3); + + bool batch ( int ); //!< process all remaining arguments (jobs) + bool job ( string ); //!< process next argument + + //! open file in consideration of overwrite/force options + bool openFileForWriting( io::File& ); + + bool dryrunAbort(); + + // delegates + virtual bool utility_option( int, bool& ) = 0; //!< process command-line option + virtual bool utility_job( JobContext& ) = 0; //!< process positional argument + +private: + void formatGroups(); + void debugUpdate( uint32_t ); + void verbose( uint32_t, const char*, va_list ); + bool process_impl(); + +private: + string _help; + + prog::Option* _longOptions; + string _shortOptions; + +protected: + const string _name; //!< executable basename + const int _argc; //!< arg count + char* const* const _argv; //!< arg vector + + // common options state + bool _optimize; //!< optimize mp4 file after modification + bool _dryrun; //!< dry-run, no writing is actually performed + bool _keepgoing; //!< contine batch processing even after error + bool _overwrite; //!< overwrite file if already exists + bool _force; //!< force overwriting a file even if read-only + uint32_t _debug; //!< mp4 file I/O verbosity + uint32_t _verbosity; //!< verbosity level, default=1 + + uint32_t _jobCount; + uint32_t _jobTotal; + uint32_t _debugVerbosity; + bool _debugImplicits; + + Group _group; // group to which standard options are added + string _usage; + string _description; + list<Group*> _groups; + +protected: + // standard options for concrete utilities to add to _group in constructor + const Option STD_OPTIMIZE; + const Option STD_DRYRUN; + const Option STD_KEEPGOING; + const Option STD_OVERWRITE; + const Option STD_FORCE; + const Option STD_QUIET; + const Option STD_DEBUG; + const Option STD_VERBOSE; + const Option STD_HELP; + const Option STD_VERSION; + const Option STD_VERSIONX; + +public: + static const bool SUCCESS; + static const bool FAILURE; +}; + +/////////////////////////////////////////////////////////////////////////////// + +}} // namespace mp4v2::util + +#endif // MP4V2_UTIL_UTILITY_H diff --git a/libutil/crc.cpp b/libutil/crc.cpp new file mode 100644 index 0000000..18d1147 --- /dev/null +++ b/libutil/crc.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1988, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "libutil/impl.h" + +namespace mp4v2 { namespace util { + +/////////////////////////////////////////////////////////////////////////////// + +uint32_t +crc32( const unsigned char* data, uint32_t size ) +{ + static const uint32_t __crctab[256] = { + 0x0, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4, + }; + +#define COMPUTE(var,ch) (var) = (var) << 8 ^ __crctab[(var) >> 24 ^ (ch)] + + uint32_t crc = 0; + const unsigned char* const max = data + size; + + for (const unsigned char* p = data; p < max; p++) + COMPUTE( crc, *p ); + + for( ; size != 0; size >>= 8 ) + COMPUTE( crc, size & 0xff ); + + return ~crc; +} + +/////////////////////////////////////////////////////////////////////////////// + +}} // namespace mp4v2::util diff --git a/libutil/crc.h b/libutil/crc.h new file mode 100644 index 0000000..470b14d --- /dev/null +++ b/libutil/crc.h @@ -0,0 +1,38 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef MP4V2_UTIL_CRC_H +#define MP4V2_UTIL_CRC_H + +namespace mp4v2 { namespace util { + +/////////////////////////////////////////////////////////////////////////////// + +MP4V2_EXPORT +uint32_t crc32( const unsigned char*, uint32_t ); // ISO/IEC 8802-3:1989 + +/////////////////////////////////////////////////////////////////////////////// + +}} // namespace mp4v2::util + +#endif // MP4V2_UTIL_CRC_H diff --git a/libutil/impl.h b/libutil/impl.h new file mode 100644 index 0000000..3824a14 --- /dev/null +++ b/libutil/impl.h @@ -0,0 +1,10 @@ +#ifndef MP4V2_UTIL_IMPL_H +#define MP4V2_UTIL_IMPL_H + +/////////////////////////////////////////////////////////////////////////////// + +#include "util.h" + +/////////////////////////////////////////////////////////////////////////////// + +#endif // MP4V2_UTIL_IMPL_H diff --git a/libutil/other.cpp b/libutil/other.cpp new file mode 100644 index 0000000..698b588 --- /dev/null +++ b/libutil/other.cpp @@ -0,0 +1,106 @@ +/////////////////////////////////////////////////////////////////////////////// +// +// 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 { + using namespace mp4v2::impl; + +/////////////////////////////////////////////////////////////////////////////// + +// search atom recursively for any 64-bit characteristics. +// nlargsize indicates number of atoms which use largesize extension. +// nversion1 indicates number of atoms which use version==1 extension. +// nspecial indicates number of special 64-bit atoms; +// eg: stbl may container one of { stco || co64 } for chunkoffsets. + +void +searchFor64bit( MP4Atom& atom, FileSummaryInfo& info ) +{ + const uint32_t max = atom.GetNumberOfChildAtoms(); + for( uint32_t i = 0; i < max; i++ ) { + MP4Atom& child = *atom.GetChildAtom( i ); + + if( child.GetLargesizeMode() ) + info.nlargesize++; + + MP4Integer8Property* version; + if( child.FindProperty( "version", (MP4Property**)&version ) && version->GetValue() == 1 ) + info.nversion1++; + + if( !strcmp( child.GetType(), "co64" )) + info.nspecial++; + + searchFor64bit( child, info ); + } +} + +/////////////////////////////////////////////////////////////////////////////// + +bool +fileFetchSummaryInfo( MP4FileHandle file, FileSummaryInfo& info ) +{ + if( file == MP4_INVALID_FILE_HANDLE ) + return true; + MP4File& mp4 = *((MP4File*)file); + + MP4Atom* root = mp4.FindAtom( "" ); + if( !root ) + return true; + + MP4FtypAtom* ftyp = (MP4FtypAtom*)root->FindAtom( "ftyp" ); + if( !ftyp ) + return true; + + info.major_brand = ftyp->majorBrand.GetValue(); + info.minor_version = ftyp->minorVersion.GetValue(); + + const uint32_t cbmax = ftyp->compatibleBrands.GetCount(); + for( uint32_t i = 0; i < cbmax; i++ ) { + string s = ftyp->compatibleBrands.GetValue( i ); + + // remove spaces so brand set is presentable + string stripped; + const string::size_type max = s.length(); + for( string::size_type pos = 0; pos < max; pos++ ) { + if( s[pos] != ' ' ) + stripped += s[pos]; + } + + if( stripped.empty() ) + continue; + + info.compatible_brands.insert( stripped ); + } + + info.nlargesize = 0; + info.nversion1 = 0; + info.nspecial = 0; + searchFor64bit( *root, info ); + + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +}} // namespace mp4v2::util diff --git a/libutil/other.h b/libutil/other.h new file mode 100644 index 0000000..83d5411 --- /dev/null +++ b/libutil/other.h @@ -0,0 +1,40 @@ +#ifndef MP4V2_UTIL_OTHER_H +#define MP4V2_UTIL_OTHER_H + +/////////////////////////////////////////////////////////////////////////////// + +namespace mp4v2 { namespace util { + +/////////////////////////////////////////////////////////////////////////////// + +struct MP4V2_EXPORT FileSummaryInfo { + typedef set<string> BrandSet; + + // standard ftyp box attributes + string major_brand; + uint32_t minor_version; + BrandSet compatible_brands; + + uint32_t nlargesize; + uint32_t nversion1; + uint32_t nspecial; +}; + +/////////////////////////////////////////////////////////////////////////////// +/// +/// Fetch mp4 file summary information. +/// +/// This function fetches summary information for <b>file</b> and information +/// is stored in <b>info</b>. +/// +/// @return On success <b>true</b>. +/// On failure <b>false</b>, and contents of <b>info</b> are undefined. +/// +MP4V2_EXPORT +bool fileFetchSummaryInfo( MP4FileHandle file, FileSummaryInfo& info ); + +/////////////////////////////////////////////////////////////////////////////// + +}} // namespace mp4v2::util + +#endif // MP4V2_UTIL_OTHER_H diff --git a/libutil/util.h b/libutil/util.h new file mode 100644 index 0000000..2ea7246 --- /dev/null +++ b/libutil/util.h @@ -0,0 +1,34 @@ +#ifndef MP4V2_UTIL_UTIL_H +#define MP4V2_UTIL_UTIL_H + +/////////////////////////////////////////////////////////////////////////////// + +#include "src/src.h" + +/////////////////////////////////////////////////////////////////////////////// + +/// @namespace mp4v2::util (private) Command-line utility support. +/// <b>WARNING: THIS IS A PRIVATE NAMESPACE. NOT FOR PUBLIC CONSUMPTION.</b> +/// +/// This namespace is used for command-line utilities. Some symbols from this +/// namespace are exported from libmp4v2 in order to support new functionality +/// which may or may not make it into some form of public API, at which time +/// it will be moved out of this namespace. +/// +namespace mp4v2 { namespace util { + using namespace std; + using namespace mp4v2::impl; +}} // namespace mp4v2::util + +/////////////////////////////////////////////////////////////////////////////// + +#include "Database.h" +#include "Timecode.h" +#include "TrackModifier.h" +#include "Utility.h" +#include "crc.h" +#include "other.h" + +/////////////////////////////////////////////////////////////////////////////// + +#endif // MP4V2_UTIL_UTIL_H |