summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--demux/demux.c82
-rw-r--r--demux/demux.h6
-rw-r--r--demux/demux_lavf.c2
-rw-r--r--demux/demux_mkv.c1
-rw-r--r--player/loadfile.c5
5 files changed, 92 insertions, 4 deletions
diff --git a/demux/demux.c b/demux/demux.c
index e498895aaf..50fc32cb96 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -121,6 +121,9 @@ struct demux_internal {
int seek_flags; // flags for next seek (if seeking==true)
double seek_pts;
+ bool refresh_seeks_enabled;
+ bool start_refresh_seek;
+
// Cached state.
bool force_cache_update;
double time_length;
@@ -141,6 +144,7 @@ struct demux_stream {
bool selected; // user wants packets from this stream
bool active; // try to keep at least 1 packet queued
bool eof; // end of demuxed stream? (true if all buffer empty)
+ bool refreshing;
size_t packs; // number of packets in buffer
size_t bytes; // total bytes of packets in buffer
double base_ts; // timestamp of the last packet returned to decoder
@@ -148,6 +152,7 @@ struct demux_stream {
double last_br_ts; // timestamp of last packet bitrate was calculated
size_t last_br_bytes; // summed packet sizes since last bitrate calculation
double bitrate;
+ int64_t last_pos;
struct demux_packet *head;
struct demux_packet *tail;
};
@@ -179,6 +184,8 @@ static void ds_flush(struct demux_stream *ds)
ds->bitrate = -1;
ds->eof = false;
ds->active = false;
+ ds->refreshing = false;
+ ds->last_pos = -1;
}
struct sh_stream *new_sh_stream(demuxer_t *demuxer, enum stream_type type)
@@ -297,7 +304,17 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
}
struct demux_internal *in = ds->in;
pthread_mutex_lock(&in->lock);
- if (!ds->selected || in->seeking) {
+
+ bool drop = false;
+ if (ds->refreshing) {
+ // Resume reading once the old position was reached (i.e. we start
+ // returning packets where we left off before the refresh).
+ drop = true;
+ if (dp->pos == ds->last_pos)
+ ds->refreshing = false;
+ }
+
+ if (!ds->selected || in->seeking || drop) {
pthread_mutex_unlock(&in->lock);
talloc_free(dp);
return 0;
@@ -306,6 +323,7 @@ int demux_add_packet(struct sh_stream *stream, demux_packet_t *dp)
dp->stream = stream->index;
dp->next = NULL;
+ ds->last_pos = dp->pos;
ds->packs++;
ds->bytes += dp->len;
if (ds->tail) {
@@ -438,6 +456,43 @@ static void ds_get_packets(struct demux_stream *ds)
}
}
+// An obscure mechanism to get stream switching to be executed faster.
+// On a switch, it seeks back, and then grabs all packets that were
+// "missing" from the packet queue of the newly selected stream.
+static void start_refreshing(struct demux_internal *in)
+{
+ struct demuxer *demux = in->d_thread;
+
+ in->start_refresh_seek = false;
+
+ double start_ts = MP_NOPTS_VALUE;
+ for (int n = 0; n < demux->num_streams; n++) {
+ struct demux_stream *ds = demux->streams[n]->ds;
+ if (ds->type == STREAM_VIDEO || ds->type == STREAM_AUDIO)
+ start_ts = MP_PTS_MIN(start_ts, ds->base_ts);
+ }
+
+ if (start_ts == MP_NOPTS_VALUE || !demux->desc->seek || !demux->seekable ||
+ demux->partially_seekable || !demux->allow_refresh_seeks)
+ return;
+
+ for (int n = 0; n < demux->num_streams; n++) {
+ struct demux_stream *ds = demux->streams[n]->ds;
+ // Streams which didn't read any packets yet can return all packets,
+ // or they'd be stuck forever; affects newly selected streams too.
+ if (ds->last_pos != -1)
+ ds->refreshing = true;
+ }
+
+ pthread_mutex_unlock(&in->lock);
+
+ // Seek back to player's current position, with a small offset added.
+ in->d_thread->desc->seek(in->d_thread, start_ts - 1.0,
+ SEEK_ABSOLUTE | SEEK_BACKWARD | SEEK_SUBPREROLL);
+
+ pthread_mutex_lock(&in->lock);
+}
+
static void execute_trackswitch(struct demux_internal *in)
{
in->tracks_switched = false;
@@ -448,6 +503,9 @@ static void execute_trackswitch(struct demux_internal *in)
in->d_thread->desc->control(in->d_thread, DEMUXER_CTRL_SWITCHED_TRACKS, 0);
pthread_mutex_lock(&in->lock);
+
+ if (in->start_refresh_seek)
+ start_refreshing(in);
}
static void execute_seek(struct demux_internal *in)
@@ -762,6 +820,7 @@ static void demux_copy(struct demuxer *dst, struct demuxer *src)
dst->filetype = src->filetype;
dst->ts_resets_possible = src->ts_resets_possible;
dst->rel_seeks = src->rel_seeks;
+ dst->allow_refresh_seeks = src->allow_refresh_seeks;
dst->start_time = src->start_time;
}
if (src->events & DEMUX_EVENT_STREAMS) {
@@ -1032,6 +1091,18 @@ int demux_seek(demuxer_t *demuxer, double rel_seek_secs, int flags)
return 1;
}
+// Enable doing a "refresh seek" on the next stream switch.
+// Note that this by design does not disable ongoing refresh seeks, and
+// does not affect previous stream switch commands (even if they were
+// asynchronous).
+void demux_set_enable_refresh_seeks(struct demuxer *demuxer, bool enabled)
+{
+ struct demux_internal *in = demuxer->in;
+ pthread_mutex_lock(&in->lock);
+ in->refresh_seeks_enabled = enabled;
+ pthread_mutex_unlock(&in->lock);
+}
+
struct sh_stream *demuxer_stream_by_demuxer_id(struct demuxer *d,
enum stream_type t, int id)
{
@@ -1058,16 +1129,19 @@ void demuxer_switch_track(struct demuxer *demuxer, enum stream_type type,
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
bool selected)
{
- // don't flush buffers if stream is already selected / unselected
- pthread_mutex_lock(&demuxer->in->lock);
+ struct demux_internal *in = demuxer->in;
+ pthread_mutex_lock(&in->lock);
bool update = false;
+ // don't flush buffers if stream is already selected / unselected
if (stream->ds->selected != selected) {
stream->ds->selected = selected;
stream->ds->active = false;
ds_flush(stream->ds);
+ if (selected && in->refresh_seeks_enabled && in->threading)
+ in->start_refresh_seek = true;
update = true;
}
- pthread_mutex_unlock(&demuxer->in->lock);
+ pthread_mutex_unlock(&in->lock);
if (update)
demux_control(demuxer, DEMUXER_CTRL_SWITCHED_TRACKS, NULL);
}
diff --git a/demux/demux.h b/demux/demux.h
index e18c013a5b..c580a22b18 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -193,6 +193,11 @@ typedef struct demuxer {
// Send relative seek requests, instead of SEEK_ABSOLUTE or SEEK_FACTOR.
// This is only done if the user explicitly uses a relative seek.
bool rel_seeks;
+ // Enable fast track switching hacks. This requires from the demuxer:
+ // - seeking is somewhat reliable; packet contents must not change
+ // - packet position (demux_packet.pos) is set, not negative, and unique
+ // - seeking leaves packet positions invariant
+ bool allow_refresh_seeks;
// Bitmask of DEMUX_EVENT_*
int events;
@@ -262,6 +267,7 @@ void demux_set_wakeup_cb(struct demuxer *demuxer, void (*cb)(void *ctx), void *c
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);
int demux_control(struct demuxer *demuxer, int cmd, void *arg);
diff --git a/demux/demux_lavf.c b/demux/demux_lavf.c
index 4d84bd20d4..83058d5105 100644
--- a/demux/demux_lavf.c
+++ b/demux/demux_lavf.c
@@ -798,6 +798,8 @@ static int demux_open_lavf(demuxer_t *demuxer, enum demux_check check)
demuxer->start_time = priv->avfc->start_time == AV_NOPTS_VALUE ?
0 : (double)priv->avfc->start_time / AV_TIME_BASE;
+ demuxer->allow_refresh_seeks = matches_avinputformat_name(priv, "mp4");
+
return 0;
}
diff --git a/demux/demux_mkv.c b/demux/demux_mkv.c
index 892045a284..169850ac52 100644
--- a/demux/demux_mkv.c
+++ b/demux/demux_mkv.c
@@ -1837,6 +1837,7 @@ static int demux_mkv_open(demuxer_t *demuxer, enum demux_check check)
process_tags(demuxer);
display_create_tracks(demuxer);
add_coverart(demuxer);
+ demuxer->allow_refresh_seeks = true;
if (demuxer->opts->mkv_probe_duration)
probe_last_timestamp(demuxer);
diff --git a/player/loadfile.c b/player/loadfile.c
index d9d6a0036c..472304d5f0 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -548,6 +548,9 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
if (current)
current->selected = false;
+ if (track && track->demuxer == mpctx->demuxer)
+ demux_set_enable_refresh_seeks(mpctx->demuxer, true);
+
reselect_demux_streams(mpctx);
mpctx->current_track[order][type] = track;
@@ -557,6 +560,8 @@ void mp_switch_track_n(struct MPContext *mpctx, int order, enum stream_type type
reselect_demux_streams(mpctx);
+ demux_set_enable_refresh_seeks(mpctx->demuxer, false);
+
if (type == STREAM_VIDEO && order == 0) {
reinit_video_chain(mpctx);
} else if (type == STREAM_AUDIO && order == 0) {