From 1de0c3327d10a43ff3532be7e92c3160c52d55c8 Mon Sep 17 00:00:00 2001 From: eugeni Date: Sun, 6 Aug 2006 18:55:34 +0000 Subject: Add matroska chapter seeking capability. git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@19348 b3059339-0415-0410-9bf9-f77b7e298cf2 --- DOCS/man/en/mplayer.1 | 2 ++ DOCS/tech/slave.txt | 5 +++ help/help_mp-en.h | 1 + input/input.c | 5 +++ input/input.h | 1 + libmpdemux/demux_mkv.c | 83 ++++++++++++++++++++++++++++++-------------------- libmpdemux/demuxer.c | 20 ++++++++++++ libmpdemux/demuxer.h | 12 ++++++++ libmpdemux/ebml.h | 2 ++ mplayer.c | 44 ++++++++++++++++++++++++++ 10 files changed, 142 insertions(+), 33 deletions(-) diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1 index f8470b067a..d3ca2837c7 100644 --- a/DOCS/man/en/mplayer.1 +++ b/DOCS/man/en/mplayer.1 @@ -316,6 +316,8 @@ Set start or end of an EDL skip and write it out to the given file. Take a screenshot. .IPs "I" Show filename on the OSD. +.IPs "! and @" +Seek to the beginning of the previous/next chapter. .RE .PD 1 .PP diff --git a/DOCS/tech/slave.txt b/DOCS/tech/slave.txt index 4a1fe627c8..51346d870b 100644 --- a/DOCS/tech/slave.txt +++ b/DOCS/tech/slave.txt @@ -177,6 +177,11 @@ seek [type] 1 is a seek to % in the movie. 2 is a seek to an absolute position of seconds. +seek_chapter [type] + Seek to the start of a chapter. + 0 is a relative seek of +/- chapters (default) + 1 is a seek to a chapter # + set_property Set a property. diff --git a/help/help_mp-en.h b/help/help_mp-en.h index e87237d8dd..3cae8360fa 100644 --- a/help/help_mp-en.h +++ b/help/help_mp-en.h @@ -210,6 +210,7 @@ static char help_text[]= #define MSGTR_OSDSubDelay "Sub delay: %d ms" #define MSGTR_OSDSpeed "Speed: x %6.2f" #define MSGTR_OSDosd "OSD: %s" +#define MSGTR_OSDChapter "Chapter: (%d) %s" // property values #define MSGTR_Enabled "enabled" diff --git a/input/input.c b/input/input.c index 2da5353bfa..5f331310ab 100644 --- a/input/input.c +++ b/input/input.c @@ -146,6 +146,8 @@ static mp_cmd_t mp_cmds[] = { { MP_CMD_SET_PROPERTY, "set_property", 2, { {MP_CMD_ARG_STRING, {0}}, {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } }, { MP_CMD_GET_PROPERTY, "get_property", 1, { {MP_CMD_ARG_STRING, {0}}, {-1,{0}} } }, + { MP_CMD_SEEK_CHAPTER, "seek_chapter", 1, { {MP_CMD_ARG_INT,{0}}, {MP_CMD_ARG_INT,{0}}, {-1,{0}} } }, + { 0, NULL, 0, {} } }; @@ -392,6 +394,9 @@ static mp_cmd_bind_t def_cmd_binds[] = { { { KEY_MUTE, 0 }, "mute" }, { { KEY_CLOSE_WIN, 0 }, "quit" }, + + { { '!', 0 }, "seek_chapter -1" }, + { { '@', 0 }, "seek_chapter 1" }, { { 0 }, NULL } }; diff --git a/input/input.h b/input/input.h index 5fe9a88121..4d83d1b113 100644 --- a/input/input.h +++ b/input/input.h @@ -70,6 +70,7 @@ #define MP_CMD_SET_PROPERTY 68 #define MP_CMD_GET_PROPERTY 69 #define MP_CMD_OSD_SHOW_PROPERTY_TEXT 70 +#define MP_CMD_SEEK_CHAPTER 71 #define MP_CMD_GUI_EVENTS 5000 #define MP_CMD_GUI_LOADFILE 5001 diff --git a/libmpdemux/demux_mkv.c b/libmpdemux/demux_mkv.c index 8c29dbb362..e60b7a8c04 100644 --- a/libmpdemux/demux_mkv.c +++ b/libmpdemux/demux_mkv.c @@ -141,11 +141,6 @@ typedef struct mkv_index uint64_t timecode, filepos; } mkv_index_t; -typedef struct mkv_chapter -{ - uint64_t start, end; -} mkv_chapter_t; - typedef struct mkv_attachment { char* name; @@ -188,8 +183,6 @@ typedef struct mkv_demuxer int64_t skip_to_timecode; int v_skip_to_keyframe, a_skip_to_keyframe; - mkv_chapter_t *chapters; - int num_chapters; int64_t stop_timecode; int last_aid; @@ -1328,7 +1321,7 @@ demux_mkv_read_chapters (demuxer_t *demuxer) uint64_t length, l; int il; - if (mkv_d->chapters) + if (demuxer->chapters) { ebml_read_skip (s, NULL); return 0; @@ -1359,18 +1352,13 @@ demux_mkv_read_chapters (demuxer_t *demuxer) case MATROSKA_ID_CHAPTERATOM: { uint64_t len, start=0, end=0; + char* name = 0; int i; + int cid; len = ebml_read_length (s, &i); l = len + i; - if (mkv_d->chapters == NULL) - mkv_d->chapters = malloc (32*sizeof(*mkv_d->chapters)); - else if (!(mkv_d->num_chapters % 32)) - mkv_d->chapters = realloc (mkv_d->chapters, - (mkv_d->num_chapters + 32) - * sizeof(*mkv_d->chapters)); - while (len > 0) { uint64_t l; @@ -1386,6 +1374,32 @@ demux_mkv_read_chapters (demuxer_t *demuxer) end = ebml_read_uint (s, &l) / 1000000; break; + case MATROSKA_ID_CHAPTERDISPLAY: + { + uint64_t len; + int i; + + len = ebml_read_length (s, &i); + l = len + i; + while (len > 0) + { + uint64_t l; + int il; + + switch (ebml_read_id (s, &il)) + { + case MATROSKA_ID_CHAPSTRING: + name = ebml_read_utf8 (s, &l); + break; + default: + ebml_read_skip (s, &l); + break; + } + len -= l + il; + } + } + break; + default: ebml_read_skip (s, &l); break; @@ -1393,12 +1407,15 @@ demux_mkv_read_chapters (demuxer_t *demuxer) len -= l + il; } - mkv_d->chapters[mkv_d->num_chapters].start = start; - mkv_d->chapters[mkv_d->num_chapters].end = end; + if (!name) + name = strdup("(unnamed)"); + + cid = demuxer_add_chapter(demuxer, name, start, end); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Chapter %u from %02d:%02d:%02d." - "%03d to %02d:%02d:%02d.%03d\n", - ++mkv_d->num_chapters, + "%03d to %02d:%02d:%02d.%03d, %s\n", + cid, (int) (start / 60 / 60 / 1000), (int) ((start / 60 / 1000) % 60), (int) ((start / 1000) % 60), @@ -1406,7 +1423,9 @@ demux_mkv_read_chapters (demuxer_t *demuxer) (int) (end / 60 / 60 / 1000), (int) ((end / 60 / 1000) % 60), (int) ((end / 1000) % 60), - (int) (end % 1000)); + (int) (end % 1000), name); + + free(name); break; } @@ -2581,19 +2600,19 @@ demux_mkv_open (demuxer_t *demuxer) else demuxer->sub->id = -2; - if (mkv_d->chapters) + if (demuxer->chapters) { - for (i=0; i < (int)mkv_d->num_chapters; i++) + for (i=0; i < (int)demuxer->num_chapters; i++) { - mkv_d->chapters[i].start -= mkv_d->first_tc; - mkv_d->chapters[i].end -= mkv_d->first_tc; + demuxer->chapters[i].start -= mkv_d->first_tc; + demuxer->chapters[i].end -= mkv_d->first_tc; } - if (dvd_last_chapter > 0 && dvd_last_chapter <= mkv_d->num_chapters) + if (dvd_last_chapter > 0 && dvd_last_chapter <= demuxer->num_chapters) { - 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 <= mkv_d->num_chapters) - mkv_d->stop_timecode = mkv_d->chapters[dvd_last_chapter].start; + if (demuxer->chapters[dvd_last_chapter-1].end != 0) + mkv_d->stop_timecode = demuxer->chapters[dvd_last_chapter-1].end; + else if (dvd_last_chapter + 1 <= demuxer->num_chapters) + mkv_d->stop_timecode = demuxer->chapters[dvd_last_chapter].start; } } @@ -2604,7 +2623,7 @@ demux_mkv_open (demuxer_t *demuxer) demuxer->movi_start = s->start_pos; demuxer->movi_end = s->end_pos; demuxer->seekable = 1; - if (mkv_d->chapters && dvd_chapter>1 && dvd_chapter<=mkv_d->num_chapters) + if (demuxer->chapters && dvd_chapter>1 && dvd_chapter<=demuxer->num_chapters) { if (!mkv_d->has_first_tc) { @@ -2612,7 +2631,7 @@ demux_mkv_open (demuxer_t *demuxer) mkv_d->has_first_tc = 1; } demux_mkv_seek (demuxer, - mkv_d->chapters[dvd_chapter-1].start/1000.0, 0.0, 1); + demuxer->chapters[dvd_chapter-1].start/1000.0, 0.0, 1); } } @@ -2659,8 +2678,6 @@ demux_close_mkv (demuxer_t *demuxer) free (mkv_d->indexes); if (mkv_d->cluster_positions) free (mkv_d->cluster_positions); - if (mkv_d->chapters) - free (mkv_d->chapters); if (mkv_d->parsed_cues) free (mkv_d->parsed_cues); if (mkv_d->parsed_seekhead) diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c index 1432dac7aa..5a97f35ffb 100644 --- a/libmpdemux/demuxer.c +++ b/libmpdemux/demuxer.c @@ -287,6 +287,12 @@ skip_streamfree: } if(demuxer->filename) free(demuxer->filename); + if (demuxer->chapters) { + for (i=0; inum_chapters; i++) + if (demuxer->chapters[i].name) + free(demuxer->chapters[i].name); + free(demuxer->chapters); + } free(demuxer); } @@ -1024,3 +1030,17 @@ int demuxer_switch_audio(demuxer_t *demuxer, int index){ index = demuxer->audio->id; return index; } + +int demuxer_add_chapter(demuxer_t* demuxer, const char* name, uint64_t start, uint64_t end){ + if (demuxer->chapters == NULL) + demuxer->chapters = malloc (32*sizeof(*demuxer->chapters)); + else if (!(demuxer->num_chapters % 32)) + demuxer->chapters = realloc (demuxer->chapters, (demuxer->num_chapters + 32) * sizeof(*demuxer->chapters)); + + demuxer->chapters[demuxer->num_chapters].start = start; + demuxer->chapters[demuxer->num_chapters].end = end; + demuxer->chapters[demuxer->num_chapters].name = strdup(name); + + return demuxer->num_chapters ++; +} + diff --git a/libmpdemux/demuxer.h b/libmpdemux/demuxer.h index de49d8273b..5cbce24259 100644 --- a/libmpdemux/demuxer.h +++ b/libmpdemux/demuxer.h @@ -172,6 +172,12 @@ typedef struct demuxers_desc_st { int (*control)(struct demuxer_st *demuxer, int cmd, void *arg); ///< Optional } demuxer_desc_t; +typedef struct demux_chapter_s +{ + uint64_t start, end; + char* name; +} demux_chapter_t; + typedef struct demuxer_st { demuxer_desc_t *desc; ///< Demuxer description structure off_t filepos; // input stream current pos. @@ -192,6 +198,9 @@ typedef struct demuxer_st { void* a_streams[MAX_A_STREAMS]; // audio streams (sh_audio_t) void* v_streams[MAX_V_STREAMS]; // video sterams (sh_video_t) char s_streams[32]; // dvd subtitles (flag) + + demux_chapter_t* chapters; + int num_chapters; void* priv; // fileformat-dependent data char** info; @@ -370,3 +379,6 @@ extern int demuxer_type_by_filename(char* filename); extern void demuxer_help(void); extern int get_demuxer_type_from_name(char *demuxer_name, int *force); + +int demuxer_add_chapter(demuxer_t* demuxer, const char* name, uint64_t start, uint64_t end); + diff --git a/libmpdemux/ebml.h b/libmpdemux/ebml.h index 3874f89b91..12fd4bee2f 100644 --- a/libmpdemux/ebml.h +++ b/libmpdemux/ebml.h @@ -128,6 +128,8 @@ #define MATROSKA_ID_CHAPTERATOM 0xB6 #define MATROSKA_ID_CHAPTERTIMESTART 0x91 #define MATROSKA_ID_CHAPTERTIMEEND 0x92 +#define MATROSKA_ID_CHAPTERDISPLAY 0x80 +#define MATROSKA_ID_CHAPSTRING 0x85 /* IDs in the cluster master */ #define MATROSKA_ID_CLUSTERTIMECODE 0xE7 diff --git a/mplayer.c b/mplayer.c index 70949e1662..d5e63b5052 100644 --- a/mplayer.c +++ b/mplayer.c @@ -4807,6 +4807,50 @@ if(step_sec>0) { case MP_CMD_KEYDOWN_EVENTS : { mplayer_put_key(cmd->args[0].v.i); } break; + case MP_CMD_SEEK_CHAPTER : { + int seek = cmd->args[0].v.i; + int abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0; + int total; + int current; + + if (!demuxer->num_chapters || !demuxer->chapters) { + if (seek > 0) { + abs_seek_pos = 0; + rel_seek_secs = 1000000000.; + } else + set_osd_msg(OSD_MSG_TEXT, 1, osd_duration, MSGTR_OSDChapter, 0, MSGTR_Unknown); + break; + } + + total = demuxer->num_chapters; + + if (abs) { + current = seek; + } else { + uint64_t now; + now = (sh_video ? sh_video->pts : (sh_audio ? sh_audio->pts : 0.)) * 1000 + .5; + + for (current = total - 1; current >= 0; --current) { + demux_chapter_t* chapter = demuxer->chapters + current; + if (chapter->start <= now) + break; + } + current += seek; + } + + if (current < 0) current = 0; + if (current >= total) { + current = total - 1; + abs_seek_pos = 0; + rel_seek_secs = 1000000000.; + } else { + abs_seek_pos = 1; + rel_seek_secs = demuxer->chapters[current].start / 1000.; + } + + set_osd_msg(OSD_MSG_TEXT, 1, osd_duration, MSGTR_OSDChapter, + current, demuxer->chapters[current].name); + } break; default : { #ifdef HAVE_NEW_GUI if ( ( use_gui )&&( cmd->id > MP_CMD_GUI_EVENTS ) ) guiGetEvent( guiIEvent,(char *)cmd->id ); -- cgit v1.2.3