diff options
Diffstat (limited to 'player/playloop.c')
-rw-r--r-- | player/playloop.c | 191 |
1 files changed, 91 insertions, 100 deletions
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. + mpctx->sleeptime = 0; + MP_TRACE(mpctx, "filtering more video\n"); } - if (!video_left || (mpctx->paused && !mpctx->restart_playback)) { - if (mpctx->paused) - video_left |= vo->hasframe; + // Actual playback starts when both audio and video are ready. + if (mpctx->video_status == STATUS_READY) break; - } - if (r != VD_NEW_FRAME && !mpctx->playing_last_frame) { - mpctx->sleeptime = 0; + if (mpctx->paused && mpctx->video_status >= STATUS_READY) break; - } mpctx->time_frame -= get_relative_time(mpctx); double audio_pts = playing_audio_pts(mpctx); - if (!mpctx->sync_audio_to_video) { + if (!mpctx->sync_audio_to_video || mpctx->video_status < STATUS_READY) { mpctx->time_frame = 0; - } else if (full_audio_buffers && !mpctx->restart_playback) { + } else if (mpctx->audio_status == STATUS_PLAYING && + mpctx->video_status == STATUS_PLAYING) + { double buffered_audio = ao_get_delay(mpctx->ao); MP_TRACE(mpctx, "audio delay=%f\n", buffered_audio); @@ -1061,7 +1058,7 @@ void run_playloop(struct MPContext *mpctx) mpctx->sleeptime = 0; mpctx->playing_last_frame = false; - // last frame case (don't set video_left - consider format changes) + // last frame case if (r != VD_NEW_FRAME) break; @@ -1115,7 +1112,7 @@ void run_playloop(struct MPContext *mpctx) duration = diff * 1e6; mpctx->last_frame_duration = diff; } - if (mpctx->restart_playback) + if (mpctx->video_status != STATUS_PLAYING) duration = -1; MP_STATS(mpctx, "start flip"); @@ -1133,65 +1130,57 @@ void run_playloop(struct MPContext *mpctx) mpctx->time_frame -= get_relative_time(mpctx); } mpctx->shown_vframes++; - if (mpctx->restart_playback) { - if (mpctx->sync_audio_to_video) { - mpctx->syncing_audio = true; - if (mpctx->d_audio) - fill_audio_out_buffers(mpctx, endpts); - mpctx->restart_playback = false; - mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL); - } - mpctx->time_frame = 0; - get_relative_time(mpctx); - } + if (mpctx->video_status < STATUS_PLAYING) + mpctx->video_status = STATUS_READY; update_avsync(mpctx); screenshot_flip(mpctx); new_frame_shown = true; mp_notify(mpctx, MPV_EVENT_TICK, NULL); + if (!mpctx->sync_audio_to_video) + mpctx->video_status = STATUS_EOF; + break; } // video - if (!video_left || mpctx->paused) { + if (mpctx->video_status == STATUS_EOF || mpctx->paused) { if (mp_time_sec() - mpctx->last_idle_tick > 0.5) { mpctx->last_idle_tick = mp_time_sec(); mp_notify(mpctx, MPV_EVENT_TICK, NULL); } } - video_left &= mpctx->sync_audio_to_video; // force no-video semantics - - if (mpctx->d_audio && (mpctx->restart_playback ? !video_left : - ao_untimed(mpctx->ao) && (mpctx->delay <= 0 || - !video_left))) + // We always make sure audio and video buffers are filled before actually + // starting playback. This code handles starting them at the same time. + if (mpctx->audio_status >= STATUS_READY && + mpctx->video_status >= STATUS_READY) { - int status = fill_audio_out_buffers(mpctx, endpts); - // Not at audio stream EOF yet - audio_left = status > -2; - } - if (mpctx->d_audio) { - /* When all audio has been written to output driver, stay in the - * main loop handling commands until it has been mostly consumed, - * except in the gapless case, where the next file will be started - * while audio from the current one still remains to be played. - */ - audio_left |= !ao_eof_reached(mpctx->ao) && !opts->gapless_audio; + if (mpctx->video_status == STATUS_READY) { + mpctx->video_status = STATUS_PLAYING; + get_relative_time(mpctx); + mpctx->sleeptime = 0; + new_frame_shown = true; + } + if (mpctx->audio_status == STATUS_READY) + fill_audio_out_buffers(mpctx, endpts); // actually play prepared buffer + if (!mpctx->restart_complete) { + mpctx->hrseek_active = false; + mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL); + mpctx->restart_complete = true; + } } - if (!video_left) - mpctx->restart_playback = false; - - update_osd_msg(mpctx); - if (!video_left && (!mpctx->paused || was_restart)) { + if (mpctx->video_status == STATUS_EOF && + mpctx->audio_status >= STATUS_PLAYING) + { double a_pos = 0; if (mpctx->d_audio) a_pos = playing_audio_pts(mpctx); mpctx->playback_pts = a_pos; - if (was_restart) - mp_notify(mpctx, MPV_EVENT_PLAYBACK_RESTART, NULL); } + update_osd_msg(mpctx); update_subtitles(mpctx); /* It's possible for the user to simultaneously switch both audio @@ -1201,12 +1190,14 @@ void run_playloop(struct MPContext *mpctx) * We want this check to trigger if we seeked to this position, * but not if we paused at it with audio possibly still buffered in * the AO. There's currently no working way to check buffered audio - * inside AO while paused. Thus the "was_restart" check below, which + * inside AO while paused. Thus the "was_audio_restart" check below, which * should trigger after seek only, when we know there's no audio * buffered. */ - if ((mpctx->d_audio || mpctx->d_video) && !audio_left && !video_left - && (!mpctx->paused || was_restart)) { + if ((mpctx->d_audio || mpctx->d_video) && !mpctx->paused && + mpctx->audio_status == STATUS_EOF && + mpctx->video_status == STATUS_EOF) + { if (end_is_chapter) { mp_seek(mpctx, (struct seek_params){ .type = MPSEEK_ABSOLUTE, @@ -1218,11 +1209,11 @@ void run_playloop(struct MPContext *mpctx) mp_handle_nav(mpctx); - if (!mpctx->stop_play && !mpctx->restart_playback) { + if (!mpctx->stop_play && mpctx->restart_complete) { // If no more video is available, one frame means one playloop iteration. // Otherwise, one frame means one video frame. - if (!video_left) + if (mpctx->video_status == STATUS_EOF) new_frame_shown = true; if (opts->playing_msg && !mpctx->playing_msg_shown && new_frame_shown) { |