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