aboutsummaryrefslogtreecommitdiff
path: root/libutil
diff options
context:
space:
mode:
Diffstat (limited to 'libutil')
-rw-r--r--libutil/Database.cpp225
-rw-r--r--libutil/Database.h101
-rw-r--r--libutil/Timecode.cpp591
-rw-r--r--libutil/Timecode.h119
-rw-r--r--libutil/TrackModifier.cpp503
-rw-r--r--libutil/TrackModifier.h160
-rw-r--r--libutil/Utility.cpp764
-rw-r--r--libutil/Utility.h206
-rw-r--r--libutil/crc.cpp110
-rw-r--r--libutil/crc.h38
-rw-r--r--libutil/impl.h10
-rw-r--r--libutil/other.cpp106
-rw-r--r--libutil/other.h40
-rw-r--r--libutil/util.h34
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