summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--demux/demux.c31
-rw-r--r--demux/demux.h2
-rw-r--r--player/audio.c22
-rw-r--r--player/core.h6
-rw-r--r--player/loadfile.c5
-rw-r--r--player/playloop.c47
6 files changed, 78 insertions, 35 deletions
diff --git a/demux/demux.c b/demux/demux.c
index 9646bf4f64..4ccb03a17d 100644
--- a/demux/demux.c
+++ b/demux/demux.c
@@ -197,6 +197,8 @@ struct demux_internal {
double highest_av_pts; // highest non-subtitle PTS seen - for duration
+ bool blocked;
+
// Cached state.
bool force_cache_update;
struct mp_tags *stream_metadata;
@@ -605,6 +607,7 @@ static void update_stream_selection_state(struct demux_internal *in,
// other streams too, because they depend on other stream's selections.
bool any_av_streams = false;
+ bool any_streams = false;
for (int n = 0; n < in->num_streams; n++) {
struct demux_stream *s = in->streams[n]->ds;
@@ -612,6 +615,7 @@ static void update_stream_selection_state(struct demux_internal *in,
s->eager = s->selected && !s->sh->attached_picture;
if (s->eager)
any_av_streams |= s->type != STREAM_SUB;
+ any_streams |= s->selected;
}
// Subtitles are only eagerly read if there are no other eagerly read
@@ -625,6 +629,9 @@ static void update_stream_selection_state(struct demux_internal *in,
}
}
+ if (!any_streams)
+ in->blocked = false;
+
// Make sure any stream reselection or addition is reflected in the seek
// ranges, and also get rid of data that is not needed anymore (or
// rather, which can't be kept consistent). This has to happen after we've
@@ -1279,7 +1286,7 @@ static bool read_packet(struct demux_internal *in)
in->eof = false;
in->idle = true;
- if (!in->reading)
+ if (!in->reading || in->blocked)
return false;
// Check if we need to read a new packet. We do this if all queues are below
@@ -1552,7 +1559,7 @@ static struct demux_packet *dequeue_packet(struct demux_stream *ds)
pkt->stream = ds->sh->index;
return pkt;
}
- if (!ds->reader_head)
+ if (!ds->reader_head || ds->in->blocked)
return NULL;
struct demux_packet *pkt = ds->reader_head;
ds->reader_head = pkt->next;
@@ -1622,7 +1629,7 @@ struct demux_packet *demux_read_packet(struct sh_stream *sh)
const char *t = stream_type_name(ds->type);
MP_DBG(in, "reading packet for %s\n", t);
in->eof = false; // force retry
- while (ds->selected && !ds->reader_head) {
+ while (ds->selected && !ds->reader_head && !in->blocked) {
in->reading = true;
// Note: the following code marks EOF if it can't continue
if (in->threading) {
@@ -1673,6 +1680,8 @@ int demux_read_packet_async(struct sh_stream *sh, struct demux_packet **out_pkt)
r = *out_pkt ? 1 : -1;
}
pthread_mutex_unlock(&ds->in->lock);
+ } else if (ds->in->blocked) {
+ r = 0;
} else {
*out_pkt = demux_read_packet(sh);
r = *out_pkt ? 1 : -1;
@@ -1698,7 +1707,7 @@ struct demux_packet *demux_read_any_packet(struct demuxer *demuxer)
struct demux_internal *in = demuxer->in;
assert(!in->threading); // doesn't work with threading
bool read_more = true;
- while (read_more) {
+ while (read_more && !in->blocked) {
for (int n = 0; n < in->num_streams; n++) {
in->reading = true; // force read_packet() to read
struct demux_packet *pkt = dequeue_packet(in->streams[n]->ds);
@@ -2223,6 +2232,7 @@ static void clear_reader_state(struct demux_internal *in)
ds_clear_reader_state(in->streams[n]->ds);
in->warned_queue_overflow = false;
in->d_user->filepos = -1; // implicitly synchronized
+ in->blocked = false;
assert(in->fw_bytes == 0);
}
@@ -2719,6 +2729,19 @@ void demux_disable_cache(demuxer_t *demuxer)
pthread_mutex_unlock(&in->lock);
}
+// Disallow reading any packets and make readers think there is no new data
+// yet, until a seek is issued.
+void demux_block_reading(struct demuxer *demuxer, bool block)
+{
+ struct demux_internal *in = demuxer->in;
+ assert(demuxer == in->d_user);
+
+ pthread_mutex_lock(&in->lock);
+ in->blocked = block;
+ pthread_cond_signal(&in->wakeup);
+ pthread_mutex_unlock(&in->lock);
+}
+
// must be called not locked
static void update_cache(struct demux_internal *in)
{
diff --git a/demux/demux.h b/demux/demux.h
index e0c68a82cb..ffed39c770 100644
--- a/demux/demux.h
+++ b/demux/demux.h
@@ -285,6 +285,8 @@ void demux_set_ts_offset(struct demuxer *demuxer, double offset);
int demux_control(struct demuxer *demuxer, int cmd, void *arg);
+void demux_block_reading(struct demuxer *demuxer, bool block);
+
void demuxer_select_track(struct demuxer *demuxer, struct sh_stream *stream,
double ref_pts, bool selected);
void demux_set_stream_autoselect(struct demuxer *demuxer, bool autoselect);
diff --git a/player/audio.c b/player/audio.c
index 9b842a9003..ccddd790e7 100644
--- a/player/audio.c
+++ b/player/audio.c
@@ -283,7 +283,6 @@ void reset_audio_state(struct MPContext *mpctx)
mpctx->delay = 0;
mpctx->audio_drop_throttle = 0;
mpctx->audio_stat_start = 0;
- mpctx->audio_allow_second_chance_seek = false;
}
void uninit_audio_out(struct MPContext *mpctx)
@@ -800,27 +799,6 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip)
}
ptsdiff = MPCLAMP(ptsdiff, -3600, 3600);
- // Heuristic: if audio is "too far" ahead, and one of them is a separate
- // track, allow a refresh seek to the correct position to fix it.
- if (ptsdiff > 0.2 && mpctx->audio_allow_second_chance_seek && sync_to_video) {
- struct ao_chain *ao_c = mpctx->ao_chain;
- if (ao_c && ao_c->track && mpctx->vo_chain && mpctx->vo_chain->track &&
- ao_c->track->demuxer != mpctx->vo_chain->track->demuxer)
- {
- struct track *track = ao_c->track;
- double pts = mpctx->video_pts;
- if (pts != MP_NOPTS_VALUE)
- pts += get_track_seek_offset(mpctx, track);
- // (disable it first to make it take any effect)
- demuxer_select_track(track->demuxer, track->stream, pts, false);
- demuxer_select_track(track->demuxer, track->stream, pts, true);
- reset_audio_state(mpctx);
- MP_VERBOSE(mpctx, "retrying audio seek\n");
- return false;
- }
- }
- mpctx->audio_allow_second_chance_seek = false;
-
int align = af_format_sample_alignment(ao_format);
*skip = (int)(-ptsdiff * play_samplerate) / align * align;
return true;
diff --git a/player/core.h b/player/core.h
index 041065e1f6..f2fed55366 100644
--- a/player/core.h
+++ b/player/core.h
@@ -412,9 +412,9 @@ typedef struct MPContext {
struct seek_params seek;
- // Allow audio to issue a second seek if audio is too far ahead (for non-hr
- // seeks with external audio tracks).
- bool audio_allow_second_chance_seek;
+ // Can be temporarily set to an external audio track after seeks. Then it
+ // must be seeked to the video position once video is done seeking.
+ struct track *seek_slave;
/* Heuristic for relative chapter seeks: keep track which chapter
* the user wanted to go to, even if we aren't exactly within the
diff --git a/player/loadfile.c b/player/loadfile.c
index edba9caa1b..4a886ff156 100644
--- a/player/loadfile.c
+++ b/player/loadfile.c
@@ -227,6 +227,8 @@ void reselect_demux_stream(struct MPContext *mpctx, struct track *track)
if (pts != MP_NOPTS_VALUE)
pts += get_track_seek_offset(mpctx, track);
demuxer_select_track(track->demuxer, track->stream, pts, track->selected);
+ if (track == mpctx->seek_slave)
+ mpctx->seek_slave = NULL;
}
// Called from the demuxer thread if a new packet is available.
@@ -548,6 +550,9 @@ bool mp_remove_track(struct MPContext *mpctx, struct track *track)
sub_destroy(track->d_sub);
+ if (mpctx->seek_slave == track)
+ mpctx->seek_slave = NULL;
+
int index = 0;
while (index < mpctx->num_tracks && mpctx->tracks[index] != track)
index++;
diff --git a/player/playloop.c b/player/playloop.c
index 3db5818773..535bff883f 100644
--- a/player/playloop.c
+++ b/player/playloop.c
@@ -239,6 +239,7 @@ void reset_playback_state(struct MPContext *mpctx)
mpctx->restart_complete = false;
mpctx->paused_for_cache = false;
mpctx->cache_buffer = 100;
+ mpctx->seek_slave = NULL;
#if HAVE_ENCODING
encode_lavc_discontinuity(mpctx->encode_lavc_ctx);
@@ -252,7 +253,7 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
{
struct MPOpts *opts = mpctx->opts;
- if (!mpctx->demuxer || seek.type == MPSEEK_NONE || seek.amount == MP_NOPTS_VALUE)
+ if (!mpctx->demuxer || !seek.type || seek.amount == MP_NOPTS_VALUE)
return;
bool hr_seek_very_exact = seek.exact == MPSEEK_VERY_EXACT;
@@ -326,13 +327,15 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
if (!demux_seek(mpctx->demuxer, demux_pts, demux_flags)) {
if (!mpctx->demuxer->seekable) {
- MP_ERR(mpctx, "Cannot seek in this file.\n");
+ MP_ERR(mpctx, "Cannot seek in this stream.\n");
MP_ERR(mpctx, "You can force it with '--force-seekable=yes'.\n");
}
return;
}
// Seek external, extra files too:
+ bool has_video = false;
+ struct track *external_audio = NULL;
for (int t = 0; t < mpctx->num_tracks; t++) {
struct track *track = mpctx->tracks[t];
if (track->selected && track->is_external && track->demuxer) {
@@ -342,7 +345,12 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
if (demux_flags & SEEK_FACTOR)
main_new_pos = seek_pts;
demux_seek(track->demuxer, main_new_pos, 0);
+ if (track->type == STREAM_AUDIO && !external_audio)
+ external_audio = track;
}
+ if (track->selected && !track->is_external && track->stream &&
+ track->type == STREAM_VIDEO && !track->stream->attached_picture)
+ has_video = true;
}
if (!(seek.flags & MPSEEK_FLAG_NOFLUSH))
@@ -352,6 +360,17 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
if (mpctx->recorder)
mp_recorder_mark_discontinuity(mpctx->recorder);
+ // When doing keyframe seeks (hr_seek=false) backwards (no SEEK_FORWARD),
+ // then video can seek before the external audio track (because video seek
+ // granularity is coarser than audio). The result would be playing video with
+ // silence until the audio seek target is reached. Work around by blocking
+ // the demuxer (decoders can't read) and seeking to video position later.
+ if (has_video && external_audio && !hr_seek && !(demux_flags & SEEK_FORWARD)) {
+ MP_VERBOSE(mpctx, "delayed seek for aid=%d\n", external_audio->user_tid);
+ demux_block_reading(external_audio->demuxer, true);
+ mpctx->seek_slave = external_audio;
+ }
+
/* Use the target time as "current position" for further relative
* seeks etc until a new video frame has been decoded */
mpctx->last_seek_pts = seek_pts;
@@ -376,9 +395,6 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek)
mp_notify(mpctx, MPV_EVENT_SEEK, NULL);
mp_notify(mpctx, MPV_EVENT_TICK, NULL);
- mpctx->audio_allow_second_chance_seek =
- !hr_seek && !(demux_flags & SEEK_FORWARD);
-
mpctx->ab_loop_clip = mpctx->last_seek_pts < opts->ab_loop[1];
mpctx->current_seek = seek;
@@ -949,6 +965,24 @@ static void handle_playback_time(struct MPContext *mpctx)
}
}
+static void handle_delayed_audio_seek(struct MPContext *mpctx)
+{
+ if (mpctx->seek_slave) {
+ if (mpctx->video_pts != MP_NOPTS_VALUE) {
+ // We know the video position now, so seek external audio to the
+ // correct position.
+ double pts = mpctx->video_pts +
+ get_track_seek_offset(mpctx, mpctx->seek_slave);
+ demux_seek(mpctx->seek_slave->demuxer, pts, 0);
+ mpctx->seek_slave = NULL;
+ } else if (mpctx->video_status >= STATUS_EOF) {
+ // We won't get a video position; don't stall the audio stream.
+ demux_block_reading(mpctx->seek_slave->demuxer, false);
+ mpctx->seek_slave = NULL;
+ }
+ }
+}
+
// We always make sure audio and video buffers are filled before actually
// starting playback. This code handles starting them at the same time.
static void handle_playback_restart(struct MPContext *mpctx)
@@ -991,7 +1025,6 @@ static void handle_playback_restart(struct MPContext *mpctx)
mpctx->hrseek_active = false;
mpctx->restart_complete = true;
mpctx->current_seek = (struct seek_params){0};
- mpctx->audio_allow_second_chance_seek = false;
handle_playback_time(mpctx);
mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL);
update_core_idle_state(mpctx);
@@ -1099,6 +1132,8 @@ void run_playloop(struct MPContext *mpctx)
fill_audio_out_buffers(mpctx);
write_video(mpctx);
+ handle_delayed_audio_seek(mpctx);
+
handle_playback_restart(mpctx);
handle_playback_time(mpctx);