You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
504 lines
14 KiB
504 lines
14 KiB
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// The contents of this file are subject to the Mozilla Public License
|
|
// Version 1.1 (the "License"); you may not use this file except in
|
|
// compliance with the License. You may obtain a copy of the License at
|
|
// http://www.mozilla.org/MPL/
|
|
//
|
|
// Software distributed under the License is distributed on an "AS IS"
|
|
// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing rights and limitations
|
|
// under the License.
|
|
//
|
|
// The Original Code is MP4v2.
|
|
//
|
|
// The Initial Developer of the Original Code is Kona Blend.
|
|
// Portions created by Kona Blend are Copyright (C) 2008.
|
|
// All Rights Reserved.
|
|
//
|
|
// Contributors:
|
|
// Kona Blend, kona8lend@@gmail.com
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "libutil/impl.h"
|
|
|
|
namespace mp4v2 { namespace util {
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
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
|