diff options
-rw-r--r-- | demux/demux.c | 31 | ||||
-rw-r--r-- | demux/demux.h | 2 | ||||
-rw-r--r-- | player/audio.c | 22 | ||||
-rw-r--r-- | player/core.h | 6 | ||||
-rw-r--r-- | player/loadfile.c | 5 | ||||
-rw-r--r-- | player/playloop.c | 47 |
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); |