1
0
mirror of https://github.com/mpv-player/mpv synced 2025-03-23 03:37:27 +00:00

demux: add shitty start of stream detection

The demuxer cache benefits slightly from knowing where the current file
or stream begins. For example, seeking "left most" when the start is
cached would not trigger a low level seek (which would be followed by
messy range joining when it notices that the newly demuxed packets
overlap with an existing range).

Unfortunately, since multimedia is so crazy (or actually FFmpeg in its
quite imperfect attempt to be able to demux anything), it's hard to tell
where a file starts. There is no feedback whether a specific seek went
to the start of the file. Packets are not tagged with a flag indicating
they were demuxed from the start position. There is no index available
that could be used to cross-check this (even if the file contains a full
and "perfect" index, like mp4). You could go by the timestamps, but who
says streams start at 0? Streams can start somewhere at an extremely
high timestamps (transport streams like to do that), or they could start
at negative times (e.g. files with audio pre-padding will do that), and
maybe some file formats simply allow negative timestamps and could start
at any negative time. Even if the affected file formats don't allow it
in theory, they may in practice. In addition, FFmpeg exports a
start_time field, which may or may not be useful. (mpv's internal mkv
demuxer also exports such a field, but doesn't bother to set it for
efficiency and robustness reasons.)

Anyway, this is all a huge load of crap, so I decided that if the user
performs a seek command to time 0 or earlier, we consider the first
packet demuxed from each stream to be at the start of the file. In
addition, just trust the start_time field. This is the "shitty" part of
this commit.

One common case of negative timestamps is audio pre-padding. Demuxers
normally behave sanely, and will treat 0 as the start of the file, and
the first packets demuxed will have negative timestamps (since they
contain data to discard), which doesn't break our assumptions in this
commit. (Although, unfortunately, do break some other demuxer cache
assumptions, and the first cached range will be shown as starting at a
negative time.)

Implementation-wise, this is quite simple. Just split the existing
initial_state flag into two, since we want to deal with two separate
aspects. In addition, this avoids the refresh seek on track switching
when it happens right after a seek, instead of only after opening the
demuxer.
This commit is contained in:
wm4 2019-05-17 23:41:03 +02:00
parent ccb1c0bec4
commit ca2b10d18d

View File

@ -174,10 +174,13 @@ struct demux_internal {
// Do this to allow the decoder thread to select streams before starting.
bool reading;
// Set if we know that we are at the start of the file. This is used to
// Set if we just performed a seek, without reading packets yet. Used to
// avoid a redundant initial seek after enabling streams. We could just
// allow it, but to avoid buggy seeking affecting normal playback, we don't.
bool initial_state;
bool after_seek;
// Set in addition to after_seek if we think we seeked to the start of the
// file (or if the demuxer was just opened).
bool after_seek_to_start;
bool tracks_switched; // thread needs to inform demuxer of this
@ -1418,7 +1421,8 @@ static void add_packet_locked(struct sh_stream *stream, demux_packet_t *dp)
}
struct demux_internal *in = ds->in;
in->initial_state = false;
in->after_seek = false;
in->after_seek_to_start = false;
double ts = dp->dts == MP_NOPTS_VALUE ? dp->pts : dp->dts;
if (dp->segmented)
@ -1615,7 +1619,7 @@ static bool read_packet(struct demux_internal *in)
if (!read_more && !prefetch_more && !refresh_more)
return false;
if (in->initial_state) {
if (in->after_seek_to_start) {
for (int n = 0; n < in->num_streams; n++)
in->current_range->streams[n]->is_bof = in->streams[n]->ds->selected;
}
@ -1623,7 +1627,8 @@ static bool read_packet(struct demux_internal *in)
// Actually read a packet. Drop the lock while doing so, because waiting
// for disk or network I/O can take time.
in->idle = false;
in->initial_state = false;
in->after_seek = false;
in->after_seek_to_start = false;
pthread_mutex_unlock(&in->lock);
struct demuxer *demux = in->d_thread;
@ -1766,7 +1771,10 @@ static void execute_seek(struct demux_internal *in)
in->seeking_in_progress = pts;
in->demux_ts = MP_NOPTS_VALUE;
in->low_level_seeks += 1;
in->initial_state = false;
in->after_seek = true;
in->after_seek_to_start =
!(flags & (SEEK_FORWARD | SEEK_FACTOR)) &&
pts <= in->d_thread->start_time;
pthread_mutex_unlock(&in->lock);
@ -2351,7 +2359,8 @@ static struct demuxer *open_given_type(struct mpv_global *global,
.min_secs = opts->min_secs,
.max_bytes = opts->max_bytes,
.max_bytes_bw = opts->max_bytes_bw,
.initial_state = true,
.after_seek = true, // (assumed identical to initial demuxer state)
.after_seek_to_start = true,
.highest_av_pts = MP_NOPTS_VALUE,
.seeking_in_progress = MP_NOPTS_VALUE,
.demux_ts = MP_NOPTS_VALUE,
@ -2951,7 +2960,7 @@ void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
ds->selected = selected;
update_stream_selection_state(in, ds);
in->tracks_switched = true;
if (ds->selected && !in->initial_state)
if (ds->selected && !in->after_seek)
initiate_refresh_seek(in, ds, MP_ADD_PTS(ref_pts, -in->ts_offset));
if (in->threading) {
pthread_cond_signal(&in->wakeup);