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.

998 lines
32 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.
// Portions created by David Byron are Copyright (C) 2010.
// All Rights Reserved.
//
// Contributors:
// Kona Blend, kona8lend@@gmail.com
// David Byron, dbyron@dbyron.com
//
///////////////////////////////////////////////////////////////////////////////
#include "util/impl.h"
namespace mp4v2 { namespace util {
///////////////////////////////////////////////////////////////////////////////
class TrackUtility : public Utility
{
private:
enum TrackLongAction {
LC_TRACK_WILDCARD = _LC_MAX,
LC_TRACK_ID,
LC_TRACK_INDEX,
LC_SAMPLE_WILDCARD,
LC_SAMPLE_ID,
LC_SAMPLE_INDEX,
LC_LIST,
LC_ENABLED,
LC_INMOVIE,
LC_INPREVIEW,
LC_LAYER,
LC_ALTGROUP,
LC_VOLUME,
LC_WIDTH,
LC_HEIGHT,
LC_LANGUAGE,
LC_HDLRNAME,
LC_UDTANAME,
LC_UDTANAME_R,
LC_COLR_PARMS,
LC_COLR_PARM_HD,
LC_COLR_PARM_SD,
LC_COLR_LIST,
LC_COLR_ADD,
LC_COLR_SET,
LC_COLR_REMOVE,
LC_PASP_PARMS,
LC_PASP_LIST,
LC_PASP_ADD,
LC_PASP_SET,
LC_PASP_REMOVE,
};
public:
TrackUtility( int, char** );
protected:
// delegates implementation
bool utility_option( int, bool& );
bool utility_job( JobContext& );
private:
bool actionList( JobContext& );
bool actionListSingle( JobContext&, uint16_t );
bool actionColorParameterList ( JobContext& );
bool actionColorParameterAdd ( JobContext& );
bool actionColorParameterSet ( JobContext& );
bool actionColorParameterRemove ( JobContext& );
bool actionPictureAspectRatioList ( JobContext& );
bool actionPictureAspectRatioAdd ( JobContext& );
bool actionPictureAspectRatioSet ( JobContext& );
bool actionPictureAspectRatioRemove ( JobContext& );
bool actionTrackModifierSet ( JobContext& );
bool actionTrackModifierRemove ( JobContext& );
private:
enum TrackMode {
TM_UNDEFINED,
TM_INDEX,
TM_ID,
TM_WILDCARD,
};
enum SampleMode {
SM_UNDEFINED,
SM_INDEX,
SM_ID,
SM_WILDCARD,
};
Group _actionGroup;
Group _parmGroup;
bool (TrackUtility::*_action)( JobContext& );
TrackMode _trackMode;
uint16_t _trackIndex;
uint32_t _trackId;
SampleMode _sampleMode;
uint16_t _sampleIndex;
uint32_t _sampleId;
qtff::ColorParameterBox::Item _colorParameterItem;
qtff::PictureAspectRatioBox::Item _pictureAspectRatioItem;
void (TrackModifier::*_actionTrackModifierSet_function)( const string& );
string _actionTrackModifierSet_name;
string _actionTrackModifierSet_value;
void (TrackModifier::*_actionTrackModifierRemove_function)();
string _actionTrackModifierRemove_name;
};
///////////////////////////////////////////////////////////////////////////////
string toStringTrackType( string );
///////////////////////////////////////////////////////////////////////////////
TrackUtility::TrackUtility( int argc, char** argv )
: Utility ( "mp4track", argc, argv )
, _actionGroup ( "ACTIONS" )
, _parmGroup ( "PARAMETERS" )
, _action ( NULL )
, _trackMode ( TM_UNDEFINED )
, _trackIndex ( 0 )
, _trackId ( MP4_INVALID_TRACK_ID )
, _sampleMode ( SM_UNDEFINED )
, _sampleIndex ( 0 )
, _sampleId ( MP4_INVALID_SAMPLE_ID )
{
// add standard options which make sense for this utility
_group.add( STD_OPTIMIZE );
_group.add( STD_DRYRUN );
_group.add( STD_KEEPGOING );
_group.add( STD_OVERWRITE );
_group.add( STD_FORCE );
_group.add( STD_QUIET );
_group.add( STD_DEBUG );
_group.add( STD_VERBOSE );
_group.add( STD_HELP );
_group.add( STD_VERSION );
_group.add( STD_VERSIONX );
_parmGroup.add( "track-any", false, LC_TRACK_WILDCARD, "act on any/all tracks" );
_parmGroup.add( "track-index", true, LC_TRACK_INDEX, "act on track index IDX", "IDX" );
_parmGroup.add( "track-id", true, LC_TRACK_ID, "act on track id ID", "ID" );
/*
_parmGroup.add( "sample-any", false, LC_SAMPLE_WILDCARD, "act on any sample (default)" );
_parmGroup.add( "sample-index", true, LC_SAMPLE_INDEX, "act on sample index IDX" );
_parmGroup.add( "sample-id", true, LC_SAMPLE_ID, "act on sample id ID" );
*/
_parmGroup.add( "colr-parms", true, LC_COLR_PARMS, "where CSV is IDX1,IDX2,IDX3", "CSV" );
_parmGroup.add( "colr-parm-hd", false, LC_COLR_PARM_HD, "equivalent to --colr-parms=1,1,1" );
_parmGroup.add( "colr-parm-sd", false, LC_COLR_PARM_SD, "equivalent to --colr-parms=6,1,6" );
_parmGroup.add( "pasp-parms", true, LC_PASP_PARMS, "where CSV is hSPACING,vSPACING", "CSV" );
_groups.push_back( &_parmGroup );
_actionGroup.add( "list", false, LC_LIST, "list all tracks in mp4" );
_actionGroup.add( "enabled", true, LC_ENABLED, "set trak.tkhd.flags (enabled bit)", "BOOL" );
_actionGroup.add( "inmovie", true, LC_INMOVIE, "set trak.tkhd.flags (inMovie bit)", "BOOL" );
_actionGroup.add( "inpreview", true, LC_INPREVIEW, "set trak.tkhd.flags (inPreview bit)", "BOOL" );
_actionGroup.add( "layer", true, LC_LAYER, "set trak.tkhd.layer", "NUM" );
_actionGroup.add( "altgroup", true, LC_ALTGROUP, "set trak.tkhd.alternate_group", "NUM" );
_actionGroup.add( "volume", true, LC_VOLUME, "set trak.tkhd.volume", "FLOAT" );
_actionGroup.add( "width", true, LC_WIDTH, "set trak.tkhd.width", "FLOAT" );
_actionGroup.add( "height", true, LC_HEIGHT, "set trak.tkhd.height", "FLOAT" );
_actionGroup.add( "language", true, LC_LANGUAGE, "set trak.mdia.mdhd.language", "CODE" );
_actionGroup.add( "hdlrname", true, LC_HDLRNAME, "set trak.mdia.hdlr.name", "STR" );
_actionGroup.add( "udtaname", true, LC_UDTANAME, "set trak.udta.name.value", "STR" );
_actionGroup.add( "udtaname-remove", false, LC_UDTANAME_R, "remove trak.udta.name atom" );
_actionGroup.add( "colr-list", false, LC_COLR_LIST, "list all colr-boxes in mp4" );
_actionGroup.add( "colr-add", false, LC_COLR_ADD, "add colr-box to a video track" );
_actionGroup.add( "colr-set", false, LC_COLR_SET, "set colr-box parms" );
_actionGroup.add( "colr-remove", false, LC_COLR_REMOVE, "remove colr-box from track" );
_actionGroup.add( "pasp-list", false, LC_PASP_LIST, "list all pasp-boxes in mp4" );
_actionGroup.add( "pasp-add", false, LC_PASP_ADD, "add pasp-box to a video track" );
_actionGroup.add( "pasp-set", false, LC_PASP_SET, "set pasp-box parms" );
_actionGroup.add( "pasp-remove", false, LC_PASP_REMOVE, "remove pasp-box from track" );
_groups.push_back( &_actionGroup );
_usage = "[OPTION]... [PARAMETERS]... ACTION file...";
_description =
// 79-cols, inclusive, max desired width
// |----------------------------------------------------------------------------|
"\nFor each mp4 file specified, perform the specified ACTION. An action must be"
"\nspecified. Some options are not applicable to some actions.";
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionColorParameterAdd( JobContext& job )
{
ostringstream oss;
oss << "adding colr-box(" << _colorParameterItem.convertToCSV() << ") -> " << job.file;
switch( _trackMode ) {
case TM_INDEX:
oss << " (track index=" << _trackIndex << ')';
break;
case TM_ID:
oss << " (track id=" << _trackId << ')';
break;
default:
case TM_WILDCARD:
return herrf( "track not specified\n" );
}
verbose1f( "%s\n", oss.str().c_str() );
if( dryrunAbort() )
return SUCCESS;
job.fileHandle = MP4Modify( job.file.c_str() );
if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
return herrf( "unable to open for write: %s\n", job.file.c_str() );
switch( _trackMode ) {
default:
case TM_INDEX:
if( qtff::ColorParameterBox::add( job.fileHandle, _trackIndex, _colorParameterItem ))
return herrf( "unable to add colr-box\n" );
break;
case TM_ID:
if( qtff::ColorParameterBox::add( job.fileHandle, _trackId, _colorParameterItem ))
return herrf( "unable to add colr-box\n" );
break;
}
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionColorParameterList( JobContext& job )
{
job.fileHandle = MP4Read( job.file.c_str() );
if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
return herrf( "unable to open for read: %s\n", job.file.c_str() );
ostringstream report;
const int widx = 3;
const int wid = 3;
const int wtype = 8;
const int wparm = 6;
const string sep = " ";
if( _jobCount == 0 ) {
report << setw(widx) << right << "IDX"
<< sep << setw(wid) << "ID"
<< sep << setw(wtype) << left << "TYPE"
<< sep << setw(wparm) << right << "PRIMRY"
<< sep << setw(wparm) << right << "XFERFN"
<< sep << setw(wparm) << right << "MATRIX"
<< sep << setw(0) << "FILE"
<< '\n';
report << setfill('-') << setw(70) << "" << setfill(' ') << '\n';
}
qtff::ColorParameterBox::ItemList itemList;
if( qtff::ColorParameterBox::list( job.fileHandle, itemList ))
return herrf( "unable to fetch list of colr-boxes" );
const qtff::ColorParameterBox::ItemList::size_type max = itemList.size();
for( qtff::ColorParameterBox::ItemList::size_type i = 0; i < max; i++ ) {
const qtff::ColorParameterBox::IndexedItem& xitem = itemList[i];
const char* type = MP4GetTrackType( job.fileHandle, xitem.trackId );
if( !type)
type = "unknown";
report << right << setw(widx) << xitem.trackIndex
<< sep << setw(wid) << xitem.trackId
<< sep << setw(wtype) << left << toStringTrackType( type )
<< sep << setw(wparm) << right << xitem.item.primariesIndex
<< sep << setw(wparm) << right << xitem.item.transferFunctionIndex
<< sep << setw(wparm) << right << xitem.item.matrixIndex;
if( i == 0 )
report << sep << setw(0) << job.file;
report << '\n';
}
verbose1f( "%s", report.str().c_str() );
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionColorParameterRemove( JobContext& job )
{
ostringstream oss;
oss << "removing colr-box from " << job.file;
switch( _trackMode ) {
case TM_INDEX:
oss << " (track index=" << _trackIndex << ')';
break;
case TM_ID:
oss << " (track id=" << _trackId << ')';
break;
case TM_WILDCARD:
oss << " (all tracks)";
break;
default:
return herrf( "track(s) not specified\n" );
}
verbose1f( "%s\n", oss.str().c_str() );
if( dryrunAbort() )
return SUCCESS;
job.fileHandle = MP4Modify( job.file.c_str() );
if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
return herrf( "unable to open for write: %s\n", job.file.c_str() );
switch( _trackMode ) {
case TM_INDEX:
if( qtff::ColorParameterBox::remove( job.fileHandle, _trackIndex ))
return herrf( "unable to remove colr-box\n" );
break;
case TM_ID:
if( qtff::ColorParameterBox::remove( job.fileHandle, _trackId ))
return herrf( "unable to remove colr-box\n" );
break;
default:
case TM_WILDCARD:
{
qtff::ColorParameterBox::ItemList itemList;
if( qtff::ColorParameterBox::list( job.fileHandle, itemList ))
return herrf( "unable to fetch list of colr-boxes" );
_trackMode = TM_INDEX;
const qtff::ColorParameterBox::ItemList::size_type max = itemList.size();
for( qtff::ColorParameterBox::ItemList::size_type i = 0; i < max; i++ ) {
const qtff::ColorParameterBox::IndexedItem& xitem = itemList[i];
_trackIndex = xitem.trackIndex;
actionColorParameterRemove( job );
}
break;
}
}
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionColorParameterSet( JobContext& job )
{
ostringstream oss;
oss << "setting colr-box(" << _colorParameterItem.convertToCSV() << ") -> " << job.file;
switch( _trackMode ) {
case TM_INDEX:
oss << " (track index=" << _trackIndex << ')';
break;
case TM_ID:
oss << " (track id=" << _trackId << ')';
break;
default:
case TM_WILDCARD:
return herrf( "track not specified\n" );
}
verbose1f( "%s\n", oss.str().c_str() );
if( dryrunAbort() )
return SUCCESS;
job.fileHandle = MP4Modify( job.file.c_str() );
if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
return herrf( "unable to open for write: %s\n", job.file.c_str() );
switch( _trackMode ) {
default:
case TM_INDEX:
if( qtff::ColorParameterBox::set( job.fileHandle, _trackIndex, _colorParameterItem ))
return herrf( "unable to set colr-box\n" );
break;
case TM_ID:
if( qtff::ColorParameterBox::set( job.fileHandle, _trackId, _colorParameterItem ))
return herrf( "unable to set colr-box\n" );
break;
}
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionList( JobContext& job )
{
if( _jobTotal > 1 )
verbose1f( "file %u of %u: %s\n", _jobCount+1, _jobTotal, job.file.c_str() );
ostringstream report;
job.fileHandle = MP4Read( job.file.c_str() );
if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
return herrf( "unable to open for read: %s\n", job.file.c_str() );
switch( _trackMode ) {
case TM_INDEX:
return actionListSingle( job, _trackIndex );
case TM_ID:
return actionListSingle( job, MP4FindTrackIndex( job.fileHandle, _trackId ));
case TM_WILDCARD:
default:
{
bool result = SUCCESS;
const uint16_t trackc = static_cast<uint16_t>( MP4GetNumberOfTracks( job.fileHandle ));
for( uint16_t i = 0; i < trackc; i++ ) {
if( actionListSingle( job, i ))
result = FAILURE;
}
return result;
}
}
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionListSingle( JobContext& job, uint16_t index )
{
TrackModifier tm( job.fileHandle, index );
ostringstream report;
tm.dump( report, ( _jobTotal > 1 ? " " : "" ));
verbose1f( "%s", report.str().c_str() );
return false;
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionPictureAspectRatioAdd( JobContext& job )
{
ostringstream oss;
oss << "adding pasp-box(" << _pictureAspectRatioItem.convertToCSV() << ") -> " << job.file;
switch( _trackMode ) {
case TM_INDEX:
oss << " (track index=" << _trackIndex << ')';
break;
case TM_ID:
oss << " (track id=" << _trackId << ')';
break;
default:
case TM_WILDCARD:
return herrf( "track not specified\n" );
}
verbose1f( "%s\n", oss.str().c_str() );
if( dryrunAbort() )
return SUCCESS;
job.fileHandle = MP4Modify( job.file.c_str() );
if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
return herrf( "unable to open for write: %s\n", job.file.c_str() );
switch( _trackMode ) {
default:
case TM_INDEX:
if( qtff::PictureAspectRatioBox::add( job.fileHandle, _trackIndex, _pictureAspectRatioItem ))
return herrf( "unable to add pasp-box\n" );
break;
case TM_ID:
if( qtff::PictureAspectRatioBox::add( job.fileHandle, _trackId, _pictureAspectRatioItem ))
return herrf( "unable to add pasp-box\n" );
break;
}
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionPictureAspectRatioList( JobContext& job )
{
job.fileHandle = MP4Read( job.file.c_str() );
if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
return herrf( "unable to open for read: %s\n", job.file.c_str() );
ostringstream report;
const int widx = 3;
const int wid = 3;
const int wtype = 8;
const int wparm = 6;
const string sep = " ";
if( _jobCount == 0 ) {
report << setw(widx) << right << "IDX"
<< sep << setw(wid) << "ID"
<< sep << setw(wtype) << left << "TYPE"
<< sep << setw(wparm) << right << "hSPACE"
<< sep << setw(wparm) << right << "vSPACE"
<< sep << setw(0) << "FILE"
<< '\n';
report << setfill('-') << setw(70) << "" << setfill(' ') << '\n';
}
qtff::PictureAspectRatioBox::ItemList itemList;
if( qtff::PictureAspectRatioBox::list( job.fileHandle, itemList ))
return herrf( "unable to fetch list of pasp-boxes" );
const qtff::PictureAspectRatioBox::ItemList::size_type max = itemList.size();
for( qtff::PictureAspectRatioBox::ItemList::size_type i = 0; i < max; i++ ) {
const qtff::PictureAspectRatioBox::IndexedItem& xitem = itemList[i];
const char* type = MP4GetTrackType( job.fileHandle, xitem.trackId );
if( !type)
type = "unknown";
report << right << setw(widx) << xitem.trackIndex
<< sep << setw(wid) << xitem.trackId
<< sep << setw(wtype) << left << toStringTrackType( type )
<< sep << setw(wparm) << right << xitem.item.hSpacing
<< sep << setw(wparm) << right << xitem.item.vSpacing;
if( i == 0 )
report << sep << setw(0) << job.file;
report << '\n';
}
verbose1f( "%s", report.str().c_str() );
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionPictureAspectRatioRemove( JobContext& job )
{
ostringstream oss;
oss << "removing pasp-box from " << job.file;
switch( _trackMode ) {
case TM_INDEX:
oss << " (track index=" << _trackIndex << ')';
break;
case TM_ID:
oss << " (track id=" << _trackId << ')';
break;
default:
case TM_WILDCARD:
oss << " (all tracks)";
}
verbose1f( "%s\n", oss.str().c_str() );
if( dryrunAbort() )
return SUCCESS;
job.fileHandle = MP4Modify( job.file.c_str() );
if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
return herrf( "unable to open for write: %s\n", job.file.c_str() );
switch( _trackMode ) {
case TM_INDEX:
if( qtff::PictureAspectRatioBox::remove( job.fileHandle, _trackIndex ))
return herrf( "unable to remove pasp-box\n" );
break;
case TM_ID:
if( qtff::PictureAspectRatioBox::remove( job.fileHandle, _trackId ))
return herrf( "unable to remove pasp-box\n" );
break;
default:
case TM_WILDCARD:
{
qtff::PictureAspectRatioBox::ItemList itemList;
if( qtff::PictureAspectRatioBox::list( job.fileHandle, itemList ))
return herrf( "unable to fetch list of pasp-boxes" );
_trackMode = TM_INDEX;
const qtff::PictureAspectRatioBox::ItemList::size_type max = itemList.size();
for( qtff::PictureAspectRatioBox::ItemList::size_type i = 0; i < max; i++ ) {
const qtff::PictureAspectRatioBox::IndexedItem& xitem = itemList[i];
_trackIndex = xitem.trackIndex;
actionPictureAspectRatioRemove( job );
}
break;
}
}
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionPictureAspectRatioSet( JobContext& job )
{
ostringstream oss;
oss << "setting pasp-box(" << _pictureAspectRatioItem.convertToCSV() << ") -> " << job.file;
switch( _trackMode ) {
case TM_INDEX:
oss << " (track index=" << _trackIndex << ')';
break;
case TM_ID:
oss << " (track id=" << _trackId << ')';
break;
default:
case TM_WILDCARD:
return herrf( "track not specified\n" );
}
verbose1f( "%s\n", oss.str().c_str() );
if( dryrunAbort() )
return SUCCESS;
job.fileHandle = MP4Modify( job.file.c_str() );
if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
return herrf( "unable to open for write: %s\n", job.file.c_str() );
switch( _trackMode ) {
default:
case TM_INDEX:
if( qtff::PictureAspectRatioBox::set( job.fileHandle, _trackIndex, _pictureAspectRatioItem ))
return herrf( "unable to set pasp-box\n" );
break;
case TM_ID:
if( qtff::PictureAspectRatioBox::set( job.fileHandle, _trackId, _pictureAspectRatioItem ))
return herrf( "unable to set pasp-box\n" );
break;
}
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionTrackModifierRemove( JobContext& job )
{
ostringstream oss;
oss << "removing " << _actionTrackModifierRemove_name << " -> " << job.file;
switch( _trackMode ) {
case TM_INDEX:
oss << " (track index=" << _trackIndex << ')';
break;
case TM_ID:
oss << " (track id=" << _trackId << ')';
break;
default:
case TM_WILDCARD:
return herrf( "track not specified\n" );
}
verbose1f( "%s\n", oss.str().c_str() );
if( dryrunAbort() )
return SUCCESS;
job.fileHandle = MP4Modify( job.file.c_str() );
if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
return herrf( "unable to open for write: %s\n", job.file.c_str() );
if( _trackMode == TM_ID )
_trackIndex = MP4FindTrackIndex( job.fileHandle, _trackId );
TrackModifier tm( job.fileHandle, _trackIndex );
(tm.*_actionTrackModifierRemove_function)();
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::actionTrackModifierSet( JobContext& job )
{
ostringstream oss;
oss << "setting " << _actionTrackModifierSet_name << "=" << _actionTrackModifierSet_value << " -> " << job.file;
switch( _trackMode ) {
case TM_INDEX:
oss << " (track index=" << _trackIndex << ')';
break;
case TM_ID:
oss << " (track id=" << _trackId << ')';
break;
default:
case TM_WILDCARD:
return herrf( "track not specified\n" );
}
verbose1f( "%s\n", oss.str().c_str() );
if( dryrunAbort() )
return SUCCESS;
job.fileHandle = MP4Modify( job.file.c_str() );
if( job.fileHandle == MP4_INVALID_FILE_HANDLE )
return herrf( "unable to open for write: %s\n", job.file.c_str() );
if( _trackMode == TM_ID )
_trackIndex = MP4FindTrackIndex( job.fileHandle, _trackId );
TrackModifier tm( job.fileHandle, _trackIndex );
(tm.*_actionTrackModifierSet_function)( _actionTrackModifierSet_value );
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::utility_job( JobContext& job )
{
if( !_action )
return herrf( "no action specified\n" );
return (this->*_action)( job );
}
///////////////////////////////////////////////////////////////////////////////
bool
TrackUtility::utility_option( int code, bool& handled )
{
handled = true;
switch( code ) {
case LC_TRACK_WILDCARD:
_trackMode = TM_WILDCARD;
break;
case LC_TRACK_INDEX:
{
_trackMode = TM_INDEX;
istringstream iss( prog::optarg );
iss >> _trackIndex;
if( iss.rdstate() != ios::eofbit )
return herrf( "invalid track index: %s\n", prog::optarg );
break;
}
case LC_TRACK_ID:
{
_trackMode = TM_ID;
istringstream iss( prog::optarg );
iss >> _trackId;
if( iss.rdstate() != ios::eofbit )
return herrf( "invalid track id: %s\n", prog::optarg );
break;
}
case LC_LIST:
_action = &TrackUtility::actionList;
break;
case LC_COLR_PARMS:
_colorParameterItem.convertFromCSV( prog::optarg );
break;
case LC_COLR_PARM_HD:
_colorParameterItem.primariesIndex = 1;
_colorParameterItem.transferFunctionIndex = 1;
_colorParameterItem.matrixIndex = 1;
break;
case LC_COLR_PARM_SD:
_colorParameterItem.primariesIndex = 6;
_colorParameterItem.transferFunctionIndex = 1;
_colorParameterItem.matrixIndex = 6;
break;
case LC_COLR_LIST:
_action = &TrackUtility::actionColorParameterList;
break;
case LC_ENABLED:
_action = &TrackUtility::actionTrackModifierSet;
_actionTrackModifierSet_function = &TrackModifier::setEnabled;
_actionTrackModifierSet_name = "enabled";
_actionTrackModifierSet_value = prog::optarg;
break;
case LC_INMOVIE:
_action = &TrackUtility::actionTrackModifierSet;
_actionTrackModifierSet_function = &TrackModifier::setInMovie;
_actionTrackModifierSet_name = "inMovie";
_actionTrackModifierSet_value = prog::optarg;
break;
case LC_INPREVIEW:
_action = &TrackUtility::actionTrackModifierSet;
_actionTrackModifierSet_function = &TrackModifier::setInPreview;
_actionTrackModifierSet_name = "inPreview";
_actionTrackModifierSet_value = prog::optarg;
break;
case LC_LAYER:
_action = &TrackUtility::actionTrackModifierSet;
_actionTrackModifierSet_function = &TrackModifier::setLayer;
_actionTrackModifierSet_name = "layer";
_actionTrackModifierSet_value = prog::optarg;
break;
case LC_ALTGROUP:
_action = &TrackUtility::actionTrackModifierSet;
_actionTrackModifierSet_function = &TrackModifier::setAlternateGroup;
_actionTrackModifierSet_name = "alternateGroup";
_actionTrackModifierSet_value = prog::optarg;
break;
case LC_VOLUME:
_action = &TrackUtility::actionTrackModifierSet;
_actionTrackModifierSet_function = &TrackModifier::setVolume;
_actionTrackModifierSet_name = "volume";
_actionTrackModifierSet_value = prog::optarg;
break;
case LC_WIDTH:
_action = &TrackUtility::actionTrackModifierSet;
_actionTrackModifierSet_function = &TrackModifier::setWidth;
_actionTrackModifierSet_name = "width";
_actionTrackModifierSet_value = prog::optarg;
break;
case LC_HEIGHT:
_action = &TrackUtility::actionTrackModifierSet;
_actionTrackModifierSet_function = &TrackModifier::setHeight;
_actionTrackModifierSet_name = "height";
_actionTrackModifierSet_value = prog::optarg;
break;
case LC_LANGUAGE:
_action = &TrackUtility::actionTrackModifierSet;
_actionTrackModifierSet_function = &TrackModifier::setLanguage;
_actionTrackModifierSet_name = "language";
_actionTrackModifierSet_value = prog::optarg;
break;
case LC_HDLRNAME:
_action = &TrackUtility::actionTrackModifierSet;
_actionTrackModifierSet_function = &TrackModifier::setHandlerName;
_actionTrackModifierSet_name = "handlerName";
_actionTrackModifierSet_value = prog::optarg;
break;
case LC_UDTANAME:
_action = &TrackUtility::actionTrackModifierSet;
_actionTrackModifierSet_function = &TrackModifier::setUserDataName;
_actionTrackModifierSet_name = "userDataName";
_actionTrackModifierSet_value = prog::optarg;
break;
case LC_UDTANAME_R:
_action = &TrackUtility::actionTrackModifierRemove;
_actionTrackModifierRemove_function = &TrackModifier::removeUserDataName;
_actionTrackModifierRemove_name = "userDataName";
break;
case LC_COLR_ADD:
_action = &TrackUtility::actionColorParameterAdd;
break;
case LC_COLR_SET:
_action = &TrackUtility::actionColorParameterSet;
break;
case LC_COLR_REMOVE:
_action = &TrackUtility::actionColorParameterRemove;
break;
case LC_PASP_PARMS:
_pictureAspectRatioItem.convertFromCSV( prog::optarg );
break;
case LC_PASP_LIST:
_action = &TrackUtility::actionPictureAspectRatioList;
break;
case LC_PASP_ADD:
_action = &TrackUtility::actionPictureAspectRatioAdd;
break;
case LC_PASP_SET:
_action = &TrackUtility::actionPictureAspectRatioSet;
break;
case LC_PASP_REMOVE:
_action = &TrackUtility::actionPictureAspectRatioRemove;
break;
default:
handled = false;
break;
}
return SUCCESS;
}
///////////////////////////////////////////////////////////////////////////////
string
toStringTrackType( 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 + ")";
}
///////////////////////////////////////////////////////////////////////////////
}} // namespace mp4v2::util
///////////////////////////////////////////////////////////////////////////////
extern "C"
int main( int argc, char** argv )
{
mp4v2::util::TrackUtility util( argc, argv );
return util.process();
}