changeset 11260:1c269ab5f3f6

Support for -chapter selection.
author mosu
date Sat, 25 Oct 2003 12:42:24 +0000
parents c0a674107197
children 835822ce4bb1
files libmpdemux/demux_mkv.cpp
diffstat 1 files changed, 122 insertions(+), 5 deletions(-) [+]
line wrap: on
line diff
--- a/libmpdemux/demux_mkv.cpp	Sat Oct 25 07:24:10 2003 +0000
+++ b/libmpdemux/demux_mkv.cpp	Sat Oct 25 12:42:24 2003 +0000
@@ -77,6 +77,9 @@
 // for e.g. "-slang ger"
 extern char *dvdsub_lang;
 extern char *audio_lang;
+// for "-chapter x-y"
+extern int dvd_chapter;
+extern int dvd_last_chapter;
 
 // default values for Matroska elements
 #define MKVD_TIMECODESCALE 1000000 // 1000000 = 1ms
@@ -187,6 +190,10 @@
   uint32_t enc_keyid_len, sig_keyid_len, signature_len;
 } mkv_content_encoding_t;
 
+typedef struct {
+  int64_t start, end;
+} mkv_chapter_t;
+
 typedef struct mkv_track {
   uint32_t tnum, xid;
   
@@ -268,6 +275,9 @@
 
   int64_t skip_to_timecode;
   bool v_skip_to_keyframe, a_skip_to_keyframe;
+
+  vector<mkv_chapter_t> *chapters; // No support for nested chapters atm.
+  uint64_t stop_timecode;
 } mkv_demuxer_t;
 
 typedef struct {
@@ -1116,6 +1126,8 @@
     delete d->in;
   if (d->segment != NULL)
     delete d->segment;
+  if (d->chapters != NULL)
+    delete d->chapters;
 
   free(d);
 }
@@ -1295,6 +1307,79 @@
   mkv_d->cues_found = 1;
 }
 
+static void parse_chapters(mkv_demuxer_t *mkv_d, uint64_t pos) {
+  EbmlElement *l2 = NULL;
+  EbmlStream *es;
+  KaxChapters *kchapters;
+  KaxEditionEntry *keentry;
+  KaxChapterAtom *kcatom;
+  KaxChapterTimeStart *kctstart;
+  KaxChapterTimeEnd *kctend;
+  int upper_lvl_el, i, k;
+  mkv_chapter_t chapter;
+
+  if (mkv_d->chapters != NULL)
+    return;
+
+  es = mkv_d->es;
+  upper_lvl_el = 0;
+
+  mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing chapters ] ---------\n");
+
+  mkv_d->in->setFilePointer(pos);
+
+  kchapters =
+    (KaxChapters *)es->FindNextElement(mkv_d->segment->Generic().Context,
+                                       upper_lvl_el, 0xFFFFFFFFL, true, 1);
+  if (kchapters == NULL)
+    return;
+
+  if (!(EbmlId(*kchapters) == KaxChapters::ClassInfos.GlobalId)) {
+    mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] No KaxChapters element found but %s.\n"
+           "[mkv] \\---- [ parsing chapters ] ---------\n",
+           kchapters->Generic().DebugName);
+    
+    return;
+  }
+
+  mkv_d->chapters = new vector<mkv_chapter_t>;
+  kchapters->Read(*es, KaxChapters::ClassInfos.Context, upper_lvl_el, l2,
+                  true);
+
+  for (i = 0; i < (int)kchapters->ListSize(); i++) {
+    keentry = (KaxEditionEntry *)(*kchapters)[i];
+    if (EbmlId(*keentry) == KaxEditionEntry::ClassInfos.GlobalId) {
+      for (k = 0; k < (int)keentry->ListSize(); k++) {
+        kcatom = (KaxChapterAtom *)(*keentry)[k];
+        if (EbmlId(*kcatom) == KaxChapterAtom::ClassInfos.GlobalId) {
+          chapter.start = 0;
+          chapter.end = 0;
+          kctstart = FINDFIRST(kcatom, KaxChapterTimeStart);
+          if (kctstart != NULL)
+            chapter.start = uint64(*kctstart) / 1000000;
+          kctend = FINDFIRST(kcatom, KaxChapterTimeEnd);
+          if (kctend != NULL)
+            chapter.end = uint64(*kctend) / 1000000;
+          mkv_d->chapters->push_back(chapter);
+          mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Chapter %u from %02d:%02d:%02d."
+                 "%03d to %02d:%02d:%02d.%03d\n", mkv_d->chapters->size(),
+                 (int)(chapter.start / 60 / 60 / 1000),
+                 (int)((chapter.start / 60 / 1000) % 60),
+                 (int)((chapter.start / 1000) % 60),
+                 (int)(chapter.start % 1000),
+                 (int)(chapter.end / 60 / 60 / 1000),
+                 (int)((chapter.end / 60 / 1000) % 60),
+                 (int)((chapter.end / 1000) % 60),
+                 (int)(chapter.end % 1000));
+        }
+      }
+    }
+  }
+
+  mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing chapters ] ---------\n");
+  delete kchapters;
+}
+
 static void parse_seekhead(mkv_demuxer_t *mkv_d, uint64_t pos) {
   EbmlElement *l2 = NULL;
   EbmlStream *es;
@@ -1369,6 +1454,8 @@
         parse_seekhead(mkv_d, seek_pos);
       else if (*id == KaxCues::ClassInfos.GlobalId)
         parse_cues(mkv_d, seek_pos);
+      else if (*id == KaxChapters::ClassInfos.GlobalId)
+        parse_chapters(mkv_d, seek_pos);
     }
 
     if (id != NULL)
@@ -1380,7 +1467,8 @@
   mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing seek head ] ---------\n");
 }
 
-extern "C" void print_wave_header(WAVEFORMATEX *h);
+extern "C" void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs,
+                               int flags);
 
 extern "C" int demux_mkv_open(demuxer_t *demuxer) {
   unsigned char signature[4];
@@ -1826,6 +1914,10 @@
           cues_to_parse.push_back(l1->GetElementPosition());
         l1->SkipData(*es, l1->Generic().Context);
 
+      } else if (EbmlId(*l1) == KaxChapters::ClassInfos.GlobalId) {
+        parse_chapters(mkv_d, l1->GetElementPosition());
+        l1->SkipData(*es, l1->Generic().Context);
+
       } else if (EbmlId(*l1) == KaxCluster::ClassInfos.GlobalId) {
         mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] |+ found cluster, headers are "
                "parsed completely :)\n");
@@ -2294,16 +2386,34 @@
     }
   }
 
+  demuxer->priv = mkv_d;
+
+  if (mkv_d->chapters != NULL) {
+    for (i = 0; i < (int)mkv_d->chapters->size(); i++) {
+      (*mkv_d->chapters)[i].start -= mkv_d->first_tc;
+      (*mkv_d->chapters)[i].end -= mkv_d->first_tc;
+    }
+    if ((dvd_last_chapter > 0) &&
+        (dvd_last_chapter <= (int)mkv_d->chapters->size())) {
+      if ((*mkv_d->chapters)[dvd_last_chapter - 1].end != 0)
+        mkv_d->stop_timecode = (*mkv_d->chapters)[dvd_last_chapter - 1].end;
+      else if ((dvd_last_chapter + 1) <= (int)mkv_d->chapters->size())
+        mkv_d->stop_timecode = (*mkv_d->chapters)[dvd_last_chapter].start;
+    }
+  }
+
   if (s->end_pos == 0)
     demuxer->seekable = 0;
   else {
     demuxer->movi_start = s->start_pos;
     demuxer->movi_end = s->end_pos;
     demuxer->seekable = 1;
+    if ((dvd_chapter != 1) && (mkv_d->chapters != NULL) &&
+        (dvd_chapter <= (int)mkv_d->chapters->size()))
+      demux_mkv_seek(demuxer, (float)(*mkv_d->chapters)[dvd_chapter - 1].start
+                     / 1000.0, 1);
   }
 
-  demuxer->priv = mkv_d;
-
   return 1;
 }
 
@@ -2718,8 +2828,15 @@
               block_duration = uint64(*kbdur);
 
             kblock = FINDFIRST(l2, KaxBlock);
-            if (kblock != NULL)
+            if (kblock != NULL) {
               kblock->SetParent(*mkv_d->cluster);
+              if ((mkv_d->stop_timecode > 0) &&
+                  ((kblock->GlobalTimecode() / 1000000 - mkv_d->first_tc) >=
+                   mkv_d->stop_timecode)) {
+                delete l2;
+                return 0;
+              }
+            }
 
             krefblock = FINDFIRST(l2, KaxReferenceBlock);
             while (krefblock != NULL) {
@@ -3026,7 +3143,7 @@
           if (!index->entries[k].is_key)
             continue;
           diff = target_timecode - (int64_t)index->entries[k].timecode;
-          if ((target_timecode <= (mkv_d->last_pts * 1000)) &&
+          if (((flags & 1) || (target_timecode <= (mkv_d->last_pts * 1000))) &&
               (diff >= 0) && (diff < min_diff)) {
             min_diff = diff;
             entry = &index->entries[k];