From e177fc3e8be6ab572f53da21135018fcee340c74 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Fri, 13 Mar 2009 03:26:32 +0200 Subject: demux_mkv: Some cleanup, fix duration printed at verbose level Clean up demux_mkv_read_info() and demux_mkv_read_chapters() somewhat. (Why do the names of static functions have a stupid prefix like that? Didn't remove it now however). Demux_mkv_read_info() now delays printing duration information until the end of the function where we hopefully have the correct timescale for converting it to seconds. The code to calculate the duration had been fixed for that earlier but the message had not. --- libmpdemux/demux_mkv.c | 288 +++++++++++++++++++++++-------------------------- 1 file changed, 134 insertions(+), 154 deletions(-) diff --git a/libmpdemux/demux_mkv.c b/libmpdemux/demux_mkv.c index 76a3874723..9c17751e2e 100644 --- a/libmpdemux/demux_mkv.c +++ b/libmpdemux/demux_mkv.c @@ -375,52 +375,49 @@ lzo_fail: } -static int -demux_mkv_read_info (demuxer_t *demuxer) +static int demux_mkv_read_info(demuxer_t *demuxer) { - mkv_demuxer_t *mkv_d = (mkv_demuxer_t *) demuxer->priv; - stream_t *s = demuxer->stream; - uint64_t length, l; - int il; - uint64_t tc_scale = 1000000; - long double duration = 0.; - - length = ebml_read_length (s, NULL); - while (length > 0) - { - switch (ebml_read_id (s, &il)) - { + mkv_demuxer_t *mkv_d = demuxer->priv; + stream_t *s = demuxer->stream; + uint64_t length, l; + int i; + uint64_t tc_scale = 1000000; + long double duration = 0.; + + length = ebml_read_length(s, NULL); + while (length > 0) { + uint32_t id = ebml_read_id(s, &i); + length -= i; + switch (id) { case MATROSKA_ID_TIMECODESCALE: - { - uint64_t num = ebml_read_uint (s, &l); - if (num == EBML_UINT_INVALID) - return 1; - tc_scale = num; - mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] | + timecode scale: %"PRIu64"\n", + tc_scale = ebml_read_uint(s, &l); + length -= l; + if (tc_scale == EBML_UINT_INVALID) + return 1; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + timecode scale: %"PRIu64"\n", tc_scale); break; - } case MATROSKA_ID_DURATION: - { - long double num = ebml_read_float (s, &l); - if (num == EBML_FLOAT_INVALID) - return 1; - duration = num; - mp_msg (MSGT_DEMUX, MSGL_V, "[mkv] | + duration: %.3Lfs\n", - duration * tc_scale / 1000000000.0); + duration = ebml_read_float(s, &l); + length -= l; + if (duration == EBML_FLOAT_INVALID) + return 1; break; - } default: - ebml_read_skip (s, &l); - break; + ebml_read_skip(s, &l); + length -= l; + break; } - length -= l + il; } - mkv_d->tc_scale = tc_scale; - mkv_d->duration = duration * tc_scale / 1000000000.0; - return 0; + mkv_d->tc_scale = tc_scale; + mkv_d->duration = duration * tc_scale / 1000000000.0; + if (duration) + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + duration: %.3fs\n", + mkv_d->duration); + + return 0; } /** @@ -1077,140 +1074,123 @@ demux_mkv_read_cues (demuxer_t *demuxer) return 0; } -static int -demux_mkv_read_chapters (demuxer_t *demuxer) +static uint64_t read_one_chapter(demuxer_t *demuxer, stream_t *s) { - stream_t *s = demuxer->stream; - uint64_t length, l; - int il; - - if (demuxer->chapters) - { - ebml_read_skip (s, NULL); - return 0; - } - - mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing chapters ] ---------\n"); - length = ebml_read_length (s, NULL); - - while (length > 0) - { - switch (ebml_read_id (s, &il)) - { - case MATROSKA_ID_EDITIONENTRY: - { - 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_CHAPTERATOM: - { - uint64_t len, start=0, end=0; - char* name = 0; - int i; - int cid; - - len = ebml_read_length (s, &i); - l = len + i; + uint64_t len, l; + uint64_t start = 0, end = 0; + char *name = 0; + int i; + uint32_t id; + + len = ebml_read_length(s, &i); + uint64_t bytes_read = len + i; + + while (len > 0) { + id = ebml_read_id(s, &i); + len -= i; + switch (id) { + case MATROSKA_ID_CHAPTERTIMESTART: + start = ebml_read_uint(s, &l) / 1000000; + len -= l; + break; - while (len > 0) - { - uint64_t l; - int il; + case MATROSKA_ID_CHAPTERTIMEEND: + end = ebml_read_uint(s, &l) / 1000000; + len -= l; + break; - switch (ebml_read_id (s, &il)) - { - case MATROSKA_ID_CHAPTERTIMESTART: - start = ebml_read_uint (s, &l) / 1000000; - break; + case MATROSKA_ID_CHAPTERDISPLAY:; + uint64_t displaylen = ebml_read_length(s, &i); + len -= displaylen + i; + while (displaylen > 0) { + id = ebml_read_id(s, &i); + displaylen -= i; + switch (id) { + case MATROSKA_ID_CHAPSTRING: + name = ebml_read_utf8(s, &l); + break; + default: + ebml_read_skip(s, &l); + break; + } + displaylen -= l; + } + break; - case MATROSKA_ID_CHAPTERTIMEEND: - end = ebml_read_uint (s, &l) / 1000000; - break; + default: + ebml_read_skip(s, &l); + len -= l; + 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; + if (!name) + name = strdup("(unnamed)"); + + int 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, %s\n", + cid, + (int) (start / 60 / 60 / 1000), + (int) ((start / 60 / 1000) % 60), + (int) ((start / 1000) % 60), + (int) (start % 1000), + (int) (end / 60 / 60 / 1000), + (int) ((end / 60 / 1000) % 60), + (int) ((end / 1000) % 60), + (int) (end % 1000), name); + + free(name); + return bytes_read; +} - default: - ebml_read_skip (s, &l); - break; - } - len -= l + il; - } +static int demux_mkv_read_chapters(struct demuxer *demuxer) +{ + stream_t *s = demuxer->stream; + uint64_t length, l; + int i; + uint32_t id; - 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, %s\n", - cid, - (int) (start / 60 / 60 / 1000), - (int) ((start / 60 / 1000) % 60), - (int) ((start / 1000) % 60), - (int) (start % 1000), - (int) (end / 60 / 60 / 1000), - (int) ((end / 60 / 1000) % 60), - (int) ((end / 1000) % 60), - (int) (end % 1000), name); - - free(name); - break; - } + if (demuxer->chapters) { + ebml_read_skip(s, NULL); + return 0; + } - default: - ebml_read_skip (s, &l); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] /---- [ parsing chapters ] ---------\n"); + length = ebml_read_length(s, NULL); + + while (length > 0) { + id = ebml_read_id(s, &i); + length -= i; + switch (id) { + case MATROSKA_ID_EDITIONENTRY:; + uint64_t editionlen = ebml_read_length(s, &i); + length -= editionlen + i; + while (editionlen > 0) { + id = ebml_read_id(s, &i); + editionlen -= i; + switch (id) { + case MATROSKA_ID_CHAPTERATOM: + l = read_one_chapter(demuxer, s); break; - } - len -= l + il; - } + default: + ebml_read_skip(s, &l); + break; + } + editionlen -= l; + } break; - } default: - ebml_read_skip (s, &l); - break; + ebml_read_skip(s, &l); + length -= l; + break; } - - length -= l + il; } - mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing chapters ] ---------\n"); - return 0; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] \\---- [ parsing chapters ] ---------\n"); + return 0; } static int -- cgit v1.2.3 From 4b33422c7b004f31eb8fb1962231f69ab6f18445 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 16 Mar 2009 05:11:22 +0200 Subject: Allocate struct demuxer with talloc Makes it possible to add data to it without explicit freeing code. --- libmpdemux/demux_demuxers.c | 3 ++- libmpdemux/demuxer.c | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/libmpdemux/demux_demuxers.c b/libmpdemux/demux_demuxers.c index a5f87e601b..dc04ecffd0 100644 --- a/libmpdemux/demux_demuxers.c +++ b/libmpdemux/demux_demuxers.c @@ -8,6 +8,7 @@ #include "stream/stream.h" #include "demuxer.h" #include "stheader.h" +#include "talloc.h" typedef struct dd_priv { demuxer_t* vd; @@ -21,7 +22,7 @@ demuxer_t* new_demuxers_demuxer(demuxer_t* vd, demuxer_t* ad, demuxer_t* sd) { demuxer_t* ret; dd_priv_t* priv; - ret = calloc(1,sizeof(demuxer_t)); + ret = talloc_zero(NULL, struct demuxer); priv = malloc(sizeof(dd_priv_t)); priv->vd = vd; diff --git a/libmpdemux/demuxer.c b/libmpdemux/demuxer.c index 4347feee0b..9d0e01450f 100644 --- a/libmpdemux/demuxer.c +++ b/libmpdemux/demuxer.c @@ -10,6 +10,7 @@ #include "config.h" #include "options.h" +#include "talloc.h" #include "mp_msg.h" #include "help_mp.h" #include "m_config.h" @@ -216,8 +217,7 @@ static const demuxer_desc_t *get_demuxer_desc_from_type(int file_format) demuxer_t *new_demuxer(struct MPOpts *opts, stream_t *stream, int type, int a_id, int v_id, int s_id, char *filename) { - demuxer_t *d = malloc(sizeof(demuxer_t)); - memset(d, 0, sizeof(demuxer_t)); + struct demuxer *d = talloc_zero(NULL, struct demuxer); d->stream = stream; d->stream_pts = MP_NOPTS_VALUE; d->reference_clock = MP_NOPTS_VALUE; @@ -386,7 +386,7 @@ void free_demuxer(demuxer_t *demuxer) } free(demuxer->attachments); } - free(demuxer); + talloc_free(demuxer); } -- cgit v1.2.3 From 327940361170a4d830bc7c120503cdd0396a125a Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 16 Mar 2009 23:30:48 +0200 Subject: demux_mkv: Parse ordered chapter information Parse the ordered chapter structure if present and place the information in the public demuxer structure. Nothing uses the information yet. --- libmpdemux/demux_mkv.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++-- libmpdemux/demuxer.h | 17 +++++++++++- libmpdemux/ebml.h | 3 ++ 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/libmpdemux/demux_mkv.c b/libmpdemux/demux_mkv.c index 9c17751e2e..8f8a53e6da 100644 --- a/libmpdemux/demux_mkv.c +++ b/libmpdemux/demux_mkv.c @@ -12,7 +12,9 @@ #include #include #include +#include +#include "talloc.h" #include "options.h" #include "stream/stream.h" #include "demuxer.h" @@ -405,6 +407,24 @@ static int demux_mkv_read_info(demuxer_t *demuxer) return 1; break; + case MATROSKA_ID_SEGMENTUID:; + l = ebml_read_length(s, &i); + length -= i; + if (l != sizeof(demuxer->matroska_data.segment_uid)) { + mp_msg(MSGT_DEMUX, MSGL_INFO, + "[mkv] segment uid invalid length %"PRIu64"\n", l); + stream_skip(s, l); + } else { + stream_read(s, demuxer->matroska_data.segment_uid, l); + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] | + segment uid"); + for (int i = 0; i < l; i++) + mp_msg(MSGT_DEMUX, MSGL_V, " %02x", + demuxer->matroska_data.segment_uid[i]); + mp_msg(MSGT_DEMUX, MSGL_V, "\n"); + } + length -= l; + break; + default: ebml_read_skip(s, &l); length -= l; @@ -1074,10 +1094,11 @@ demux_mkv_read_cues (demuxer_t *demuxer) return 0; } -static uint64_t read_one_chapter(demuxer_t *demuxer, stream_t *s) +static uint64_t read_one_chapter(struct demuxer *demuxer, stream_t *s) { uint64_t len, l; uint64_t start = 0, end = 0; + struct matroska_chapter chapter = {}; char *name = 0; int i; uint32_t id; @@ -1117,6 +1138,24 @@ static uint64_t read_one_chapter(demuxer_t *demuxer, stream_t *s) } break; + case MATROSKA_ID_CHAPTERSEGMENTUID: + l = ebml_read_length(s, &i); + len -= l + i; + if (l != sizeof(chapter.segment_uid)) { + mp_msg(MSGT_DEMUX, MSGL_INFO, + "[mkv] chapter segment uid invalid length %"PRIu64"\n", + l); + stream_skip(s, l); + } else { + stream_read(s, chapter.segment_uid, l); + chapter.has_segment_uid = true; + mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Chapter segment uid "); + for (int i = 0; i < l; i++) + mp_msg(MSGT_DEMUX, MSGL_V, "%02x ", chapter.segment_uid[i]); + mp_msg(MSGT_DEMUX, MSGL_V, "\n"); + } + break; + default: ebml_read_skip(s, &l); len -= l; @@ -1128,6 +1167,15 @@ static uint64_t read_one_chapter(demuxer_t *demuxer, stream_t *s) 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; + // 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++; mp_msg(MSGT_DEMUX, MSGL_V, "[mkv] Chapter %u from %02d:%02d:%02d." "%03d to %02d:%02d:%02d.%03d, %s\n", @@ -1160,13 +1208,23 @@ 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; while (length > 0) { id = ebml_read_id(s, &i); length -= i; switch (id) { - case MATROSKA_ID_EDITIONENTRY:; + 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; uint64_t editionlen = ebml_read_length(s, &i); length -= editionlen + i; + bool ordered = false; while (editionlen > 0) { id = ebml_read_id(s, &i); editionlen -= i; @@ -1174,12 +1232,25 @@ static int demux_mkv_read_chapters(struct demuxer *demuxer) case MATROSKA_ID_CHAPTERATOM: l = read_one_chapter(demuxer, s); break; + case MATROSKA_ID_EDITIONFLAGORDERED: + ordered = ebml_read_uint(s, &l); + mp_msg(MSGT_DEMUX, MSGL_V, + "[mkv] Ordered chapter flag: %d\n", ordered); + break; + default: ebml_read_skip(s, &l); break; } 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; + } break; default: diff --git a/libmpdemux/demuxer.h b/libmpdemux/demuxer.h index 4815c8b7d9..8d1f1d0b88 100644 --- a/libmpdemux/demuxer.h +++ b/libmpdemux/demuxer.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "stream/stream.h" @@ -187,6 +188,18 @@ typedef struct demux_chapter char* name; } demux_chapter_t; +struct matroska_data { + unsigned char segment_uid[16]; + // Ordered chapter information if any + struct matroska_chapter { + uint64_t start; + uint64_t end; + bool has_segment_uid; + unsigned char segment_uid[16]; + } *ordered_chapters; + int num_ordered_chapters; +}; + typedef struct demux_attachment { char* name; @@ -220,10 +233,12 @@ typedef struct demuxer { demux_chapter_t* chapters; int num_chapters; - + demux_attachment_t* attachments; int num_attachments; + struct matroska_data matroska_data; + void* priv; // fileformat-dependent data char** info; struct MPOpts *opts; diff --git a/libmpdemux/ebml.h b/libmpdemux/ebml.h index c4d7256d91..345f08b46a 100644 --- a/libmpdemux/ebml.h +++ b/libmpdemux/ebml.h @@ -54,6 +54,7 @@ #define MATROSKA_ID_WRITINGAPP 0x5741 #define MATROSKA_ID_MUXINGAPP 0x4D80 #define MATROSKA_ID_DATEUTC 0x4461 +#define MATROSKA_ID_SEGMENTUID 0x73A4 /* ID in the tracks master */ #define MATROSKA_ID_TRACKENTRY 0xAE @@ -126,11 +127,13 @@ /* IDs in the chapters master */ #define MATROSKA_ID_EDITIONENTRY 0x45B9 +#define MATROSKA_ID_EDITIONFLAGORDERED 0x45DD #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 +#define MATROSKA_ID_CHAPTERSEGMENTUID 0x6E67 /* IDs in the cluster master */ #define MATROSKA_ID_CLUSTERTIMECODE 0xE7 -- cgit v1.2.3 From 73fb23c1cfc40a9e0683f0a82cb1a669005eaa67 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Thu, 19 Mar 2009 05:25:12 +0200 Subject: Add improved relative seek mode When the new mode is active relative seeks are converted to absolute ones (current video pts + relative seek amount) and forward/backward flag before being sent to the demuxer. This mode is used if the demuxer has set the accurate_seek field in the demuxer struct and there is a video stream. At the moment the mkv and lavf demuxers enable the flag. This change is useful for later Matroska ordered chapter support (and for more general timelime editing), but also fixes problems in existing functionality. The main problem with the old mode, where relative seeks are passed directly to the demuxer, is that the user wants to seek relative to the currently displayed position but the demuxer does not know what that position is. There can be an arbitrary amount of buffering between the demuxer read position and what is displayed on the screen. In some situations this makes small seeks fail to move backward at all (especially visible at high playback speed, when audio needs to be demuxed and decoded further ahead to fill the output buffers after resampling). Some container formats that can be used with the lavf demuxer do not always have reliable timestamps that could be used for unambiguous absolute seeking. However I made the demuxer always enable the new mode because it already converted all seeks to absolute ones before sending them to libavformat, so cases without reliable absolute seeks were failing already and this should only improve the working cases. --- libmpdemux/demux_lavf.c | 7 +++++++ libmpdemux/demux_mkv.c | 24 +++++++++++++++++------- libmpdemux/demuxer.h | 5 +++++ mplayer.c | 10 ++++++++++ 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/libmpdemux/demux_lavf.c b/libmpdemux/demux_lavf.c index 047315e74c..626ab7d651 100644 --- a/libmpdemux/demux_lavf.c +++ b/libmpdemux/demux_lavf.c @@ -22,6 +22,7 @@ #include // #include #include +#include #include "config.h" #include "options.h" @@ -533,6 +534,8 @@ static demuxer_t* demux_open_lavf(demuxer_t *demuxer){ demuxer->video->id=-2; // audio-only } //else if (best_video > 0 && demuxer->video->id == -1) demuxer->video->id = best_video; + demuxer->accurate_seek = true; + return demuxer; } @@ -612,6 +615,10 @@ static void demux_seek_lavf(demuxer_t *demuxer, float rel_seek_secs, float audio } else { if (rel_seek_secs < 0) avsflags = AVSEEK_FLAG_BACKWARD; } + if (flags & SEEK_FORWARD) + avsflags = 0; + else if (flags & SEEK_BACKWARD) + avsflags = AVSEEK_FLAG_BACKWARD; if (flags & SEEK_FACTOR) { if (priv->avfc->duration == 0 || priv->avfc->duration == AV_NOPTS_VALUE) return; diff --git a/libmpdemux/demux_mkv.c b/libmpdemux/demux_mkv.c index 8f8a53e6da..ac10a41cb0 100644 --- a/libmpdemux/demux_mkv.c +++ b/libmpdemux/demux_mkv.c @@ -2295,6 +2295,8 @@ demux_mkv_open (demuxer_t *demuxer) demuxer->seekable = 1; } + demuxer->accurate_seek = true; + return DEMUXER_TYPE_MATROSKA; } @@ -2962,6 +2964,12 @@ demux_mkv_fill_buffer (demuxer_t *demuxer, demux_stream_t *ds) static void demux_mkv_seek (demuxer_t *demuxer, float rel_seek_secs, float audio_delay, int flags) { + if (!(flags & (SEEK_BACKWARD | SEEK_FORWARD))) { + if (flags & SEEK_ABSOLUTE || rel_seek_secs < 0) + flags |= SEEK_BACKWARD; + else + flags |= SEEK_FORWARD; + } free_cached_dps (demuxer); if (!(flags & SEEK_FACTOR)) /* time in secs */ { @@ -3016,12 +3024,12 @@ demux_mkv_seek (demuxer_t *demuxer, float rel_seek_secs, float audio_delay, int for (i=0; i < mkv_d->num_cluster_pos; i++) { diff = mkv_d->cluster_positions[i] - target_filepos; - if (rel_seek_secs < 0 && diff < 0 && -diff < min_diff) + if (flags & SEEK_BACKWARD && diff < 0 && -diff < min_diff) { cluster_pos = mkv_d->cluster_positions[i]; min_diff = -diff; } - else if (rel_seek_secs > 0 + else if (flags & SEEK_FORWARD && (diff < 0 ? -1 * diff : diff) < min_diff) { cluster_pos = mkv_d->cluster_positions[i]; @@ -3045,14 +3053,14 @@ demux_mkv_seek (demuxer_t *demuxer, float rel_seek_secs, float audio_delay, int diff = target_timecode + mkv_d->first_tc - (int64_t) mkv_d->indexes[i].timecode * mkv_d->tc_scale / 1000000.0; - if ((flags & SEEK_ABSOLUTE || target_timecode <= mkv_d->last_pts*1000)) { - // Absolute seek or seek backward: find the last index - // position before target time + if (flags & SEEK_BACKWARD) { + // Seek backward: find the last index position + // before target time if (diff < 0 || diff >= min_diff) continue; } else { - // Relative seek forward: find the first index position + // Seek forward: find the first index position // after target time. If no such index exists, find last // position between current position and target time. if (diff <= 0) { @@ -3076,8 +3084,10 @@ demux_mkv_seek (demuxer_t *demuxer, float rel_seek_secs, float audio_delay, int if (demuxer->video->id >= 0) mkv_d->v_skip_to_keyframe = 1; - if (rel_seek_secs > 0.0) + if (flags & SEEK_FORWARD) mkv_d->skip_to_timecode = target_timecode; + else + mkv_d->skip_to_timecode = 0; mkv_d->a_skip_to_keyframe = 1; demux_mkv_fill_buffer(demuxer, NULL); diff --git a/libmpdemux/demuxer.h b/libmpdemux/demuxer.h index 8d1f1d0b88..032552b751 100644 --- a/libmpdemux/demuxer.h +++ b/libmpdemux/demuxer.h @@ -96,6 +96,8 @@ struct MPOpts; #define SEEK_ABSOLUTE (1 << 0) #define SEEK_FACTOR (1 << 1) +#define SEEK_FORWARD (1 << 2) +#define SEEK_BACKWARD (1 << 3) #define MP_INPUT_BUFFER_PADDING_SIZE 8 @@ -221,6 +223,9 @@ typedef struct demuxer { int type; // demuxer type: mpeg PS, mpeg ES, avi, avi-ni, avi-nini, asf int file_format; // file format: mpeg/avi/asf int seekable; // flag + /* Set if using absolute seeks for small movements is OK (no pts resets + * that would make pts ambigious, preferably supports back/forward flags */ + bool accurate_seek; // demux_stream_t *audio; // audio buffer/demuxer demux_stream_t *video; // video buffer/demuxer diff --git a/mplayer.c b/mplayer.c index 1690f75ddb..a30fe21899 100644 --- a/mplayer.c +++ b/mplayer.c @@ -2463,6 +2463,16 @@ static void edl_update(MPContext *mpctx) static int seek(MPContext *mpctx, double amount, int style) { current_module = "seek"; + if (mpctx->demuxer->accurate_seek && mpctx->sh_video + && !(style & (SEEK_ABSOLUTE | SEEK_FACTOR))) { + style |= SEEK_ABSOLUTE; + if (amount > 0) + style |= SEEK_FORWARD; + else + style |= SEEK_BACKWARD; + amount += mpctx->sh_video->pts; + } + if (demux_seek(mpctx->demuxer, amount, audio_delay, style) == 0) return -1; -- cgit v1.2.3 From 8d25a357ef29b24526be76ed04396cac3ecc9f62 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Fri, 20 Mar 2009 02:56:49 +0200 Subject: Set the fullscreen option value too when fs state changes If the fullscreen state was changed via keyboard commands or the slave interface the option value was not changed. However that value is used if the VO is reconfigured. Set the option too to avoid switching back to the previous state in that case. --- command.c | 1 + 1 file changed, 1 insertion(+) diff --git a/command.c b/command.c index caf1ca5037..0138052c2a 100644 --- a/command.c +++ b/command.c @@ -1027,6 +1027,7 @@ static int mp_property_fullscreen(m_option_t *prop, int action, void *arg, #endif if (mpctx->video_out->config_ok) vo_control(mpctx->video_out, VOCTRL_FULLSCREEN, 0); + mpctx->opts.fullscreen = vo_fs; return M_PROPERTY_OK; default: return m_property_flag(prop, action, arg, &vo_fs); -- cgit v1.2.3 From 8ca11dda9e73865ccb97c1c3563c1f10b226f633 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Fri, 20 Mar 2009 15:22:15 +0200 Subject: VO: Don't force window position in X11 VOs Disable by default the code that forcefully moved the video output window to the middle of the screen whenever it was reconfigured or created. That behavior was really annoying when switching video streams within a file, and overriding the window manager like that is not good default behavior for the initial creation of a window either. Add a new option "-force-window-position" that can be used to restore the old behavior. --- DOCS/man/en/mplayer.1 | 7 +++++++ cfg-mplayer.h | 2 ++ libvo/x11_common.c | 6 +++++- options.h | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/DOCS/man/en/mplayer.1 b/DOCS/man/en/mplayer.1 index 3b6c1873d8..cec09f8735 100644 --- a/DOCS/man/en/mplayer.1 +++ b/DOCS/man/en/mplayer.1 @@ -3055,6 +3055,13 @@ VESA framebuffer does not support mode changing. Override framebuffer mode configuration file (default: /etc/\:fb.modes). . .TP +.B \-force\-window\-position +Forcefully move MPlayer's video output window to default location whenever +there is a change in video parameters, video stream or file. +This used to be the default behavior. +Currently only affects X11 VOs. +. +.TP .B \-fs (also see \-zoom) Fullscreen playback (centers movie, and paints black bands around it). Not supported by all video output drivers. diff --git a/cfg-mplayer.h b/cfg-mplayer.h index 5fe0bff69c..152b9bf9bd 100644 --- a/cfg-mplayer.h +++ b/cfg-mplayer.h @@ -160,6 +160,8 @@ const m_option_t mplayer_opts[]={ OPT_INTRANGE("screenh", vo_screenheight, CONF_OLD, 0, 4096), // Geometry string {"geometry", &vo_geometry, CONF_TYPE_STRING, 0, 0, 0, NULL}, + OPT_FLAG_ON("force-window-position", force_window_position, 0), + OPT_FLAG_OFF("noforce-window-position", force_window_position, 0), // set aspect ratio of monitor - useful for 16:9 TV-out OPT_FLOATRANGE("monitoraspect", force_monitor_aspect, 0, 0.0, 9.0), OPT_FLOATRANGE("monitorpixelaspect", monitor_pixel_aspect, 0, 0.2, 9.0), diff --git a/libvo/x11_common.c b/libvo/x11_common.c index b70c0b8dd6..434cdbd04c 100644 --- a/libvo/x11_common.c +++ b/libvo/x11_common.c @@ -933,7 +933,11 @@ static void vo_x11_nofs_sizepos(struct vo *vo, int x, int y, { vo->dwidth = width; vo->dheight = height; - XMoveResizeWindow(vo->x11->display, vo->x11->window, x, y, width, height); + if (vo->opts->force_window_position) + XMoveResizeWindow(vo->x11->display, vo->x11->window, x, y, width, + height); + else + XResizeWindow(vo->x11->display, vo->x11->window, width, height); } } diff --git a/options.h b/options.h index 2f35fe2a76..7e1a67c2b9 100644 --- a/options.h +++ b/options.h @@ -10,6 +10,7 @@ typedef struct MPOpts { int screen_size_y; int vo_screenwidth; int vo_screenheight; + int force_window_position; float force_monitor_aspect; float monitor_pixel_aspect; int vidmode; -- cgit v1.2.3 From 9156a396a57c765090ea555bae9008ae862a8e49 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Fri, 27 Mar 2009 06:38:55 +0200 Subject: vf_ass: Free private data at uninit --- libmpcodecs/vf_ass.c | 1 + 1 file changed, 1 insertion(+) diff --git a/libmpcodecs/vf_ass.c b/libmpcodecs/vf_ass.c index 30bdcb0149..c6c2a92423 100644 --- a/libmpcodecs/vf_ass.c +++ b/libmpcodecs/vf_ass.c @@ -373,6 +373,7 @@ static void uninit(struct vf_instance* vf) free(vf->priv->planes[1]); free(vf->priv->planes[2]); free(vf->priv->line_limits); + free(vf->priv); } static const unsigned int fmt_list[]={ -- cgit v1.2.3 From 694c067e19dcbabe8f6121923fe628b9bfa6ac32 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 30 Mar 2009 02:06:58 +0300 Subject: options: Move osd_level and osd_duration to options struct --- cfg-mplayer.h | 4 ++-- command.c | 30 +++++++++++++++++------------- defaultopts.c | 2 ++ mp_osd.h | 1 - mplayer.c | 23 +++++++++++++---------- mplayer.h | 1 - options.h | 2 ++ 7 files changed, 36 insertions(+), 27 deletions(-) diff --git a/cfg-mplayer.h b/cfg-mplayer.h index 152b9bf9bd..5b5a1ac176 100644 --- a/cfg-mplayer.h +++ b/cfg-mplayer.h @@ -242,8 +242,8 @@ const m_option_t mplayer_opts[]={ {"crash-debug", &crash_debug, CONF_TYPE_FLAG, CONF_GLOBAL, 0, 1, NULL}, {"nocrash-debug", &crash_debug, CONF_TYPE_FLAG, CONF_GLOBAL, 1, 0, NULL}, #endif - {"osdlevel", &osd_level, CONF_TYPE_INT, CONF_RANGE, 0, 3, NULL}, - {"osd-duration", &osd_duration, CONF_TYPE_INT, CONF_MIN, 0, 0, NULL}, + OPT_INTRANGE("osdlevel", osd_level, 0, 0, 3), + OPT_INTRANGE("osd-duration", osd_duration, 0, 0, 3600000), #ifdef CONFIG_MENU {"menu", &use_menu, CONF_TYPE_FLAG, CONF_GLOBAL, 0, 1, NULL}, {"nomenu", &use_menu, CONF_TYPE_FLAG, CONF_GLOBAL, 1, 0, NULL}, diff --git a/command.c b/command.c index 0138052c2a..5e76158116 100644 --- a/command.c +++ b/command.c @@ -172,7 +172,7 @@ static void log_sub(struct MPContext *mpctx) static int mp_property_osdlevel(m_option_t *prop, int action, void *arg, MPContext *mpctx) { - return m_property_choice(prop, action, arg, &osd_level); + return m_property_choice(prop, action, arg, &mpctx->opts.osd_level); } /// Loop (RW) @@ -385,6 +385,7 @@ static int mp_property_time_pos(m_option_t *prop, int action, static int mp_property_chapter(m_option_t *prop, int action, void *arg, MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; int chapter = -1; float next_pts = 0; int chapter_num; @@ -440,13 +441,13 @@ static int mp_property_chapter(m_option_t *prop, int action, void *arg, mpctx->rel_seek_secs = next_pts; } if (chapter_name) - set_osd_msg(OSD_MSG_TEXT, 1, osd_duration, + set_osd_msg(OSD_MSG_TEXT, 1, opts->osd_duration, MSGTR_OSDChapter, chapter + 1, chapter_name); } else if (step_all > 0) mpctx->rel_seek_secs = 1000000000.; else - set_osd_msg(OSD_MSG_TEXT, 1, osd_duration, + set_osd_msg(OSD_MSG_TEXT, 1, opts->osd_duration, MSGTR_OSDChapter, 0, MSGTR_Unknown); if (chapter_name) free(chapter_name); @@ -468,6 +469,7 @@ static int mp_property_chapters(m_option_t *prop, int action, void *arg, static int mp_property_angle(m_option_t *prop, int action, void *arg, MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; int angle = -1; int angles; char *angle_name = NULL; @@ -519,7 +521,7 @@ static int mp_property_angle(m_option_t *prop, int action, void *arg, return M_PROPERTY_NOT_IMPLEMENTED; } angle = demuxer_set_angle(mpctx->demuxer, angle); - set_osd_msg(OSD_MSG_TEXT, 1, osd_duration, + set_osd_msg(OSD_MSG_TEXT, 1, opts->osd_duration, MSGTR_OSDAngle, angle, angles); if (angle_name) free(angle_name); @@ -2285,6 +2287,7 @@ static struct { /// Handle commands that set a property. static int set_property_command(MPContext *mpctx, mp_cmd_t *cmd) { + struct MPOpts *opts = &mpctx->opts; int i, r; m_option_t* prop; const char *pname; @@ -2335,7 +2338,7 @@ static int set_property_command(MPContext *mpctx, mp_cmd_t *cmd) if (val) { set_osd_msg(set_prop_cmd[i].osd_id >= 0 ? set_prop_cmd[i].osd_id : OSD_MSG_PROPERTY + i, - 1, osd_duration, set_prop_cmd[i].osd_msg, val); + 1, opts->osd_duration, set_prop_cmd[i].osd_msg, val); free(val); } } @@ -2377,6 +2380,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) struct MPOpts *opts = &mpctx->opts; sh_audio_t * const sh_audio = mpctx->sh_audio; sh_video_t * const sh_video = mpctx->sh_video; + int osd_duration = opts->osd_duration; if (!set_property_command(mpctx, cmd)) switch (cmd->id) { case MP_CMD_SEEK:{ @@ -2621,18 +2625,18 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) int v = cmd->args[0].v.i; int max = (term_osd && !sh_video) ? MAX_TERM_OSD_LEVEL : MAX_OSD_LEVEL; - if (osd_level > max) - osd_level = max; + if (opts->osd_level > max) + opts->osd_level = max; if (v < 0) - osd_level = (osd_level + 1) % (max + 1); + opts->osd_level = (opts->osd_level + 1) % (max + 1); else - osd_level = v > max ? max : v; + opts->osd_level = v > max ? max : v; /* Show OSD state when disabled, but not when an explicit argument is given to the OSD command, i.e. in slave mode. */ - if (v == -1 && osd_level <= 1) + if (v == -1 && opts->osd_level <= 1) set_osd_msg(OSD_MSG_OSD_STATUS, 0, osd_duration, MSGTR_OSDosd, - osd_level ? MSGTR_OSDenabled : + opts->osd_level ? MSGTR_OSDenabled : MSGTR_OSDdisabled); else rm_osd_msg(OSD_MSG_OSD_STATUS); @@ -3181,7 +3185,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) pointer_y = (int) (dy * (double) sh_video->disp_h); mp_dvdnav_update_mouse_pos(mpctx->stream, pointer_x, pointer_y, &button); - if (osd_level > 1 && button > 0) + if (opts->osd_level > 1 && button > 0) set_osd_msg(OSD_MSG_TEXT, 1, osd_duration, "Selected button number %d", button); } @@ -3208,7 +3212,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) command = mp_dvdnav_bindings[i].cmd; mp_dvdnav_handle_input(mpctx->stream,command,&button); - if (osd_level > 1 && button > 0) + if (opts->osd_level > 1 && button > 0) set_osd_msg(OSD_MSG_TEXT, 1, osd_duration, "Selected button number %d", button); } diff --git a/defaultopts.c b/defaultopts.c index 2f77b16fcd..fb841e64a1 100644 --- a/defaultopts.c +++ b/defaultopts.c @@ -17,6 +17,8 @@ void set_default_mplayer_options(struct MPOpts *opts) .vo_gamma_contrast = 1000, .vo_gamma_saturation = 1000, .vo_gamma_hue = 1000, + .osd_level = 1, + .osd_duration = 1000, .loop_times = -1, .user_correct_pts = -1, .key_fifo_size = 7, diff --git a/mp_osd.h b/mp_osd.h index d4ba8adbaf..6a1b3b94fe 100644 --- a/mp_osd.h +++ b/mp_osd.h @@ -16,7 +16,6 @@ #define MAX_TERM_OSD_LEVEL 1 // These appear in options list -extern int osd_duration; extern int term_osd; struct MPContext; diff --git a/mplayer.c b/mplayer.c index a30fe21899..ec91b2a10e 100644 --- a/mplayer.c +++ b/mplayer.c @@ -201,10 +201,8 @@ int enqueue=0; static int list_properties = 0; -int osd_level=1; // if nonzero, hide current OSD contents when GetTimerMS() reaches this unsigned int osd_visible; -int osd_duration = 1000; int term_osd = 1; static char* term_osd_esc = "\x1b[A\r\x1b[K"; @@ -1403,6 +1401,7 @@ static void clear_osd_msgs(void) { static mp_osd_msg_t* get_osd_msg(struct MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; mp_osd_msg_t *msg,*prev,*last = NULL; static unsigned last_update = 0; unsigned now = GetTimerMS(); @@ -1428,14 +1427,16 @@ static mp_osd_msg_t* get_osd_msg(struct MPContext *mpctx) // Look for the first message in the stack with high enough level. for(msg = osd_msg_stack ; msg ; last = msg, msg = prev) { prev = msg->prev; - if(msg->level > osd_level && hidden_dec_done) continue; + if (msg->level > opts->osd_level && hidden_dec_done) + continue; // The message has a high enough level or it is the first hidden one // in both cases we decrement the timer or kill it. if(!msg->started || msg->time > diff) { if(msg->started) msg->time -= diff; else msg->started = 1; // display it - if(msg->level <= osd_level) return msg; + if (msg->level <= opts->osd_level) + return msg; hidden_dec_done = 1; continue; } @@ -1461,8 +1462,9 @@ static mp_osd_msg_t* get_osd_msg(struct MPContext *mpctx) */ void set_osd_bar(struct MPContext *mpctx, int type,const char* name,double min,double max,double val) { - - if(osd_level < 1) return; + struct MPOpts *opts = &mpctx->opts; + if (opts->osd_level < 1) + return; if(mpctx->sh_video) { osd_visible = (GetTimerMS() + 1000) | 1; @@ -1472,8 +1474,8 @@ void set_osd_bar(struct MPContext *mpctx, int type,const char* name,double min,d return; } - set_osd_msg(OSD_MSG_BAR,1,osd_duration,"%s: %d %%", - name,ROUND(100*(val-min)/(max-min))); + set_osd_msg(OSD_MSG_BAR, 1, opts->osd_duration, "%s: %d %%", + name, ROUND(100*(val-min)/(max-min))); } @@ -1488,6 +1490,7 @@ void set_osd_bar(struct MPContext *mpctx, int type,const char* name,double min,d static void update_osd_msg(struct MPContext *mpctx) { + struct MPOpts *opts = &mpctx->opts; mp_osd_msg_t *msg; struct osd_state *osd = mpctx->osd; char osd_text_timer[128]; @@ -1504,7 +1507,7 @@ static void update_osd_msg(struct MPContext *mpctx) if(mpctx->sh_video) { // fallback on the timer - if(osd_level>=2) { + if (opts->osd_level >= 2) { int len = demuxer_get_time_length(mpctx->demuxer); int percentage = -1; char percentage_text[10]; @@ -1518,7 +1521,7 @@ static void update_osd_msg(struct MPContext *mpctx) else percentage_text[0] = 0; - if (osd_level == 3) + if (opts->osd_level == 3) snprintf(osd_text_timer, 63, "%c %02d:%02d:%02d / %02d:%02d:%02d%s", mpctx->osd_function,pts/3600,(pts/60)%60,pts%60, diff --git a/mplayer.h b/mplayer.h index c59b4ca723..5ca10cec6b 100644 --- a/mplayer.h +++ b/mplayer.h @@ -17,7 +17,6 @@ extern char * video_driver; extern char * audio_driver; extern float audio_delay; -extern int osd_level; extern unsigned int osd_visible; extern char * font_name; diff --git a/options.h b/options.h index 7e1a67c2b9..33dcccd883 100644 --- a/options.h +++ b/options.h @@ -25,6 +25,8 @@ typedef struct MPOpts { int vo_gamma_saturation; int vo_gamma_hue; + int osd_level; + int osd_duration; int loop_times; int correct_pts; int user_correct_pts; -- cgit v1.2.3 From 0f590fce191097887a81d0a6676cb0a943ce54e4 Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Mon, 30 Mar 2009 03:13:17 +0300 Subject: core: Clean up OSD seek info logic Clean up the code and make the behavior more consistent. Before bits of the OSD information were triggered in different places, and various property commands that affect playback position only showed the seek bar while the main seek command also triggered showing the percentage in OSD text. Now only the seek and chapter commands trigger all information and others nothing (which is consistent with most property behavior). --- command.c | 11 ++++++----- mp_core.h | 7 ++++++- mplayer.c | 43 +++++++++++++++++-------------------------- 3 files changed, 29 insertions(+), 32 deletions(-) diff --git a/command.c b/command.c index 5e76158116..8fc8c85e7d 100644 --- a/command.c +++ b/command.c @@ -2227,7 +2227,7 @@ static struct { /// set/adjust or toggle command int toggle; /// progressbar type - int osd_progbar; + int osd_progbar; // -1 is special value for seek indicators /// osd msg id if it must be shared int osd_id; /// osd msg template @@ -2235,7 +2235,7 @@ static struct { } set_prop_cmd[] = { // general { "loop", MP_CMD_LOOP, 0, 0, -1, MSGTR_LoopStatus }, - { "chapter", MP_CMD_SEEK_CHAPTER, 0, 0, -1, NULL }, + { "chapter", MP_CMD_SEEK_CHAPTER, 0, -1, -1, NULL }, { "angle", MP_CMD_SWITCH_ANGLE, 0, 0, -1, NULL }, { "pause", MP_CMD_PAUSE, 0, 0, -1, NULL }, // audio @@ -2317,7 +2317,9 @@ static int set_property_command(MPContext *mpctx, mp_cmd_t *cmd) if (r <= 0) return 1; - if (set_prop_cmd[i].osd_progbar) { + if (set_prop_cmd[i].osd_progbar == -1) + mpctx->add_osd_seek_info = true; + else if (set_prop_cmd[i].osd_progbar) { if (prop->type == CONF_TYPE_INT) { if (mp_property_do(pname, M_PROPERTY_GET, &r, mpctx) > 0) set_osd_bar(mpctx, set_prop_cmd[i].osd_progbar, @@ -2386,8 +2388,7 @@ void run_command(MPContext *mpctx, mp_cmd_t *cmd) case MP_CMD_SEEK:{ float v; int abs; - if (sh_video) - mpctx->osd_show_percentage = sh_video->fps; + mpctx->add_osd_seek_info = true; v = cmd->args[0].v.f; abs = (cmd->nargs > 1) ? cmd->args[1].v.i : 0; if (abs == 2) { /* Absolute seek to a specific timestamp in seconds */ diff --git a/mp_core.h b/mp_core.h index c9a843a78e..13c8f149f0 100644 --- a/mp_core.h +++ b/mp_core.h @@ -54,7 +54,12 @@ typedef struct MPContext { struct mp_fifo *key_fifo; struct input_ctx *input; struct osd_state *osd; - int osd_show_percentage; + + bool add_osd_seek_info; + // if nonzero, hide current OSD contents when GetTimerMS() reaches this + unsigned int osd_show_percentage_until; + unsigned int osd_visible; + int osd_function; const ao_functions_t *audio_out; struct play_tree *playtree; diff --git a/mplayer.c b/mplayer.c index ec91b2a10e..50f1ec4f0a 100644 --- a/mplayer.c +++ b/mplayer.c @@ -201,9 +201,6 @@ int enqueue=0; static int list_properties = 0; -// if nonzero, hide current OSD contents when GetTimerMS() reaches this -unsigned int osd_visible; - int term_osd = 1; static char* term_osd_esc = "\x1b[A\r\x1b[K"; static char* playing_msg = NULL; @@ -211,7 +208,6 @@ static char* playing_msg = NULL; static double seek_to_sec; static off_t seek_to_byte=0; static off_t step_sec=0; -static int loop_seek=0; static m_time_size_t end_at = { .type = END_AT_NONE, .pos = 0 }; @@ -312,7 +308,6 @@ static char* rtc_device; edl_record_ptr edl_records = NULL; ///< EDL entries memory area edl_record_ptr next_edl_record = NULL; ///< only for traversing edl_records -short edl_decision = 0; ///< 1 when an EDL operation has been made. FILE* edl_fd = NULL; ///< fd to write to when in -edlout mode. int use_filedir_conf; @@ -1408,16 +1403,18 @@ static mp_osd_msg_t* get_osd_msg(struct MPContext *mpctx) unsigned diff; char hidden_dec_done = 0; - if (osd_visible) { + if (mpctx->osd_visible) { // 36000000 means max timed visibility is 1 hour into the future, if // the difference is greater assume it's wrapped around from below 0 - if (osd_visible - now > 36000000) { - osd_visible = 0; + if (mpctx->osd_visible - now > 36000000) { + mpctx->osd_visible = 0; vo_osd_progbar_type = -1; // disable vo_osd_changed(OSDTYPE_PROGBAR); mpctx->osd_function = mpctx->paused ? OSD_PAUSE : OSD_PLAY; } } + if (mpctx->osd_show_percentage_until - now > 36000000) + mpctx->osd_show_percentage_until = 0; if(!last_update) last_update = now; diff = now >= last_update ? now - last_update : 0; @@ -1467,7 +1464,7 @@ void set_osd_bar(struct MPContext *mpctx, int type,const char* name,double min,d return; if(mpctx->sh_video) { - osd_visible = (GetTimerMS() + 1000) | 1; + mpctx->osd_visible = (GetTimerMS() + 1000) | 1; vo_osd_progbar_type = type; vo_osd_progbar_value = 256*(val-min)/(max-min); vo_osd_changed(OSDTYPE_PROGBAR); @@ -1494,7 +1491,15 @@ static void update_osd_msg(struct MPContext *mpctx) mp_osd_msg_t *msg; struct osd_state *osd = mpctx->osd; char osd_text_timer[128]; - + + if (mpctx->add_osd_seek_info) { + set_osd_bar(mpctx, 0, "Position", 0, 100, + demuxer_get_percent_pos(mpctx->demuxer)); + if (mpctx->sh_video) + mpctx->osd_show_percentage_until = (GetTimerMS() + 1000) | 1; + mpctx->add_osd_seek_info = false; + } + // Look if we have a msg if((msg = get_osd_msg(mpctx))) { if (strcmp(osd->osd_text, msg->msg)) { @@ -1513,7 +1518,7 @@ static void update_osd_msg(struct MPContext *mpctx) char percentage_text[10]; int pts = demuxer_get_current_time(mpctx->demuxer); - if (mpctx->osd_show_percentage) + if (mpctx->osd_show_percentage_until) percentage = demuxer_get_percent_pos(mpctx->demuxer); if (percentage >= 0) @@ -1533,10 +1538,6 @@ static void update_osd_msg(struct MPContext *mpctx) } else osd_text_timer[0]=0; - // always decrement the percentage timer - if(mpctx->osd_show_percentage) - mpctx->osd_show_percentage--; - if (strcmp(osd->osd_text, osd_text_timer)) { strncpy(osd->osd_text, osd_text_timer, 63); vo_osd_changed(OSDTYPE_OSD); @@ -2446,7 +2447,6 @@ static void edl_update(MPContext *mpctx) mp_msg(MSGT_CPLAYER, MSGL_DBG4, "EDL_SKIP: start [%f], stop " "[%f], length [%f]\n", next_edl_record->start_sec, next_edl_record->stop_sec, next_edl_record->length_sec); - edl_decision = 1; } else if (next_edl_record->action == EDL_MUTE) { mpctx->edl_muted = !mpctx->edl_muted; @@ -3928,22 +3928,13 @@ if (step_sec > 0 && !mpctx->paused) { play_n_frames=play_n_frames_mf; mpctx->stop_play=0; mpctx->abs_seek_pos=SEEK_ABSOLUTE; mpctx->rel_seek_secs=seek_to_sec; - loop_seek = 1; } if(mpctx->rel_seek_secs || mpctx->abs_seek_pos){ - if (seek(mpctx, mpctx->rel_seek_secs, mpctx->abs_seek_pos) >= 0) { - // Set OSD: - if(!loop_seek){ - if( !edl_decision ) - set_osd_bar(mpctx, 0,"Position",0,100,demuxer_get_percent_pos(mpctx->demuxer)); - } - } + seek(mpctx, mpctx->rel_seek_secs, mpctx->abs_seek_pos); mpctx->rel_seek_secs=0; mpctx->abs_seek_pos=0; - loop_seek=0; - edl_decision = 0; } #ifdef CONFIG_GUI -- cgit v1.2.3 From 58497380e5222749892414089d26916095c0485c Mon Sep 17 00:00:00 2001 From: Uoti Urpala Date: Sun, 29 Mar 2009 22:45:06 +0300 Subject: Initial ordered chapters support Add basic support for Matroska ordered chapters. The switching between segments is implemented as a general edit timeline that could also be used for other purposes. Some things still need improvement. In particular the current code does not try to do any proper mapping between audio/video/subtitle streams of different files and there should be options for better control of how MPlayer searches other files for the required content. --- Makefile | 1 + command.c | 2 +- mencoder.c | 2 +- mp_core.h | 18 +++ mpcommon.c | 7 +- mpcommon.h | 3 +- mplayer.c | 330 ++++++++++++++++++++++++++++++++++++++++++++++++++---- osdep/findfiles.c | 97 ++++++++++++++++ osdep/findfiles.h | 2 + 9 files changed, 437 insertions(+), 25 deletions(-) create mode 100644 osdep/findfiles.c create mode 100644 osdep/findfiles.h diff --git a/Makefile b/Makefile index 914df402f8..b1dd5b591d 100644 --- a/Makefile +++ b/Makefile @@ -213,6 +213,7 @@ SRCS_COMMON = asxparser.c \ libmpdemux/yuv4mpeg_ratio.c \ libvo/osd.c \ libvo/sub.c \ + osdep/findfiles.c \ osdep/$(GETCH) \ osdep/$(TIMER) \ stream/open.c \ diff --git a/command.c b/command.c index 8fc8c85e7d..7c0352398f 100644 --- a/command.c +++ b/command.c @@ -1544,7 +1544,7 @@ static int mp_property_sub(m_option_t *prop, int action, void *arg, d_sub->id = opts->sub_id; } #endif - update_subtitles(mpctx->sh_video, d_sub, 1); + update_subtitles(mpctx->sh_video, d_sub, 0, 1); return M_PROPERTY_OK; } diff --git a/mencoder.c b/mencoder.c index 1661722679..6fb7f03600 100644 --- a/mencoder.c +++ b/mencoder.c @@ -1469,7 +1469,7 @@ if(sh_audio && !demuxer2){ } else #endif - update_subtitles(sh_video, d_dvdsub, 0); + update_subtitles(sh_video, d_dvdsub, 0, 0); frame_data = (s_frame_data){ .start = NULL, .in_size = 0, .frame_time = 0., .already_read = 0 }; diff --git a/mp_core.h b/mp_core.h index 13c8f149f0..6b7719b7f3 100644 --- a/mp_core.h +++ b/mp_core.h @@ -47,6 +47,17 @@ typedef enum { EXIT_ERROR } exit_reason_t; +struct content_source { + struct stream *stream; + struct demuxer *demuxer; +}; + +struct timeline_part { + double start; + double source_start; + struct content_source *source; +}; + typedef struct MPContext { struct MPOpts opts; struct m_config *mconfig; @@ -69,6 +80,13 @@ typedef struct MPContext { int play_tree_step; unsigned int initialized_flags; // which subsystems have been initialized + struct content_source *sources; + int num_sources; + struct timeline_part *timeline; + int num_timeline_parts; + int timeline_part; + double video_offset; + struct stream *stream; struct demuxer *demuxer; struct sh_audio *sh_audio; diff --git a/mpcommon.c b/mpcommon.c index 99d70c0ef4..e649f3e49d 100644 --- a/mpcommon.c +++ b/mpcommon.c @@ -69,7 +69,8 @@ if (HAVE_CMOV) } -void update_subtitles(sh_video_t *sh_video, demux_stream_t *d_dvdsub, int reset) +void update_subtitles(sh_video_t *sh_video, demux_stream_t *d_dvdsub, + double video_offset, int reset) { struct MPOpts *opts = sh_video->opts; unsigned char *packet=NULL; @@ -152,10 +153,10 @@ void update_subtitles(sh_video_t *sh_video, demux_stream_t *d_dvdsub, int reset) double endpts; vo_sub = &subs; while (d_dvdsub->first) { - double pts = ds_get_next_pts(d_dvdsub); + double pts = ds_get_next_pts(d_dvdsub) + video_offset; if (pts > curpts) break; - endpts = d_dvdsub->first->endpts; + endpts = d_dvdsub->first->endpts + video_offset; len = ds_get_packet_sub(d_dvdsub, &packet); if (type == 'm') { if (len < 2) continue; diff --git a/mpcommon.h b/mpcommon.h index a0c6fb8ff8..c9bfd92122 100644 --- a/mpcommon.h +++ b/mpcommon.h @@ -10,7 +10,8 @@ extern struct ass_track_s *ass_track; extern subtitle *vo_sub_last; void print_version(const char* name); -void update_subtitles(sh_video_t *sh_video, demux_stream_t *d_dvdsub, int reset); +void update_subtitles(sh_video_t *sh_video, demux_stream_t *d_dvdsub, + double video_offset, int reset); void update_teletext(sh_video_t *sh_video, demuxer_t *demuxer, int reset); int select_audio(demuxer_t* demuxer, int audio_id, char* audio_lang); diff --git a/mplayer.c b/mplayer.c index 50f1ec4f0a..84b9d6ad0a 100644 --- a/mplayer.c +++ b/mplayer.c @@ -76,6 +76,7 @@ #include "osdep/getch2.h" #include "osdep/timer.h" +#include "osdep/findfiles.h" #ifdef CONFIG_GUI #include "gui/interface.h" @@ -565,6 +566,20 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask){ if(mask&INITIALIZED_DEMUXER){ mpctx->initialized_flags&=~INITIALIZED_DEMUXER; current_module="free_demuxer"; + if (mpctx->num_sources) { + mpctx->demuxer = mpctx->sources[0].demuxer; + for (int i = 1; i < mpctx->num_sources; i++) { + free_stream(mpctx->sources[i].stream); + free_demuxer(mpctx->sources[i].demuxer); + } + } + talloc_free(mpctx->sources); + mpctx->sources = NULL; + mpctx->num_sources = 0; + talloc_free(mpctx->timeline); + mpctx->timeline = NULL; + mpctx->num_timeline_parts = 0; + mpctx->video_offset = 0; if(mpctx->demuxer){ mpctx->stream=mpctx->demuxer->stream; free_demuxer(mpctx->demuxer); @@ -1493,8 +1508,13 @@ static void update_osd_msg(struct MPContext *mpctx) char osd_text_timer[128]; if (mpctx->add_osd_seek_info) { - set_osd_bar(mpctx, 0, "Position", 0, 100, - demuxer_get_percent_pos(mpctx->demuxer)); + double percentage; + if (mpctx->timeline && mpctx->sh_video) + percentage = mpctx->sh_video->pts * 100 / + mpctx->timeline[mpctx->num_timeline_parts].start; + else + percentage = demuxer_get_percent_pos(mpctx->demuxer); + set_osd_bar(mpctx, 0, "Position", 0, 100, percentage); if (mpctx->sh_video) mpctx->osd_show_percentage_until = (GetTimerMS() + 1000) | 1; mpctx->add_osd_seek_info = false; @@ -1513,13 +1533,21 @@ static void update_osd_msg(struct MPContext *mpctx) if(mpctx->sh_video) { // fallback on the timer if (opts->osd_level >= 2) { - int len = demuxer_get_time_length(mpctx->demuxer); + int len; + if (mpctx->timeline) + len = mpctx->timeline[mpctx->num_timeline_parts].start; + else + len = demuxer_get_time_length(mpctx->demuxer); int percentage = -1; char percentage_text[10]; int pts = demuxer_get_current_time(mpctx->demuxer); if (mpctx->osd_show_percentage_until) - percentage = demuxer_get_percent_pos(mpctx->demuxer); + if (mpctx->timeline) + percentage = mpctx->sh_video->pts * 100 / + mpctx->timeline[mpctx->num_timeline_parts].start; + else + percentage = demuxer_get_percent_pos(mpctx->demuxer); if (percentage >= 0) snprintf(percentage_text, 9, " (%d%%)", percentage); @@ -1682,7 +1710,7 @@ static double written_audio_pts(struct MPContext *mpctx) // to get the length in original units without speedup or slowdown a_pts -= buffered_output * mpctx->opts.playback_speed / ao_data.bps; - return a_pts; + return a_pts + mpctx->video_offset; } // Return pts value corresponding to currently playing audio. @@ -2220,7 +2248,7 @@ static double update_video_nocorrect_pts(struct MPContext *mpctx, #endif if (decoded_frame) { // These updates are done here for vf_expand OSD/subtitles - update_subtitles(sh_video, mpctx->d_sub, 0); + update_subtitles(sh_video, mpctx->d_sub, mpctx->video_offset, 0); update_teletext(sh_video, mpctx->demuxer, 0); update_osd_msg(mpctx); current_module = "filter video"; @@ -2252,6 +2280,8 @@ static double update_video(struct MPContext *mpctx, int *blit_frame) unsigned char *packet = NULL; bool hit_eof = false; int in_size = ds_get_packet_pts(mpctx->d_video, &packet, &pts); + if (pts != MP_NOPTS_VALUE) + pts += mpctx->video_offset; if (in_size < 0) { // try to extract last frames in case of decoder lag in_size = 0; @@ -2266,7 +2296,7 @@ static double update_video(struct MPContext *mpctx, int *blit_frame) framedrop_type, pts); if (decoded_frame) { // These updates are done here for vf_expand OSD/subtitles - update_subtitles(sh_video, mpctx->d_sub, 0); + update_subtitles(sh_video, mpctx->d_sub, mpctx->video_offset, 0); update_teletext(sh_video, mpctx->demuxer, 0); update_osd_msg(mpctx); current_module = "filter video"; @@ -2459,6 +2489,48 @@ static void edl_update(MPContext *mpctx) } } +static void reinit_decoders(struct MPContext *mpctx) +{ + reinit_video_chain(mpctx); + reinit_audio_chain(mpctx); + mp_property_do("sub", M_PROPERTY_SET, &mpctx->global_sub_pos, mpctx); +} + +static bool timeline_set_part(struct MPContext *mpctx, int i) +{ + struct timeline_part *p = mpctx->timeline + mpctx->timeline_part; + struct timeline_part *n = mpctx->timeline + i; + mpctx->timeline_part = i; + mpctx->video_offset = n->start - n->source_start; + if (n->source == p->source) + return false; + uninit_player(mpctx, INITIALIZED_VCODEC | (mpctx->opts.fixed_vo && mpctx->opts.video_id != -2 ? 0 : INITIALIZED_VO) | INITIALIZED_AO | INITIALIZED_ACODEC); + mpctx->demuxer = n->source->demuxer; + mpctx->d_video = mpctx->demuxer->video; + mpctx->d_audio = mpctx->demuxer->audio; + mpctx->d_sub = mpctx->demuxer->sub; + mpctx->sh_video = mpctx->d_video->sh; + mpctx->sh_audio = mpctx->d_audio->sh; + return true; +} + +// Given pts, switch playback to the corresponding part. +// Return offset within that part. +static double timeline_set_from_time(struct MPContext *mpctx, double pts, + bool *need_reset) +{ + if (pts < 0) + pts = 0; + for (int i = 0; i < mpctx->num_timeline_parts; i++) { + struct timeline_part *p = mpctx->timeline + i; + if (pts < (p+1)->start) { + *need_reset = timeline_set_part(mpctx, i); + return pts - p->start + p->source_start; + } + } + return -1; +} + // style & SEEK_ABSOLUTE == 0 means seek relative to current position, == 1 means absolute // style & SEEK_FACTOR == 0 means amount in seconds, == 2 means fraction of file length @@ -2466,7 +2538,13 @@ static void edl_update(MPContext *mpctx) static int seek(MPContext *mpctx, double amount, int style) { current_module = "seek"; - if (mpctx->demuxer->accurate_seek && mpctx->sh_video + if (mpctx->stop_play == AT_END_OF_FILE) + mpctx->stop_play = KEEP_PLAYING; + if (mpctx->timeline && style & SEEK_FACTOR) { + amount *= mpctx->timeline[mpctx->num_timeline_parts].start; + style &= ~SEEK_FACTOR; + } + if ((mpctx->demuxer->accurate_seek || mpctx->timeline) && mpctx->sh_video && !(style & (SEEK_ABSOLUTE | SEEK_FACTOR))) { style |= SEEK_ABSOLUTE; if (amount > 0) @@ -2476,7 +2554,27 @@ static int seek(MPContext *mpctx, double amount, int style) amount += mpctx->sh_video->pts; } - if (demux_seek(mpctx->demuxer, amount, audio_delay, style) == 0) + /* At least the liba52 decoder wants to read from the input stream + * during initialization, so reinit must be done after the demux_seek() + * call that clears possible stream EOF. */ + bool need_reset = false; + if (mpctx->timeline) { + amount = timeline_set_from_time(mpctx, amount, &need_reset); + if (amount == -1) { + mpctx->stop_play = AT_END_OF_FILE; + // Clear audio from current position + if (mpctx->sh_audio) { + mpctx->audio_out->reset(); + mpctx->sh_audio->a_buffer_len = 0; + mpctx->sh_audio->a_out_buffer_len = 0; + } + return -1; + } + } + int seekresult = demux_seek(mpctx->demuxer, amount, audio_delay, style); + if (need_reset) + reinit_decoders(mpctx); + if (seekresult == 0) return -1; if (mpctx->sh_video) { @@ -2493,8 +2591,8 @@ static int seek(MPContext *mpctx, double amount, int style) // Not all demuxers set d_video->pts during seek, so this value // (which is used by at least vobsub and ed