mirror of
https://github.com/mpv-player/mpv
synced 2025-01-03 13:32:16 +00:00
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:
parent
e24e0ccd68
commit
70df1608d6
@ -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 ---
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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--;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user