diff options
Diffstat (limited to 'player')
-rw-r--r-- | player/audio.c | 3 | ||||
-rw-r--r-- | player/core.h | 2 | ||||
-rw-r--r-- | player/misc.c | 13 | ||||
-rw-r--r-- | player/osd.c | 2 | ||||
-rw-r--r-- | player/playloop.c | 32 | ||||
-rw-r--r-- | player/video.c | 62 |
6 files changed, 67 insertions, 47 deletions
diff --git a/player/audio.c b/player/audio.c index aae1198835..8aba3d860b 100644 --- a/player/audio.c +++ b/player/audio.c @@ -625,7 +625,8 @@ static bool get_sync_samples(struct MPContext *mpctx, int *skip) !mp_audio_buffer_samples(mpctx->ao_chain->ao_buffer)) return false; // no audio read yet - bool sync_to_video = mpctx->vo_chain && mpctx->video_status != STATUS_EOF; + bool sync_to_video = mpctx->vo_chain && mpctx->video_status != STATUS_EOF && + !mpctx->vo_chain->is_sparse; double sync_pts = MP_NOPTS_VALUE; if (sync_to_video) { diff --git a/player/core.h b/player/core.h index 865b2e9f53..a6606af91c 100644 --- a/player/core.h +++ b/player/core.h @@ -230,6 +230,8 @@ enum playback_status { STATUS_EOF, // playback has ended, or is disabled }; +const char *mp_status_str(enum playback_status st); + #define NUM_PTRACKS 2 typedef struct MPContext { diff --git a/player/misc.c b/player/misc.c index f6a63ec7ef..4246c1c6f9 100644 --- a/player/misc.c +++ b/player/misc.c @@ -301,3 +301,16 @@ void merge_playlist_files(struct playlist *pl) playlist_add_file(pl, edl); talloc_free(edl); } + +const char *mp_status_str(enum playback_status st) +{ + switch (st) { + case STATUS_SYNCING: return "syncing"; + case STATUS_FILLING: return "filling"; + case STATUS_READY: return "ready"; + case STATUS_PLAYING: return "playing"; + case STATUS_DRAINING: return "draining"; + case STATUS_EOF: return "eof"; + default: return "bug"; + } +} diff --git a/player/osd.c b/player/osd.c index 93d6302cc0..ff9bd9b429 100644 --- a/player/osd.c +++ b/player/osd.c @@ -192,7 +192,7 @@ static char *get_term_status_msg(struct MPContext *mpctx) saddf(&line, " x%4.2f", opts->playback_speed); // A-V sync - if (mpctx->ao_chain && mpctx->vo_chain && !mpctx->vo_chain->is_coverart) { + if (mpctx->ao_chain && mpctx->vo_chain && !mpctx->vo_chain->is_sparse) { saddf(&line, " A-V:%7.3f", mpctx->last_av_difference); if (fabs(mpctx->total_avsync_change) > 0.05) saddf(&line, " ct:%7.3f", mpctx->total_avsync_change); diff --git a/player/playloop.c b/player/playloop.c index 6c390a76e2..597bcd3981 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -290,10 +290,11 @@ static void mp_seek(MPContext *mpctx, struct seek_params seek) double demux_pts = seek_pts; - bool hr_seek = opts->correct_pts && seek.exact != MPSEEK_KEYFRAME && - ((opts->hr_seek == 0 && seek.type == MPSEEK_ABSOLUTE) || - opts->hr_seek > 0 || seek.exact >= MPSEEK_EXACT) && - seek_pts != MP_NOPTS_VALUE; + bool hr_seek = (opts->correct_pts && seek.exact != MPSEEK_KEYFRAME && + seek_pts != MP_NOPTS_VALUE) && + (seek.exact >= MPSEEK_EXACT || opts->hr_seek == 1 || + (opts->hr_seek >= 0 && seek.type == MPSEEK_ABSOLUTE) || + (opts->hr_seek == 2 && (!mpctx->vo_chain || mpctx->vo_chain->is_sparse))); if (seek.type == MPSEEK_FACTOR || seek.amount < 0 || (seek.type == MPSEEK_ABSOLUTE && seek.amount < mpctx->last_chapter_pts)) @@ -661,7 +662,7 @@ static void handle_osd_redraw(struct MPContext *mpctx) return; } // Don't redraw immediately during a seek (makes it significantly slower). - bool use_video = mpctx->vo_chain && !mpctx->vo_chain->is_coverart; + bool use_video = mpctx->vo_chain && !mpctx->vo_chain->is_sparse; if (use_video && mp_time_sec() - mpctx->start_timestamp < 0.1) { mp_set_timeout(mpctx, 0.1); return; @@ -1110,13 +1111,6 @@ static void handle_playback_restart(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; - // Do not wait for video stream if it only has sparse frames. - if (mpctx->vo_chain && mpctx->vo_chain->is_sparse && - mpctx->video_status < STATUS_READY) - { - mpctx->video_status = STATUS_READY; - } - if (mpctx->audio_status < STATUS_READY || mpctx->video_status < STATUS_READY) return; @@ -1127,6 +1121,7 @@ static void handle_playback_restart(struct MPContext *mpctx) mpctx->video_status = STATUS_PLAYING; get_relative_time(mpctx); mp_wakeup_core(mpctx); + MP_DBG(mpctx, "starting video playback\n"); } if (mpctx->audio_status == STATUS_READY) { @@ -1139,14 +1134,7 @@ static void handle_playback_restart(struct MPContext *mpctx) return; } - // Video needed, but not started yet -> wait. - if (mpctx->vo_chain && - !mpctx->vo_chain->is_coverart && - !mpctx->vo_chain->is_sparse && - mpctx->video_status <= STATUS_READY) - return; - - MP_VERBOSE(mpctx, "starting audio playback\n"); + MP_DBG(mpctx, "starting audio playback\n"); mpctx->audio_status = STATUS_PLAYING; fill_audio_out_buffers(mpctx); // actually play prepared buffer mp_wakeup_core(mpctx); @@ -1178,7 +1166,9 @@ static void handle_playback_restart(struct MPContext *mpctx) mpctx->playing_msg_shown = true; mp_wakeup_core(mpctx); update_ab_loop_clip(mpctx); - MP_VERBOSE(mpctx, "playback restart complete @ %f\n", mpctx->playback_pts); + MP_VERBOSE(mpctx, "playback restart complete @ %f, audio=%s, video=%s\n", + mpctx->playback_pts, mp_status_str(mpctx->video_status), + mp_status_str(mpctx->audio_status)); // Continuous seeks past EOF => treat as EOF instead of repeating seek. if (mpctx->seek.type == MPSEEK_RELATIVE && mpctx->seek.amount > 0 && diff --git a/player/video.c b/player/video.c index 5d9879bc51..d1e1554ef0 100644 --- a/player/video.c +++ b/player/video.c @@ -261,7 +261,7 @@ void reinit_video_chain_src(struct MPContext *mpctx, struct track *track) vo_c->dec_src = track->dec->f->pins[0]; vo_c->filter->container_fps = track->dec->fps; vo_c->is_coverart = !!track->stream->attached_picture; - vo_c->is_sparse = track->stream->still_image; + vo_c->is_sparse = track->stream->still_image || vo_c->is_coverart; track->vo_c = vo_c; vo_c->track = track; @@ -402,23 +402,28 @@ static void shift_frames(struct MPContext *mpctx) mpctx->num_next_frames -= 1; } +static bool use_video_lookahead(struct MPContext *mpctx) +{ + return mpctx->video_out && + !(mpctx->video_out->driver->caps & VO_CAP_NORETAIN) && + !(mpctx->opts->untimed || mpctx->video_out->driver->untimed) && + !mpctx->opts->video_latency_hacks; +} + static int get_req_frames(struct MPContext *mpctx, bool eof) { // On EOF, drain all frames. if (eof) return 1; - if (mpctx->video_out->driver->caps & VO_CAP_NORETAIN) + if (!use_video_lookahead(mpctx)) return 1; if (mpctx->vo_chain && mpctx->vo_chain->is_sparse) return 1; - if (mpctx->opts->untimed || mpctx->video_out->driver->untimed) - return 1; - // Normally require at least 2 frames, so we can compute a frame duration. - int min = mpctx->opts->video_latency_hacks ? 1 : 2; + int min = 2; // On the first frame, output a new frame as quickly as possible. if (mpctx->video_pts == MP_NOPTS_VALUE) @@ -452,6 +457,7 @@ static bool have_new_frame(struct MPContext *mpctx, bool eof) } // Fill mpctx->next_frames[] with a newly filtered or decoded image. +// logical_eof: is set to true if there is EOF after currently queued frames // returns VD_* code static int video_output_image(struct MPContext *mpctx, bool *logical_eof) { @@ -471,6 +477,7 @@ static int video_output_image(struct MPContext *mpctx, bool *logical_eof) } if (vo_c->is_coverart) { + *logical_eof = true; if (vo_has_frame(mpctx->video_out)) return VD_EOF; hrseek = false; @@ -533,9 +540,10 @@ static int video_output_image(struct MPContext *mpctx, bool *logical_eof) if (!hrseek) mp_image_unrefp(&mpctx->saved_frame); - // If hr-seek went past EOF, use the last frame. - if (mpctx->saved_frame && r == VD_EOF) { - add_new_frame(mpctx, mpctx->saved_frame); + if (r == VD_EOF) { + // If hr-seek went past EOF, use the last frame. + if (mpctx->saved_frame) + add_new_frame(mpctx, mpctx->saved_frame); mpctx->saved_frame = NULL; *logical_eof = true; } @@ -1010,7 +1018,8 @@ void write_video(struct MPContext *mpctx) bool logical_eof = false; int r = video_output_image(mpctx, &logical_eof); - MP_TRACE(mpctx, "video_output_image: %d\n", r); + MP_TRACE(mpctx, "video_output_image: r=%d/eof=%d/st=%s\n", r, logical_eof, + mp_status_str(mpctx->video_status)); if (r < 0) goto error; @@ -1039,9 +1048,7 @@ void write_video(struct MPContext *mpctx) if (mpctx->video_status <= STATUS_PLAYING) { mpctx->video_status = STATUS_DRAINING; get_relative_time(mpctx); - if (mpctx->num_past_frames == 1 && mpctx->past_frames[0].pts == 0 && - !mpctx->ao_chain) - { + if (vo_c->is_sparse && !mpctx->ao_chain) { MP_VERBOSE(mpctx, "assuming this is an image\n"); mpctx->time_frame += opts->image_display_duration; } else if (mpctx->last_frame_duration > 0) { @@ -1082,6 +1089,20 @@ void write_video(struct MPContext *mpctx) return; } + if (logical_eof && !mpctx->num_past_frames && mpctx->num_next_frames == 1 && + use_video_lookahead(mpctx) && !vo_c->is_sparse) + { + // Too much danger to accidentally mark video as sparse when e.g. + // seeking exactly to the last frame, so as a heuristic, do this only + // if it looks like the "first" video frame (unreliable, but often + // works out well). Helps with seeking with single-image video tracks, + // as well as detecting whether as video track is really an image. + if (mpctx->next_frames[0]->pts == 0) { + MP_VERBOSE(mpctx, "assuming single-image video stream\n"); + vo_c->is_sparse = true; + } + } + // Filter output is different from VO input? struct mp_image_params p = mpctx->next_frames[0]->params; if (!vo->params || !mp_image_params_equal(&p, vo->params)) { @@ -1199,17 +1220,10 @@ void write_video(struct MPContext *mpctx) mp_notify(mpctx, MPV_EVENT_TICK, NULL); - if (vo_c->filter->got_output_eof && !mpctx->num_next_frames && - mpctx->ao_chain) - { - MP_VERBOSE(mpctx, "assuming this was the last video frame\n"); - // The main point of doing this is to prevent use of this for the - // playback_pts if audio is still running (=> seek behavior). - mpctx->video_status = STATUS_EOF; - } - - // hr-seek past EOF -> returns last frame, but terminates playback. - if (logical_eof) + // hr-seek past EOF -> returns last frame, but terminates playback. The + // early EOF is needed to trigger the exit before the next seek is executed. + // Always using early EOF breaks other cases, like images. + if (logical_eof && !mpctx->num_next_frames && mpctx->ao_chain) mpctx->video_status = STATUS_EOF; if (mpctx->video_status != STATUS_EOF) { |