Support for multiple editions in Matroska
authorDaniel Dawson <ddawson@icehouse.net>
Tue, 1 Dec 2009 12:28:34 +0000 (1 04:28 -0800)
committerUoti Urpala <uau@glyph.nonexistent.invalid>
Fri, 4 Dec 2009 17:43:17 +0000 (4 19:43 +0200)
Add code to intelligently choose an appropriate Matroska edition when
there are several. Will choose, in descending order of preference: the
edition chosen by the user through the option "-edition <edition id>"
if it exists, the first edition with EditionFlagDefault set to 1 if
there is one, or the first edition.

DOCS/man/en/mplayer.1
cfg-common-opts.h
defaultopts.c
libmpdemux/demux_mkv.c
libmpdemux/ebml.h
options.h

index 5a23c4d..0393c47 100644 (file)
@@ -1301,6 +1301,12 @@ Specify which chapter to start playing at.
 Optionally specify which chapter to end playing at (default: 1).
 .
 .TP
+.B \-edition <edition ID> (Matroska, MPlayer only)
+Specify the edition (set of chapters) to use, where 0 is the first. If set to
+-1 (the default), MPlayer will choose the first edition declared as a default,
+or if there is no default, the first edition defined.
+.
+.TP
 .B \-cookies (network only)
 Send cookies when making HTTP requests.
 .
index 7927bed..19a1312 100644 (file)
@@ -40,6 +40,7 @@
        {"dvd-speed", "MPlayer was compiled without libdvdread support.\n", CONF_TYPE_PRINT, 0, 0, 0, NULL},
        {"dvd", "MPlayer was compiled without libdvdread support.\n", CONF_TYPE_PRINT, CONF_NOCFG, 0, 0, NULL},
 #endif /* CONFIG_DVDREAD */
+       OPT_INTRANGE("edition", edition_id, 0, -1, 8190),
        {"alang", &audio_lang, CONF_TYPE_STRING, 0, 0, 0, NULL},
        {"slang", &dvdsub_lang, CONF_TYPE_STRING, 0, 0, 0, NULL},
 
index 2bd6f7e..b5917c5 100644 (file)
@@ -21,6 +21,7 @@ void set_default_mplayer_options(struct MPOpts *opts)
         .osd_duration = 1000,
         .loop_times = -1,
         .ordered_chapters = 1,
+        .edition_id = -1,
         .user_correct_pts = -1,
         .key_fifo_size = 7,
         .doubleclick_time = 300,
index 3874298..c07224a 100644 (file)
@@ -1085,7 +1085,9 @@ demux_mkv_read_cues (demuxer_t *demuxer)
   return 0;
 }
 
-static uint64_t read_one_chapter(struct demuxer *demuxer, stream_t *s)
+static uint64_t read_one_chapter(struct demuxer *demuxer, stream_t *s,
+                                 struct matroska_chapter *chapters,
+                                 int chapter_num)
 {
     uint64_t len, l;
     uint64_t start = 0, end = 0;
@@ -1167,17 +1169,10 @@ static uint64_t read_one_chapter(struct demuxer *demuxer, stream_t *s)
     if (!name)
         name = strdup("(unnamed)");
 
-    int cid = demuxer_add_chapter(demuxer, name, start, end);
-    struct matroska_data *m = &demuxer->matroska_data;
-    m->ordered_chapters = talloc_realloc(demuxer, m->ordered_chapters,
-                                         struct matroska_chapter,
-                                         m->num_ordered_chapters + 1);
     chapter.start = start;
     chapter.end = end;
-    chapter.name = talloc_strdup(m->ordered_chapters, name);
-    // Will be undone later if this is a normal chapter rather than ordered
-    m->ordered_chapters[m->num_ordered_chapters] = chapter;
-    m->num_ordered_chapters++;
+    chapter.name = talloc_strdup(chapters, name);
+    chapters[chapter_num] = chapter;
 
     if (badchapter) {
         memset(&chapter.segment_uid, 0, sizeof(chapter.segment_uid));
@@ -1186,7 +1181,7 @@ static uint64_t read_one_chapter(struct demuxer *demuxer, stream_t *s)
 
     mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Chapter %u from %02d:%02d:%02d."
            "%03d to %02d:%02d:%02d.%03d, %s\n",
-           cid,
+           chapter_num,
            (int) (start / 60 / 60 / 1000),
            (int) ((start / 60 / 1000) % 60),
            (int) ((start / 1000) % 60),
@@ -1203,6 +1198,7 @@ cleanup:
 
 static int demux_mkv_read_chapters(struct demuxer *demuxer)
 {
+    struct MPOpts *opts = demuxer->opts;
     stream_t *s = demuxer->stream;
     uint64_t length, l;
     int i;
@@ -1216,29 +1212,40 @@ static int demux_mkv_read_chapters(struct demuxer *demuxer)
     mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing chapters ] ---------\n");
     length = ebml_read_length(s, NULL);
 
-    bool have_edition = false;
+    struct matroska_chapter *selected_chapters = NULL;
+    int num_selected_chapters = 0;
+    bool have_default = false;
+    bool have_user_specified = false;
+    int selected_edition = -1;
+    bool se_is_ordered = false;
+    int cur_idx = -1;
     while (length > 0) {
         id = ebml_read_id(s, &i);
         length -= i;
         switch (id) {
         case MATROSKA_ID_EDITIONENTRY:
-            if (have_edition) {
-                mp_msg(MSGT_DEMUX, MSGL_WARN, "[mkv] Multiple edition entries"
-                       " - ignoring all but first!\n");
-                ebml_read_skip(s, &l);
-                length -= l;
-                break;
-            }
-            have_edition = true;
+            cur_idx++;
+            mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] New edition %d\n", cur_idx);
             uint64_t editionlen = ebml_read_length(s, &i);
             length -= editionlen + i;
+            bool defaultflag = false;
             bool ordered = false;
+            struct matroska_chapter *chapters = NULL;
+            int num_chapters = 0;
             while (editionlen > 0) {
                 id = ebml_read_id(s, &i);
                 editionlen -= i;
                 switch (id) {
                 case MATROSKA_ID_CHAPTERATOM:
-                    l = read_one_chapter(demuxer, s);
+                    chapters = talloc_realloc(demuxer, chapters,
+                                              struct matroska_chapter,
+                                              num_chapters + 1);
+                    l = read_one_chapter(demuxer, s, chapters, num_chapters++);
+                    break;
+                case MATROSKA_ID_EDITIONFLAGDEFAULT:
+                    defaultflag = ebml_read_uint(s, &l);
+                    mp_msg(MSGT_DEMUX, MSGL_V,
+                           "[mkv] Default edition flag: %d\n", defaultflag);
                     break;
                 case MATROSKA_ID_EDITIONFLAGORDERED:
                     ordered = ebml_read_uint(s, &l);
@@ -1252,13 +1259,25 @@ static int demux_mkv_read_chapters(struct demuxer *demuxer)
                 }
                 editionlen -= l;
             }
-            if (!ordered) {
-                // The chapters should be interpreted as normal ones,
-                // so undo the addition of this information.
-                talloc_free(demuxer->matroska_data.ordered_chapters);
-                demuxer->matroska_data.ordered_chapters = NULL;
-                demuxer->matroska_data.num_ordered_chapters = 0;
+            if (cur_idx == opts->edition_id) {
+                have_user_specified = true;
+                mp_msg(MSGT_DEMUX, MSGL_V,
+                       "[mkv] Found user-selected edition\n");
+            } else if (!have_user_specified && !have_default && defaultflag) {
+                have_default = true;
+                mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Default edition: %d\n",
+                       cur_idx);
+            } else if (selected_edition < 0) {
+                ;
+            } else {
+                talloc_free(chapters);
+                break;
             }
+            selected_edition = cur_idx;
+            talloc_free(selected_chapters);
+            selected_chapters = chapters;
+            num_selected_chapters = num_chapters;
+            se_is_ordered = ordered;
             break;
 
         default:
@@ -1267,6 +1286,25 @@ static int demux_mkv_read_chapters(struct demuxer *demuxer)
             break;
         }
     }
+    if (cur_idx > 0)
+        mp_msg(MSGT_DEMUX, MSGL_INFO,
+               "[mkv] Found %d editions, will play #%d (first is 0).\n",
+               cur_idx + 1, selected_edition);
+
+    for (i = 0; i < num_selected_chapters; i++)
+        demuxer_add_chapter(demuxer, selected_chapters[i].name,
+                            selected_chapters[i].start,
+                            selected_chapters[i].end);
+    struct matroska_data *m = &demuxer->matroska_data;
+    talloc_free(m->ordered_chapters);
+    if (se_is_ordered) {
+        m->ordered_chapters = selected_chapters;
+        m->num_ordered_chapters = num_selected_chapters;
+    } else {
+        m->ordered_chapters = NULL;
+        m->num_ordered_chapters = 0;
+        talloc_free(selected_chapters);
+    }
 
     mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing chapters ] ---------\n");
     return 0;
index a224bcd..c3a7850 100644 (file)
 
 /* IDs in the chapters master */
 #define MATROSKA_ID_EDITIONENTRY         0x45B9
+#define MATROSKA_ID_EDITIONFLAGDEFAULT   0x45DB
 #define MATROSKA_ID_EDITIONFLAGORDERED   0x45DD
 #define MATROSKA_ID_CHAPTERATOM          0xB6
 #define MATROSKA_ID_CHAPTERTIMESTART     0x91
index 56cd6b6..90ce239 100644 (file)
--- a/options.h
+++ b/options.h
@@ -29,6 +29,7 @@ typedef struct MPOpts {
     int osd_duration;
     int loop_times;
     int ordered_chapters;
+    int edition_id;
     int correct_pts;
     int user_correct_pts;
     int user_pts_assoc_mode;