video: add option to reduce latency by 1 or 2 frames

The playback start logic explicitly waits until the first frame has been
displayed. Usually this will introduce a wait of 1 vsync. For normal
playback this doesn't matter, but with respect to low latency needs,
this only leads to additional data getting queued up in the demuxer or
network buffers.

Another thing is that the timing logic decodes 1 frame ahead (= 1 frame
extra latency) to determine the exact duration of a frame.

To be fair, there doesn't really seem to be a hard reason why this is
needed. With the current code, enabling the option does lead to A/V
desync sometimes (if the demuxer FPS is too inaccurate), and also frame
drops at playback start in some situations. But this all seems to be
avoidable, if the timing logic were to be rewritten completely, which
should probably happen in the future. Thus the new option comes with the
warning that it can be removed any time. This is also why the option has
"hack" in the name.
This commit is contained in:
wm4 2018-03-02 15:37:34 +01:00 committed by Jan Ekström
parent c917992359
commit 775b86212d
5 changed files with 31 additions and 4 deletions

View File

@ -679,6 +679,26 @@ Video
``--vo=vdpau`` has its own code for the ``vo`` framedrop mode. Slight
differences to other VOs are possible.
``--video-latency-hacks=<yes|no>``
Enable some things which tend to reduce video latency by 1 or 2 frames
(default: no). Note that this option might be removed without notice once
the player's timing code does not inherently need to do these things
anymore.
This does:
- Use the demuxer reported FPS for frame dropping. This avoids that the
player needs to decode 1 frame in advance, lowering total latency in
effect. This also means that if the demuxer reported FPS is wrong, or
the video filter chain changes FPS (e.g. deinterlacing), then it could
drop too many or not enough frames.
- Disable waiting for the first video frame. Normally the player waits for
the first video frame to be fully rendered before starting playback
properly. Some VOs will lazily initialize stuff when rendering the first
frame, so if this is not done, there is some likeliness that the VO has
to drop some frames if rendering the first frame takes longer than needed.
``--display-fps=<fps>``
Set the display FPS used with the ``--video-sync=display-*`` modes. By
default, a detected value is used. Keep in mind that setting an incorrect

View File

@ -52,6 +52,7 @@ demuxer-lavf-probe-info=nostreams # avoid probing unless absolutely needed
demuxer-lavf-analyzeduration=0.1 # if it probes, reduce it
video-sync=audio # DS currently requires reading ahead a frame
interpolation=no # requires reference frames (more buffering)
video-latency-hacks=yes # typically 1 or 2 video frame less latency
# Compatibility alias (deprecated)
[opengl-hq]

View File

@ -608,6 +608,7 @@ const m_option_t mp_opts[] = {
{"vo", 1},
{"decoder", 2},
{"decoder+vo", 3})),
OPT_FLAG("video-latency-hacks", video_latency_hacks, 0),
OPT_DOUBLE("display-fps", frame_drop_fps, M_OPT_MIN, .min = 0),

View File

@ -221,6 +221,7 @@ typedef struct MPOpts {
float default_max_pts_correction;
int autosync;
int frame_dropping;
int video_latency_hacks;
double frame_drop_fps;
int term_osd;
int term_osd_bar;

View File

@ -413,13 +413,15 @@ static int get_req_frames(struct MPContext *mpctx, bool eof)
if (mpctx->opts->untimed || mpctx->video_out->driver->untimed)
return 1;
int min = mpctx->opts->video_latency_hacks ? 1 : 2;
// On the first frame, output a new frame as quickly as possible.
// But display-sync likes to have a correct frame duration always.
if (mpctx->video_pts == MP_NOPTS_VALUE)
return mpctx->opts->video_sync == VS_DEFAULT ? 1 : 2;
return mpctx->opts->video_sync == VS_DEFAULT ? 1 : min;
int req = vo_get_num_req_frames(mpctx->video_out);
return MPCLAMP(req, 2, MP_ARRAY_SIZE(mpctx->next_frames) - 1);
return MPCLAMP(req, min, MP_ARRAY_SIZE(mpctx->next_frames) - 1);
}
// Whether it's fine to call add_new_frame() now.
@ -1149,8 +1151,10 @@ void write_video(struct MPContext *mpctx)
if (mpctx->video_status < STATUS_PLAYING) {
mpctx->video_status = STATUS_READY;
// After a seek, make sure to wait until the first frame is visible.
vo_wait_frame(vo);
MP_VERBOSE(mpctx, "first video frame after restart shown\n");
if (!opts->video_latency_hacks) {
vo_wait_frame(vo);
MP_VERBOSE(mpctx, "first video frame after restart shown\n");
}
}
screenshot_flip(mpctx);