View Full Version : Video player Shaders guide (MPC-HC, mpc-BE, mpv)
butterw2
2nd November 2023, 02:12
Contrast adjustment via S-Curve
Contrast increase
Contrast(0.10) is an effective method to improve the viewing of hazy web-videos. But this isn't always applicable (blows-up the white/black ranges then clips them).
An S-curve (boosts midtone contrast ...at the cost of degraded contrast in highlights/shadows !) can avoid this problem if used with a suitably low blending parameter (ex: S in [0, 0.125]).
Symmetrical s-curves:
x: pixel rgb in [0, 1.0].
the max boost occurs at the midpoint (x=0.5).
- Sigmoid(0.05), out = 1.0 / ( 1.0 + exp(-14*x +7 )) ), (1 texture, 12 arithmetic) >> my sCurve shader (https://github.com/butterw/bShaders/tree/master/A-pack).
max contrast boost: S*2.5
contrast degradation at the black and white points: S.
- S2(0.125), out = 0.5 + x1 / (0.5 + abs(x1)) with x1 = x - 0.5, (1 texture, 9 arithmetic)
max contrast boost: S
contrast degradation at the black and white points: S/2.
Curve #2 in sweetfx.curves.
- S3(0.10), out = x*x*(3-2*x), (1 texture, 5 arithmetic).
max contrast boost: S/2.
contrast degradation at the black and white points: S.
S-curve vs contrast(0.10) comparison: https://raw.githubusercontent.com/butterw/bShaders/master/img/contrast_vs_sCurves.png
- Contrast(C), out = x*(1 + C) -0.5*C, (1 texture, 1 arithmetic)
- contrast boost= C.
! But when the curve clips, the contrast is zero (contrast.10 clips below x=0.04).
Conclusion: Sigmoid sCurve(0.05) and S2(0.125) are good alternatives to the contrast.10 or expand10_240 adjustment. Contrast is only increased in the midtones, but using a s-curve means the input is not clipped.
Contrast reduction
S-curves can also be used to reduce contrast. For negative values of the parameter S, midtone contrast is decreased, and near-Black (darks) and near-White (brights) contrast is increased.
Notes:
- It is not typically necessary to increase contrast for movies (though a low Strength s-curve adjustment could be beneficial in special cases, ex: faded colors in older films).
! Because s-curves (with S positive) degrade the contrast (and lower brightness) in shadows, they can have a detrimental effect in dark scenes.
butterw2
11th November 2023, 14:07
#Display on a 1440p monitor using mpv v0.37(gpu-next) with igpu (intel uhd730).
mpv has recently changed its default scalers in (https://forum.doom9.org/showthread.php?p=1936377#post1936377) and I've tested different sources on my 1440p monitor.
Win10> mpv --vo=gpu-next --scale=lanczos --cscale=catmull-rom --sigmoid-upscaling --dither-depth=auto
- low-res sources (low quality web videos) >> upscale 2x or 1.5x
Sources up to 480p will need scaling (the display is otherwise too small), ex: 2x upscale: --window-scale=2. The quality of the video limits how much you can upscale/sharpen.
igv\FSRCNNX_x2_16_0_4_1_distort.glsl sometimes works OK for 2x upscale with strong artefact removal. It runs for sources up to 360p on my igpu.
For 480p sources, I would probably limit the upscale to 1.5x and not apply any sharpener.
- Low quality web sources (720p, 1080p) >> watch at 1x or downscale 0.75x
Bad quality 1080p (upscaled) web videos with are quite common. Downscaling 0.75x is an option.
Use --correct-downscaling, dscale=hermite (bicubic with b=c=0) with --linear-downscaling.
- OK quality videos (720p, 1080p), ex: 1080p BDRip 2000kbps x265 >> upscale to 1440p.
Upscaling to fullscreen with lanczos or spline36 should work OK (scaling ratio: 2x, 1.33x). A little extra sharpening might be beneficial.
Current mpv spatial sharpeners/external upscalers (by igpu usage):
lumasharpen.glsl (luma) < AMD_CAS_lite_rgb.glsl < agyild/FSR.glsl (amd FSR v1.02 mod), upscale ratio: >1 to 2.0 < igv/SSimSuperRes.glsl (SSSR Scaler>1) < igv/Adaptive-sharpen.glsl (0.4)
I will be looking more at sharpeners (at 1 to 2x upscale) as they can improve perceived quality. This typically requires an OK quality source. Filmgrain shaders should also be considered.
For low quality videos, a little contrast boosting can often help, ex: sCurve(0.05).
butterw2
12th November 2023, 15:26
Resolution info available to shaders (there are major differences)
Image resolution (W, H) info available to user shaders with different renderer:
- EVR-CP pre-resize (dx9) shader: you can access video file resolution. Shader does not apply to padding with black bars.
- EVR-CP post-resize (dx9) shader: you can only access the fullscreen resolution not the video file resolution. Shader applies to black bars. >> Not great, could still work in fullscreen.
- MPC-VR (dx11) shader (post-resize only): you can only access windowed resolution (including black bars). Shader does not apply to black bars.
- Mpv shaders (vo=gpu-next)
(LUMA, MAIN: pre-resize rgb, SCALED, OUTPUT: post-resize rgb): you can access both video file resolution (input_size) and resized resolution (target_size, doesn't include black bars). Shader does not apply to black bars.
Trying to get a consistent sharpen strength across shaders (by adjusting the detection level):
Without adjustment:
1) Sharpening depends on the sharpening kernel used.
2) for a given (fixed size) kernel, the sharpen effect depends on the resolution of the image.
- at 100% zoom the source resolution, otherwise the scaled resolution.
The higher the resolution, the lower the sharpening effect for the same sharpen strength parameter.
3) because different renderers do not provide the same resolution information, it isn't possible to make this 100% work the same across renderers. Mpv does provides the full information pre-resize and post-resize.
EDIT: A pre-resize sharpen has a consistent effect°, whatever the display resolution. For a post-resize sharpen, the idea is to normalize the detection for a picture display size of 1280 pixels. If the picture is displayed at a higher resolution, we increase the detection. The following seems to work for an unsharp shader:
detail = detail * max(W, H)/1280. with W, H scaled picture dimensions in pixels.
° In the case of a pre-resize sharpen the effect will depend on the source resolution, so it may make sense to normalize the detection vs source resolution.
butterw2
18th November 2023, 19:34
Contrast Adaptive Sharpening dx9/dx11 hlsl shader (AMD FidelityFX CAS): https://gist.github.com/butterw/ceb89a68bc0aa3b0e317660fb4bacaa3
(9 texture, 47 arithmetic).
The algorithm adjusts the amount of sharpening per pixel to target an even level of sharpness across the image. Areas of the input image that are already sharp are sharpened less, while areas that lack detail are sharpened more. This allows for higher overall natural visual sharpness with fewer artifacts.
parameters:
- CAS: Contrast sharpening Amount. ex: 0.35 [.. to 2.66]. Negative values are possible for lighter sharpening.
- Show_Edge: integer [0 or 1] 0: sharpened image, 1: detail.
Note: doesn't generate a bright pixel at the border with the padded black bars in post-resize fullscreen mode.
stax76
21st November 2023, 09:58
I've updated awesome-mpv (https://github.com/stax76/awesome-mpv) today, syncing it with the user scripts list in the wiki. I noticed you added a script that is similar with an old script of mine, showing a simpler time format when seeking.
https://github.com/butterw/bShaders/blob/master/mpv/settings/scripts/btime.js
https://github.com/stax76/mpv-scripts/blob/main/misc.lua#L138
https://github.com/stax76/mpv-scripts/blob/main/misc.lua#L276
In the past I was also using the seek event, but this caused a problem with another popular mpv feature, users were dissatisfied, so I changed it to not use the seek event. I don't remember what the problem was, unfortunately, maybe changing chapters, I checked the issue tracker, but couldn't find it. I also had a JavaScript version, that was before I learned Lua.
butterw2
22nd November 2023, 12:17
I would probably never have coded btime.js without the inspiration from your original javascript code, so thanks for that.
I prefer the javascript syntax and haven't run into any performance issues with my scripts so far, but translating to lua isn't impossible. I use lua scripts on mpv-android (works on my years-old phone, but also on a weak android-tv device I have).
Using seek event you can display time each time there is a seek (screenshot (https://raw.githubusercontent.com/butterw/bShaders/master/mpv/settings/scripts/btime_osd-bar.jpg)). It should work perfectly work with core mpv, if not it's a bug (or a design flaw) IMO. Now that mpv v0.37 has been released (ex: https://github.com/shinchiro/mpv-winbuild-cmake/releases) I would encourage anyone to report any such issues at github.com/mpv-player/mpv.
Obviously when mixing (complex) user scripts there is always potential for conflict, so manually binding a time display function (to every seek command) can avoid such issues.
To avoid these issues, mpv should make one alternative time-display format available by default as ${playback-time/short}. The mpc-hc format (00:10:05 / 02:15:00, 03:05 / 55:00) would be good here.
stax76
25th November 2023, 15:44
I think I remember now what the problem was, something with the built-in OSC, maybe changing chapters with button controls in the OSC, things overlap then.
butterw2
28th November 2023, 19:13
# Filmgrain in shaders, and how to blend it.
https://github.com/butterw/bShaders/tree/master/FilmGrain_Noise
Filmgrain is a physical characteric of film media, digital camera sensors don't have grain, only noise.
Adding filmgrain can be beneficial for sources that have been heavily denoised or are completely devoid of noise/grain (ex: some generative AI outputs). A small amount of grain paradoxically increases perceived sharpness and quality. Adding too much just degrades the signal over noise ratio.
Grain shaders typically generate a dynamic luma noise pattern and blend it with the original image. Grain is calculated for every (x, y) pixel of the image using a pseudo-random hash function. This requires a number of arithmetic operations but no texture samples, so it can be handled quite easily by current gpus.
float grain = good_hash_function(tex.xy, seed); // random variable with a gaussian distribution.
The simplest way to blend the grain with the original image is by adding it:
out = c0.rgb + Strength*grain
with c0 and out pixel.rgb values in [0, 1.0].
grain, positive or negative grayscale value, ex: in [-0.125, 0.125].
The grain pattern can be displayed with: return 0.5+grain; // Show_Grain
Screenshots of the grain pattern can be analysed using the histogram feature of photo editors.
The grain pattern is dynamic, meaning it changes every frame. This is done by updating the seed of the pseudo-random hash function using a uniform variable provided by the video player ('mpc' or ''mpv''),
ex: the player 'clock' in seconds, the integer ''frame'' 'counter' or a ''random'' variable from a uniform distribution in [0, 1.0].
grain shaders:
1) per pixel pseudo-random grayscale noise (with approximate gaussian distribution).
2) additive blending method does not significantly alter the original brightness and contrast of the image.
3) the grain is applied uniformly to the image.
4) grain pattern is updated every frame.
Grain should only be added after any sharpening operations are performed (sharpening generated grain is undesirable).
When upscaling with no post-resize sharpening, grain can be added pre-resize. There are 2 benefits with regards to 1):
- any resizing (ex bicubic A=-0.60, scaling ratio=1.33x) smooths the shape of the distribution and results in a clean gaussian.
- the grain size is scaled. This may achieve a more pleasing result for scaling ratios up to 2x, as single pixels are fairly small at 1080p or higher.
With regards to 4) it can be beneficial to slow down the grain at higher framerates (30, 50 or 60fps video) vs 24 frames-per-second film. A shader using the frame counter as seed can limit the grain update to ex: fps/2 with seed = floor(0.5*counter);
We can change 3) by using a luma-based curve (ex: a parabola). The idea is to apply less grain to dark and bright areas vs the midtones.
To test this out, I used an online palette generator to generate a 9 color grayscale RGB gradient palette. The first color is black, the last white and middle color is midgray128. The palette image was converted to a x264 mp4 video with correct colors (and the desired duration and frame rate) using ffmpeg. https://github.com/butterw/bShaders/blob/73835ae8f3857d6713f10b7582c0e7e51d23a86d/test_LimitedRange/graycard9_1392x336p30-15s_0-32-64-96-128-159-191-223-255.mp4
Results with grain.hlsl shader (1 texture, 42 arithmetic), no resize:
grain.35 (strong grain). (top) C:1 uniform grain vs C0:C1:0.30 (https://raw.githubusercontent.com/butterw/bShaders/4bd779e4e81152c6c5f1b0ead0ff15141c61e09f/FilmGrain_Noise/grain.35_D1vs.30.png)
EDIT An alternative blend that avoids having too much grain on darks is:
c0.rgb = c0.rgb* (1 + 1.30*Strength*grain) = c0.rgb + c0.rgb*1.30*Strength*grain;
This is a simple linear shaping function (https://raw.githubusercontent.com/butterw/bShaders/45c093f0a721981e01ae6ae4b2961b41f87d9586/FilmGrain_Noise/grain-profile_parabola_linear_uniform.png) (y=ax+b with a=1.30 and b=0). It has less grain on darks and midtones vs parabola(C:0.25) but higher grain in highlights (the grain will be visible there).
butterw2
2nd December 2023, 21:45
# tunable parabolic shaping function, in/out: [0, 1.0]
The grain.hlsl/.glsl shader (previous post) can apply less grain to dark and bright areas vs midtones by multiplying grain with a shaping function based on luma. grain = gshape(luma)*grain;
The shaping function is a tunable parabola (https://raw.githubusercontent.com/butterw/bShaders/88173f2273f9de714c220dae2ebef1fb825e4140/FilmGrain_Noise/grain_densityCurve-vsLuma_D0-black_D1-red.png) centered at midgray, it has a zero-derivative at x=0.5. parabola(x:0.5)=1;,
tuning parameter C, ex: 0.30, typ in [0 to 1.0]
By default, the values in x:0 and x:1.0 are set by parameter C: Curve01. parabola(x:0)=parabola(x:1)=C
But its possible to use different values of C for x>0.5 and x<=0.5. ex: float C = (x>0.5) ? Curve1: Curve0;
for negative C, use: max(gshape(luma, C), 0)
float pow2(float x) { return x*x; }
float gshape(float x, float C) {
// tunable parabola(x, C) centered in x=0.5: C + (1-C)*(1 -(2*x-1)^2) = (C-1)*(2*x-1)^2 +1 = (C-1)*(2*(x-0.5))^2 +1
return lerp(1 -pow2(2*x -1), 1, C);
}
butterw2
7th December 2023, 17:18
# restart-mpv to recompile shaders and reload configuration
mpv-daily (v0.37.0-70-g562450f5) fixes --watch-later-options-append=glsl-shaders
This applies to input.conf: Q quit-watch-later
It also corrects the glsl-shaders property display, which now uses the platform file-list separator (";" on windows, ':' elsewhere) instead of "," previously. The separator is required when multiple shaders are used.
This does mean scripts using the glsl-shaders property may need to be updated.
Ex: here is a standalone update to my script restart-mpv.js. Use case: restart mpv to recompile shaders (and reload scripts, .conf files) after they have been modified.
v0.10 is backwards compatible: https://github.com/butterw/bShaders/commit/1c75f06ef7539d5cf1d9644cfde08d4b5206a8d7. I'll provide updated versions of my mpv shader/OSD scripts in the next release of A-pack.
With mpv v0.37-dev, restart-mpv can now be done directly from input.conf without any script required (tested on windows):
F5 run mpv.com --glsl-shaders=${glsl-shaders} --start=${playback-time} --pause=${pause} "${path}"; quit #restart-mpv
# Script Keybindings in mpv:
The scripts adds a Shift+F5 script-binding by default. I would recommend setting the keybinding explicitly in input.conf: this is where all your keybindings should be defined, not in scripts. The option --no-input-default-bindings disables regular script key bindings and the built-in defaults.
mp.add_key_binding("Shift+F5", "restart-mpv", restart_mpv); // input.conf: Shift+F5 script-binding restart-mpv
script-message or script-message-to (no default keybinding defined, string parameter support) can be used instead of script-binding.
//mp.register_script_message("restart-mpv", restart_mpv); // input.conf: Shift+F5 script-message restart-mpv
restart_mpv is the name of the function that gets called when the message is received
mp.add_key_binding(null can be good (no default keybinding defined), but you should use _ rather than - in the script name, ex: X script-binding my_script/restart-mpv. Also unlike script-message-to it doesn't support parameters.
With X script-message-to my_script restart-mpv, the message "restart-mpv" is only sent to my_script, this avoids the possibility of random collisions
butterw2
13th December 2023, 11:50
mpv-menu-plugin (https://github.com/tsl0922/mpv-menu-plugin): customizable native context menu for mpv on windows , requires mpv v0.37 or later.
https://github.com/tsl0922/mpv-menu-plugin/releases
previous v1.10 release: 100% C, some dynamic submenus available.
EDIT-2: The current version (v2.0-dev) delegates the dynamic menu updates to a lua script. Without script the menu will be purely static. The default script adds some new dynamic submenus (ex: #@tracks, #@playlist, #@profiles) and it is possible to create your own (by forking the code).
This recent C-plugin project (GPLv2 license) provides mpv with a customizable right-click menu with submenus and commands (the corresponding hotkeys are displayed).
Installation: unzip menu.zip. Put the plugin (the file menu.dll) into mpv's portable_config/scripts dir. Then edit (or create) your portable_config/input.conf file to show the menu. #menu: is used to indicate menu entries.
The following input.conf example binds a dynamic context-menu (navigate chapters, select audio channel and subtitles) to right-click:
# The default syntax is similar to mpv.net:
_ show-text hello #menu: mpv-plugin
Ctrl+a show-text hello #menu: Foo > bar #this here is a user comment. The following line is a separator.
_ ignore #menu: -
_ ignore #menu: Audio #@tracks/audio
_ ignore #menu: Subtitle #@tracks/sub
_ ignore #menu: Chapters #@chapters
j cycle sub #menu: Subtitle > Cycle/Next
v cycle sub-visibility #menu: Subtitle > / Visibility
f cycle fullscreen #menu: / fullscreen
_ ignore #menu: -
Ctrl+w quit #menu: X Quit
MBTN_RIGHT script-message-to menu show
Here is a screenshot demonstrating a small (static) context menu and a pause-indicator in the window titlebar (discussed in next post).
https://raw.githubusercontent.com/butterw/bShaders/ebbd4e35a424ef117e66904189f8e438fc912c2f/mpv/settings/scripts/mpv-menu_win10-light-1440p-hidpi125_pause-indicator.png
butterw2
19th December 2023, 17:40
# mpv: how to create and display a shaders-On indicator (°)
Knowing whether shaders are loaded can be useful. Here's what is currently possible in mpv (v0.37):
- The shader list is stored in the property glsl-shaders (a list of file-path strings).
- The simplest is just to display glsl-shaders ${glsl-shaders}, but this is often not practical because this property can be long and typically needs to be parsed for display.
- Using property-expansion (https://mpv.io/manual/stable/#property-expansion) and based on the content of glsl-shaders we can display a single character shaders-On indicator (°).
- This indicator can be used in various places: window title, OSD, terminal-status, etc. It is typically initialized in mpv.conf or via command-line, but these properties can also be changed via input.conf commands.
- Once set the display will automatically update when glsl-shaders changes.
- Unfortunately there are 2 possible values for an empty shader list which evaluate differently. Always (re-)initialize shaders to "", otherwise you need to check for 2 values here instead of just one (this also avoids the default osd display being (error) when toggling off the last shader).
- my shaders-On indicator: ${!glsl-shaders==:°} evaluates to "°" if glsl-shaders is not an empty string, otherwise "".
- Other indicators are of course possible, ex: a pause indicator, which I use in the mpv window title: ${?pause==yes:|| } this evaluate to "|| " if paused, otherwise "".
playlist indicator: ${!playlist-count==1:${!playlist-count==0: #${playlist-pos-1}/${playlist-count}}} evaluates to ", 1/5" if the playlist has a greater than one length, otherwise "".
Indicators can be combined and displayed via the following string properties (they support property-expansion):
title, term-status-msg
osd-msg1, etc.
osd-playing-msg, term-playing-msg (only displayed when a new video is loaded, default: "")
ex mpv.conf or command-line, --title="${!glsl-shaders==:°}${?pause==yes:|| }${?media-title:${media-title}}${!media-title:No file}${!playlist-count==1:${!playlist-count==0: #${playlist-pos-1}/${playlist-count}}}"
butterw2
19th December 2023, 23:22
mpv: shaders-On but shader not applied !
setting glsl-shaders in mpv does not guarantee that the shader will be applied. Here are the possible issues:
1) Fails to open shader file, likely because the file-path is wrong, ex: Cannot open file 'abba.glsl': No such file or directory.
This can happen when typing shader names at command-line or because the shader has been renamed. This type of error can be displayed on OSD with a script. (https://github.com/butterw/bShaders/blob/master/mpv/settings/scripts/error-osd.lua)
2) Shader compilation error (from vo, ex: [vo/gpu/d3d11]), likely because of a syntax error.
With vo=gpu the video output is blue (helpful for shader developers), but this is not the case with gpu-next. The compiler output printed in red in the terminal is quite long.
3) No errors, but shader not applied because there are no active shader HOOKs.
Mpv shaders can contain multiple HOOKs that are executed conditionally. For instance a LUMA hook will only run if the input is YUV. Prescalers are often only applied for some scaling ratios (specified in WHEN conditions expressed in reverse polish notation). The active shader passes can viewed on page 2 of the built-in stats display. The same information is available in the property vo-passes. The pass names are taken from the hook's DESC line and don't always reference the name of the shader.
Conclusions about troubleshooting shader issues:
- first check the shader is loaded in glsl-shaders.
- look at the terminal output for any errors (they are displayed in red).
- display stats page2 to check whether there are active shader passes. If not check the conditions under which the desired shader HOOK is supposed to be executed.
- If paused, toggling fullscreen forces a screen refresh.
- If all seems OK, make sure the user parameter values used by the shader are correct.
- If you've modified (and saved) the shader source code you need to restart mpv (https://forum.doom9.org/showthread.php?p=1994854#post1994854) to recompile the shader.
butterw2
21st December 2023, 16:21
Dynamic mpv-menu example in dark mode, the played video file has an audio track, but no subtitles or chapters:
https://raw.githubusercontent.com/butterw/bShaders/85c3e9f5812c456ecebb56062c00b5a861361177/mpv/settings/scripts/mpv-menu_dynamic_win10-dark.png
In the mpv/portable_config for A-pack, I will be using mpv-menu as a command launcher/hotkey memo for useful commands that may not be used frequently, but also as a way to use mpv with only the mouse when required.
The menu contains different types of commands. Here I used a prefix symbol to identify them: / indicates that the command toggles between 2 states, ex: fullscreen can be on or off. : indicates a permanent (until disabled) osd, ' indicates a limited duration osd. + or - increase or decrease value, = reset to default value (ex: 100 for volume). X indicates that the command quits mpv.
[EDIT] This is a standard windows menu, it supports standard hotkeys and can be navigated with the keyboard: Arrow keys can be used to navigate, Return/enter to execute command or exit menu. Alt/Esc closes an open menu. The first letter of commands/submenus can be used as accelerators, so use different starting letters for items of a same (sub)menu when possible.
Depending on your preference, you could include a fair number of commands in the menu using 2 levels of submenus, or you could just go with a minimalist launcher.
butterw2
26th December 2023, 14:00
mpv: Disable (--no-osc) or Hide the On screen Controller
By default, mpv overlays (and auto-hides) a seekbar+buttons at the bottom of the screen. This feature is called the On-Screen-Controller and is based on a built-in script called osc.lua (https://github.com/mpv-player/mpv/blob/master/player/lua/osc.lua). The OSC offers only limited configuration options (https://github.com/mpv-player/mpv/DOCS/man/osc.rst) through script-opts/osc.conf. The default OSC is bad, the result is maybe passable after tuning the config. If you are decidedly not impressed with the OSC, you can disable it completely with --no-osc (input.conf or command-line option). The drawback is you won't have a seekbar available unless you get a replacement script. It's not difficult to do a better design, the problem is that Osc.lua is 3000 lines and gets regular commits, so any replacement fork requires regular updates. I present my own custom seekbar in the next post (not osc-based).
The alternative is to change the OSC visibility (hiding it when it is not required).
This can be done via a hotkey, ex input.conf: DEL script-binding osc/visibility #menu: View > OSC # OSC visibility: auto, always, never
You can override default OSC visibility (priority: script defaults < portable_config\script-opts\osc.conf < mpv.conf < command-line)
--script-opts-add=osc-visibility=never # <always, auto, never> default: auto
--script-opts-add=osc-showwindowed=no #default: yes. no: the OSC is not displayed in windowed mode
butterw2
1st January 2024, 23:56
mpv --no-osc: custom seekbar using a (kick-ass) overlay. Intro: showing custom OSD text in mpv (https://forum.doom9.org/showthread.php?p=1990436#post1990436)
The mpv OSD is rendered with libass (also used for displaying subtitles). ASS_Tags can be used to render:
- text (font, color, position on screen).
- shapes (ex: the mpv logo) and of particular interest here rectangles (color, position).
For this, I would recommend to first create an overlay with mp.create_osd_overlay() in a script. I am doing this with basic (mpv uses mujs, ES 5.1) javascript.
Objective: I am trying to do a modern seekbar, colored bar with transparency similar to youtube.
UI elements associated with a hidable seekbar:
- seek time tooltip (for seeking with the mouse)
- playback-time / duration display.
- an optional status bar for information display, possibly with some command buttons.
I don't really need on screen buttons, as I typically use (keyboard /mouse) keybindings +right-click context menu. I do want to use a short time display format.
Mpv limitations:
- The built-in OSC... but we can disable it using --no-osc
- No built-in support for custom time formats.
- Mpv has a built-in progress bar feature called the osd-bar. It looks terrible by default, but it is fairly customizable. However, there are no options to change the fill color of the osd-bar (it uses the main osd color).
- The mpv text OSD default is (Font: sans-serif -> Arial), large size (=55), White color, thick black borders (size=3), transparent back-color. The OSD scales with window size, this only really works in fullscreen or with a large window. The big font causes some serious issues at small sizes and it doesn't work so well on light pictures either (the reason for the thick text borders).
Interestingly VLC uses a similar default and achieves a much nicer result using Freetype2 Font renderer (white text, small, black outline, shadow opacity 128). The time OSD display is shown at the top-right. It's readable (but a bit ugly) down to 50% zoom.
Overall it's easy to get a nice text display on a large dark background (just use a large white font). The difficult cases are:
1) white or grey pictures.
2) when you have to scale the display to a 360p (or less) window.
My preference for OSD text is to use a smaller font and not to scale the OSD with window size: Segoe UI (size=44), light font on dark background.
- To be able to keep small text readable (ex time-display) you probably need to use light text on dark background, ex: mpc-hc in dark mode has a nice hidable seekbar+status bar. This doesn't work with a modern transparent design though.
- When displaying text with a dark background in mpv, you will likely want some extra padding. This is true for regular OSD and overlay. Here are 2 ways to do it:
1) Add padding with a space char before and after. Use nbsp char (rather than regular space char, which only works with show-text and only before the text).
ex, input.conf: a show-text " Hello " #text padded with nbsp. Ass has a not very readable "\\h" tag.
2) With show-text, you can use --osd-shadow-offset=4
- Youtube does achieve acceptable results with gray time display text on a shaded background (this is the only case where they don't use a dark background for text). However I have not been able to achieve the same level of quality in mpv, maybe because of inferior font border rendering ?
- Youtube uses the google font Robota Medium (before that it used Arial). These fonts are good for a small time display (ex: size 16, bold). Specifically for time-readouts it is important that the font should have constant width digits (this is the case for Segoe UI, but not for ex: Segoe UI Semibold !).
my OSD text display and red seekbar:
https://raw.githubusercontent.com/butterw/bShaders/db4c939ba50de6fc883025b9070440110853e6c8/mpv/settings/scripts/seek-bar.jpg
EDIT: a simple javascript script showing overlay features: overlay0.js (https://github.com/butterw/bShaders/commit/ed400d9257405dd57ba2e787a9d9a2ae0c06c03f#diff-c87bf7d6cbe8d4410a7fa169c6602da13972a43e649a86c778838bd8a82f04d0), the release version will have a hideable seekbar and custom time display.
Note: The script observes changes in the percent-pos property, triggering updates to the overlay for each new frame. This is overkill for just displaying a progress bar / custom playtime on screen. Updates using a (ex: 1 second) periodic timer would be sufficient here.
stax76
2nd January 2024, 12:19
To be continued...
I find it interesting how other users use mpv, thanks for sharing.
butterw2
5th January 2024, 11:52
# Ass_Tags in mpv
The Ass_Tags format (https://aegisub.org/docs/3.2/ASS_Tags/) consists in a combination of \tag strings to display a text element or a shape. There are some quirks, so expect some trial and error:
- Each \tag needs to be escaped to "\\tag" for use in javascript strings.
- A simplified syntax seems to work fine ex: &AA rather than &HAA& in the doc.
- Color is specified as hexa &BBGGRR
- !!! alpha &OO is opacity not transparency: 00 is solid color and FF is transparent. 80 is mid-transparency.
\\c \\alpha : global color and alpha
\\1c \\1a : primary fill color, osd-color
\\3c \\3a : border color, osd-border-color
\\4c \\4a : shadow color, osd-shadow-color
- The background color itself cannot be modified in tags* (uses osd-back-color). You can specify global \\alpha or you can revert to the \\rDefault style which has transparent background.
- Font is changed with "\\fnSegoe UI\\fs18\\b1" (Segoe UI, size 18, bold). OSD style: osd-font, osd-font-size, osd-bold.
- Font size is the height of the characters in osd pixels. Unless you explicitly specify font size (\\fs18), the text will scale with the window.
- In addition to text, fonts are used to display icons on screen. Many useful characters/symbols are available in the windows standard fonts (if you can use a large fontsize), and there also dedicated iconic fonts available.
- You can use system installed fonts or fonts in portable_config/fonts.
- for text, you can specify just an anchor, osd-margins will be applied (--osd-margin-x, --osd-margin-y ). If you don't specify an anchor the default will be used, this isn't recommended as it will cover standard osd.
- You can specify precise position with \\pos(x,y), but you also need to consider the anchor point. It uses numpad numbering. The default \\an7 anchors the top right corner of the bounding box to position.
- The default (x,y) coordinate system is normalized to a display height of 720. The overlay will scale with the window size and the coordinates include any black bar padding.
- \\pos(0,0) is top-left of the OSD.
- I've chosen to switch to displayed osd pixels coordinates. It's simpler to use and the overlay doesn't scale with window size.
- For shapes you need to specify a \\p1 scale-factor, ! Do not specify it for text.
- Rectangles are filled. You can specify you don't want any border or shadow with \\bord0\\shad0.
- The overlay data consists in the concatenation with "\n" of all text and shape strings. The order in which you draw can have a visual impact.
- The overlay is always on top of the normal OSD (ex: show-text commands) when displayed. It can be hidden.
*It seems mpv messes with OSD parameter values for background fill. The shadow parameter can be used either to pad text background color or for actual font shadows depending on the alpha value of osd-back-color ? In mpv, text borders and shadows don't look so good, just set a non completely transparent alpha value to osd-back-color and avoid the nonsense.
stax76
8th January 2024, 06:29
Is learning and using Lua not an option? Most script authors prefer it.
Here is a short intro:
https://www.youtube.com/watch?v=jUuqBZwwkQw
butterw2
8th January 2024, 13:08
Good video, but no I don't think someone who wants to write a simple script for mpv needs to learn lua. Lua is the default in mpv, it's probably the better solution for advanced mpv scripts if you know it (and don't hate it), but both can work. Javascript is widely used and it's syntax is going to be easy for anyone who has ever done any c/java/python. Lua is also supposed to be faster, the mpv ES5 Javascript engine is old (meaning some useful functions are missing) and it's true that most available mpv scripts are in .lua.
Modifying existing scripts is the preferred approach if working code is already available. Typically you only need to write a few lines of new code, which can be done with a text editor with syntaxic coloring like notepad++. When debugging scripts you can just print your (number or string) variables to console or screen, but it doesnt work so well when you have to deal with objects (ex: lua table). A tool such as https://github.com/tsl0922/mpv-debug-plugin can be useful to visualize the content of mpv properties (including user-data properties).
A simple color progress-bar OSD overlay script is 100 lines of code, the final version with a hidable seekbar and custom time-format support isn't going to be huge, so the language used doesn't matter that much.
--
printd("x:", x), an enhanced print function for the purpose of debugging scripts: can print tables, differentiates string values from regular values.
- in js: function printd(pre, x) { print(pre, JSON.stringify(x)) }
- in lua: local utils = require('mp.utils')
local function printd(pre, x) print(pre, utils.to_string(x)) end
butterw2
14th January 2024, 16:36
mpv script: short time format OSD btime.js v0.40 (2024-01-19) (https://github.com/butterw/bShaders/blob/master/mpv/settings/scripts/btime.js).
Now configurable via portable_config/script-opts/btime.conf (https://github.com/butterw/bShaders/blob/master/mpv/portable_config/script-opts/btime.conf)
Install: Copy script to portable_config/scripts, portable_config/input.conf: F11 script-binding btime/show
playtime / duration for the different supported formats:
00:00:08 / 00:01:12 (mpv default)
00:08 / 01:12 (mpc-hc, symetrical)
0:08 / 1:12 (btime, youtube)
-1.04 / 1:12 (alt + btime)
00:06:04 / 01:27:09 (mpv default, mpc-hc)
0:06:04 / 1:27:09 (symetrical)
06:04 / 1:27:09 (btime)
6:04 / 1:27:09 (youtube)
-1:21:05 / 1:27:09 (alt + btime)
01:02:08 / 01:27:09 (mpv default, mpc-hc)
1:02:08 / 1:27:09 (btime, youtube, symetrical)
-25:01 / 1:27:09 (alt + btime)
Options:
The OSD time format can be customized by the user via the format option. by default, uses a short time format similar to youtube, but better suited for movies (format=btime).
- You can switch to mpc-hc or youtube format.
- alt option allows to switch to remaining-time display.
- displays via standard OSD either using a keybinding or on seek (with seek=yes).
- the duration of the display can be configured (ms option). The text readout message isn't updated on screen, so duration (ms=1500) shouldn't be set too long.
- the custom duration string is updated each time a new file is loaded. It could be made available to other script by saving it in the property user-data/duration.
btime.conf
# format=btime <youtube, btime, symetrical, mpc-hc, mpv> time format: playback-time / duration
# alt=no <yes, no> alternative format: - remaining-time / duration
# percent=no <yes, no> percent progress format: (percent-pos%) time-pos, OR with alt: -remaining-time (percent-pos%)
# seek=yes <yes, no> display on seek event
# ms=-1 <-1, value> duration of OSD display in milliseconds ex:1500, -1: standard OSD duration
Added in v0.40 percent=yes, a percent progress format with playback-time or remaining-time:
(40%) 2:25 (percent, btime)
(100%) 1:35:25
-25:15 (75%)
-1:01:25 (23%) (alt+btime, percent)
butterw2
16th January 2024, 18:15
mpv-menu-plugin: custom dynamic menus
menu_window_scale.lua (https://github.com/butterw/bShaders/blob/master/mpv/settings/scripts/menu_window_scale.lua): custom window-scale submenu, with checkmark for current value.
Edit: Using the new (dyn_menu v2.1) message interface, a script (lua or js) now has full control over menu items and dynamic submenus tagged with a predefined or user-created #@keyword in input.conf, without needing to fork dyn_menu.lua. You can dynamically change the title, the displayed hotkeys, the command, the checked state, and the disabled (grayed) state of menu items.
mpv-menu-plugin v2.1 (req mpv 0.37 on windows) has been released.
- static menus are simple and work fine.
- dyn_menu.lua provides a few useful dynamic submenus: #@tracks, #@chapters, #@playlist
#@audio-devices is also available if needed.
- dyn_menu.lua has a new message interface to allow user scripts to create custom dynamic submenus. Advantage of this approach: dyn_menu.lua doesn't need to be modified, allowing independent updates of user scripts and dyn_menu.lua.
- By default menu items are unchecked. The addition of a state-based checkmark for specific menu entries can improve menu readability significantly. However, this requires a dynamic update when the value changes.
#@property:check syntax, ex: #@mute:check is easy to use, but currently only works for a few (boolean) properties: mute, pause, fullscreen, border, ontop, deband, deinterlace, sub-visibility.
How custom dynamic menus work
at startup mpv loads scripts menu.dll and dyn_menu.lua > parses #:menu entries + associated keybindings in input.conf > creates user-data/menu/items (a nested table)
- the menu (user-data/menu/items) is displayed by win32 c-plugin (menu.dll)
- the default script dyn_menu.lua handles:
-- #@keyword dynamic menu updates
-- messaging to third party plugins (ex: recentmenu.lua) with data in json format.
Displayed keybindings vs actual keybindings
- There is no mechanism in mpv to change runtime input.conf keybinding: what you define in input.conf always applies.
- By default, the menu displays the keybindings associated to menu entries exactly as defined in input.conf. The menu doesn't check the keybindings work.
If you can launch a command from the menu, but not from the keybinding this would indicates some keybinding conflict, ex:
-- the key is not what you thought, ex: CTRL+O isn't the same as CTRL+o
-- you may have redefined the keybinding further down in input.conf.
-- or the key is getting stolen ex: by the OS (ex: you can't use printscreen on windows).
- It is possible to change the keybindings displayed in the menu. However what is displayed in the menu doesn't affect actual keybindings.
-- Why you might want to change what is displayed in the menu: some mpv keynames are a bit long or mpv uses local key names which are sometimes un-intuitive on non US-keyboard.
I created a custom dynamic submenu #@window-scale, this allows full control over the submenu and its entries. A checkmark for the current value is useful here.
Items created in dynamic submenus do not have associated hotkeys from input.conf. In input.conf, I do have hotkeys associated with the window-scale commands. But trying to retrieve them would not be simple, it is much simpler to display no hotkeys (https://github.com/butterw/bShaders/commit/2484df7cfcdedfa1be8d513ab603ed92e83d275f). I decided to display fake hotkeys (position is consistent with US keyboard keys, and more meaningful than the local key names).
https://raw.githubusercontent.com/butterw/bShaders/master/mpv/scr/mpv-menu-plugin_v2.1_btime.png
Notes:
- I've displayed my time readout in the screenshot (using the short format=btime from previous post).
- To dismiss the menu use Alt/Esc keys, or left-click on an empty command (ex: --- menu space in my screenshot).
- add _ ignore at the end of input.conf to avoid binding _.
butterw2
19th January 2024, 15:27
mpv (on win10/11): my Tips
mpv is a cross-plaform open-source, ffmpeg-based, command-line video player. By default, it uses a glsl shader-based renderer (and libplacebo with the option --vo=gpu-next). Mpv has extensive configuration options and generally good documentation. It can be scripted (using either lua or javascript) and supports c-plugins.
My tips for mpv newbies:
- create your mpv folder: with no spaces in path and with user write permissions.
- on windows, current mpv0.37-dev build (ex: Shinchiro x86_64 weekly (https://sourceforge.net/projects/mpv-player-windows/files/64bit/)) works well. Just download and unzip the build to mpv folder. You can update when you want using updater.bat.
-- Create mpv/portable_config for your .conf files and the portable_config/scripts subfolder.
- Get a copy of an existing config or create your own, no .conf files are provided by default.
- Start mpv from terminal in the mpv folder, you can use drag&drop. mpv pathto.myvideo.mp4
-- On windows the command prompt (cmd) will uses mpv.com, use a shortcut to mpv.exe if you don't want a terminal window (you can still use the console if needed).
- Startup options: user default startup options are configured in mpv.conf file. Command-line options have higher priority if specified.
- It is possible to define multiple configuration [profiles] in mpv.conf. Select the profiles to apply with the option --profile=
- For info on mpv options, check out the mpv.io/manual, some decent configuration guides are also available on the web but might not be 100% up-to-date.
- ! hardware decoder isn't enabled by default. I use --hwdec=d3d11va
- For old igpu: you likely need options from --profile=fast
- mpv isn't a GUI-based player and the On-Screen-Controler (built-in osc.lua) is probably the weakest feature. Fortunately it can be hidden, disabled (--no-osc) then replaced.
- For runtime commands you can use keybindings, it also possible to use the console (but the syntax isn't always simple). On windows, a custom context menu is available with context-menu-plugin (https://github.com/tsl0922/mpv-menu-plugin).
- Define all your hotkeys in portable_config/input.conf, disable default keybindings with --no-input-default-bindings
-- the format is: KEY command1; command2 #:menu for context-menu entries #comments
- mpv is easy to script with .lua or .js (javascript ES5.1).
- Keep only the scripts you need in portable_config/scripts as they are autoloaded by default. You can manually load scripts with --scripts=
- I keep shaders and manually loaded scripts in mpv/s subfolder. It's also possible to use portable_config/shaders, the path can use ~~ for portable_config.
glsl-shaders property is a path list (if you have multiple shaders the separator is ; on windows, : elsewhere). ex: ---glsl-shaders=s/bw.hk;s/x_10_240.glsl Vs =~~/shaders/bw.hk;~~/shaders/x10_240.glsl
- Scripts can support configuration via portable_config/script-opts, myScript.conf is the configuration file for myScript.lua, ! option syntax must be strictly respected. Script configuration options can also be specified in mpv.conf or via command-line.
- Use _ rather than - in script names.
- mpv-debug-plugin (https://github.com/tsl0922/mpv-debug-plugin) is a mpv GUI tool that can help you make sense of mpv commands, properties and options.
- if you have questions reddit r/mpv is a good place to ask.
butterw2
22nd January 2024, 20:21
mpv: seek menu
I'm using a static context-menu with some relative jumps/seeks (10, 30, 60, 300s), and like youtube some absolute percent seeks (10, 25, 50, 75, 90%). When the menu is displayed the first letter of menu entries acts as an accelerator, this allows to get more hotkeys without having to permanently define them. To block unwanted accelerators, you can add a space in front of the title. I also use input.conf: kp0 no-osd seek 0 absolute; set pause no #menu: Play > from start.
Some keys names in mpv aren't great (also, not very consistent), so there are a couple cases where I wanted key name substitutions in the menu, Ex: Ctrl+WHEEL_DOWN >> ctrl+WhDn. The length of the displayed menu titles and keybindings set the width of the context menu and some key combination are quite long, ex:Ctrl+Shift+KP_ENTER, another reason for changing displayed hotkeys would be to show multiple hotkeys for the same command on the same line.
https://raw.githubusercontent.com/butterw/bShaders/637eac08eb69ce74d8a3c1e557a500265e7efde3/mpv/scr/mpv-menu_jump_seek.png
One feature that is sometimes useful is the ability to define one or more seek-points by setting a time marker. A custom Markers menu with some user-data/markers properties should allow to do this (currently you could set ab-loop-a, or use revert-seek mark). The next release of context-menu-plugin will support copy/paste to clipboard, meaning a Go to Time feature becomes possible (at runtime: copy a timestamp to clipboard, then seek to it).
anta777
15th March 2024, 19:41
How to check the shader is loaded in glsl-shaders?
Find
CTRL+0 show-text "Shaders: ${glsl-shaders:}"
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.