player: handle rebasing start time differently

Most of this is explained in the DOCS additions.

This gives us slightly more sanity, because there is less interaction
between the various parts. The goal is getting rid of the video_offset
entirely.

The simplification extends to the user API. In particular, we don't need
to fix missing parts in the API, such as the lack for a seek command
that seeks relatively to the start time. All these things are now
transparent.

(If someone really wants to know the real timestamps/start time, new
properties would have to be added.)
This commit is contained in:
wm4 2015-11-16 22:47:17 +01:00
parent e24e0ccd68
commit 70df1608d6
13 changed files with 79 additions and 44 deletions

View File

@ -21,6 +21,12 @@ Interface changes
--- mpv 0.14.0 ---
- add "vsync-ratio" property
- add --rebase-start-time option
This is a breaking change to start time handling. Instead of making start
time handling an aspect of different options and properties (like
"time-pos" vs. "playback-time"), make it dependent on the new option. For
compatibility, the "time-start" property now always returns 0, so code
which attempted to handle rebasing manually will not break.
--- mpv 0.13.0 ---
- remove VO opengl-cb frame queue suboptions (no replacement)
--- mpv 0.12.0 ---

View File

@ -943,8 +943,9 @@ Property list
Position in current file in seconds.
``time-start``
Return the start time of the file. (Usually 0, but some kind of files,
especially transport streams, can have a different start time.)
Deprecated. Always returns 0. Before mpv 0.14, this used to return the start
time of the file (could affect e.g. transport streams). See
``--rebase-start-time`` option.
``time-remaining``
Remaining length of the file in seconds. Note that the file duration is not
@ -954,9 +955,11 @@ Property list
``time-remaining`` scaled by the current ``speed``.
``playback-time`` (RW)
The playback time, which is the time relative to playback start. (This can
be different from the ``time-pos`` property if the file does not start at
position ``0``, in which case ``time-pos`` is the source timestamp.)
Position in current file in seconds. Unlike ``time-pos``, the time is
clamped to the range of the file. (Inaccurate file durations etc. could
make it go out of range. Also helpful when the user attempts to seek
outside of the file, as the seek target time is considered the current
position during seeking.)
``chapter`` (RW)
Current chapter number. The number of the first chapter is 0.

View File

@ -69,7 +69,8 @@ Playback Control
The general format for absolute times is ``[[hh:]mm:]ss[.ms]``. If the time
is given with a prefix of ``+`` or ``-``, the seek is relative from the start
or end of the file.
or end of the file. (Since mpv 0.14, the start of the file is always
considered 0.)
``pp%`` seeks to percent position pp (0-100).
@ -101,6 +102,14 @@ Playback Control
Stop after a given time relative to the start time.
See ``--start`` for valid option values and examples.
``--rebase-start-time=<yes|no>``
Whether to move the file start time to ``00:00:00`` (default: yes). This
is less awkward for files which start at a random timestamp, such as
transport streams. On the other hand, if there are timestamp resets, the
resulting behavior can be rather weird. For this reason, and in case you
are actually interested in the real timestamps, this behavior can be
disabled with ``no``.
``--speed=<0.01-100>``
Slow down or speed up playback by the factor given as parameter.

View File

@ -124,6 +124,8 @@ struct demux_internal {
bool refresh_seeks_enabled;
bool start_refresh_seek;
double ts_offset; // timestamp offset to apply everything
// Cached state.
bool force_cache_update;
double time_length;
@ -164,6 +166,8 @@ struct demux_stream {
#define MP_PTS_MIN(a, b) MPMIN(PTS_OR_DEF(a, b), PTS_OR_DEF(b, a))
#define MP_PTS_MAX(a, b) MPMAX(PTS_OR_DEF(a, b), PTS_OR_DEF(b, a))
#define MP_ADD_PTS(a, b) ((a) == MP_NOPTS_VALUE ? (a) : ((a) + (b)))
static void demuxer_sort_chapters(demuxer_t *demuxer);
static void *demux_thread(void *pctx);
static void update_cache(struct demux_internal *in);
@ -189,6 +193,14 @@ static void ds_flush(struct demux_stream *ds)
ds->last_pos = -1;
}
void demux_set_ts_offset(struct demuxer *demuxer, double offset)
{
struct demux_internal *in = demuxer->in;
pthread_mutex_lock(&in->lock);
in->ts_offset = offset;
pthread_mutex_unlock(&in->lock);
}
struct sh_stream *new_sh_stream(demuxer_t *demuxer, enum stream_type type)
{
assert(demuxer == demuxer->in->d_thread);
@ -603,6 +615,9 @@ static struct demux_packet *dequeue_packet(struct demux_stream *ds)
if (pkt->pos >= ds->in->d_user->filepos)
ds->in->d_user->filepos = pkt->pos;
pkt->pts = MP_ADD_PTS(pkt->pts, ds->in->ts_offset);
pkt->dts = MP_ADD_PTS(pkt->dts, ds->in->ts_offset);
return pkt;
}
@ -664,7 +679,7 @@ double demux_get_next_pts(struct sh_stream *sh)
pthread_mutex_lock(&sh->ds->in->lock);
ds_get_packets(sh->ds);
if (sh->ds->head)
res = sh->ds->head->pts;
res = MP_ADD_PTS(sh->ds->head->pts, sh->ds->in->ts_offset);
pthread_mutex_unlock(&sh->ds->in->lock);
}
return res;
@ -1148,6 +1163,8 @@ int demux_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
in->seeking = true;
in->seek_flags = flags;
in->seek_pts = rel_seek_secs;
if (flags & SEEK_ABSOLUTE)
in->seek_pts = MP_ADD_PTS(in->seek_pts, -in->ts_offset);
if (!in->threading)
execute_seek(in);
@ -1419,6 +1436,8 @@ static int cached_demux_control(struct demux_internal *in, int cmd, void *arg)
r->ts_duration = MPMAX(0, r->ts_range[1] - r->ts_range[0]);
if (!num_packets || in->seeking)
r->ts_duration = 0;
r->ts_range[0] = MP_ADD_PTS(r->ts_range[0], in->ts_offset);
r->ts_range[1] = MP_ADD_PTS(r->ts_range[1], in->ts_offset);
return DEMUXER_CTRL_OK;
}
}

View File

@ -271,6 +271,7 @@ bool demux_cancel_test(struct demuxer *demuxer);
void demux_flush(struct demuxer *demuxer);
int demux_seek(struct demuxer *demuxer, double rel_seek_secs, int flags);
void demux_set_enable_refresh_seeks(struct demuxer *demuxer, bool enabled);
void demux_set_ts_offset(struct demuxer *demuxer, double offset);
int demux_control(struct demuxer *demuxer, int cmd, void *arg);

View File

@ -198,6 +198,8 @@ const m_option_t mp_opts[] = {
OPT_REL_TIME("end", play_end, 0),
OPT_REL_TIME("length", play_length, 0),
OPT_FLAG("rebase-start-time", rebase_start_time, 0),
OPT_TIME("ab-loop-a", ab_loop[0], 0, .min = MP_NOPTS_VALUE),
OPT_TIME("ab-loop-b", ab_loop[1], 0, .min = MP_NOPTS_VALUE),
@ -763,6 +765,7 @@ const struct MPOpts mp_default_opts = {
.consolecontrols = 1,
.playlist_pos = -1,
.play_frames = -1,
.rebase_start_time = 1,
.keep_open = 0,
.stream_id = { { [STREAM_AUDIO] = -1,
[STREAM_VIDEO] = -1,

View File

@ -173,6 +173,7 @@ typedef struct MPOpts {
struct m_rel_time play_start;
struct m_rel_time play_end;
struct m_rel_time play_length;
int rebase_start_time;
int play_frames;
double ab_loop[2];
double step_sec;

View File

@ -658,11 +658,8 @@ static int mp_property_percent_pos(void *ctx, struct m_property *prop,
static int mp_property_time_start(void *ctx, struct m_property *prop,
int action, void *arg)
{
MPContext *mpctx = ctx;
double start = get_start_time(mpctx);
if (start < 0)
return M_PROPERTY_UNAVAILABLE;
return property_time(action, arg, start);
// minor backwards-compat.
return property_time(action, arg, 0);
}
/// Current position in seconds (RW)
@ -723,8 +720,7 @@ static int mp_property_playback_time(void *ctx, struct m_property *prop,
return M_PROPERTY_UNAVAILABLE;
if (action == M_PROPERTY_SET) {
double target = get_start_time(mpctx) + *(double *)arg;
queue_seek(mpctx, MPSEEK_ABSOLUTE, target, MPSEEK_DEFAULT, true);
queue_seek(mpctx, MPSEEK_ABSOLUTE, *(double *)arg, MPSEEK_DEFAULT, true);
return M_PROPERTY_OK;
}
return property_time(action, arg, get_playback_time(mpctx));

View File

@ -454,7 +454,6 @@ void mp_print_version(struct mp_log *log, int always);
void wakeup_playloop(void *ctx);
// misc.c
double get_start_time(struct MPContext *mpctx);
double get_main_demux_pts(struct MPContext *mpctx);
double get_track_video_offset(struct MPContext *mpctx, struct track *track);
double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t);

View File

@ -714,6 +714,9 @@ struct track *mp_add_external_file(struct MPContext *mpctx, char *filename,
if (!demuxer)
goto err_out;
if (filter != STREAM_SUB && opts->rebase_start_time)
demux_set_ts_offset(demuxer, -demuxer->start_time);
struct track *first = NULL;
for (int n = 0; n < demuxer->num_streams; n++) {
struct sh_stream *sh = demuxer->streams[n];
@ -910,6 +913,10 @@ static void load_chapters(struct MPContext *mpctx)
talloc_free(mpctx->chapters);
mpctx->num_chapters = src->num_chapters;
mpctx->chapters = demux_copy_chapter_data(src->chapters, src->num_chapters);
if (mpctx->opts->rebase_start_time) {
for (int n = 0; n < mpctx->num_chapters; n++)
mpctx->chapters[n].pts -= src->start_time;
}
}
if (free_src)
free_demuxer_and_stream(src);
@ -954,8 +961,11 @@ static void open_demux_thread(void *pctx)
args->err = MPV_ERROR_LOADING_FAILED;
}
}
if (args->demux)
if (args->demux) {
args->tl = timeline_load(global, args->log, args->demux);
if (global->opts->rebase_start_time)
demux_set_ts_offset(args->demux, -args->demux->start_time);
}
}
static void open_demux_reentrant(struct MPContext *mpctx)

View File

@ -56,21 +56,20 @@ double get_relative_time(struct MPContext *mpctx)
double rel_time_to_abs(struct MPContext *mpctx, struct m_rel_time t)
{
double length = get_time_length(mpctx);
double start = get_start_time(mpctx);
switch (t.type) {
case REL_TIME_ABSOLUTE:
return t.pos;
case REL_TIME_RELATIVE:
if (t.pos >= 0) {
return start + t.pos;
return t.pos;
} else {
if (length >= 0)
return MPMAX(start + length + t.pos, 0.0);
return MPMAX(length + t.pos, 0.0);
}
break;
case REL_TIME_PERCENT:
if (length >= 0)
return start + length * (t.pos / 100.0);
return length * (t.pos / 100.0);
break;
case REL_TIME_CHAPTER:
if (chapter_start_time(mpctx, t.pos) != MP_NOPTS_VALUE)
@ -87,12 +86,11 @@ double get_play_end_pts(struct MPContext *mpctx)
if (opts->play_end.type) {
end = rel_time_to_abs(mpctx, opts->play_end);
} else if (opts->play_length.type) {
double startpts = get_start_time(mpctx);
double start = rel_time_to_abs(mpctx, opts->play_start);
if (start == MP_NOPTS_VALUE)
start = startpts;
start = 0;
double length = rel_time_to_abs(mpctx, opts->play_length);
if (start != MP_NOPTS_VALUE && length != MP_NOPTS_VALUE)
if (length != MP_NOPTS_VALUE)
end = start + length;
}
if (opts->chapterrange[1] > 0) {
@ -117,18 +115,11 @@ double get_main_demux_pts(struct MPContext *mpctx)
return main_new_pos;
}
double get_start_time(struct MPContext *mpctx)
{
return mpctx->demuxer ? mpctx->demuxer->start_time : 0;
}
// Get the offset from the given track to the video.
double get_track_video_offset(struct MPContext *mpctx, struct track *track)
{
if (track && track->under_timeline)
return mpctx->video_offset;
if (track && track->is_external)
return get_start_time(mpctx);
return 0;
}

View File

@ -363,8 +363,7 @@ void set_osd_bar_chapters(struct MPContext *mpctx, int type)
} else {
int num = get_chapter_count(mpctx);
for (int n = 0; n < num; n++) {
double time = chapter_start_time(mpctx, n) -
get_start_time(mpctx);
double time = chapter_start_time(mpctx, n);
if (time >= 0) {
float pos = time / len;
MP_TARRAY_APPEND(mpctx, mpctx->osd_progbar.stops,

View File

@ -205,7 +205,7 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek,
case MPSEEK_FACTOR: ;
double len = get_time_length(mpctx);
if (len >= 0)
target_time = seek.amount * len + get_start_time(mpctx);
target_time = seek.amount * len;
break;
}
@ -405,15 +405,14 @@ double get_playback_time(struct MPContext *mpctx)
double cur = get_current_time(mpctx);
if (cur == MP_NOPTS_VALUE)
return cur;
double start = get_start_time(mpctx);
// During seeking, the time corresponds to the last seek time - apply some
// cosmetics to it.
if (mpctx->playback_pts == MP_NOPTS_VALUE) {
double length = get_time_length(mpctx);
if (length >= 0)
cur = MPCLAMP(cur, start, start + length);
cur = MPCLAMP(cur, 0, length);
}
return cur >= start ? cur - start : cur;
return cur;
}
// Return playback position in 0.0-1.0 ratio, or -1 if unknown.
@ -423,15 +422,15 @@ double get_current_pos_ratio(struct MPContext *mpctx, bool use_range)
if (!demuxer)
return -1;
double ans = -1;
double start = get_start_time(mpctx);
double start = 0;
double len = get_time_length(mpctx);
if (use_range) {
double startpos = rel_time_to_abs(mpctx, mpctx->opts->play_start);
double endpos = get_play_end_pts(mpctx);
if (endpos == MP_NOPTS_VALUE || endpos > start + MPMAX(0, len))
endpos = start + MPMAX(0, len);
if (startpos == MP_NOPTS_VALUE || startpos < start)
startpos = start;
if (endpos == MP_NOPTS_VALUE || endpos > MPMAX(0, len))
endpos = MPMAX(0, len);
if (startpos == MP_NOPTS_VALUE || startpos < 0)
startpos = 0;
if (endpos < startpos)
endpos = startpos;
start = startpos;
@ -506,7 +505,7 @@ char *chapter_name(struct MPContext *mpctx, int chapter)
double chapter_start_time(struct MPContext *mpctx, int chapter)
{
if (chapter == -1)
return get_start_time(mpctx);
return 0;
if (chapter >= 0 && chapter < mpctx->num_chapters)
return mpctx->chapters[chapter].pts;
return MP_NOPTS_VALUE;
@ -785,8 +784,7 @@ static void handle_loop_file(struct MPContext *mpctx)
if (opts->loop_file && mpctx->stop_play == AT_END_OF_FILE) {
mpctx->stop_play = KEEP_PLAYING;
set_osd_function(mpctx, OSD_FFW);
queue_seek(mpctx, MPSEEK_ABSOLUTE, get_start_time(mpctx),
MPSEEK_DEFAULT, true);
queue_seek(mpctx, MPSEEK_ABSOLUTE, 0, MPSEEK_DEFAULT, true);
if (opts->loop_file > 0)
opts->loop_file--;
}