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
998 lines
32 KiB
4 years ago
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// 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();
|
||
|
}
|