diff options
Diffstat (limited to 'player/video.c')
-rw-r--r-- | player/video.c | 170 |
1 files changed, 115 insertions, 55 deletions
diff --git a/player/video.c b/player/video.c index c13bdb4074..f0372b69a5 100644 --- a/player/video.c +++ b/player/video.c @@ -21,7 +21,6 @@ #include <math.h> #include <assert.h> -#include "config.h" #include "mpv_talloc.h" #include "common/msg.h" @@ -60,6 +59,7 @@ static const char av_desync_help_text[] = "Audio/Video desynchronisation detected! Possible reasons include too slow\n" "hardware, temporary CPU spikes, broken drivers, and broken files. Audio\n" "position will not match to the video (see A-V status field).\n" +"Consider trying `--profile=fast` and/or `--hwdec=auto-safe` as they may help.\n" "\n"; static bool recreate_video_filters(struct MPContext *mpctx) @@ -119,6 +119,8 @@ void reset_video_state(struct MPContext *mpctx) mpctx->mistimed_frames_total = 0; mpctx->drop_message_shown = 0; mpctx->display_sync_drift_dir = 0; + mpctx->display_sync_error = 0; + mpctx->display_sync_active = 0; mpctx->video_status = mpctx->vo_chain ? STATUS_SYNCING : STATUS_EOF; } @@ -128,9 +130,9 @@ void uninit_video_out(struct MPContext *mpctx) uninit_video_chain(mpctx); if (mpctx->video_out) { vo_destroy(mpctx->video_out); + mpctx->video_out = NULL; mp_notify(mpctx, MPV_EVENT_VIDEO_RECONFIG, NULL); } - mpctx->video_out = NULL; } static void vo_chain_uninit(struct vo_chain *vo_c) @@ -176,9 +178,9 @@ int init_video_decoder(struct MPContext *mpctx, struct track *track) // If possible, set this as parent so the decoder gets the hwdec and DR // interfaces. - // Note: at least mpv_opengl_cb_uninit_gl() relies on being able to get - // rid of all references to the VO by destroying the VO chain. Thus, - // decoders not linked to vo_chain must not use the hwdec context. + // Note: We rely on being able to get rid of all references to the VO by + // destroying the VO chain. Thus, decoders not linked to vo_chain + // must not use the hwdec context. if (track->vo_c) parent = track->vo_c->filter->f; @@ -276,19 +278,13 @@ void reinit_video_chain_src(struct MPContext *mpctx, struct track *track) if (!recreate_video_filters(mpctx)) goto err_out; + update_content_type(mpctx, track); update_screensaver_state(mpctx); vo_set_paused(vo_c->vo, get_internal_paused(mpctx)); - // If we switch on video again, ensure audio position matches up. - if (mpctx->ao_chain && mpctx->ao_chain->ao) { - ao_reset(mpctx->ao_chain->ao); - mpctx->ao_chain->start_pts_known = false; - mpctx->audio_status = STATUS_SYNCING; - } - reset_video_state(mpctx); - reset_subtitle_state(mpctx); + term_osd_set_subs(mpctx, NULL); return; @@ -391,8 +387,9 @@ static void handle_new_frame(struct MPContext *mpctx) frame_time = 0; } } - mpctx->delay -= frame_time; mpctx->time_frame += frame_time / mpctx->video_speed; + if (mpctx->ao_chain && mpctx->ao_chain->audio_started) + mpctx->delay -= frame_time; if (mpctx->video_status >= STATUS_PLAYING) adjust_sync(mpctx, pts, frame_time); MP_TRACE(mpctx, "frametime=%5.3f\n", frame_time); @@ -562,7 +559,7 @@ static bool check_for_hwdec_fallback(struct MPContext *mpctx) { struct vo_chain *vo_c = mpctx->vo_chain; - if (!vo_c->filter->failed_output_conversion || !vo_c->track) + if (!vo_c->filter->failed_output_conversion || !vo_c->track || !vo_c->track->dec) return false; if (mp_decoder_wrapper_control(vo_c->track->dec, @@ -573,6 +570,20 @@ static bool check_for_hwdec_fallback(struct MPContext *mpctx) return true; } +static bool check_for_forced_eof(struct MPContext *mpctx) +{ + struct vo_chain *vo_c = mpctx->vo_chain; + + if (!vo_c->track || !vo_c->track->dec) + return false; + + struct mp_decoder_wrapper *dec = vo_c->track->dec; + bool forced_eof = false; + + mp_decoder_wrapper_control(dec, VDCTRL_CHECK_FORCED_EOF, &forced_eof); + return forced_eof; +} + /* Update avsync before a new video frame is displayed. Actually, this can be * called arbitrarily often before the actual display. * This adjusts the time of the next video frame */ @@ -583,7 +594,7 @@ static void update_avsync_before_frame(struct MPContext *mpctx) if (mpctx->video_status < STATUS_READY) { mpctx->time_frame = 0; - } else if (mpctx->display_sync_active || opts->video_sync == VS_NONE) { + } else if (mpctx->display_sync_active || vo->opts->video_sync == VS_NONE) { // don't touch the timing } else if (mpctx->audio_status == STATUS_PLAYING && mpctx->video_status == STATUS_PLAYING && @@ -598,7 +609,7 @@ static void update_avsync_before_frame(struct MPContext *mpctx) if (opts->autosync) { /* Smooth reported playback position from AO by averaging - * it with the value expected based on previus value and + * it with the value expected based on previous value and * time elapsed since then. May help smooth video timing * with audio output that have inaccurate position reporting. * This is badly implemented; the behavior of the smoothing @@ -635,8 +646,9 @@ static void update_av_diff(struct MPContext *mpctx, double offset) if (mpctx->vo_chain && mpctx->vo_chain->is_sparse) return; - double a_pos = playing_audio_pts(mpctx); + double a_pos = written_audio_pts(mpctx); if (a_pos != MP_NOPTS_VALUE && mpctx->video_pts != MP_NOPTS_VALUE) { + a_pos -= mpctx->audio_speed * ao_get_delay(mpctx->ao); mpctx->last_av_difference = a_pos - mpctx->video_pts + opts->audio_delay + offset; } @@ -665,20 +677,16 @@ double calc_average_frame_duration(struct MPContext *mpctx) // effective video FPS. If this is not possible, try to do it for multiples, // which still leads to an improved end result. // Both parameters are durations in seconds. -static double calc_best_speed(double vsync, double frame, int max_factor) +static double calc_best_speed(double vsync, double frame, + double max_change, int max_factor) { double ratio = frame / vsync; - double best_scale = -1; - double best_dev = INFINITY; for (int factor = 1; factor <= max_factor; factor++) { double scale = ratio * factor / rint(ratio * factor); - double dev = fabs(scale - 1); - if (dev < best_dev) { - best_scale = scale; - best_dev = dev; - } + if (fabs(scale - 1) <= max_change) + return scale; } - return best_scale; + return -1; } static double find_best_speed(struct MPContext *mpctx, double vsync) @@ -689,10 +697,15 @@ static double find_best_speed(struct MPContext *mpctx, double vsync) double dur = mpctx->past_frames[n].approx_duration; if (dur <= 0) continue; - total += calc_best_speed(vsync, dur / mpctx->opts->playback_speed, + double best = calc_best_speed(vsync, dur / mpctx->opts->playback_speed, + mpctx->opts->sync_max_video_change / 100, mpctx->opts->sync_max_factor); + if (best <= 0) + continue; + total += best; num++; } + // If it doesn't work, play at normal speed. return num > 0 ? total / num : 1; } @@ -734,12 +747,14 @@ static double compute_audio_drift(struct MPContext *mpctx, double vsync) return (sum_x * sum_y - num * sum_xy) / (sum_x * sum_x - num * sum_xx); } -static void adjust_audio_resample_speed(struct MPContext *mpctx, double vsync) +static void adjust_audio_drift_compensation(struct MPContext *mpctx, double vsync) { struct MPOpts *opts = mpctx->opts; - int mode = opts->video_sync; + int mode = mpctx->video_out->opts->video_sync; - if (mode != VS_DISP_RESAMPLE || mpctx->audio_status != STATUS_PLAYING) { + if ((mode != VS_DISP_RESAMPLE && mode != VS_DISP_TEMPO) || + mpctx->audio_status != STATUS_PLAYING) + { mpctx->speed_factor_a = mpctx->speed_factor_v; return; } @@ -797,7 +812,7 @@ static void handle_display_sync_frame(struct MPContext *mpctx, { struct MPOpts *opts = mpctx->opts; struct vo *vo = mpctx->video_out; - int mode = opts->video_sync; + int mode = vo->opts->video_sync; if (!mpctx->display_sync_active) { mpctx->display_sync_error = 0.0; @@ -811,28 +826,25 @@ static void handle_display_sync_frame(struct MPContext *mpctx, bool resample = mode == VS_DISP_RESAMPLE || mode == VS_DISP_RESAMPLE_VDROP || mode == VS_DISP_RESAMPLE_NONE; bool drop = mode == VS_DISP_VDROP || mode == VS_DISP_RESAMPLE || - mode == VS_DISP_ADROP || mode == VS_DISP_RESAMPLE_VDROP; + mode == VS_DISP_ADROP || mode == VS_DISP_RESAMPLE_VDROP || + mode == VS_DISP_TEMPO; drop &= frame->can_drop; if (resample && using_spdif_passthrough(mpctx)) return; - double vsync = vo_get_vsync_interval(vo) / 1e6; + double vsync = vo_get_vsync_interval(vo) / 1e9; if (vsync <= 0) return; - double adjusted_duration = MPMAX(0, mpctx->past_frames[0].approx_duration); - adjusted_duration /= opts->playback_speed; + double approx_duration = MPMAX(0, mpctx->past_frames[0].approx_duration); + double adjusted_duration = approx_duration / opts->playback_speed; if (adjusted_duration > 0.5) return; mpctx->speed_factor_v = 1.0; - if (mode != VS_DISP_VDROP) { - double best = find_best_speed(mpctx, vsync); - // If it doesn't work, play at normal speed. - if (fabs(best - 1.0) <= opts->sync_max_video_change / 100) - mpctx->speed_factor_v = best; - } + if (mode != VS_DISP_VDROP) + mpctx->speed_factor_v = find_best_speed(mpctx, vsync); // Determine for how many vsyncs a frame should be displayed. This can be // e.g. 2 for 30hz on a 60hz display. It can also be 0 if the video @@ -898,8 +910,8 @@ static void handle_display_sync_frame(struct MPContext *mpctx, mpctx->past_frames[0].num_vsyncs = num_vsyncs; mpctx->past_frames[0].av_diff = mpctx->last_av_difference; - if (resample || mode == VS_DISP_ADROP) { - adjust_audio_resample_speed(mpctx, vsync); + if (resample || mode == VS_DISP_ADROP || mode == VS_DISP_TEMPO) { + adjust_audio_drift_compensation(mpctx, vsync); } else { mpctx->speed_factor_a = 1.0; } @@ -910,11 +922,21 @@ static void handle_display_sync_frame(struct MPContext *mpctx, frame->vsync_interval = vsync; frame->vsync_offset = -prev_error; frame->ideal_frame_duration = frame_duration; + frame->ideal_frame_vsync = (-prev_error / frame_duration) * approx_duration; + frame->ideal_frame_vsync_duration = (vsync / frame_duration) * approx_duration; frame->num_vsyncs = num_vsyncs; frame->display_synced = true; + frame->approx_duration = approx_duration; + + // Adjust frame virtual vsyncs by the repeat count + if (drop_repeat > 0) + frame->ideal_frame_vsync_duration /= drop_repeat; mpctx->display_sync_active = true; - update_playback_speed(mpctx); + // Try to avoid audio underruns that may occur if we update + // the playback speed while in the STATUS_SYNCING state. + if (mpctx->video_status != STATUS_SYNCING) + update_playback_speed(mpctx); MP_STATS(mpctx, "value %f aspeed", mpctx->speed_factor_a - 1); MP_STATS(mpctx, "value %f vspeed", mpctx->speed_factor_v - 1); @@ -996,6 +1018,32 @@ static void calculate_frame_duration(struct MPContext *mpctx) MP_STATS(mpctx, "value %f frame-duration-approx", MPMAX(0, approx_duration)); } +static void apply_video_crop(struct MPContext *mpctx, struct vo *vo) +{ + for (int n = 0; n < mpctx->num_next_frames; n++) { + struct m_geometry *gm = &vo->opts->video_crop; + struct mp_image_params p = mpctx->next_frames[n]->params; + if (gm->xy_valid || (gm->wh_valid && (gm->w > 0 || gm->h > 0))) + { + m_rect_apply(&p.crop, p.w, p.h, gm); + } + + if (p.crop.x1 == 0 && p.crop.y1 == 0) + return; + + if (!mp_image_crop_valid(&p)) { + char *str = m_option_type_rect.print(NULL, gm); + MP_WARN(vo, "Ignoring invalid --video-crop=%s for %dx%d image\n", + str, p.w, p.h); + talloc_free(str); + *gm = (struct m_geometry){0}; + mp_property_do("video-crop", M_PROPERTY_SET, gm, mpctx); + return; + } + mpctx->next_frames[n]->params.crop = p.crop; + } +} + void write_video(struct MPContext *mpctx) { struct MPOpts *opts = mpctx->opts; @@ -1041,6 +1089,11 @@ void write_video(struct MPContext *mpctx) if (r == VD_EOF) { if (check_for_hwdec_fallback(mpctx)) return; + if (check_for_forced_eof(mpctx)) { + uninit_video_chain(mpctx); + handle_force_window(mpctx, true); + return; + } if (vo_c->filter->failed_output_conversion) goto error; @@ -1078,7 +1131,11 @@ void write_video(struct MPContext *mpctx) } } - MP_DBG(mpctx, "video EOF (status=%d)\n", mpctx->video_status); + // Avoid pointlessly spamming the logs every frame. + if (!vo_c->is_sparse || !vo_c->sparse_eof_signalled) { + MP_DBG(mpctx, "video EOF (status=%d)\n", mpctx->video_status); + vo_c->sparse_eof_signalled = vo_c->is_sparse; + } return; } @@ -1104,9 +1161,12 @@ void write_video(struct MPContext *mpctx) } } + // Inject vo crop to notify and reconfig if needed + apply_video_crop(mpctx, vo); + // 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)) { + struct mp_image_params *p = &mpctx->next_frames[0]->params; + if (!vo->params || !mp_image_params_static_equal(p, vo->params)) { // Changing config deletes the current frame; wait until it's finished. if (vo_still_displaying(vo)) { vo_request_wakeup_on_done(vo); @@ -1115,16 +1175,16 @@ void write_video(struct MPContext *mpctx) const struct vo_driver *info = mpctx->video_out->driver; char extra[20] = {0}; - if (p.p_w != p.p_h) { + if (p->p_w != p->p_h) { int d_w, d_h; - mp_image_params_get_dsize(&p, &d_w, &d_h); + mp_image_params_get_dsize(p, &d_w, &d_h); snprintf(extra, sizeof(extra), " => %dx%d", d_w, d_h); } char sfmt[20] = {0}; - if (p.hw_subfmt) - snprintf(sfmt, sizeof(sfmt), "[%s]", mp_imgfmt_to_name(p.hw_subfmt)); + if (p->hw_subfmt) + snprintf(sfmt, sizeof(sfmt), "[%s]", mp_imgfmt_to_name(p->hw_subfmt)); MP_INFO(mpctx, "VO: [%s] %dx%d%s %s%s\n", - info->name, p.w, p.h, extra, mp_imgfmt_to_name(p.imgfmt), sfmt); + info->name, p->w, p->h, extra, mp_imgfmt_to_name(p->imgfmt), sfmt); MP_VERBOSE(mpctx, "VO: Description: %s\n", info->description); int vo_r = vo_reconfig2(vo, mpctx->next_frames[0]); @@ -1147,7 +1207,7 @@ void write_video(struct MPContext *mpctx) } double time_frame = MPMAX(mpctx->time_frame, -1); - int64_t pts = mp_time_us() + (int64_t)(time_frame * 1e6); + int64_t pts = mp_time_ns() + (int64_t)(time_frame * 1e9); // wait until VO wakes us up to get more frames // (NB: in theory, the 1st frame after display sync mode change uses the @@ -1189,7 +1249,7 @@ void write_video(struct MPContext *mpctx) diff /= mpctx->video_speed; if (mpctx->time_frame < 0) diff += mpctx->time_frame; - frame->duration = MPCLAMP(diff, 0, 10) * 1e6; + frame->duration = MP_TIME_S_TO_NS(MPCLAMP(diff, 0, 10)); } mpctx->video_pts = mpctx->next_frames[0]->pts; |