diff --git a/doc/mkvextract.1 b/doc/mkvextract.1 index 1072363f9..403e3c7b8 100644 --- a/doc/mkvextract.1 +++ b/doc/mkvextract.1 @@ -16,6 +16,9 @@ tags <\fIinname\fR> [\fIoptions\fR] attachments <\fIinname\fR> [\fIoptions\fR] [\fIAID1\fR:\fIout1\fR [\fIAID2\fR:\fIout2\fR ...]] .br .B mkvextract +chapters <\fIinname\fR> [\fIoptions\fR] +.br +.B mkvextract <\-h|\-V> @@ -23,7 +26,8 @@ attachments <\fIinname\fR> [\fIoptions\fR] [\fIAID1\fR:\fIout1\fR [\fIAID2\fR:\f .LP This program extracts specific parts from a Matroska file to other useful formats. The first argument tells \fBmkvextract\fR what to extract. Currently -supported is the extraction of \fBtracks\fR, \fBtags\fR and \fBattachments\fR. +supported is the extraction of \fBtracks\fR, \fBtags\fR, \fBattachments\fR and +\fBchapters\fR. The second argument is the name of the source file. It must be a Matroska file. .LP @@ -60,6 +64,12 @@ be given mutliple times. The attachment IDs are the same as the ones output by \fB\-v\fR, \fB\-\-verbose\fR Be verbose and show all the important Matroska elements as they're read. +.LP +Command line syntax for the \fBchapters\fR extraction mode: +.TP +\fB\-v\fR, \fB\-\-verbose\fR +Be verbose and show all the important Matroska elements as they're read. + .LP If one of the following options is used as the only command line argument additional information about \fBmkvextract\fR is output. @@ -107,6 +117,11 @@ SSA and ASS text subtitles will be written as SSA/ASS files respectively. .LP \fBAttachments\fR are written to they output file as they are. No conversion whatsoever is done. +.LP +\fBChapters\fR are converted to a XML format. This format is the same that +\fBmkvmerge\fR supports for reading chapters. \fBNOTE:\fR At the moment +\fBmkvmerge\fR does not yet support this format, but it will in the upcoming +release which will be in mid August. .SH EXAMPLES diff --git a/src/Makefile.am b/src/Makefile.am index b0d2f8f41..c788e3a13 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,6 +48,7 @@ mkvinfo_SOURCES = mkvinfo.cpp mkvinfo.h \ mkvextract_SOURCES = mkvextract.cpp \ base64.cpp base64.h \ + chapters.cpp chapters.h \ common.cpp common.h \ mm_io.cpp mm_io.h \ tagwriter.cpp tagwriter.h diff --git a/src/chapters.cpp b/src/chapters.cpp index e28bb86f3..b30f79973 100644 --- a/src/chapters.cpp +++ b/src/chapters.cpp @@ -102,7 +102,8 @@ static bool probe_simple_chapters(mm_text_io_c *in) { // CHAPTER01=00:00:00.000 // CHAPTER01NAME=Hallo Welt -static KaxChapters *parse_simple_chapters(mm_text_io_c *in) { +static KaxChapters *parse_simple_chapters(mm_text_io_c *in, int64_t min_tc, + int64_t max_tc) { KaxChapters *chaps; KaxEditionEntry *edition; KaxChapterAtom *atom; @@ -146,25 +147,25 @@ static KaxChapters *parse_simple_chapters(mm_text_io_c *in) { name = line.substr(14); mode = 0; - if (edition == NULL) - edition = &GetChild(*chaps); - if (atom == NULL) - atom = &GetChild(*edition); - else - atom = &GetNextChild(*edition, *atom); + if ((start >= min_tc) && ((start <= max_tc) || (max_tc == -1))) { + if (edition == NULL) + edition = &GetChild(*chaps); + if (atom == NULL) + atom = &GetChild(*edition); + else + atom = &GetNextChild(*edition, *atom); - *static_cast(&GetChild(*atom)) = - create_unique_uint32(); - *static_cast(&GetChild(*atom)) = - start; - *static_cast(&GetChild(*atom)) = - start; // FIXME! - display = &GetChild(*atom); - *static_cast - (&GetChild(*display)) = - cstr_to_UTFstring(name.c_str()); - *static_cast(&GetChild(*display)) = - "eng"; + *static_cast(&GetChild(*atom)) = + create_unique_uint32(); + *static_cast(&GetChild(*atom)) = + (start - min_tc) * 1000000; + display = &GetChild(*atom); + *static_cast + (&GetChild(*display)) = + cstr_to_UTFstring(name.c_str()); + *static_cast(&GetChild(*display)) = + "eng"; + } } } @@ -177,11 +178,12 @@ static bool probe_xml_chapters(mm_text_io_c *) { return false; } -static KaxChapters *parse_xml_chapters(mm_text_io_c *) { +static KaxChapters *parse_xml_chapters(mm_text_io_c *, int64_t, int64_t) { return NULL; } -KaxChapters *parse_chapters(const char *file_name) { +KaxChapters *parse_chapters(const char *file_name, int64_t min_tc, + int64_t max_tc) { mm_text_io_c *in; try { @@ -192,10 +194,10 @@ KaxChapters *parse_chapters(const char *file_name) { } if (probe_simple_chapters(in)) - return parse_simple_chapters(in); + return parse_simple_chapters(in, min_tc, max_tc); if (probe_xml_chapters(in)) - return parse_xml_chapters(in); + return parse_xml_chapters(in, min_tc, max_tc); mxprint(stderr, "Error: Unknown file format for '%s'. It does not contain " "a supported chapter format.\n", file_name); @@ -205,3 +207,132 @@ KaxChapters *parse_chapters(const char *file_name) { return NULL; } +#define is_id(ref) (e->Generic().GlobalId == ref::ClassInfos.GlobalId) +#define is_id2(e, ref) (e->Generic().GlobalId == ref::ClassInfos.GlobalId) + +static FILE *o; + +static void pt(int level, const char *tag) { + int i; + + for (i = 0; i < level; i++) + mxprint(o, " "); + mxprint(o, "%s", tag); +} + +static void write_chapter_atom_xml(KaxChapterAtom *atom, int level); + +static void write_chapter_display_xml(KaxChapterDisplay *display, int level) { + int i; + EbmlElement *e; + char *s; + + pt(level, "\n"); + + for (i = 0; i < display->ListSize(); i++) { + e = (*display)[i]; + if (is_id(KaxChapterString)) { + pt(level + 1, ""); + s = UTFstring_to_cstrutf8(UTFstring(*static_cast + (e)).c_str()); + mxprint(o, "%s\n", s); + safefree(s); + + } else if (is_id(KaxChapterLanguage)) { + pt(level + 1, ""); + mxprint(o, "%s\n", string(*static_cast + (e)).c_str()); + + + } else if (is_id(KaxChapterCountry)) { + pt(level + 1, ""); + mxprint(o, "%s\n", string(*static_cast + (e)).c_str()); + + + } else if (is_id(KaxChapterAtom)) + write_chapter_atom_xml((KaxChapterAtom *)e, level + 1); + + } + + pt(level, "\n"); +} + +static void write_chapter_track_xml(KaxChapterTrack *track, int level) { + int i; + EbmlElement *e; + + pt(level, "\n"); + + for (i = 0; i < track->ListSize(); i++) { + e = (*track)[i]; + if (is_id(KaxChapterTrackNumber)) { + pt(level + 1, ""); + mxprint(o, "%u(e))); + + } else if (is_id(KaxChapterAtom)) + write_chapter_atom_xml((KaxChapterAtom *)e, level + 1); + + } + + pt(level, "\n"); +} + +static void write_chapter_atom_xml(KaxChapterAtom *atom, int level) { + int i; + EbmlElement *e; + uint64_t v; + + pt(level, "\n"); + + for (i = 0; i < atom->ListSize(); i++) { + e = (*atom)[i]; + if (is_id(KaxChapterUID)) { + pt(level + 1, ""); + mxprint(o, "%u(e))); + + } else if (is_id(KaxChapterTimeStart)) { + pt(level + 1, ""); + v = uint64(*static_cast(e)) / 1000000; + mxprint(o, "%02llu:%02llu:%02llu.%03llu\n", + v / 1000 / 60 / 60, (v / 1000 / 60) % 60, (v / 1000) % 60, + v % 1000); + + } else if (is_id(KaxChapterTimeEnd)) { + pt(level + 1, ""); + v = uint64(*static_cast(e)) / 1000000; + mxprint(o, "%02llu:%02llu:%02llu.%03llu\n", + v / 1000 / 60 / 60, (v / 1000 / 60) % 60, (v / 1000) % 60, + v % 1000); + + } else if (is_id(KaxChapterTrack)) + write_chapter_track_xml((KaxChapterTrack *)e, level + 1); + + else if (is_id(KaxChapterDisplay)) + write_chapter_display_xml((KaxChapterDisplay *)e, level + 1); + + else if (is_id(KaxChapterAtom)) + write_chapter_atom_xml((KaxChapterAtom *)e, level + 1); + + } + + pt(level, "\n"); +} + +void write_chapters_xml(KaxChapters *chapters, FILE *out) { + int i, j; + KaxEditionEntry *edition; + + o = out; + + for (i = 0; i < chapters->ListSize(); i++) { + if (is_id2((*chapters)[i], KaxEditionEntry)) { + mxprint(out, " \n"); + edition = (KaxEditionEntry *)(*chapters)[i]; + for (j = 0; j < edition->ListSize(); j++) + if (is_id2((*edition)[j], KaxChapterAtom)) + write_chapter_atom_xml((KaxChapterAtom *)(*edition)[j], 2); + mxprint(out, " \n"); + } + } +} diff --git a/src/chapters.h b/src/chapters.h index 7243f3466..95b588aad 100644 --- a/src/chapters.h +++ b/src/chapters.h @@ -21,11 +21,16 @@ #ifndef __CHAPTERS_H #define __CHAPTERS_H +#include + #include using namespace libmatroska; -KaxChapters *parse_chapters(const char *file_name); +KaxChapters *parse_chapters(const char *file_name, int64_t min_tc = 0, + int64_t max_tc = -1); + +void write_chapters_xml(KaxChapters *chapters, FILE *out); #endif // __CHAPTERS_H