| /////////////////////////////////////////////////////////////////////////////// | |
| // | |
| // 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 Exception( oss.str(), __FILE__, __LINE__, __FUNCTION__ ); | |
| } | |
| } | |
| 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 Exception( oss.str(), __FILE__, __LINE__, __FUNCTION__ ); | |
| } | |
| 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 Exception( oss.str(), __FILE__, __LINE__, __FUNCTION__ ); | |
| } | |
| 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 Exception( oss.str(), __FILE__, __LINE__, __FUNCTION__ ); | |
| } | |
| 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()), (uint32_t)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 Exception( oss.str(), __FILE__, __LINE__, __FUNCTION__ ); | |
| } | |
| 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 |