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

///////////////////////////////////////////////////////////////////////////////
//
// 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