diff --git a/doc/mkvmerge.1 b/doc/mkvmerge.1 index 5da8b9554..36254f653 100644 --- a/doc/mkvmerge.1 +++ b/doc/mkvmerge.1 @@ -261,6 +261,13 @@ Defaults: no manual sync correction (which is the same as \fId\fR = 0 and This option can be used multiple times for an input file applying to several tracks by selecting different track IDs each time. .TP +\fB\-\-delay\fR <\fITID\fR:\fIx\fR> +The delay to apply to the packets of the track by simply adjusting the +timecodes. +The argument \fIx\fR must be postfixed with \fBs\fR, \fBms\fR, \fBus\fR or +\fBns\fR to specify seconds, milliseconds, microseconds and nanoseconds +respectively. +.TP \fB\-\-cues\fR <\fITID\fR:\fInone\fR|\fIiframes\fR|\fIall\fR> Controls for which tracks cue (index) entries are created for the given track (see section \fBTRACK IDS\fR). \fInone\fR inhibits the creation of cue entries. diff --git a/mkvmerge.vcproj b/mkvmerge.vcproj index 6e9e20712..08733c602 100644 --- a/mkvmerge.vcproj +++ b/mkvmerge.vcproj @@ -24,6 +24,7 @@ MinimalRebuild="TRUE" BasicRuntimeChecks="3" RuntimeLibrary="3" + RuntimeTypeInfo="TRUE" UsePrecompiledHeader="0" WarningLevel="3" Detect64BitPortabilityProblems="TRUE" @@ -73,6 +74,7 @@ AdditionalIncludeDirectories="".\avilib-0.6.10";.\librmff;src\common;src\input;src\output;.;.\src;..\..\work\libebml;..\..\work\libmatroska;..\..\..\zlib;..\..\..\libogg\include;..\..\..\libvorbis\include;..\..\..\expat\lib;..\..\..\flac\include" PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;FLAC__NO_DLL" RuntimeLibrary="2" + RuntimeTypeInfo="TRUE" UsePrecompiledHeader="0" WarningLevel="3" Detect64BitPortabilityProblems="TRUE" @@ -552,6 +554,12 @@ + + + + diff --git a/src/common/xml_element_mapping.cpp b/src/common/xml_element_mapping.cpp index 5d24b1c1e..fe947335a 100644 --- a/src/common/xml_element_mapping.cpp +++ b/src/common/xml_element_mapping.cpp @@ -62,84 +62,93 @@ namespace libmatroska { using namespace libmatroska; -parser_element_t chapter_elements[] = { - {"Chapters", ebmlt_master, 0, 0, 0, KaxChapters_TheId, NULL, NULL}, +parser_element_t *chapter_elements = NULL; +parser_element_t *tag_elements = NULL; - {"EditionEntry", ebmlt_master, 1, 0, 0, KaxEditionEntry_TheId, NULL, NULL}, - {"EditionUID", ebmlt_uint, 2, 0, NO_MAX_VALUE, KaxEditionUID_TheId, NULL, - NULL}, - {"EditionFlagHidden", ebmlt_bool, 2, 0, 0, KaxEditionFlagHidden_TheId, - NULL, NULL}, - {"EditionProcessed", ebmlt_uint, 2, 0, NO_MAX_VALUE, - KaxEditionProcessed_TheId, NULL, NULL}, - {"EditionFlagDefault", ebmlt_bool, 2, 0, 0, KaxEditionFlagDefault_TheId, - NULL, NULL}, +void xml_element_map_init() +{ + static parser_element_t _chapter_elements[] = { + {"Chapters", ebmlt_master, 0, 0, 0, KaxChapters_TheId, NULL, NULL}, - {"ChapterAtom", ebmlt_master, 2, 0, 0, KaxChapterAtom_TheId, NULL, - NULL}, - {"ChapterUID", ebmlt_uint, 3, 0, NO_MAX_VALUE, KaxChapterUID_TheId, NULL, - NULL}, - {"ChapterTimeStart", ebmlt_time, 3, 0, 0, KaxChapterTimeStart_TheId, NULL, - NULL}, - {"ChapterTimeEnd", ebmlt_time, 3, 0, 0, KaxChapterTimeEnd_TheId, NULL, - NULL}, - {"ChapterFlagHidden", ebmlt_bool, 3, 0, 0, KaxChapterFlagHidden_TheId, - NULL, NULL}, - {"ChapterFlagEnabled", ebmlt_bool, 3, 0, 0, KaxChapterFlagEnabled_TheId, - NULL, NULL}, - {"ChapterProcessedPrivate", ebmlt_binary, 3, 0, 0, - KaxChapterProcessedPrivate_TheId, NULL, NULL}, + {"EditionEntry", ebmlt_master, 1, 0, 0, KaxEditionEntry_TheId, NULL, NULL}, + {"EditionUID", ebmlt_uint, 2, 0, NO_MAX_VALUE, KaxEditionUID_TheId, NULL, + NULL}, + {"EditionFlagHidden", ebmlt_bool, 2, 0, 0, KaxEditionFlagHidden_TheId, + NULL, NULL}, + {"EditionProcessed", ebmlt_uint, 2, 0, NO_MAX_VALUE, + KaxEditionProcessed_TheId, NULL, NULL}, + {"EditionFlagDefault", ebmlt_bool, 2, 0, 0, KaxEditionFlagDefault_TheId, + NULL, NULL}, - {"ChapterProcess", ebmlt_master, 3, 0, 0, KaxChapterProcess_TheId, - NULL, NULL}, - {"ChapterProcessTime", ebmlt_uint, 4, 0, 0, KaxChapterProcessTime_TheId, - NULL, NULL}, - {"ChapterProcessCommand", ebmlt_binary, 4, 0, 0, - KaxChapterProcessCommand_TheId, NULL, NULL}, + {"ChapterAtom", ebmlt_master, 2, 0, 0, KaxChapterAtom_TheId, NULL, + NULL}, + {"ChapterUID", ebmlt_uint, 3, 0, NO_MAX_VALUE, KaxChapterUID_TheId, NULL, + NULL}, + {"ChapterTimeStart", ebmlt_time, 3, 0, 0, KaxChapterTimeStart_TheId, NULL, + NULL}, + {"ChapterTimeEnd", ebmlt_time, 3, 0, 0, KaxChapterTimeEnd_TheId, NULL, + NULL}, + {"ChapterFlagHidden", ebmlt_bool, 3, 0, 0, KaxChapterFlagHidden_TheId, + NULL, NULL}, + {"ChapterFlagEnabled", ebmlt_bool, 3, 0, 0, KaxChapterFlagEnabled_TheId, + NULL, NULL}, + {"ChapterProcessedPrivate", ebmlt_binary, 3, 0, 0, + KaxChapterProcessedPrivate_TheId, NULL, NULL}, - {"ChapterTrack", ebmlt_master, 3, 0, 0, KaxChapterTrack_TheId, - NULL, NULL}, - {"ChapterTrackNumber", ebmlt_uint, 4, 0, NO_MAX_VALUE, - KaxChapterTrackNumber_TheId, NULL, NULL}, + {"ChapterProcess", ebmlt_master, 3, 0, 0, KaxChapterProcess_TheId, + NULL, NULL}, + {"ChapterProcessTime", ebmlt_uint, 4, 0, 0, KaxChapterProcessTime_TheId, + NULL, NULL}, + {"ChapterProcessCommand", ebmlt_binary, 4, 0, 0, + KaxChapterProcessCommand_TheId, NULL, NULL}, - {"ChapterDisplay", ebmlt_master, 3, 0, 0, KaxChapterDisplay_TheId, - NULL, NULL}, - {"ChapterString", ebmlt_ustring, 4, 0, 0, KaxChapterString_TheId, - NULL, NULL}, - {"ChapterLanguage", ebmlt_string, 4, 0, 0, KaxChapterLanguage_TheId, - NULL, NULL}, - {"ChapterCountry", ebmlt_string, 4, 0, 0, KaxChapterCountry_TheId, - NULL, NULL}, + {"ChapterTrack", ebmlt_master, 3, 0, 0, KaxChapterTrack_TheId, + NULL, NULL}, + {"ChapterTrackNumber", ebmlt_uint, 4, 0, NO_MAX_VALUE, + KaxChapterTrackNumber_TheId, NULL, NULL}, - {NULL, ebmlt_master, 0, 0, 0, EbmlId((uint32_t)0, 0), NULL, NULL} -}; + {"ChapterDisplay", ebmlt_master, 3, 0, 0, KaxChapterDisplay_TheId, + NULL, NULL}, + {"ChapterString", ebmlt_ustring, 4, 0, 0, KaxChapterString_TheId, + NULL, NULL}, + {"ChapterLanguage", ebmlt_string, 4, 0, 0, KaxChapterLanguage_TheId, + NULL, NULL}, + {"ChapterCountry", ebmlt_string, 4, 0, 0, KaxChapterCountry_TheId, + NULL, NULL}, -parser_element_t tag_elements[] = { - {"Tags", ebmlt_master, 0, 0, 0, KaxTags_TheId, NULL, NULL}, + {NULL, ebmlt_master, 0, 0, 0, EbmlId((uint32_t)0, 0), NULL, NULL} + }; - {"Tag", ebmlt_master, 1, 0, 0, KaxTag_TheId, NULL, NULL}, + static parser_element_t _tag_elements[] = { + {"Tags", ebmlt_master, 0, 0, 0, KaxTags_TheId, NULL, NULL}, - {"Targets", ebmlt_master, 2, 0, 0, KaxTagTargets_TheId, NULL, NULL}, - {"TrackUID", ebmlt_uint, 3, 0, NO_MAX_VALUE, KaxTagTrackUID_TheId, - NULL, NULL}, - {"EditionUID", ebmlt_uint, 3, 0, NO_MAX_VALUE, KaxTagEditionUID_TheId, - NULL, NULL}, - {"ChapterUID", ebmlt_uint, 3, 0, NO_MAX_VALUE, KaxTagChapterUID_TheId, - NULL, NULL}, - {"AttachmentUID", ebmlt_uint, 3, 0, NO_MAX_VALUE, KaxTagAttachmentUID_TheId, - NULL, NULL}, - {"TargetType", ebmlt_string, 3, 0, 0, KaxTagTargetType_TheId, NULL, NULL}, - {"TargetTypeValue", ebmlt_uint, 3, 0, NO_MAX_VALUE, - KaxTagTargetTypeValue_TheId, NULL, NULL}, + {"Tag", ebmlt_master, 1, 0, 0, KaxTag_TheId, NULL, NULL}, - {"Simple", ebmlt_master, 2, 0, 0, KaxTagSimple_TheId, NULL, NULL}, - {"Name", ebmlt_ustring, 3, 0, 0, KaxTagName_TheId, NULL, NULL}, - {"String", ebmlt_ustring, 3, 0, 0, KaxTagString_TheId, NULL, NULL}, - {"Binary", ebmlt_binary, 3, 0, 0, KaxTagBinary_TheId, NULL, NULL}, - {"TagLanguage", ebmlt_string, 3, 0, 0, KaxTagLangue_TheId, NULL, NULL}, - {"DefaultLanguage", ebmlt_bool, 3, 0, 1, KaxTagDefault_TheId, NULL, NULL}, + {"Targets", ebmlt_master, 2, 0, 0, KaxTagTargets_TheId, NULL, NULL}, + {"TrackUID", ebmlt_uint, 3, 0, NO_MAX_VALUE, KaxTagTrackUID_TheId, + NULL, NULL}, + {"EditionUID", ebmlt_uint, 3, 0, NO_MAX_VALUE, KaxTagEditionUID_TheId, + NULL, NULL}, + {"ChapterUID", ebmlt_uint, 3, 0, NO_MAX_VALUE, KaxTagChapterUID_TheId, + NULL, NULL}, + {"AttachmentUID", ebmlt_uint, 3, 0, NO_MAX_VALUE, + KaxTagAttachmentUID_TheId, NULL, NULL}, + {"TargetType", ebmlt_string, 3, 0, 0, KaxTagTargetType_TheId, NULL, NULL}, + {"TargetTypeValue", ebmlt_uint, 3, 0, NO_MAX_VALUE, + KaxTagTargetTypeValue_TheId, NULL, NULL}, - {NULL, ebmlt_master, 0, 0, 0, EbmlId((uint32_t)0, 0), NULL, NULL} + {"Simple", ebmlt_master, 2, 0, 0, KaxTagSimple_TheId, NULL, NULL}, + {"Name", ebmlt_ustring, 3, 0, 0, KaxTagName_TheId, NULL, NULL}, + {"String", ebmlt_ustring, 3, 0, 0, KaxTagString_TheId, NULL, NULL}, + {"Binary", ebmlt_binary, 3, 0, 0, KaxTagBinary_TheId, NULL, NULL}, + {"TagLanguage", ebmlt_string, 3, 0, 0, KaxTagLangue_TheId, NULL, NULL}, + {"DefaultLanguage", ebmlt_bool, 3, 0, 1, KaxTagDefault_TheId, NULL, NULL}, + + {NULL, ebmlt_master, 0, 0, 0, EbmlId((uint32_t)0, 0), NULL, NULL} + }; + + chapter_elements = _chapter_elements; + tag_elements = _tag_elements; }; int diff --git a/src/common/xml_element_mapping.h b/src/common/xml_element_mapping.h index 26d0dd52c..4e2f46c78 100644 --- a/src/common/xml_element_mapping.h +++ b/src/common/xml_element_mapping.h @@ -41,8 +41,8 @@ typedef struct { parser_element_callback_t end_hook; } parser_element_t; -extern parser_element_t MTX_DLL_API chapter_elements[]; -extern parser_element_t MTX_DLL_API tag_elements[]; +extern parser_element_t MTX_DLL_API *chapter_elements; +extern parser_element_t MTX_DLL_API *tag_elements; #define chapter_element_map_index(name) \ xml_element_map_index(chapter_elements, name) @@ -51,5 +51,6 @@ extern parser_element_t MTX_DLL_API tag_elements[]; int MTX_DLL_API xml_element_map_index(const parser_element_t *element_map, const char *name); +void MTX_DLL_API xml_element_map_init(); #endif diff --git a/src/extract/mkvextract.cpp b/src/extract/mkvextract.cpp index a3580a666..42e95d253 100644 --- a/src/extract/mkvextract.cpp +++ b/src/extract/mkvextract.cpp @@ -63,6 +63,7 @@ #include "mkvextract.h" #include "mm_io.h" #include "tagwriter.h" +#include "xml_element_mapping.h" using namespace libmatroska; using namespace std; @@ -406,6 +407,8 @@ main(int argc, utf8_init(NULL); conv_utf8 = utf8_init("UTF-8"); + xml_element_map_init(); + parse_args(argc, argv, input_file, mode); if (mode == MODE_TRACKS) { extract_tracks(input_file); diff --git a/src/mkvinfo.cpp b/src/mkvinfo.cpp index d6144d54c..d3150c533 100644 --- a/src/mkvinfo.cpp +++ b/src/mkvinfo.cpp @@ -1925,6 +1925,8 @@ setup() { #endif cc_local_utf8 = utf8_init(NULL); + + xml_element_map_init(); } void diff --git a/src/mkvmerge.cpp b/src/mkvmerge.cpp index 5b2774e1b..da13f800e 100644 --- a/src/mkvmerge.cpp +++ b/src/mkvmerge.cpp @@ -88,6 +88,7 @@ #include "r_vobsub.h" #include "r_wav.h" #include "tagparser.h" +#include "xml_element_mapping.h" using namespace libmatroska; using namespace std; @@ -296,7 +297,7 @@ usage() { " appended to another track of another file.\n" " --timecode-scale Force the timecode scale factor to n.\n" "\n File splitting and linking (more global options):\n" - " --split \n" + " --split \n" " Create a new file after d bytes (KB, MB, GB)\n" " or after a specific time.\n" " --split-max-files Create at most n files.\n" @@ -335,6 +336,8 @@ usage() { " linear drifts. p defaults to 1000 if\n" " omitted. Both o and p can be floating point\n" " numbers.\n" + " --delay Delay to apply to the packets of the track\n" + " by simply adjusting the timecodes.\n" " --default-track Sets the 'default' flag for this track.\n" " --track-name Sets the name for a track.\n" " --cues \n" @@ -1007,6 +1010,49 @@ parse_split(const char *arg) { split_by_time = false; } +/** \brief Parse the \c --delay argument + * + * time based: A number that must be postfixed with s, + * ms, us or ns to specify seconds, + * milliseconds, microseconds and nanoseconds respectively. + */ +static void +parse_delay(const char *arg, + audio_sync_t &async) { + char *colon, *unit; + string orig = arg; + int64_t mult; + + // Extract the track number. + if ((colon = strchr(arg, ':')) == NULL) + mxerror(_("Invalid delay option. No track ID specified in " + "'--delay %s'.\n"), arg); + + *colon = 0; + if (!parse_int(arg, async.id)) + mxerror(_("Invalid track ID specified in '--delay %s'.\n"), orig.c_str()); + + colon++; + for (unit = colon; isdigit(*unit); unit++) + ; + if (unit == colon) + mxerror(_("Missing delay given in '--delay %s'.\n"), orig.c_str()); + mult = 1; + if (!strcasecmp(unit, "s")) + mult = 1000000000; + else if (!strcasecmp(unit, "ms")) + mult = 1000000; + else if (!strcasecmp(unit, "us")) + mult = 1000; + else if (strcasecmp(unit, "ns")) + mxerror(_("Invalid unit given in '--delay %s'.\n"), orig.c_str()); + *unit = 0; + + if (!parse_int(colon, async.displacement)) + mxerror(_("Invalid delay given in '--delay %s'.\n"), orig.c_str()); + async.displacement *= mult; +} + /** \brief Parse the \c --cues argument * * The argument must have the form \c TID:cuestyle, e.g. \c 0:none. @@ -2282,6 +2328,14 @@ parse_args(int argc, ti->cue_creations->push_back(cues); i++; + } else if (!strcmp(this_arg, "--delay")) { + if (next_arg == NULL) + mxerror(_("'--delay' lacks the delay to apply.\n")); + + parse_delay(next_arg, async); + ti->packet_delays->push_back(async); + i++; + } else if (!strcmp(this_arg, "--default-track")) { if (next_arg == NULL) mxerror(_("'--default-track' lacks the track ID.\n")); @@ -2598,6 +2652,8 @@ setup() { cc_local_utf8 = utf8_init(NULL); cluster_helper = new cluster_helper_c(); + + xml_element_map_init(); } /** \brief Initialize global variables diff --git a/src/pr_generic.cpp b/src/pr_generic.cpp index c4e4b489b..a96bee772 100644 --- a/src/pr_generic.cpp +++ b/src/pr_generic.cpp @@ -80,6 +80,16 @@ generic_packetizer_c::generic_packetizer_c(generic_reader_c *nreader, initial_displacement = ti->async.displacement; ti->async.displacement = 0; + // Let's see if the user has specified a delay for this track. + ti->packet_delay = 0; + for (i = 0; i < ti->packet_delays->size(); i++) { + as = &(*ti->packet_delays)[i]; + if ((as->id == ti->id) || (as->id == -1)) { // -1 == all tracks + ti->packet_delay = as->displacement; + break; + } + } + // Let's see if the user has specified which cues he wants for this track. ti->cues = CUES_UNSPECIFIED; for (i = 0; i < ti->cue_creations->size(); i++) { @@ -850,7 +860,7 @@ generic_packetizer_c::add_packet(memory_c &mem, pack->duration = duration; pack->duration_mandatory = duration_mandatory; pack->source = this; - pack->assigned_timecode = get_next_timecode(timecode); + pack->assigned_timecode = get_next_timecode(timecode) + ti->packet_delay; if (reader->max_timecode_seen < (pack->assigned_timecode + pack->duration)) reader->max_timecode_seen = pack->assigned_timecode + pack->duration; @@ -1327,6 +1337,7 @@ track_info_c::track_info_c(): sub_charset(NULL), tags_ptr(NULL), tags(NULL), + packet_delay(0), compression(COMPRESSION_NONE), track_name(NULL), ext_timecodes(NULL), @@ -1349,6 +1360,7 @@ track_info_c::track_info_c(): sub_charsets = new vector; all_tags = new vector; aac_is_sbr = new vector; + packet_delays = new vector; compression_list = new vector; track_names = new vector; all_ext_timecodes = new vector; @@ -1464,6 +1476,9 @@ track_info_c::operator =(const track_info_c &src) { aac_is_sbr = new vector(*src.aac_is_sbr); + packet_delay = src.packet_delay; + packet_delays = new vector(*src.packet_delays); + compression_list = new vector(*src.compression_list); compression = src.compression; diff --git a/src/pr_generic.h b/src/pr_generic.h index 2b385038e..2b56d9207 100644 --- a/src/pr_generic.h +++ b/src/pr_generic.h @@ -198,6 +198,9 @@ public: vector *aac_is_sbr; // For AAC+/HE-AAC/SBR + vector *packet_delays; // As given on the command line + int64_t packet_delay; // For this very track + vector *compression_list; // As given on the command line int compression; // For this very track