mpv
mpv copied to clipboard
Frame doubling (to better support FreeSync/G-Sync)
mpv version and platform
N/A, Windows 10 x64
Reproduction steps
Play a 24fps video using a monitor which has adaptive sync but which does not have built-in low-framerate compensation. (Most 4K monitors fall under this, for example. A 40-60hz freesync range is typical with no LFC.)
Expected behavior
Each frame should be presented twice, resulting in a 48fps effective framerate and allowing freesync to kick in. The video should therefore play free of typical 24-on-60 judder.
Win10's built-in "Films & TV" player can do this, and so can Edge. Verified using my monitor's OSD. No other player that I've tested can do it. I think it would be a great feature to add for owners of adaptive sync displays.
I would suggest that the video's actual framerate should be multiplied by the largest integer such that the result does not exceed the monitor's reported refresh rate. For example, 24fps content would be doubled to 48 on 60hz displays, and tripled to 72 on 75hz displays. This maximises the likelihood that adaptive sync will kick in.
Actual behavior
The video is presented at 24fps (confirmed with Afterburner's OSD), and freesync does not kick in. The monitor remains at 60fps, and the expected judder is present.
Log file
N/A
Sample files
N/A, any 24fps file
you can use the fps ffmpeg filter for that, --vf=fps=fps=48
. just simply write a script that sets this filter dynamically by observing/reading the display-fps
and fps
properties, similar to this script.
I thought drivers are already capable of doing this under the hood?
Solving this generally is probably going to be rather tricky, since we don't even know the input framerate. But you could approximate it maybe with --tscale=nearest
and some options to control the display FPS? Dunno.
@Akemi I'll check that out, thanks for the advice. It would be nice to have it built-in instead of a separate lua script, though.
@haasn
I thought drivers are already capable of doing this
Not in general. With FreeSync this is called Low Framerate Compensation. Not all monitors have it, and I don't believe any 4K FreeSync displays do at the moment. Even when it's available, it has been known to cause flicker and other issues. Far better to output somewhere in the native freesync range (usually 40-60).
we don't even know the input framerate
Maybe not in special cases, but the vast majority of files have a fixed known framerate, don't they?
i don't think this is a feature that will get a native implementation within mpv.
yeah most videos are fixed fps, though not all files provide the correct fps information. mpv calculates an estimated fps but that can only be used after a few frames have been decoded and hence is not available at file start/load.
Maybe not in special cases, but the vast majority of files have a fixed known framerate, don't they?
Problem is that even when their metadata contains it you don't know if it's correct. I've got plenty of files here claiming slightly wrong values or such that are completely ridiculous (0 or 9000). You don't notice this because players really don't care about this value. Relying on it would definitely lead to problems.
Well, it looks like my ignorance of video timing techniques is showing. If the framerate isn't reliably known upfront, then the duration of the frame that is about to be presented must at least be known immediately before said presentation, right? So to greatly oversimplify (for example I assume present() returns instantly, which it definitely doesn't), I assume it might look something like this right now:
present(frame.data);
sleep_ms(frame.duration);
The frame doubling would look something like
present(frame.data);
sleep_ms(frame.duration/2);
present(frame.data);
sleep_ms(frame.duration/2);
Actually, couldn't we do it like this? Combining the two things we do know:
- The display framerate (which for adaptive sync I assume is meant to be an uppter limit)?
- The presentation duration of the frame (by comparing this and the next frame's timestamp)
We could calculate the number of repetitions as floor(frame_duration / vsync_interval)
. So for an example of a 41.7ms frame on a 16.6ms monitor, we would end up repeating it twice, each repeat for a duration of exactly 20.85ms, giving us an effective total framerate of 48 Hz (by repeating each 24 Hz frame twice).
Yes- if I follow you correctly, that would achieve what I'm looking for. The monitor's reported framerate is indeed an upper limit- the lower limit is usually around 40. (Any lower than that requires LFC which is a bit of a minefield and, as I said above, not currently available on 4K monitors).
Any possibility we could get this reopened, @Akemi?
sure.
Just curious, does interpolation
do the job?
@zc62 The whole point of adaptive sync is NOT to synchronize to the display refresh rate but to just present at a certain rate and the monitor adjusts to it.
The whole implementation should be really simple:
- the monitor runs at the max refresh rate
- a configuration option tells mpv the min_adaptive_refresh_rate
- frame_repeat_count = ceil(min_adaptive_refresh_rate / video_fps)
- present each frame at 1 / (video_fps * frame_repeat_count) time intervals
Example 1:
- 120 Hz monitor with 40 Hz min refresh rate
- 23.976 fps video
- => frame_repeat_count = 2
- => each frame (including the repeated ones) is presented at ~20.8542 ms intervals (= 47,952 fps)
Example 2:
- same 120 Hz monitor with 40 Hz min refresh rate
- 60 fps video
- => frame_repeat_count = 1
- => each frame (including the repeated ones) is presented at ~16.667 ms intervals (= 60 fps)
Example 3 (worst case):
- awful 90 Hz monitor with 60 Hz min refresh rate
- 50 fps video
- => frame_repeat_count = 2
- => each frame (including the repeated ones) is presented at 10ms intervals (= 100 fps)
- since 100 fps is greater than the monitor max refresh rate there are two simple options (ignoring the more complex vsync case):
- no vsync => tearing
- enhanced sync => no tearing, some judder (the higher the monitor refresh rate, the better)
You don't need any interpolation, no waiting for vsync. Just present the frames at a high enough rate (by showing them multiple times if needed) and the monitor will adjust.
I have suggested this years ago. It's way simpler than any of the interpolation/resampling logic implemented right now.
I've recently acquired a freesync display, which has the range set to 57-144Hz. When I play a 24fps video file with mpv in fullscreen, the OSD of my monitor shows something around 72Hz. I think the feature is already there? Nothing needs to be done in mpv. GPU APIs or drivers handle this. You could try to enable the recently added 'Variable Refresh Rate' for d3d11 in win10 1903, but I tried vulkan API, works too.
Solving this generally is probably going to be rather tricky, since we don't even know the input framerate.
I think it's okay. It's only needed to know pts of this frame and next frame. And you insert a frame between them (if the interval is not already short enough).
(And if effective FPS is already high enough, then that interval will always be short enough and nothing will be inserted.)
Doubling the framerate is trivial, you can probably construct something with existing libavfilter filters.
FWIW, my freesync display allow frame doubles just fine - and from an nvidia gpu no less.
FWIW, my freesync display allow frame doubles just fine - and from an nvidia gpu no less.
Could you post your config file? I have a 35-60Hz screen running off a 2080Ti but I can't get freesync to work properly with mpv, for 24 fps videos it uses 2.500 vsync rate resulting in 60 fps instead of doubling to just 2.000 at 48fps.
@Artins90 You're using interpolation. Turn it off.
Do you mean --video-sync=display...?
Sorry, I do mean that - although I think he has to also be turning on interpolation for it to be doing 2.5 rate, right?
@Artins90 You're using interpolation. Turn it off.
Without interpolation the 24 fps video runs at 60 fps with 2.5 vsync rate. If I delete "video-sync=display-resample" from the config file, the afterburner osd rports 24 fps but my screen can't do 24 fps since it's outside the freesync window and the monitor doesn't support frame doubling on its own.
I would like to know if there is a way to have mpv output each frame of the 24 fps video 2 times, resulting in 48 fps playback, which is within the freesync range supported by the monitor, forcing vsync rate to 2.000 rather than 2.500 would do the trick.
Well, sure, then you're in the same boat as the original reporter. I was just saying my display does frame doubling so everything works fine. There's no config magic that will allow it to work with a non-doubling display.
I don't think interpolation affects how many frames it outputs. It'll just affect performance in various ways.
And also, with --video-sync on default, it won't measure or report any relevant statistics (it'll only show the nominal FPS reported by the OS on the stats screen).
Also I guess you can try --vf=fps=48/1.001
(if I understand the problem correctly, but I didn't try to). Of course this is for testing only and very much not recommended for normal use. It's meant for files with ~23 fps, and won't work 100% correctly (mkv rounded timestamps and all that).
Does this help?
video-sync=display-resample
interpolation=yes
tscale=oversample
override-display-fps=48
That'll make mpv frame double, but I have no idea if that results in usable or sane timing, since it will still be driven by the vsync loop. Actually, maybe you could use that but set it to the maximum fps your display is capable of handling? That would more than frame double though.
This will just set 48 Hz as initial nominal display fps. Then mpv will notice that it can output more and adjust the display fps. (No idea how it works with freesync. I even have a fresync display, but the POS is not recognized as such, and it's probably completely different from the Windows behavior anyway.)
Oh, right, it doesn't change the pattern unless you combine it with --untimed
but then the timing is fucked anyway.
Also I guess you can try
--vf=fps=48/1.001
(if I understand the problem correctly, but I didn't try to). Of course this is for testing only and very much not recommended for normal use. It's meant for files with ~23 fps, and won't work 100% correctly (mkv rounded timestamps and all that).
It works, I have never seen video play so smooth, it's quite impressive.
OpenGL wouldn't trigger G-sync no matter what, after switching to DX11 and setting:
video-sync=audio
vf=fps=48/1.001
now g-sync works when playing the video in fullscreen, windowed mode disables it despite having set g-sync to both windowed and fullscreen in the Nvidia control panel, that's not a problem though.
It would be nice if vf=fps= could be set to 2x the video fps automatically only when the result of such multiplication falls within the variable refresh range of the monitor (in my case 35-60Hz), maybe falling back to "display-resample" automatically when the result of the multiplication is outside the range.
Guys, mpv does not support any form of adaptive sync. The monitor refresh rate is whatever you configure in Windows. It might be 60, 75, 120, 144 ... Hz and whatever the video fps, it will be displayed at that fixed refresh rate, even in fullscreen.
Afaik, and I could be wrong here, it also doesn't support "mode setting" depending on video fps, that is change the monitor refresh rate e.g. to 60 Hz when playing a 30 fps video.
I have a similar observation to what philipl had.
My display can do 57-144Hz, and if I enable interpolation and display-resample, the display reports a constant 144Hz.
If I use default interpolation=no
and video-sync=audio
, a 24fps video would trigger 72Hz for my freesync display (though the actual refresh rate reported by the display OSD fluctuates, don't know why.) The OSD of mpv still reports 144Hz (as wm4 has explained).