aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar mosu <mosu@b3059339-0415-0410-9bf9-f77b7e298cf2>2003-10-25 12:42:24 +0000
committerGravatar mosu <mosu@b3059339-0415-0410-9bf9-f77b7e298cf2>2003-10-25 12:42:24 +0000
commit180f0624567f0b50c472429462a3e8172dcfb1b5 (patch)
tree49f2211fca3e91d76ac36e004207fd517ee485d7
parent7b5500b40f1f12a40ce764f2bc921e56609783c5 (diff)
Support for -chapter selection.
git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@11261 b3059339-0415-0410-9bf9-f77b7e298cf2
-rw-r--r--libmpdemux/demux_mkv.cpp127
1 files changed, 122 insertions, 5 deletions
diff --git a/libmpdemux/demux_mkv.cpp b/libmpdemux/demux_mkv.cpp
index bf10c97943..23a1b0c3a7 100644
--- a/libmpdemux/demux_mkv.cpp
+++ b/libmpdemux/demux_mkv.cpp
@@ -77,6 +77,9 @@ using namespace std;
// 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 @@ typedef struct {
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 @@ typedef struct mkv_demuxer {
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 @@ static void free_mkv_demuxer(mkv_demuxer_t *d) {
delete d->in;
if (d->segment != NULL)
delete d->segment;
+ if (d->chapters != NULL)
+ delete d->chapters;
free(d);
}
@@ -1295,6 +1307,79 @@ static void parse_cues(mkv_demuxer_t *mkv_d, uint64_t pos) {
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 @@ static void parse_seekhead(mkv_demuxer_t *mkv_d, uint64_t pos) {
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 @@ static void parse_seekhead(mkv_demuxer_t *mkv_d, uint64_t pos) {
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 @@ extern "C" int demux_mkv_open(demuxer_t *demuxer) {
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 @@ extern "C" int demux_mkv_open(demuxer_t *demuxer) {
}
}
+ 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 @@ extern "C" int demux_mkv_fill_buffer(demuxer_t *d) {
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 @@ extern "C" void demux_mkv_seek(demuxer_t *demuxer, float rel_seek_secs,
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];