diff options
-rw-r--r-- | DOCS/man/input.rst | 12 | ||||
-rw-r--r-- | DOCS/man/options.rst | 37 | ||||
-rw-r--r-- | options/options.c | 4 | ||||
-rw-r--r-- | player/command.c | 11 | ||||
-rw-r--r-- | player/osd.c | 11 | ||||
-rw-r--r-- | player/playloop.c | 12 | ||||
-rw-r--r-- | player/video.c | 8 | ||||
-rw-r--r-- | video/out/vo.c | 119 | ||||
-rw-r--r-- | video/out/vo.h | 5 | ||||
-rw-r--r-- | video/out/vo_vdpau.c | 1 | ||||
-rw-r--r-- | video/out/x11_common.c | 7 |
11 files changed, 187 insertions, 40 deletions
diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index 612c0cad88..f62bbce24f 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -159,7 +159,9 @@ List of Input Commands Take a single screenshot. <each-frame> Take a screenshot each frame. Issue this command again to stop taking - screenshots. + screenshots. Note that you should disable framedropping when using + this mode - or you might receive duplicate images in cases when a + frame was dropped. ``screenshot_to_file "<filename>" [subtitles|video|window]`` Take a screenshot and save it to a given file. The format of the file will @@ -635,8 +637,12 @@ Property list disabled. ``drop-frame-count`` - Frames dropped because they arrived to late. Unavailable if video - is disabled + Frames dropped because they arrived to late. Doesn't necessarily indicate + actual framedrops, just the number of times the decoder was asked to drop. + Unavailable if video is disabled + +``vo-drop-frame-count`` + Frames dropped by VO (when using ``--framedrop=vo``). ``percent-pos`` (RW) Position in current file (0-100). The advantage over using this instead of diff --git a/DOCS/man/options.rst b/DOCS/man/options.rst index 356bd903b3..7839480918 100644 --- a/DOCS/man/options.rst +++ b/DOCS/man/options.rst @@ -430,16 +430,39 @@ Video Do not sleep when outputting video frames. Useful for benchmarks when used with ``--no-audio.`` -``--framedrop=<no|yes>`` - Skip displaying some frames to maintain A/V sync on slow systems. Video - filters are not applied to such frames. For B-frames even decoding is - skipped completely. May produce unwatchably choppy output. - - The ``--vd-lavc-framedrop`` option controls what frames to drop. +``--framedrop=<mode>`` + Skip displaying some frames to maintain A/V sync on slow systems, or + playing high framerate video on video outputs that have an upper framerate + limit. + + The argument selects the drop methods, and can be one of the following: + + <no> + Disable any framedropping (default). + <vo> + Drop late frames on video output. This still decodes and filters all + frames, but doesn't render them on the VO. It tries to query the + display FPS (X11 only, not correct on multi-monitor systems), or + assumes infinite display FPS if that fails. Drops are indicated in + the terminal status line as ``D: `` field. If the decoder is too slow, + in theory all frames would have to be dropped (because all frames are + too late) - to avoid this, frame dropping stops if the effective + framerate is below 10 FPS. + <decoder> + Old, decoder-based framedrop mode. (This is the same as ``--framedrop=yes`` + in mpv 0.5.x and before.) This tells the decoder to skip frames (unless + they are needed to decode future frames). May help with slow systems, + but can produce unwatchably choppy output, or even freeze the display + complete. Not recommended. + The ``--vd-lavc-framedrop`` option controls what frames to drop. + <decoder+vo> + Enable both modes. Not recommended. .. note:: - Practical use of this feature is questionable. Disabled by default. + ``--vo=vdpau`` (also the default VO) always has the ``vo`` framedrop + mode enabled. It doesn't increment the ``D:`` field in the statusline + either. ``--hwdec=<api>`` Specify the hardware video decoding API that should be used if possible. diff --git a/options/options.c b/options/options.c index caf0171a7a..af294a7709 100644 --- a/options/options.c +++ b/options/options.c @@ -458,7 +458,9 @@ const m_option_t mp_opts[] = { OPT_CHOICE("framedrop", frame_dropping, 0, ({"no", 0}, - {"yes", 1})), + {"vo", 1}, + {"decoder", 2}, + {"decoder+vo", 3})), OPT_FLAG("untimed", untimed, M_OPT_FIXED), diff --git a/player/command.c b/player/command.c index c0800d612d..9ca2ce8812 100644 --- a/player/command.c +++ b/player/command.c @@ -386,6 +386,16 @@ static int mp_property_drop_frame_cnt(void *ctx, struct m_property *prop, return m_property_int_ro(action, arg, mpctx->drop_frame_cnt); } +static int mp_property_vo_drop_frame_count(void *ctx, struct m_property *prop, + int action, void *arg) +{ + MPContext *mpctx = ctx; + if (!mpctx->d_video) + return M_PROPERTY_UNAVAILABLE; + + return m_property_int_ro(action, arg, vo_get_drop_count(mpctx->video_out)); +} + /// Current position in percent (RW) static int mp_property_percent_pos(void *ctx, struct m_property *prop, int action, void *arg) @@ -2686,6 +2696,7 @@ static const struct m_property mp_properties[] = { {"avsync", mp_property_avsync}, {"total-avsync-change", mp_property_total_avsync_change}, {"drop-frame-count", mp_property_drop_frame_cnt}, + {"vo-drop-frame-count", mp_property_vo_drop_frame_count}, {"percent-pos", mp_property_percent_pos}, {"time-start", mp_property_time_start}, {"time-pos", mp_property_time_pos}, diff --git a/player/osd.c b/player/osd.c index 4efac8e7e9..6fba20468e 100644 --- a/player/osd.c +++ b/player/osd.c @@ -39,6 +39,8 @@ #include "demux/demux.h" #include "sub/osd.h" +#include "video/out/vo.h" + #include "core.h" #include "command.h" @@ -219,8 +221,13 @@ void print_status(struct MPContext *mpctx) #endif { // VO stats - if (mpctx->d_video && mpctx->drop_frame_cnt) - saddf(&line, " Late: %d", mpctx->drop_frame_cnt); + if (mpctx->d_video) { + if (mpctx->drop_frame_cnt) + saddf(&line, " Late: %d", mpctx->drop_frame_cnt); + int64_t c = vo_get_drop_count(mpctx->video_out); + if (c > 0) + saddf(&line, " D: %"PRId64, c); + } } float cache = mp_get_cache_percent(mpctx); diff --git a/player/playloop.c b/player/playloop.c index 33e3f050cd..e3d1ada323 100644 --- a/player/playloop.c +++ b/player/playloop.c @@ -66,11 +66,10 @@ void pause_player(struct MPContext *mpctx) mpctx->osd_function = 0; mpctx->paused_for_cache = false; - if (mpctx->video_out && mpctx->d_video && mpctx->video_out->config_ok) - vo_control(mpctx->video_out, VOCTRL_PAUSE, NULL); - if (mpctx->ao && mpctx->d_audio) - ao_pause(mpctx->ao); // pause audio, keep data if possible + ao_pause(mpctx->ao); + if (mpctx->video_out) + vo_set_paused(mpctx->video_out, true); // Only print status if there's actually a file being played. if (mpctx->num_sources) @@ -97,8 +96,9 @@ void unpause_player(struct MPContext *mpctx) if (mpctx->ao && mpctx->d_audio) ao_resume(mpctx->ao); - if (mpctx->video_out && mpctx->d_video && mpctx->video_out->config_ok) - vo_control(mpctx->video_out, VOCTRL_RESUME, NULL); // resume video + if (mpctx->video_out) + vo_set_paused(mpctx->video_out, false); + (void)get_relative_time(mpctx); // ignore time that passed during pause end: diff --git a/player/video.c b/player/video.c index 43b51bfd58..ffe24e621c 100644 --- a/player/video.c +++ b/player/video.c @@ -278,8 +278,7 @@ int reinit_video_chain(struct MPContext *mpctx) vo_control(mpctx->video_out, saver_state ? VOCTRL_RESTORE_SCREENSAVER : VOCTRL_KILL_SCREENSAVER, NULL); - vo_control(mpctx->video_out, mpctx->paused ? VOCTRL_PAUSE - : VOCTRL_RESUME, NULL); + vo_set_paused(mpctx->video_out, mpctx->paused); mpctx->sync_audio_to_video = !sh->attached_picture; mpctx->vo_pts_history_seek_ts++; @@ -343,7 +342,7 @@ static int check_framedrop(struct MPContext *mpctx) if (d < -mpctx->dropped_frames * frame_time - 0.100) { mpctx->drop_frame_cnt++; mpctx->dropped_frames++; - return mpctx->opts->frame_dropping; + return !!(mpctx->opts->frame_dropping & 2); } else mpctx->dropped_frames = 0; } @@ -576,7 +575,8 @@ static int update_video(struct MPContext *mpctx, double endpts) } - bool vo_framedrop = !!mpctx->video_out->driver->flip_page_timed; + //bool vo_framedrop = !!mpctx->video_out->driver->flip_page_timed; + bool vo_framedrop = !!(mpctx->opts->frame_dropping & 1); int min_frames = vo_framedrop ? 2 : 1; // framedrop needs duration // Already enough video buffered? diff --git a/video/out/vo.c b/video/out/vo.c index e0c8d3ebed..72f98e8859 100644 --- a/video/out/vo.c +++ b/video/out/vo.c @@ -130,9 +130,16 @@ struct vo_internal { bool hasframe; bool request_redraw; + bool paused; int64_t flip_queue_offset; // queue flip events at most this much in advance + int64_t last_flip; + int64_t vsync_interval; + int64_t drop_count; + bool dropped_frame; // the previous frame was dropped + struct mp_image *dropped_image; // used to possibly redraw the dropped frame + int64_t wakeup_pts; // time at which to pull frame from decoder bool rendering; // true if an image is being rendered @@ -323,6 +330,11 @@ static void run_reconfig(void *p) vo->params = NULL; } forget_frames(vo); // implicitly synchronized + + double display_fps = 1000.0; // assume infinite if unset + vo->driver->control(vo, VOCTRL_GET_DISPLAY_FPS, &display_fps); + vo->in->vsync_interval = 1e6 / display_fps; + MP_VERBOSE(vo, "Assuming %f FPS for framedrop.\n", display_fps); } int vo_reconfig(struct vo *vo, struct mp_image_params *params, int flags) @@ -358,7 +370,9 @@ static void forget_frames(struct vo *vo) { struct vo_internal *in = vo->in; in->hasframe = false; + in->drop_count = 0; mp_image_unrefp(&in->frame_queued); + mp_image_unrefp(&in->dropped_image); } #ifndef __MINGW32__ @@ -496,6 +510,17 @@ void vo_wait_frame(struct vo *vo) pthread_mutex_unlock(&in->lock); } +static int64_t prev_sync(struct vo *vo, int64_t ts) +{ + struct vo_internal *in = vo->in; + + int64_t diff = (int64_t)(ts - in->last_flip); + int64_t offset = diff % MPMAX(in->vsync_interval, 1); + if (offset < 0) + offset += in->vsync_interval; + return ts - offset; +} + static bool render_frame(struct vo *vo) { struct vo_internal *in = vo->in; @@ -510,33 +535,59 @@ static bool render_frame(struct vo *vo) return false; } + mp_image_unrefp(&in->dropped_image); + in->rendering = true; in->frame_queued = NULL; - pthread_mutex_unlock(&in->lock); + // The next time a flip (probably) happens. + int64_t next_vsync = prev_sync(vo, mp_time_us()) + in->vsync_interval; + int64_t end_time = pts + duration; + + in->dropped_frame = end_time < next_vsync; + in->dropped_frame &= !!(vo->global->opts->frame_dropping & 1); + in->dropped_frame &= !(vo->driver->caps & VO_CAP_FRAMEDROP) && + !vo->driver->untimed && !vo->driver->encode; + // Even if we're hopelessly behind, rather degrade to 10 FPS playback, + // instead of just freezing the display forever. + in->dropped_frame &= mp_time_us() - in->last_flip < 100 * 1000; + + if (in->dropped_frame) { + in->drop_count += 1; + in->dropped_image = img; + } else { + pthread_mutex_unlock(&in->lock); - vo->driver->draw_image(vo, img); + vo->driver->draw_image(vo, img); - int64_t target = pts - in->flip_queue_offset; - while (1) { - int64_t now = mp_time_us(); - if (target <= now) - break; - mp_sleep_us(target - now); - } + int64_t target = pts - in->flip_queue_offset; + while (1) { + int64_t now = mp_time_us(); + if (target <= now) + break; + mp_sleep_us(target - now); + } - if (vo->driver->flip_page_timed) - vo->driver->flip_page_timed(vo, pts, duration); - else - vo->driver->flip_page(vo); + if (vo->driver->flip_page_timed) + vo->driver->flip_page_timed(vo, pts, duration); + else + vo->driver->flip_page(vo); + + in->last_flip = mp_time_us(); + + MP_DBG(vo, "phase: %ld\n", (long)(in->last_flip % in->vsync_interval)); + + pthread_mutex_lock(&in->lock); + } vo->want_redraw = false; - pthread_mutex_lock(&in->lock); in->request_redraw = false; in->rendering = false; + pthread_cond_signal(&in->wakeup); // for vo_wait_frame() mp_input_wakeup(vo->input_ctx); + pthread_mutex_unlock(&in->lock); return true; @@ -546,15 +597,28 @@ static void do_redraw(struct vo *vo) { struct vo_internal *in = vo->in; + vo->want_redraw = false; + pthread_mutex_lock(&in->lock); in->request_redraw = false; + bool skip = !in->paused && in->dropped_frame; + struct mp_image *img = in->dropped_image; + if (!skip) { + in->dropped_image = NULL; + in->dropped_frame = false; + } pthread_mutex_unlock(&in->lock); - vo->want_redraw = false; - - if (!vo->config_ok || vo->driver->control(vo, VOCTRL_REDRAW_FRAME, NULL) < 1) + if (!vo->config_ok || skip) return; + if (img) { + vo->driver->draw_image(vo, img); + } else { + if (vo->driver->control(vo, VOCTRL_REDRAW_FRAME, NULL) < 1) + return; + } + if (vo->driver->flip_page_timed) vo->driver->flip_page_timed(vo, 0, -1); else @@ -601,6 +665,27 @@ static void *vo_thread(void *ptr) return NULL; } +void vo_set_paused(struct vo *vo, bool paused) +{ + struct vo_internal *in = vo->in; + pthread_mutex_lock(&in->lock); + if (in->paused != paused) { + in->paused = true; + if (in->paused && in->dropped_frame) + in->request_redraw = true; + } + pthread_mutex_unlock(&in->lock); + vo_control(vo, paused ? VOCTRL_PAUSE : VOCTRL_RESUME, NULL); +} + +int64_t vo_get_drop_count(struct vo *vo) +{ + pthread_mutex_lock(&vo->in->lock); + int64_t r = vo->in->drop_count; + pthread_mutex_unlock(&vo->in->lock); + return r; +} + // Make the VO redraw the OSD at some point in the future. void vo_redraw(struct vo *vo) { diff --git a/video/out/vo.h b/video/out/vo.h index 0a8dca8c41..9077ab49b1 100644 --- a/video/out/vo.h +++ b/video/out/vo.h @@ -85,6 +85,7 @@ enum mp_voctrl { VOCTRL_SET_COMMAND_LINE, // char** VOCTRL_GET_ICC_PROFILE_PATH, // char** + VOCTRL_GET_DISPLAY_FPS, // double* VOCTRL_GET_PREF_DEINT, // int* }; @@ -134,6 +135,8 @@ struct voctrl_screenshot_args { // VO does handle mp_image_params.rotate in 90 degree steps #define VO_CAP_ROTATE90 1 +// VO does framedrop itself (vo_vdpau). Untimed/encoding VOs never drop. +#define VO_CAP_FRAMEDROP 2 struct vo; struct osd_state; @@ -282,6 +285,8 @@ bool vo_has_frame(struct vo *vo); void vo_redraw(struct vo *vo); void vo_seek_reset(struct vo *vo); void vo_destroy(struct vo *vo); +void vo_set_paused(struct vo *vo, bool paused); +int64_t vo_get_drop_count(struct vo *vo); void vo_set_flip_queue_offset(struct vo *vo, int64_t us); void vo_wakeup(struct vo *vo); diff --git a/video/out/vo_vdpau.c b/video/out/vo_vdpau.c index 28f1a68c0c..e3498be1d1 100644 --- a/video/out/vo_vdpau.c +++ b/video/out/vo_vdpau.c @@ -1138,6 +1138,7 @@ static int control(struct vo *vo, uint32_t request, void *data) const struct vo_driver video_out_vdpau = { .description = "VDPAU with X11", .name = "vdpau", + .caps = VO_CAP_FRAMEDROP, .preinit = preinit, .query_format = query_format, .reconfig = reconfig, diff --git a/video/out/x11_common.c b/video/out/x11_common.c index 5c3c5aea0d..ef72fb4ca7 100644 --- a/video/out/x11_common.c +++ b/video/out/x11_common.c @@ -1492,6 +1492,13 @@ int vo_x11_control(struct vo *vo, int *events, int request, void *arg) case VOCTRL_UPDATE_WINDOW_TITLE: vo_x11_update_window_title(vo); return VO_TRUE; + case VOCTRL_GET_DISPLAY_FPS: { + double fps = vo_x11_vm_get_fps(vo); + if (fps <= 0) + break; + *(double *)arg = fps; + return VO_TRUE; + } } return VO_NOTIMPL; } |