mirror of
https://gitlab.com/mbunkus/mkvtoolnix.git
synced 2024-12-24 20:01:53 +00:00
Chapters are re-timecoded correctly in both cases (with and without file linking). Updated documentation about chapters.
This commit is contained in:
parent
b705914778
commit
5742bca3dc
@ -1,5 +1,7 @@
|
|||||||
2003-07-29 Moritz Bunkus <moritz@bunkus.org>
|
2003-07-29 Moritz Bunkus <moritz@bunkus.org>
|
||||||
|
|
||||||
|
* mkvextract: Support for extracting chapter information.
|
||||||
|
|
||||||
* mkvmerge: Added support for simple chapter files (CHAPTER01=...,
|
* mkvmerge: Added support for simple chapter files (CHAPTER01=...,
|
||||||
CHAPTER01NAME=Hello World etc).
|
CHAPTER01NAME=Hello World etc).
|
||||||
|
|
||||||
|
128
doc/mkvmerge.1
128
doc/mkvmerge.1
@ -32,6 +32,10 @@ Write to the file '\fIout\fR'.
|
|||||||
.TP
|
.TP
|
||||||
\fB\-\-title\fR <\fItitle\fR>
|
\fB\-\-title\fR <\fItitle\fR>
|
||||||
Sets the general title for the output file, e.g. the movie name.
|
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
|
.LP
|
||||||
General output control (still global, advanced options):
|
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.
|
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
|
.SH SUBTITLES
|
||||||
.LP
|
.LP
|
||||||
There are several text subtitle formats that can be embedded into Matroska.
|
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
|
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
|
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
|
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.
|
although 'segment linking' would be more appropriate.
|
||||||
.LP
|
.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
|
is automatically generated by \fBmkvmerge\fR. The linking is done primarily
|
||||||
via putting the segment UIDs of the previous/next file into the segment
|
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.
|
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.
|
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
|
.SH DEFAULT VALUES
|
||||||
.LP
|
.LP
|
||||||
The Matroska specs say that some elements have a default value. Usually an
|
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
|
\-\-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
|
.SH NOTES
|
||||||
.LP
|
.LP
|
||||||
What works:
|
What works:
|
||||||
@ -579,22 +624,19 @@ taken from other OGM files.
|
|||||||
.TP
|
.TP
|
||||||
*
|
*
|
||||||
SSA/ASS subtitles from SSA/ASS files
|
SSA/ASS subtitles from SSA/ASS files
|
||||||
|
.TP
|
||||||
|
*
|
||||||
|
Simple chapters.
|
||||||
|
.TP
|
||||||
|
*
|
||||||
|
Full tags support.
|
||||||
.LP
|
.LP
|
||||||
What not works:
|
What not works:
|
||||||
.TP
|
.TP
|
||||||
*
|
*
|
||||||
Manual audio synchronization for PCM sound (who needs it anyway?)
|
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
|
.SH AUTHOR
|
||||||
.I mkvmerge
|
.I mkvmerge
|
||||||
was written by Moritz Bunkus <moritz@bunkus.org>.
|
was written by Moritz Bunkus <moritz@bunkus.org>.
|
||||||
|
@ -103,7 +103,7 @@ static bool probe_simple_chapters(mm_text_io_c *in) {
|
|||||||
// CHAPTER01NAME=Hallo Welt
|
// CHAPTER01NAME=Hallo Welt
|
||||||
|
|
||||||
static KaxChapters *parse_simple_chapters(mm_text_io_c *in, int64_t min_tc,
|
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;
|
KaxChapters *chaps;
|
||||||
KaxEditionEntry *edition;
|
KaxEditionEntry *edition;
|
||||||
KaxChapterAtom *atom;
|
KaxChapterAtom *atom;
|
||||||
@ -119,6 +119,8 @@ static KaxChapters *parse_simple_chapters(mm_text_io_c *in, int64_t min_tc,
|
|||||||
atom = NULL;
|
atom = NULL;
|
||||||
edition = NULL;
|
edition = NULL;
|
||||||
|
|
||||||
|
printf("off: %lld\n", offset);
|
||||||
|
|
||||||
while (in->getline2(line)) {
|
while (in->getline2(line)) {
|
||||||
strip(line);
|
strip(line);
|
||||||
if (line.length() == 0)
|
if (line.length() == 0)
|
||||||
@ -158,7 +160,7 @@ static KaxChapters *parse_simple_chapters(mm_text_io_c *in, int64_t min_tc,
|
|||||||
*static_cast<EbmlUInteger *>(&GetChild<KaxChapterUID>(*atom)) =
|
*static_cast<EbmlUInteger *>(&GetChild<KaxChapterUID>(*atom)) =
|
||||||
create_unique_uint32();
|
create_unique_uint32();
|
||||||
*static_cast<EbmlUInteger *>(&GetChild<KaxChapterTimeStart>(*atom)) =
|
*static_cast<EbmlUInteger *>(&GetChild<KaxChapterTimeStart>(*atom)) =
|
||||||
(start - min_tc) * 1000000;
|
(start - offset) * 1000000;
|
||||||
display = &GetChild<KaxChapterDisplay>(*atom);
|
display = &GetChild<KaxChapterDisplay>(*atom);
|
||||||
*static_cast<EbmlUnicodeString *>
|
*static_cast<EbmlUnicodeString *>
|
||||||
(&GetChild<KaxChapterString>(*display)) =
|
(&GetChild<KaxChapterString>(*display)) =
|
||||||
@ -178,12 +180,13 @@ static bool probe_xml_chapters(mm_text_io_c *) {
|
|||||||
return false;
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
KaxChapters *parse_chapters(const char *file_name, int64_t min_tc,
|
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;
|
mm_text_io_c *in;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -194,10 +197,10 @@ KaxChapters *parse_chapters(const char *file_name, int64_t min_tc,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (probe_simple_chapters(in))
|
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))
|
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 "
|
mxprint(stderr, "Error: Unknown file format for '%s'. It does not contain "
|
||||||
"a supported chapter format.\n", file_name);
|
"a supported chapter format.\n", file_name);
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
using namespace libmatroska;
|
using namespace libmatroska;
|
||||||
|
|
||||||
KaxChapters *parse_chapters(const char *file_name, int64_t min_tc = 0,
|
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);
|
void write_chapters_xml(KaxChapters *chapters, FILE *out);
|
||||||
|
|
||||||
|
@ -666,3 +666,7 @@ int64_t cluster_helper_c::get_max_timecode() {
|
|||||||
int64_t cluster_helper_c::get_first_timecode() {
|
int64_t cluster_helper_c::get_first_timecode() {
|
||||||
return first_timecode;
|
return first_timecode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t cluster_helper_c::get_timecode_offset() {
|
||||||
|
return timecode_offset;
|
||||||
|
}
|
||||||
|
@ -72,6 +72,7 @@ public:
|
|||||||
int get_cluster_content_size();
|
int get_cluster_content_size();
|
||||||
int64_t get_max_timecode();
|
int64_t get_max_timecode();
|
||||||
int64_t get_first_timecode();
|
int64_t get_first_timecode();
|
||||||
|
int64_t get_timecode_offset();
|
||||||
void find_next_splitpoint();
|
void find_next_splitpoint();
|
||||||
int get_next_splitpoint();
|
int get_next_splitpoint();
|
||||||
|
|
||||||
|
@ -1799,6 +1799,7 @@ void create_next_output_file(bool last_file, bool first_file) {
|
|||||||
void finish_file() {
|
void finish_file() {
|
||||||
int i;
|
int i;
|
||||||
KaxChapters *chapters_here;
|
KaxChapters *chapters_here;
|
||||||
|
int64_t start, end, offset;
|
||||||
|
|
||||||
// Render the cues.
|
// Render the cues.
|
||||||
if (write_cues && cue_writing_requested) {
|
if (write_cues && cue_writing_requested) {
|
||||||
@ -1819,9 +1820,14 @@ void finish_file() {
|
|||||||
out->restore_pos();
|
out->restore_pos();
|
||||||
|
|
||||||
if ((kax_chapters != NULL) && (pass > 0)) {
|
if ((kax_chapters != NULL) && (pass > 0)) {
|
||||||
chapters_here = parse_chapters(chapter_file_name,
|
if (no_linking)
|
||||||
cluster_helper->get_first_timecode(),
|
offset = cluster_helper->get_timecode_offset();
|
||||||
cluster_helper->get_max_timecode());
|
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);
|
kax_chapters_void->ReplaceWith(*chapters_here, *out, true);
|
||||||
delete kax_chapters_void;
|
delete kax_chapters_void;
|
||||||
kax_chapters_void = NULL;
|
kax_chapters_void = NULL;
|
||||||
|
Loading…
Reference in New Issue
Block a user