diff --git a/ChangeLog b/ChangeLog index 57234b250..65cdd9e93 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2003-07-29 Moritz Bunkus + * mkvextract: Support for extracting chapter information. + * mkvmerge: Added support for simple chapter files (CHAPTER01=..., CHAPTER01NAME=Hello World etc). diff --git a/doc/mkvmerge.1 b/doc/mkvmerge.1 index d50840137..545200658 100644 --- a/doc/mkvmerge.1 +++ b/doc/mkvmerge.1 @@ -32,6 +32,10 @@ Write to the file '\fIout\fR'. .TP \fB\-\-title\fR <\fItitle\fR> Sets the general title for the output file, e.g. the movie name. +.TP +\fB\-\-chapters <\fIfile\fR> +Read chapter information from the \fIfile\fR. See the section about chapters +below for details. .LP General output control (still global, advanced options): @@ -407,6 +411,36 @@ in \fBmkvinfo\fR's output then please read the section about \fBDEFAULT VALUES\fR. +.SH TRACK IDS +.LP +Some of the options for \fBmkvmerge\fR need a track ID to specify which track +they should be applied to. Those track IDs are printed by the readers when +demuxing the current input file, or if \fBmkvmerge\fR is called with the +\fB\-\-identify\fR option. Track IDs are assigned like this: +.TP +* +AVI files: The video track has the ID 0. All audio tracks get the ID 1, 2... +.TP +* +AAC, AC3, MP3, SRT and WAV files: The one 'track' in that file gets the ID 0. +.TP +* +Ogg/OGM files: The track's ID is its serial number as given in the Ogg stream +header page. +.TP +* +Matroska files: The track's ID is the track number as reported by \fBmkvinfo\fR +or \fBmkvmerge \-\-identify\fR. It is \fBnot\fR the track UID. +.LP +The special track ID '-1' is a wildcard and applies the given switch to all +tracks that are read from an input file. This was the bahviour of these +switches prior to version 0.4.4. +.LP +The options that use the track IDs are: \fB\-\-atracks\fR, \fB\-\-vtracks\fR, +\fB\-\-stracks\fR, \fB\-\-sync\fR, \fB\-\-default-track\fR, \fB\-\-cues\fR +and \fB\-\-language\fR. + + .SH SUBTITLES .LP There are several text subtitle formats that can be embedded into Matroska. @@ -437,10 +471,10 @@ markup is not supported correctly. Matroska supports file linking which simply says that a specific file is the predecessor or successsor of the current file. To be precise, it's not really the files that are linked but the Matroska segments. As most files will -probably only put one Matroska segment into a file I simply say 'file linking' +probably only contain one Matroska segment I simply say 'file linking' although 'segment linking' would be more appropriate. .LP -Each segment is identified by a unique 128 bit wide segmend UID. This UID +Each segment is identified by a unique 128 bit wide segment UID. This UID is automatically generated by \fBmkvmerge\fR. The linking is done primarily via putting the segment UIDs of the previous/next file into the segment header information. \fBmkvinfo(1)\fR prints these UIDs if it finds them. @@ -470,36 +504,6 @@ with \'\fB\-\-link\-to\-next\fR\'. If splitting is not used then the one output file will be linked to both of the two UIDs. -.SH TRACK IDS -.LP -Some of the options for \fBmkvmerge\fR need a track ID to specify which track -they should be applied to. Those track IDs are printed by the readers when -demuxing the current input file, or if \fBmkvmerge\fR is called with the -\fB\-\-identify\fR option. Track IDs are assigned like this: -.TP -* -AVI files: The video track has the ID 0. All audio tracks get the ID 1, 2... -.TP -* -AAC, AC3, MP3, SRT and WAV files: The one 'track' in that file gets the ID 0. -.TP -* -Ogg/OGM files: The track's ID is its serial number as given in the Ogg stream -header page. -.TP -* -Matroska files: The track's ID is the track number as reported by \fBmkvinfo\fR -or \fBmkvmerge \-\-identify\fR. It is \fBnot\fR the track UID. -.LP -The special track ID '-1' is a wildcard and applies the given switch to all -tracks that are read from an input file. This was the bahviour of these -switches prior to version 0.4.4. -.LP -The options that use the track IDs are: \fB\-\-atracks\fR, \fB\-\-vtracks\fR, -\fB\-\-stracks\fR, \fB\-\-sync\fR, \fB\-\-default-track\fR, \fB\-\-cues\fR -and \fB\-\-language\fR. - - .SH DEFAULT VALUES .LP The Matroska specs say that some elements have a default value. Usually an @@ -532,6 +536,47 @@ $ \fBmkvmerge -o output.mkv -A video.avi sound.ogg \-\-attachment\-description \-\-attach\-file really_cool_font.ttf +.SH CHAPTERS +.LP +The Matroska chapter system is more powerful than the old known system used +by OGMs. The full specs can be found at +.URL http://cvs.corecodec.org/cgi-bin/viewcvs.cgi/*checkout*/matroska/doc/website/technical/specs/chapters/index.html +.LP +\fBmkvmerge\fR's chapter support is still a bit limited, though. At the moment +it only supports reading chapter data in the same format that the OGM tools +expect. It looks basically like this: +.LP +CHAPTER01=00:00:00.000 +.br +CHAPTER01NAME=Intro +.br +CHAPTER02=00:02:30.000 +.br +CHAPTER02NAME=Baby prepares to rock +.br +CHAPTER03=00:02:42.300 +.br +CHAPTER03NAME=Baby rocks the house +.LP +\fBmkvmerge\fR will transform every pair or lines (CHAPTERxx and CHAPTERxxNAME) +into one Matroska \fIChapterAtom\fR. It does not set any +\fIChapterTrackNumber\fR which means that the chapters all apply to all +tracks in the file. +.LP +When splitting files \fBmkvmerge\fR will correctly adjust the chapters as +well. This means that each file only includes the chapter entries that +apply to it, and that the timecodes will be offset to match the new timecodes +of each output file. +.LP +Another shortcoming is that \fBmkvmerge\fR will silently discards chapters +found in source files - e.g. when reading them from an OGM file or another +Matroska file. You can use \fBmkvextract\fR to export chapters from another +Matroska file and use the result with \fBmkvmerge\fR. +.LP +The upcoming version of \fBmkvmerge\fR will address most of the shortcomings +mentioned above. + + .SH NOTES .LP What works: @@ -579,22 +624,19 @@ taken from other OGM files. .TP * SSA/ASS subtitles from SSA/ASS files +.TP +* +Simple chapters. +.TP +* +Full tags support. .LP What not works: .TP * Manual audio synchronization for PCM sound (who needs it anyway?) -.LP -Planned functionality: -.TP -* -support for other subtitle formats -.TP -* -chapter information -.TP -* -a lot of other stuff, like tags, user information etc. + + .SH AUTHOR .I mkvmerge was written by Moritz Bunkus . diff --git a/src/chapters.cpp b/src/chapters.cpp index b30f79973..1b12e9615 100644 --- a/src/chapters.cpp +++ b/src/chapters.cpp @@ -103,7 +103,7 @@ static bool probe_simple_chapters(mm_text_io_c *in) { // CHAPTER01NAME=Hallo Welt static KaxChapters *parse_simple_chapters(mm_text_io_c *in, int64_t min_tc, - int64_t max_tc) { + int64_t max_tc, int64_t offset) { KaxChapters *chaps; KaxEditionEntry *edition; KaxChapterAtom *atom; @@ -119,6 +119,8 @@ static KaxChapters *parse_simple_chapters(mm_text_io_c *in, int64_t min_tc, atom = NULL; edition = NULL; + printf("off: %lld\n", offset); + while (in->getline2(line)) { strip(line); if (line.length() == 0) @@ -158,7 +160,7 @@ static KaxChapters *parse_simple_chapters(mm_text_io_c *in, int64_t min_tc, *static_cast(&GetChild(*atom)) = create_unique_uint32(); *static_cast(&GetChild(*atom)) = - (start - min_tc) * 1000000; + (start - offset) * 1000000; display = &GetChild(*atom); *static_cast (&GetChild(*display)) = @@ -178,12 +180,13 @@ static bool probe_xml_chapters(mm_text_io_c *) { return false; } -static KaxChapters *parse_xml_chapters(mm_text_io_c *, int64_t, int64_t) { +static KaxChapters *parse_xml_chapters(mm_text_io_c *, int64_t, int64_t, + int64_t) { return NULL; } KaxChapters *parse_chapters(const char *file_name, int64_t min_tc, - int64_t max_tc) { + int64_t max_tc, int64_t offset) { mm_text_io_c *in; try { @@ -194,10 +197,10 @@ KaxChapters *parse_chapters(const char *file_name, int64_t min_tc, } if (probe_simple_chapters(in)) - return parse_simple_chapters(in, min_tc, max_tc); + return parse_simple_chapters(in, min_tc, max_tc, offset); if (probe_xml_chapters(in)) - return parse_xml_chapters(in, min_tc, max_tc); + return parse_xml_chapters(in, min_tc, max_tc, offset); mxprint(stderr, "Error: Unknown file format for '%s'. It does not contain " "a supported chapter format.\n", file_name); diff --git a/src/chapters.h b/src/chapters.h index 95b588aad..70e621176 100644 --- a/src/chapters.h +++ b/src/chapters.h @@ -28,7 +28,7 @@ using namespace libmatroska; KaxChapters *parse_chapters(const char *file_name, int64_t min_tc = 0, - int64_t max_tc = -1); + int64_t max_tc = -1, int64_t offset = 0); void write_chapters_xml(KaxChapters *chapters, FILE *out); diff --git a/src/cluster_helper.cpp b/src/cluster_helper.cpp index bfa14fe61..72bf574d1 100644 --- a/src/cluster_helper.cpp +++ b/src/cluster_helper.cpp @@ -666,3 +666,7 @@ int64_t cluster_helper_c::get_max_timecode() { int64_t cluster_helper_c::get_first_timecode() { return first_timecode; } + +int64_t cluster_helper_c::get_timecode_offset() { + return timecode_offset; +} diff --git a/src/cluster_helper.h b/src/cluster_helper.h index df1cd4b3e..541d6244c 100644 --- a/src/cluster_helper.h +++ b/src/cluster_helper.h @@ -72,6 +72,7 @@ public: int get_cluster_content_size(); int64_t get_max_timecode(); int64_t get_first_timecode(); + int64_t get_timecode_offset(); void find_next_splitpoint(); int get_next_splitpoint(); diff --git a/src/mkvmerge.cpp b/src/mkvmerge.cpp index 8526fab84..f2981e2d2 100644 --- a/src/mkvmerge.cpp +++ b/src/mkvmerge.cpp @@ -1799,6 +1799,7 @@ void create_next_output_file(bool last_file, bool first_file) { void finish_file() { int i; KaxChapters *chapters_here; + int64_t start, end, offset; // Render the cues. if (write_cues && cue_writing_requested) { @@ -1819,9 +1820,14 @@ void finish_file() { out->restore_pos(); if ((kax_chapters != NULL) && (pass > 0)) { - chapters_here = parse_chapters(chapter_file_name, - cluster_helper->get_first_timecode(), - cluster_helper->get_max_timecode()); + if (no_linking) + offset = cluster_helper->get_timecode_offset(); + else + offset = 0; + start = cluster_helper->get_first_timecode() + offset; + end = cluster_helper->get_max_timecode() + offset; + + chapters_here = parse_chapters(chapter_file_name, start, end, offset); kax_chapters_void->ReplaceWith(*chapters_here, *out, true); delete kax_chapters_void; kax_chapters_void = NULL;