summaryrefslogtreecommitdiffstats
path: root/player/video.c
diff options
context:
space:
mode:
Diffstat (limited to 'player/video.c')
-rw-r--r--player/video.c170
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;