diff options
-rw-r--r-- | audio/decode/ad_lavc.c | 8 | ||||
-rw-r--r-- | audio/decode/ad_mpg123.c | 4 | ||||
-rw-r--r-- | audio/decode/ad_spdif.c | 5 | ||||
-rw-r--r-- | audio/decode/dec_audio.c | 47 | ||||
-rw-r--r-- | audio/decode/dec_audio.h | 7 | ||||
-rw-r--r-- | player/audio.c | 298 | ||||
-rw-r--r-- | player/core.h | 26 | ||||
-rw-r--r-- | player/loadfile.c | 6 | ||||
-rw-r--r-- | player/playloop.c | 191 | ||||
-rw-r--r-- | player/video.c | 17 |
10 files changed, 328 insertions, 281 deletions
diff --git a/audio/decode/ad_lavc.c b/audio/decode/ad_lavc.c index cb8cfa8c82..4ff69c3f20 100644 --- a/audio/decode/ad_lavc.c +++ b/audio/decode/ad_lavc.c @@ -309,8 +309,10 @@ static int decode_packet(struct dec_audio *da) mp_audio_set_null_data(&da->decoded); struct demux_packet *mpkt = priv->packet; - if (!mpkt) - mpkt = demux_read_packet(da->header); + if (!mpkt) { + if (demux_read_packet_async(da->header, &mpkt) == 0) + return AD_WAIT; + } priv->packet = talloc_steal(priv, mpkt); @@ -343,7 +345,7 @@ static int decode_packet(struct dec_audio *da) } // LATM may need many packets to find mux info if (ret == AVERROR(EAGAIN)) - return 0; + return AD_OK; } if (ret < 0) { MP_ERR(da, "Error decoding audio.\n"); diff --git a/audio/decode/ad_mpg123.c b/audio/decode/ad_mpg123.c index dc3ec69531..30a4790746 100644 --- a/audio/decode/ad_mpg123.c +++ b/audio/decode/ad_mpg123.c @@ -218,7 +218,9 @@ static int decode_packet(struct dec_audio *da) mp_audio_set_null_data(&da->decoded); - struct demux_packet *pkt = demux_read_packet(da->header); + struct demux_packet *pkt; + if (demux_read_packet_async(da->header, &pkt) == 0) + return AD_WAIT; if (!pkt) return AD_EOF; diff --git a/audio/decode/ad_spdif.c b/audio/decode/ad_spdif.c index c215f92e63..528e42fbc4 100644 --- a/audio/decode/ad_spdif.c +++ b/audio/decode/ad_spdif.c @@ -191,7 +191,10 @@ static int decode_packet(struct dec_audio *da) spdif_ctx->out_buffer_len = 0; - struct demux_packet *mpkt = demux_read_packet(da->header); + struct demux_packet *mpkt; + if (demux_read_packet_async(da->header, &mpkt) == 0) + return AD_WAIT; + if (!mpkt) return AD_EOF; diff --git a/audio/decode/dec_audio.c b/audio/decode/dec_audio.c index 38044b4657..c7580a23d8 100644 --- a/audio/decode/dec_audio.c +++ b/audio/decode/dec_audio.c @@ -89,27 +89,7 @@ static int init_audio_codec(struct dec_audio *d_audio, const char *decoder) return 0; } - // Decode enough until we know the audio format. - for (int tries = 1; ; tries++) { - if (mp_audio_config_valid(&d_audio->decoded)) { - MP_VERBOSE(d_audio, "Initial decode succeeded after %d packets.\n", - tries); - break; - } - if (tries >= 50) { - MP_ERR(d_audio, "initial decode failed\n"); - uninit_decoder(d_audio); - return 0; - } - d_audio->ad_driver->decode_packet(d_audio); - } - d_audio->decode_buffer = mp_audio_buffer_create(NULL); - if (!reinit_audio_buffer(d_audio)) { - uninit_decoder(d_audio); - return 0; - } - return 1; } @@ -171,9 +151,6 @@ int audio_init_best_codec(struct dec_audio *d_audio, char *audio_decoders) talloc_asprintf(d_audio, "%s [%s:%s]", decoder->desc, decoder->family, decoder->decoder); MP_VERBOSE(d_audio, "Selected audio codec: %s\n", d_audio->decoder_desc); - MP_VERBOSE(d_audio, "AUDIO: %d Hz, %d ch, %s\n", - d_audio->decoded.rate, d_audio->decoded.channels.num, - af_fmt_to_str(d_audio->decoded.format)); } else { MP_ERR(d_audio, "Failed to initialize an audio decoder for codec '%s'.\n", d_audio->header->codec ? d_audio->header->codec : "<unknown>"); @@ -238,6 +215,24 @@ int audio_init_filters(struct dec_audio *d_audio, int in_samplerate, return 1; } +/* Decode packets until we know the audio format. Then reinit the buffer. + * Returns AD_OK on success, negative AD_* code otherwise. + * Also returns AD_OK if already initialized (and does nothing). + */ +int initial_audio_decode(struct dec_audio *da) +{ + while (!mp_audio_config_valid(&da->decoded)) { + if (da->decoded.samples > 0) + return AD_ERR; // invalid format, rather than uninitialized + int ret = da->ad_driver->decode_packet(da); + if (ret < 0) + return ret; + } + if (mp_audio_buffer_samples(da->decode_buffer) > 0) // avoid accidental flush + return AD_OK; + return reinit_audio_buffer(da) ? AD_OK : AD_ERR; +} + // Filter len bytes of input, put result into outbuf. static int filter_n_bytes(struct dec_audio *da, struct mp_audio_buffer *outbuf, int len) @@ -270,6 +265,9 @@ static int filter_n_bytes(struct dec_audio *da, struct mp_audio_buffer *outbuf, break; } + if (error == AD_WAIT) + return error; + // Filter struct mp_audio filter_data; mp_audio_buffer_peek(da->decode_buffer, &filter_data); @@ -306,6 +304,9 @@ static int filter_n_bytes(struct dec_audio *da, struct mp_audio_buffer *outbuf, int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf, int minsamples) { + if (!d_audio->afilter) + return AD_ERR; + // Indicates that a filter seems to be buffering large amounts of data int huge_filter_buffer = 0; diff --git a/audio/decode/dec_audio.h b/audio/decode/dec_audio.h index f9269b5272..6d2c61d862 100644 --- a/audio/decode/dec_audio.h +++ b/audio/decode/dec_audio.h @@ -51,15 +51,16 @@ struct dec_audio { enum { AD_OK = 0, AD_ERR = -1, - AD_NEW_FMT = -2, - AD_ASYNC_PLAY_DONE = -3, - AD_EOF = -4, + AD_EOF = -2, + AD_NEW_FMT = -3, + AD_WAIT = -4, }; struct mp_decoder_list *audio_decoder_list(void); int audio_init_best_codec(struct dec_audio *d_audio, char *audio_decoders); int audio_decode(struct dec_audio *d_audio, struct mp_audio_buffer *outbuf, int minsamples); +int initial_audio_decode(struct dec_audio *d_audio); void audio_reset_decoding(struct dec_audio *d_audio); void audio_uninit(struct dec_audio *d_audio); diff --git a/player/audio.c b/player/audio.c index 4867cf5fcc..3a7d26daa7 100644 --- a/player/audio.c +++ b/player/audio.c @@ -19,6 +19,7 @@ #include <stddef.h> #include <stdbool.h> #include <inttypes.h> +#include <limits.h> #include <math.h> #include <assert.h> @@ -103,6 +104,8 @@ void reinit_audio_chain(struct MPContext *mpctx) mp_notify(mpctx, MPV_EVENT_AUDIO_RECONFIG, NULL); + mpctx->audio_status = STATUS_SYNCING; + if (!(mpctx->initialized_flags & INITIALIZED_ACODEC)) { mpctx->initialized_flags |= INITIALIZED_ACODEC; assert(!mpctx->d_audio); @@ -117,9 +120,19 @@ void reinit_audio_chain(struct MPContext *mpctx) } assert(mpctx->d_audio); + if (!mpctx->ao_buffer) + mpctx->ao_buffer = mp_audio_buffer_create(mpctx); + struct mp_audio in_format; mp_audio_buffer_get_format(mpctx->d_audio->decode_buffer, &in_format); + if (!mp_audio_config_valid(&in_format)) { + // We don't know the audio format yet - so configure it later as we're + // resyncing. fill_audio_buffers() will call this function again. + mpctx->sleeptime = 0; + return; + } + if (mpctx->ao_decoder_fmt && (mpctx->initialized_flags & INITIALIZED_AO) && !mp_audio_config_equals(mpctx->ao_decoder_fmt, &in_format) && opts->gapless_audio < 0) @@ -169,7 +182,6 @@ void reinit_audio_chain(struct MPContext *mpctx) struct mp_audio fmt; ao_get_format(ao, &fmt); - mpctx->ao_buffer = mp_audio_buffer_create(ao); mp_audio_buffer_reinit(mpctx->ao_buffer, &fmt); mpctx->ao_decoder_fmt = talloc(NULL, struct mp_audio); @@ -185,7 +197,6 @@ void reinit_audio_chain(struct MPContext *mpctx) if (recreate_audio_filters(mpctx) < 0) goto init_error; - mpctx->syncing_audio = true; return; init_error: @@ -206,6 +217,9 @@ double written_audio_pts(struct MPContext *mpctx) struct mp_audio in_format; mp_audio_buffer_get_format(d_audio->decode_buffer, &in_format); + if (!mp_audio_config_valid(&in_format) || !d_audio->afilter) + return MP_NOPTS_VALUE;; + // first calculate the end pts of audio that has been output by decoder double a_pts = d_audio->pts; if (a_pts == MP_NOPTS_VALUE) @@ -240,7 +254,7 @@ double written_audio_pts(struct MPContext *mpctx) double playing_audio_pts(struct MPContext *mpctx) { double pts = written_audio_pts(mpctx); - if (pts == MP_NOPTS_VALUE) + if (pts == MP_NOPTS_VALUE || !mpctx->ao) return pts; return pts - mpctx->opts->playback_speed * ao_get_delay(mpctx->ao); } @@ -273,142 +287,109 @@ static int write_to_ao(struct MPContext *mpctx, struct mp_audio *data, int flags return 0; } -static int write_silence_to_ao(struct MPContext *mpctx, int samples, int flags, - double pts) -{ - struct mp_audio tmp = {0}; - mp_audio_buffer_get_format(mpctx->ao_buffer, &tmp); - tmp.samples = samples; - char *p = talloc_size(NULL, tmp.samples * tmp.sstride); - for (int n = 0; n < tmp.num_planes; n++) - tmp.planes[n] = p; - mp_audio_fill_silence(&tmp, 0, tmp.samples); - int r = write_to_ao(mpctx, &tmp, 0, pts); - talloc_free(p); - return r; -} - -static int audio_start_sync(struct MPContext *mpctx, int playsize) +// Return the number of samples that must be skipped or prepended to reach the +// target audio pts after a seek (for A/V sync or hr-seek). +// Return value (*skip): +// >0: skip this many samples +// =0: don't do anything +// <0: prepend this many samples of silence +// Returns false if PTS is not known yet. +static bool get_sync_samples(struct MPContext *mpctx, int *skip) { - struct ao *ao = mpctx->ao; struct MPOpts *opts = mpctx->opts; - struct dec_audio *d_audio = mpctx->d_audio; - int res; + *skip = 0; - assert(d_audio); + if (mpctx->audio_status != STATUS_SYNCING) + return true; - struct mp_audio out_format; - ao_get_format(ao, &out_format); + struct mp_audio out_format = {0}; + ao_get_format(mpctx->ao, &out_format); + double play_samplerate = out_format.rate / opts->playback_speed; - // Timing info may not be set without - res = audio_decode(d_audio, mpctx->ao_buffer, 1); - if (res < 0) - return res; - - int samples; - bool did_retry = false; - double written_pts; - double real_samplerate = out_format.rate / opts->playback_speed; - bool hrseek = mpctx->hrseek_active; // audio only hrseek - mpctx->hrseek_active = false; - while (1) { - written_pts = written_audio_pts(mpctx); - double ptsdiff; - if (hrseek) - ptsdiff = written_pts - mpctx->hrseek_pts; - else - ptsdiff = written_pts - mpctx->video_next_pts - mpctx->delay - + mpctx->audio_delay; - samples = ptsdiff * real_samplerate; - - // ogg demuxers give packets without timing - if (written_pts <= 1 && d_audio->pts == MP_NOPTS_VALUE) { - if (!did_retry) { - // Try to read more data to see packets that have pts - res = audio_decode(d_audio, mpctx->ao_buffer, out_format.rate); - if (res < 0) - return res; - did_retry = true; - continue; - } - samples = 0; - } + bool is_pcm = !(out_format.format & AF_FORMAT_SPECIAL_MASK); // no spdif + if (!opts->initial_audio_sync || !is_pcm) { + mpctx->audio_status = STATUS_FILLING; + return true; + } - if (fabs(ptsdiff) > 300 || isnan(ptsdiff)) // pts reset or just broken? - samples = 0; - - if (samples > 0) - break; - - mpctx->syncing_audio = false; - int skip_samples = -samples; - int a = MPMIN(skip_samples, MPMAX(playsize, 2500)); - res = audio_decode(d_audio, mpctx->ao_buffer, a); - if (skip_samples <= mp_audio_buffer_samples(mpctx->ao_buffer)) { - mp_audio_buffer_skip(mpctx->ao_buffer, skip_samples); - if (res < 0) - return res; - return audio_decode(d_audio, mpctx->ao_buffer, playsize); + double written_pts = written_audio_pts(mpctx); + if (written_pts == MP_NOPTS_VALUE && !mp_audio_buffer_samples(mpctx->ao_buffer)) + return false; // no audio read yet + + bool sync_to_video = mpctx->d_video && mpctx->sync_audio_to_video; + + double sync_pts = MP_NOPTS_VALUE; + if (sync_to_video) { + if (mpctx->video_next_pts != MP_NOPTS_VALUE) { + sync_pts = mpctx->video_next_pts; + } else if (mpctx->video_status < STATUS_READY) { + return false; // wait until we know a video PTS } - mp_audio_buffer_clear(mpctx->ao_buffer); - if (res < 0) - return res; + } else if (mpctx->hrseek_active) { + sync_pts = mpctx->hrseek_pts; } - if (hrseek) - // Don't add silence in audio-only case even if position is too late - return 0; - if (samples >= playsize) { - /* This case could fall back to the one below with - * samples = playsize, but then silence would keep accumulating - * in ao_buffer if the AO accepts less data than it asks for - * in playsize. */ - write_silence_to_ao(mpctx, playsize, 0, - written_pts - samples / real_samplerate); - return AD_ASYNC_PLAY_DONE; + if (sync_pts == MP_NOPTS_VALUE) { + mpctx->audio_status = STATUS_FILLING; + return true; // syncing disabled } - mpctx->syncing_audio = false; - mp_audio_buffer_prepend_silence(mpctx->ao_buffer, samples); - return audio_decode(d_audio, mpctx->ao_buffer, playsize); + + if (sync_to_video) + sync_pts += mpctx->delay - mpctx->audio_delay; + + double ptsdiff = written_pts - sync_pts; + // Missing timestamp, or PTS reset, or just broken. + if (written_pts == MP_NOPTS_VALUE || fabs(ptsdiff) > 300) { + MP_WARN(mpctx, "Failed audio resync.\n"); + mpctx->audio_status = STATUS_FILLING; + return true; + } + + *skip = -ptsdiff * play_samplerate; + return true; } int fill_audio_out_buffers(struct MPContext *mpctx, double endpts) { struct MPOpts *opts = mpctx->opts; - struct ao *ao = mpctx->ao; - int playsize; - int playflags = 0; - bool audio_eof = false; - bool signal_eof = false; - bool partial_fill = false; struct dec_audio *d_audio = mpctx->d_audio; - struct mp_audio out_format; - ao_get_format(ao, &out_format); - // Can't adjust the start of audio with spdif pass-through. - bool modifiable_audio_format = !(out_format.format & AF_FORMAT_SPECIAL_MASK); assert(d_audio); - if (mpctx->paused) - playsize = 1; // just initialize things (audio pts at least) - else - playsize = ao_get_space(ao); - - // Coming here with hrseek_active still set means audio-only - if (!mpctx->d_video || !mpctx->sync_audio_to_video) - mpctx->syncing_audio = false; - if (!opts->initial_audio_sync || !modifiable_audio_format) { - mpctx->syncing_audio = false; - mpctx->hrseek_active = false; + if (!d_audio->afilter || !mpctx->ao) { + // Probe the initial audio format. Returns AD_OK (and does nothing) if + // the format is already known. + int r = initial_audio_decode(mpctx->d_audio); + if (r == AD_WAIT) + return -1; // continue later when new data is available + if (r != AD_OK) { + MP_ERR(mpctx, "Error initializing audio.\n"); + struct track *track = mpctx->current_track[0][STREAM_AUDIO]; + mp_deselect_track(mpctx, track); + return -2; + } + reinit_audio_chain(mpctx); + return -1; // try again next iteration } - int res; - if (mpctx->syncing_audio || mpctx->hrseek_active) - res = audio_start_sync(mpctx, playsize); - else - res = audio_decode(d_audio, mpctx->ao_buffer, playsize); + // if paused, just initialize things (audio format & pts) + int playsize = 1; + if (!mpctx->paused) + playsize = ao_get_space(mpctx->ao); + + int skip = 0; + bool sync_known = get_sync_samples(mpctx, &skip); + if (skip > 0) { + playsize = MPMIN(skip + 1, MPMAX(playsize, 2500)); // buffer extra data + } else if (skip < 0) { + playsize = MPMAX(1, playsize + skip); // silence will be prepended + } - if (res < 0) { // EOF, error or format change - if (res == AD_NEW_FMT) { + int status = AD_OK; + if (playsize > mp_audio_buffer_samples(mpctx->ao_buffer)) { + status = audio_decode(d_audio, mpctx->ao_buffer, playsize); + if (status == AD_WAIT) + return -1; + if (status == AD_NEW_FMT) { /* The format change isn't handled too gracefully. A more precise * implementation would require draining buffered old-format audio * while displaying video, then doing the output format switch. @@ -416,16 +397,56 @@ int fill_audio_out_buffers(struct MPContext *mpctx, double endpts) if (mpctx->opts->gapless_audio < 1) uninit_player(mpctx, INITIALIZED_AO); reinit_audio_chain(mpctx); - return -1; - } else if (res == AD_ASYNC_PLAY_DONE) - return 0; - else if (res == AD_EOF) - audio_eof = true; + mpctx->sleeptime = 0; + return -1; // retry on next iteration + } + } + + bool end_sync = status != AD_OK; // (on error/EOF, start playback immediately) + if (skip >= 0) { + int max = mp_audio_buffer_samples(mpctx->ao_buffer); + mp_audio_buffer_skip(mpctx->ao_buffer, MPMIN(skip, max)); + // If something is left, we definitely reached the target time. + end_sync |= sync_known && skip < max; + } else if (skip < 0) { + if (-skip < 1000000) { // heuristic against making the buffer too large + mp_audio_buffer_prepend_silence(mpctx->ao_buffer, -skip); + } else { + MP_ERR(mpctx, "Audio starts too late: sync. failed.\n"); + ao_reset(mpctx->ao); + } + end_sync = true; + } + + if (mpctx->audio_status == STATUS_SYNCING) { + if (end_sync) + mpctx->audio_status = STATUS_FILLING; + mpctx->sleeptime = 0; + return -1; // continue on next iteration + } + + assert(mpctx->audio_status >= STATUS_FILLING); + + // Even if we're done decoding and syncing, let video start first - this is + // required, because sending audio to the AO already starts playback. + if (mpctx->audio_status == STATUS_FILLING && mpctx->sync_audio_to_video && + mpctx->video_status <= STATUS_READY) + { + mpctx->audio_status = STATUS_READY; + return -1; } + bool audio_eof = status == AD_EOF; + bool partial_fill = false; + int playflags = 0; + + struct mp_audio out_format = {0}; + ao_get_format(mpctx->ao, &out_format); + double play_samplerate = out_format.rate / opts->playback_speed; + if (endpts != MP_NOPTS_VALUE) { double samples = (endpts - written_audio_pts(mpctx) - mpctx->audio_delay) - * out_format.rate / opts->playback_speed; + * play_samplerate; if (playsize > samples) { playsize = MPMAX(samples, 0); audio_eof = true; @@ -437,17 +458,14 @@ int fill_audio_out_buffers(struct MPContext *mpctx, double endpts) playsize = mp_audio_buffer_samples(mpctx->ao_buffer); partial_fill = true; } - if (!playsize) - return partial_fill && audio_eof ? -2 : -partial_fill; - - if (audio_eof && partial_fill) { - if (opts->gapless_audio) { - // With gapless audio, delay this to ao_uninit. There must be only - // 1 final chunk, and that is handled when calling ao_uninit(). - signal_eof = true; - } else { + + audio_eof &= partial_fill; + + if (audio_eof) { + // With gapless audio, delay this to ao_uninit. There must be only + // 1 final chunk, and that is handled when calling ao_uninit(). + if (opts->gapless_audio) playflags |= AOPLAY_FINAL_CHUNK; - } } if (mpctx->paused) @@ -458,10 +476,18 @@ int fill_audio_out_buffers(struct MPContext *mpctx, double endpts) data.samples = MPMIN(data.samples, playsize); int played = write_to_ao(mpctx, &data, playflags, written_audio_pts(mpctx)); assert(played >= 0 && played <= data.samples); - mp_audio_buffer_skip(mpctx->ao_buffer, played); - return signal_eof ? -2 : -partial_fill; + mpctx->audio_status = STATUS_PLAYING; + if (audio_eof) { + mpctx->audio_status = STATUS_DRAINING; + // Wait until the AO has played all queued data. In the gapless case, + // we trigger EOF immediately, and let it play asynchronously. + if (ao_eof_reached(mpctx->ao) || opts->gapless_audio) + mpctx->audio_status = STATUS_EOF; + } + + return 0; } // Drop data queued for output, or which the AO is currently outputting. diff --git a/player/core.h b/player/core.h index adb9185da0..8789060ce8 100644 --- a/player/core.h +++ b/player/core.h @@ -149,6 +149,22 @@ enum { VD_WAIT = 3, // no EOF, but no output; wait until wakeup }; +/* Note that playback can be paused, stopped, etc. at any time. While paused, + * playback restart is still active, because you want seeking to work even + * if paused. + * The main purpose of distinguishing these states is proper reinitialization + * of A/V sync. + */ +enum playback_status { + // code may compare status values numerically + STATUS_SYNCING, // seeking for a position to resume + STATUS_FILLING, // decoding more data (so you start with full buffers) + STATUS_READY, // buffers full, playback can be started any time + STATUS_PLAYING, // normal playback + STATUS_DRAINING, // decoding has ended; still playing out queued buffers + STATUS_EOF, // playback has ended, or is disabled +}; + #define NUM_PTRACKS 2 typedef struct MPContext { @@ -234,16 +250,11 @@ typedef struct MPContext { struct vo *video_out; - /* We're starting playback from scratch or after a seek. Show first - * video frame immediately and reinitialize sync. */ - bool restart_playback; + enum playback_status video_status, audio_status; + bool restart_complete; /* Set if audio should be timed to start with video frame after seeking, * not set when e.g. playing cover art */ bool sync_audio_to_video; - /* After playback restart (above) or audio stream change, adjust audio - * stream by cutting samples or adding silence at the beginning to make - * audio playback position match video position. */ - bool syncing_audio; bool hrseek_active; bool hrseek_framedrop; double hrseek_pts; @@ -465,6 +476,7 @@ void run_playloop(struct MPContext *mpctx); void idle_loop(struct MPContext *mpctx); void handle_force_window(struct MPContext *mpctx, bool reconfig); void add_frame_pts(struct MPContext *mpctx, double pts); +void finish_playback_restart(struct MPContext *mpctx); // scripting.c struct mp_scripting { diff --git a/player/loadfile.c b/player/loadfile.c index 0a855e5cd9..657e50f6da 100644 --- a/player/loadfile.c +++ b/player/loadfile.c @@ -84,6 +84,7 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) mixer_uninit_audio(mpctx->mixer); audio_uninit(mpctx->d_audio); mpctx->d_audio = NULL; + mpctx->audio_status = STATUS_EOF; reselect_demux_streams(mpctx); } @@ -111,6 +112,7 @@ void uninit_player(struct MPContext *mpctx, unsigned int mask) if (mpctx->d_video) video_uninit(mpctx->d_video); mpctx->d_video = NULL; + mpctx->video_status = STATUS_EOF; mpctx->sync_audio_to_video = false; reselect_demux_streams(mpctx); } @@ -1258,7 +1260,6 @@ goto_reopen_demuxer: ; mpctx->time_frame = 0; mpctx->drop_message_shown = 0; - mpctx->restart_playback = true; mpctx->video_pts = 0; mpctx->last_vo_pts = MP_NOPTS_VALUE; mpctx->last_frame_duration = 0; @@ -1276,6 +1277,9 @@ goto_reopen_demuxer: ; mpctx->eof_reached = false; mpctx->last_chapter = -2; mpctx->seek = (struct seek_params){ 0 }; + mpctx->video_status = mpctx->d_video ? STATUS_SYNCING : STATUS_EOF; + mpctx->audio_status = mpctx->d_audio ? STATUS_SYNCING : STATUS_EOF; + mpctx->restart_complete = false; // If there's a timeline force an absolute seek to initialize state double startpos = rel_time_to_abs(mpctx, opts->play_start); diff --git a/player/playloop.c b/player/playloop.c index b09a72cca3..57cb0e0284 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -146,14 +146,14 @@ void add_step_frame(struct MPContext *mpctx, int dir) } } -static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac) +static void seek_reset(struct MPContext *mpctx, bool reset_ao) { if (mpctx->d_video) { video_reset_decoding(mpctx->d_video); vo_seek_reset(mpctx->video_out); } - if (mpctx->d_audio && reset_ac) { + if (mpctx->d_audio) { audio_reset_decoding(mpctx->d_audio); if (reset_ao) clear_audio_output_buffers(mpctx); @@ -168,7 +168,6 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac) mpctx->last_frame_duration = 0; mpctx->delay = 0; mpctx->time_frame = 0; - mpctx->restart_playback = true; mpctx->hrseek_active = false; mpctx->hrseek_framedrop = false; mpctx->total_avsync_change = 0; @@ -176,6 +175,9 @@ static void seek_reset(struct MPContext *mpctx, bool reset_ao, bool reset_ac) mpctx->dropped_frames = 0; mpctx->playback_pts = MP_NOPTS_VALUE; mpctx->eof_reached = false; + mpctx->video_status = mpctx->d_video ? STATUS_SYNCING : STATUS_EOF; + mpctx->audio_status = mpctx->d_audio ? STATUS_SYNCING : STATUS_EOF; + mpctx->restart_complete = false; #if HAVE_ENCODING encode_lavc_discontinuity(mpctx->encode_lavc_ctx); @@ -231,12 +233,9 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek, seek.amount += get_current_time(mpctx); } - /* At least the liba52 decoder wants to read from the input stream - * during initialization, so reinit must be done after the demux_seek() - * call that clears possible stream EOF. */ - bool need_reset = false; double demuxer_amount = seek.amount; if (mpctx->timeline) { + bool need_reset = false; demuxer_amount = timeline_set_from_time(mpctx, seek.amount, &need_reset); if (demuxer_amount == -1) { @@ -249,11 +248,12 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek, } return -1; } - } - if (need_reset) { - reinit_video_chain(mpctx); - reinit_subs(mpctx, 0); - reinit_subs(mpctx, 1); + if (need_reset) { + reinit_video_chain(mpctx); + reinit_audio_chain(mpctx); + reinit_subs(mpctx, 0); + reinit_subs(mpctx, 1); + } } int demuxer_style = 0; @@ -290,11 +290,7 @@ static int mp_seek(MPContext *mpctx, struct seek_params seek, } } - if (need_reset) - reinit_audio_chain(mpctx); - /* If we just reinitialized audio it doesn't need to be reset, - * and resetting could lose audio some decoders produce during init. */ - seek_reset(mpctx, !timeline_fallthrough, !need_reset); + seek_reset(mpctx, !timeline_fallthrough); if (timeline_fallthrough) { // Important if video reinit happens. @@ -375,7 +371,7 @@ void execute_queued_seek(struct MPContext *mpctx) /* If the user seeks continuously (keeps arrow key down) * try to finish showing a frame from one location before doing * another seek (which could lead to unchanging display). */ - if (!mpctx->seek.immediate && mpctx->restart_playback && + if (!mpctx->seek.immediate && !mpctx->restart_complete && mp_time_sec() - mpctx->start_timestamp < 0.3) return; mp_seek(mpctx, mpctx->seek, false); @@ -547,7 +543,8 @@ bool mp_seek_chapter(struct MPContext *mpctx, int chapter) static void update_avsync(struct MPContext *mpctx) { - if (!mpctx->d_audio || !mpctx->d_video) + if (mpctx->audio_status != STATUS_PLAYING || + mpctx->video_status != STATUS_PLAYING) return; double a_pos = playing_audio_pts(mpctx); @@ -578,7 +575,7 @@ static void adjust_sync(struct MPContext *mpctx, double frame_time) { struct MPOpts *opts = mpctx->opts; - if (!mpctx->d_audio || mpctx->syncing_audio) + if (mpctx->audio_status != STATUS_PLAYING) return; double a_pts = written_audio_pts(mpctx) - mpctx->delay; @@ -790,7 +787,7 @@ static void handle_sstep(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; if (opts->step_sec > 0 && !mpctx->stop_play && !mpctx->paused && - !mpctx->restart_playback) + mpctx->restart_complete) { set_osd_function(mpctx, OSD_FFW); queue_seek(mpctx, MPSEEK_RELATIVE, opts->step_sec, 0, true); @@ -900,11 +897,8 @@ static double get_wakeup_period(struct MPContext *mpctx) void run_playloop(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; - bool full_audio_buffers = false; - bool audio_left = false, video_left = false; double endpts = get_play_end_pts(mpctx); bool end_is_chapter = false; - bool was_restart = mpctx->restart_playback; bool new_frame_shown = false; #if HAVE_ENCODING @@ -930,12 +924,8 @@ void run_playloop(struct MPContext *mpctx) endpts = end; } - if (mpctx->d_audio && !mpctx->restart_playback && !ao_untimed(mpctx->ao)) { - int status = fill_audio_out_buffers(mpctx, endpts); - full_audio_buffers = status >= 0; - // Not at audio stream EOF yet - audio_left = status > -2; - } + if (mpctx->d_audio) + fill_audio_out_buffers(mpctx, endpts); if (mpctx->video_out) { vo_check_events(mpctx->video_out); @@ -957,8 +947,11 @@ void run_playloop(struct MPContext *mpctx) double frame_time = 0; int r = update_video(mpctx, endpts, !still_playing, &frame_time); + MP_TRACE(mpctx, "update_video: %d (still_playing=%d)\n", r, still_playing); + + if (r == VD_WAIT) // Demuxer will wake us up for more packets to decode. + break; - MP_TRACE(mpctx, "update_video: %d\n", r); if (r < 0) { MP_FATAL(mpctx, "Could not initialize video chain.\n"); int uninit = INITIALIZED_VCODEC; @@ -979,49 +972,53 @@ void run_playloop(struct MPContext *mpctx) mpctx->playing_last_frame = true; MP_VERBOSE(mpctx, "showing last frame\n"); } - if (mpctx->playing_last_frame) { - r = VD_PROGRESS; // don't stop playback yet - MP_TRACE(mpctx, "still showing last frame\n"); - } } - video_left = r > 0; - - if (r == VD_WAIT) - break; - - if (mpctx->restart_playback) - mpctx->sleeptime = 0; - - if (r == VD_NEW_FRAME) + if (r == VD_NEW_FRAME) { MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time); - if (r == VD_NEW_FRAME && !mpctx->restart_playback) { - mpctx->time_frame += frame_time / opts->playback_speed; - adjust_sync(mpctx, frame_time); - } + if (mpctx->video_status > STATUS_PLAYING) + mpctx->video_status = STATUS_PLAYING; - if (!video_left) { + if (mpctx->video_status >= STATUS_READY) { + mpctx->time_frame += frame_time / opts->playback_speed; + adjust_sync(mpctx, frame_time); + } + } else if (r == VD_EOF && mpctx->playing_last_frame) { + // Let video timing code continue displaying. + mpctx->video_status = STATUS_DRAINING; + MP_VERBOSE(mpctx, "still showing last frame\n"); + } else if (r <= 0) { + // EOF or error mpctx->delay = 0; mpctx->last_av_difference = 0; + mpctx->video_status = STATUS_EOF; + if (mpctx->paused && vo->hasframe) + mpctx->video_status = STATUS_DRAINING; + MP_VERBOSE(mpctx, "video EOF\n"); + } else { + if (mpctx->video_status > STATUS_PLAYING) + mpctx->video_status = STATUS_PLAYING; + + // Decode more in next iteration.< |