diff --git a/options/options.c b/options/options.c
index 48baef13ff731..002cd1c624660 100644
--- a/options/options.c
+++ b/options/options.c
@@ -803,6 +803,7 @@ static const m_option_t mp_opts[] = {
{"", OPT_SUBSTRUCT(video_equalizer, mp_csp_equalizer_conf)},
{"use-filedir-conf", OPT_BOOL(use_filedir_conf)},
+ {"osd-lua", OPT_BOOL(lua_load_osd)},
{"osd-level", OPT_CHOICE(osd_level,
{"0", 0}, {"1", 1}, {"2", 2}, {"3", 3})},
{"osd-on-seek", OPT_CHOICE(osd_on_seek,
@@ -1016,6 +1017,7 @@ static const struct MPOpts mp_default_opts = {
.osd_on_seek = 1,
.osd_duration = 1000,
#if HAVE_LUA
+ .lua_load_osd = false,
.lua_load_osc = true,
.lua_load_ytdl = true,
.lua_ytdl_format = NULL,
diff --git a/options/options.h b/options/options.h
index 014e41de6d27e..f3030033ff409 100644
--- a/options/options.h
+++ b/options/options.h
@@ -182,6 +182,7 @@ typedef struct MPOpts {
char **script_files;
char **script_opts;
bool js_memory_report;
+ bool lua_load_osd;
bool lua_load_osc;
bool lua_load_ytdl;
char *lua_ytdl_format;
diff --git a/player/core.h b/player/core.h
index b5e2f09ac3e01..7fdfd1e50b52f 100644
--- a/player/core.h
+++ b/player/core.h
@@ -452,7 +452,7 @@ typedef struct MPContext {
struct mp_ipc_ctx *ipc_ctx;
- int64_t builtin_script_ids[9];
+ int64_t builtin_script_ids[10];
mp_mutex abort_lock;
diff --git a/player/lua.c b/player/lua.c
index 8ecdeac410fe5..5c64d6c90d8b1 100644
--- a/player/lua.c
+++ b/player/lua.c
@@ -67,6 +67,9 @@ static const char * const builtin_lua_scripts[][2] = {
},
{"mp.options",
# include "player/lua/options.lua.inc"
+ },
+ {"@osd.lua",
+# include "player/lua/osd.lua.inc"
},
{"@osc.lua",
# include "player/lua/osc.lua.inc"
diff --git a/player/lua/meson.build b/player/lua/meson.build
index 72c92261fbaef..e9746321910b7 100644
--- a/player/lua/meson.build
+++ b/player/lua/meson.build
@@ -1,4 +1,4 @@
-lua_files = ['defaults.lua', 'assdraw.lua', 'options.lua', 'osc.lua',
+lua_files = ['defaults.lua', 'assdraw.lua', 'options.lua', 'osd.lua', 'osc.lua',
'ytdl_hook.lua', 'stats.lua', 'console.lua', 'auto_profiles.lua',
'input.lua', 'fzy.lua', 'select.lua', 'positioning.lua',
'commands.lua', 'context_menu.lua']
diff --git a/player/lua/osd.lua b/player/lua/osd.lua
new file mode 100644
index 0000000000000..381cb3ed14782
--- /dev/null
+++ b/player/lua/osd.lua
@@ -0,0 +1,725 @@
+local assdraw = require 'mp.assdraw'
+local msg = require 'mp.msg'
+local opt = require 'mp.options'
+
+-- -----------------------------------------------
+-- Customization Options
+-- -----------------------------------------------
+
+local user_opts = {
+ -- Time OSD stays on screen after any change
+ duration = 1.0,
+
+ -- Bar geometry
+ bar_width = 0.75, -- bar width (as fraction of screen width)
+ bar_height = 0.04, -- bar height (as fraction of screen height)
+ bar_x_offset = 0, -- horizontal position for bar counting from center of screen (as fraction of screen width)
+ bar_y_pos = 0.75, -- vertical position for bar counting from top of screen (as fraction of screen height)
+ bar_radius = 0, -- corner radius of the bar (as fraction of screen diagonal)
+ round_fill_end = true, -- toggle for rounding fill bar right corners
+ show_bar_outline = true, -- toggle for bar outline
+ outline_gap = 0.003, -- gap between bar and it's outline (as fraction of screen diagonal)
+ marker_width = 0.005, -- marker width (as fraction of bar width)
+ marker_height = 0.2, -- marker height (as fraction of bar height)
+
+ -- Colors
+ bar_body_color = "#FFFFFF", -- color of the bar's section that isn't filled
+ bar_body_border_color = "#000000", -- color of the outer border of the bar's section that isn't filled
+ bar_fill_color = "#FFFFFF", -- color of the bar's filled section
+ bar_fill_border_color = "#000000", -- color of the outer border of the bar's filled section
+ bar_outline_color = "#FFFFFF", -- color of the bar's outline
+ bar_outline_border_color= "#000000", -- color of the outer border of the bar's outline
+ text_color = "#FFFFFF", -- color of the text of the property that is being changed (eg: volume, seek, etc)
+ text_border_color = "#000000", -- color of the text's border
+ text_shadow_color = "#000000", -- color of the text's shadow
+ icon_color = "#FFFFFF", -- color of the icon of the property that is being changed
+ icon_border_color = "#000000", -- color of the icon's border
+ text_box_color = "#FFFFFF", -- color of the text's background
+ text_box_border_color = "#000000", -- color of the text's box
+
+ -- Alphas (aka opacity)
+ -- goes from 0 (fully opaque) to 255 (fully transparent)
+ bar_body_alpha = 255, -- alpha of the bar's section that isn't filled
+ bar_body_border_alpha = 0, -- alpha of the outer border of the bar's section that isn't filled
+ bar_fill_alpha = 0, -- alpha of the bar's filled section
+ bar_fill_border_alpha = 0, -- alpha of the outer border of the bar's filled section
+ bar_outline_alpha = 0, -- alpha of the bar's outline
+ bar_outline_border_alpha= 0, -- alpha of the outer border of the bar's outline
+ icon_border_alpha = 0, -- alpha of the icon border
+ text_alpha = 0, -- opacity of text
+ text_border_alpha = 0, -- opacity of text outline
+ text_shadow_alpha = 0, -- opacity of text shadow
+ icon_alpha = 0, -- opacity of icon
+ text_box_alpha = 200, -- opacity of text background
+ text_box_border_alpha = 255, -- opacity of text background border
+
+ -- Text
+ font_size = 0.030, -- text size (as fraction of screen diagonal)
+ font = "", -- optional text font name
+ label_x_offset = 0.02, -- horizontal offset for label counting from left of screen (as fraction of screen width)
+ label_y_offset = 0.02, -- vertical offset for label counting from top of screen (as fraction of screen height)
+
+ text_border = 0.002, -- size of text outline (as fraction of screen diagonal)
+ text_shadow = 0.001, -- size of text shadow (as fraction of screen diagonal)
+
+ text_box = false, -- toggle for background square behind label
+ label_box_size = 0.5, -- controls how wide the background box is compared to the text
+ label_box_pad_x = 0.04, -- horizontal padding inside the text background box (as fraction of screen width)
+ label_box_pad_y = 0.02, -- vertical padding inside the text background box (as fraction of screen height)
+
+ text_center = false, -- align label with center of screen
+
+ -- Icons
+ show_icons_label = false, -- toggle for showing icon at start of label
+ show_icons_bar = true, -- toggle for showing icon next to bar
+ icon_size = 0.055, -- size of icon next to bar (as fraction of screen diagonal)
+ distance_bar = 0.020, -- horizontal distance from bar (as fraction of screen width)
+ icon_y_offset = -0.005, -- vertical offset from bar level (as fraction of screen height)
+ icon_volume = "🔈", -- icon for Volume
+ icon_mute = "🔇", -- icon for Muting
+ icon_play = "▶", -- icon for Playing
+ icon_pause = "⏸", -- icon for Pausing
+ icon_seek = "⏩", -- icon for Seek
+ icon_brightness = "🔆", -- icon for Brightness
+ icon_saturation = "◑", -- icon for Saturation
+ icon_contrast = "◐", -- icon for Contrast
+
+ bar_when_pausing = false, -- toggle for showing seek bar when pausing
+
+ -- Feature on/off switches
+ show_volume = true, -- toggle for showing changes in Volume
+ show_seek = true, -- toggle for showing changes when seeking
+ show_pause = true, -- toggle for showing state when pausing/ playing
+ show_brightness = true, -- toggle for showing changes in Brightness
+ show_saturation = true, -- toggle for showing changes in Saturation
+ show_contrast = true, -- toggle for showing changes in Contrast
+}
+
+
+-- 3. Runtime state
+local state = {
+ hide_timer = nil,
+}
+
+-- -----------------------------------------------
+-- Utility Helpers
+-- -----------------------------------------------
+
+local function clamp(value, min_v, max_v)
+ if value < min_v then return min_v end
+ if value > max_v then return max_v end
+ return value
+end
+
+local function to_ratio(value, max_value)
+ if max_value == 0 then return 0 end
+ return clamp(value / max_value, 0, 1)
+end
+
+local function percent_to_ass_alpha(value)
+ value = clamp(value, 0, 255)
+
+ local p = math.floor(value + 0.5)
+
+ return string.format("&H%02X&", p)
+end
+
+local function scale_value(value, scale)
+ return value * scale
+end
+
+-- Build the label string, optionally with a leading icon
+local function make_label(icon, text, ratio)
+ local icon_str = ""
+ if user_opts.show_icons_label and icon and icon ~= "" then
+ icon_str = icon .. " "
+ end
+
+ if ratio == -1 then
+ return string.format("%s%s", icon_str, text)
+ else
+ return string.format("%s%s: %d%%", icon_str, text, math.floor(ratio * 100 + 0.5))
+ end
+end
+
+
+-- Converts colors in HEX formatting to ASS/SSA formatting
+local function osd_color_convert(color)
+ return "&H" .. color:sub(6,7) .. color:sub(4,5) .. color:sub(2,3) .. "&"
+end
+
+local function validate_colors()
+ for key, value in pairs(user_opts) do
+ if type(value) == "string" and value:sub(1,1) == "#" then
+ if value:find("^#%x%x%x%x%x%x$") == nil then
+ msg.warn("'" .. value .. "' is not a valid color")
+ end
+
+ -- if invalid, color will fallback to black, same as OSC behaviour
+ user_opts[key] = osd_color_convert(value)
+ end
+ end
+end
+
+
+
+-- -----------------------------------------------
+-- OSD Helpers
+-- -----------------------------------------------
+
+local function clear_osd()
+ local w, h = mp.get_osd_size()
+ mp.set_osd_ass(w, h, "")
+end
+
+local function schedule_clear()
+ if state.hide_timer then
+ state.hide_timer:kill()
+ end
+
+ state.hide_timer = mp.add_timeout(user_opts.duration, clear_osd)
+end
+
+-- -----------------------------------------------
+-- ASS Style Helpers
+-- -----------------------------------------------
+
+-- Primary color tag
+local function ass_color(color)
+ return string.format("{\\c%s}", color)
+end
+
+-- Alpha tag
+local function ass_alpha(alpha)
+ return string.format("{\\1a%s}", percent_to_ass_alpha(alpha))
+end
+
+-- Font size tag
+local function ass_fs(size)
+ return string.format("{\\fs%d}", size)
+end
+
+local function ass_border(size)
+ return string.format("{\\bord%d}", size)
+end
+
+local function ass_shadow(size)
+ return string.format("{\\shad%d}", size)
+end
+
+local function ass_border_color(color)
+ return string.format("{\\3c%s}", color)
+end
+
+local function ass_border_alpha(alpha)
+ return string.format("{\\3a%s}", percent_to_ass_alpha(alpha))
+end
+
+local function ass_shadow_color(color)
+ return string.format("{\\4c%s}", color)
+end
+
+local function remove_border()
+ return "{\\bord0}"
+end
+
+local function remove_shadow()
+ return "{\\shad0}"
+end
+
+local function position_center()
+ return "{\\an5}"
+end
+
+local function position_top_left()
+ return "{\\an7}"
+end
+
+-- Optional font name tag
+local function ass_font_tag()
+ if user_opts.font and user_opts.font ~= "" then
+ return string.format("{\\fn%s}", user_opts.font)
+ end
+ return ""
+end
+
+-- Reset all overrides
+local function ass_reset()
+ return "{\\r}"
+end
+
+-- -----------------------------------------------
+-- Drawing Functions
+-- -----------------------------------------------
+
+local function draw_text_center(ass, s_diagonal, s_width, s_height, text, approx_w, approx_h)
+
+ local s_box_pad_x = scale_value(user_opts.label_box_pad_x, s_width)
+ local s_box_pad_y = scale_value(user_opts.label_box_pad_y, s_height)
+
+ -- background box
+ if user_opts.text_box then
+ ass:new_event()
+ ass:pos(s_width / 2, s_height / 2)
+ ass:append(ass_reset())
+ ass:append(ass_color(user_opts.text_box_color))
+ ass:append(ass_alpha(user_opts.text_box_alpha))
+ ass:append(ass_border_color(user_opts.text_box_border_color))
+ ass:append(ass_border_alpha(user_opts.text_box_border_alpha))
+ ass:append(remove_shadow())
+
+ ass:draw_start()
+ ass:rect_cw(-approx_w/2 - s_box_pad_x, -approx_h/2 - s_box_pad_y, approx_w/2 + s_box_pad_x, approx_h/2 + s_box_pad_y)
+ ass:draw_stop()
+ end
+
+ -- text
+ ass:new_event()
+ ass:pos(s_width / 2, s_height / 2)
+ ass:append(ass_reset())
+ ass:append(ass_font_tag())
+ ass:append(ass_fs(scale_value(user_opts.font_size, s_diagonal)))
+ ass:append(ass_color(user_opts.text_color))
+ ass:append(ass_alpha(user_opts.text_alpha))
+ ass:append(position_center())
+ ass:append(text)
+
+end
+
+local function draw_text(ass, s_diagonal, s_width, s_height, lx, ly, text, approx_w, approx_h)
+
+ local s_box_pad_x = scale_value(user_opts.label_box_pad_x, s_width)
+ local s_box_pad_y = scale_value(user_opts.label_box_pad_y, s_height)
+
+ -- background box
+ if user_opts.text_box then
+ ass:new_event()
+ ass:pos(0, 0)
+ ass:append(ass_reset())
+ ass:append(ass_color(user_opts.text_box_color))
+ ass:append(ass_alpha(user_opts.text_box_alpha))
+ ass:append(ass_border_color(user_opts.text_box_border_color))
+ ass:append(ass_border_alpha(user_opts.text_box_border_alpha))
+ ass:append(remove_shadow())
+ ass:draw_start()
+ ass:rect_cw(lx - s_box_pad_x, ly - s_box_pad_y, lx + approx_w + s_box_pad_x, ly + approx_h + s_box_pad_y)
+ ass:draw_stop()
+ end
+
+ -- text
+ ass:new_event()
+ ass:pos(lx, ly)
+ ass:append(ass_reset())
+ ass:append(position_top_left())
+ ass:append(ass_font_tag())
+ ass:append(ass_fs(scale_value(user_opts.font_size, s_diagonal)))
+ ass:append(ass_color(user_opts.text_color))
+ ass:append(ass_alpha(user_opts.text_alpha))
+ ass:append(ass_border(scale_value(user_opts.text_border, s_diagonal)))
+ ass:append(ass_border_color(user_opts.text_border_color))
+ ass:append(ass_shadow(scale_value(user_opts.text_shadow, s_diagonal)))
+ ass:append(ass_shadow_color(user_opts.text_shadow_color))
+ ass:append(ass_border_color(user_opts.text_border_color))
+ ass:append(ass_border_alpha(user_opts.text_border_alpha))
+ ass:append(text)
+end
+
+local function draw_label(ass, s_diagonal, s_width, s_height, lx, ly, icon, label, ratio)
+
+ local text = make_label(icon, label, ratio)
+
+ -- approximate text size
+ local approx_h = scale_value(user_opts.font_size, s_diagonal)
+ local approx_w = #text * approx_h * user_opts.label_box_size
+
+ if user_opts.text_center then
+ draw_text_center(ass, s_diagonal, s_width, s_height, text, approx_w, approx_h)
+ else
+ draw_text(ass, s_diagonal, s_width, s_height, lx, ly, text, approx_w, approx_h)
+ end
+
+end
+
+local function draw_icon(ass, s_diagonal, s_width, s_height, x, y, icon)
+ if not (user_opts.show_icons_bar and icon and icon ~= "") then return end
+
+ local v_icon_size = scale_value(user_opts.icon_size, s_diagonal)
+
+ ass:new_event()
+ ass:pos(x - scale_value(user_opts.distance_bar, s_width) - v_icon_size / 2, y + scale_value(user_opts.bar_height, s_height) / 2 - v_icon_size / 2 + scale_value(user_opts.icon_y_offset, s_height))
+ ass:append(ass_reset())
+ ass:append(position_top_left())
+ ass:append(ass_fs(v_icon_size))
+ ass:append(ass_color(user_opts.icon_color))
+ ass:append(ass_alpha(user_opts.icon_alpha))
+ ass:append(ass_border_color(user_opts.icon_border_color))
+ ass:append(ass_border_alpha(user_opts.icon_border_alpha))
+ ass:append(icon)
+end
+
+local function draw_outline(ass, s_diagonal, s_width, s_height, x, y)
+ if not user_opts.show_bar_outline then return end
+
+ ass:new_event()
+ ass:pos(x, y)
+ ass:append(ass_reset())
+ ass:append(ass_color(user_opts.bar_outline_color))
+ ass:append(ass_alpha(user_opts.bar_outline_alpha))
+ ass:append(ass_border_color(user_opts.bar_outline_border_color))
+ ass:append(ass_border_alpha(user_opts.bar_outline_border_alpha))
+
+ local v_bar_width = scale_value(user_opts.bar_width, s_width)
+ local v_bar_height = scale_value(user_opts.bar_height, s_height)
+
+ local g = scale_value(user_opts.outline_gap, s_diagonal)
+ local r = clamp(scale_value(user_opts.bar_radius, v_bar_height), 0, v_bar_height / 2)
+ local x0 = -g
+ local y0 = -g
+ local x1 = v_bar_width + g
+ local y1 = v_bar_height + g
+
+ ass:draw_start()
+
+ -- outer path
+ if r > 0 and user_opts.round_fill_end then
+ ass:move_to(x0 + r, y0)
+ ass:line_to(x1 - r, y0)
+ ass:line_to(x1, y0 + r)
+ ass:line_to(x1, y1 - r)
+ ass:line_to(x1 - r, y1)
+ ass:line_to(x0 + r, y1)
+ ass:line_to(x0, y1 - r)
+ ass:line_to(x0, y0 + r)
+ elseif r > 0 then
+ ass:move_to(x0 + r, y0)
+ ass:line_to(x1, y0)
+ ass:line_to(x1, y1)
+ ass:line_to(x0 + r, y1)
+ ass:line_to(x0, y1 - r)
+ ass:line_to(x0, y0 + r)
+ else
+ ass:move_to(x0, y0)
+ ass:line_to(x1, y0)
+ ass:line_to(x1, y1)
+ ass:line_to(x0, y1)
+ end
+
+ -- inner hole
+ if r > 0 and user_opts.round_fill_end then
+ ass:move_to(r, 0)
+ ass:line_to(0, r)
+ ass:line_to(0, v_bar_height - r)
+ ass:line_to(r, v_bar_height)
+ ass:line_to(v_bar_width - r, v_bar_height)
+ ass:line_to(v_bar_width, v_bar_height - r)
+ ass:line_to(v_bar_width, r)
+ ass:line_to(v_bar_width - r, 0)
+ elseif r > 0 then
+ ass:move_to(r, 0)
+ ass:line_to(0, r)
+ ass:line_to(0, v_bar_height - r)
+ ass:line_to(r, v_bar_height)
+ ass:line_to(v_bar_width, v_bar_height)
+ ass:line_to(v_bar_width, 0)
+ else
+ ass:move_to(0, 0)
+ ass:line_to(0, v_bar_height)
+ ass:line_to(v_bar_width, v_bar_height)
+ ass:line_to(v_bar_width, 0)
+ end
+
+ ass:draw_stop()
+end
+
+local function draw_bar_body(ass, s_diagonal, s_width, s_height, x, y)
+
+ local v_bar_height = scale_value(user_opts.bar_height, s_height)
+
+ ass:new_event()
+ ass:pos(x, y)
+ ass:append(ass_reset())
+ ass:append(ass_color(user_opts.bar_body_color))
+ ass:append(ass_alpha(user_opts.bar_body_alpha))
+ ass:append(ass_border_color(user_opts.bar_body_border_color))
+ ass:append(ass_border_alpha(user_opts.bar_body_border_alpha))
+
+ ass:draw_start()
+
+ local r = clamp(scale_value(user_opts.bar_radius, v_bar_height), 0, v_bar_height / 2)
+
+ if r > 0 then
+ local bw, bh = scale_value(user_opts.bar_width, s_width), v_bar_height
+ ass:move_to(r, 0)
+ ass:line_to(bw - r, 0)
+ ass:line_to(bw, r)
+ ass:line_to(bw, bh - r)
+ ass:line_to(bw - r, bh)
+ ass:line_to(r, bh)
+ ass:line_to(0, bh - r)
+ ass:line_to(0, r)
+ else
+ ass:rect_cw(0, 0, scale_value(user_opts.bar_width, s_width), v_bar_height)
+ end
+
+ ass:draw_stop()
+end
+
+local function draw_fill(ass, s_diagonal, s_width, s_height, x, y, filled)
+ if filled <= 0 then return end
+
+ local v_bar_height = scale_value(user_opts.bar_height, s_height)
+
+ ass:new_event()
+ ass:pos(x, y)
+ ass:append(ass_reset())
+ ass:append(ass_color(user_opts.bar_fill_color))
+ ass:append(ass_alpha(user_opts.bar_fill_alpha))
+ ass:append(ass_border_color(user_opts.bar_fill_border_color))
+ ass:append(ass_border_alpha(user_opts.bar_fill_border_alpha))
+
+ ass:draw_start()
+
+ local r = clamp(scale_value(user_opts.bar_radius, v_bar_height), 0, v_bar_height / 2)
+
+ if r > 0 then
+ local bh = v_bar_height
+ if user_opts.round_fill_end then
+ -- rounded on both left and right ends
+ ass:move_to(r, 0)
+ ass:line_to(filled - r, 0)
+ ass:line_to(filled, r)
+ ass:line_to(filled, bh - r)
+ ass:line_to(filled - r, bh)
+ ass:line_to(r, bh)
+ ass:line_to(0, bh - r)
+ ass:line_to(0, r)
+ else
+ -- rounded only on left, flat on right
+ ass:move_to(r, 0)
+ ass:line_to(filled, 0)
+ ass:line_to(filled, bh)
+ ass:line_to(r, bh)
+ ass:line_to(0, bh - r)
+ ass:line_to(0, r)
+ end
+ else
+ ass:rect_cw(0, 0, filled, v_bar_height)
+ end
+
+ ass:draw_stop()
+end
+
+local function draw_marker(ass, s_diagonal, s_width, s_height, x, y, marker_position)
+
+ local v_bar_height = scale_value(user_opts.bar_height, s_height)
+
+ local bord = scale_value(user_opts.text_border, s_diagonal)
+ local mark_x = x + (scale_value(user_opts.bar_width, s_width) * marker_position) + bord / 2
+ local half_base = scale_value(user_opts.bar_width, s_width) * user_opts.marker_width
+ local mark_height = v_bar_height * user_opts.marker_height
+
+ local bar_top = y
+ local bar_bottom = y + v_bar_height
+
+ local g = scale_value(user_opts.outline_gap, s_diagonal)
+
+ -- triangle to form V shape
+ ass:new_event()
+ ass:pos(0, 0)
+ ass:append(ass_reset())
+ ass:append(ass_color(user_opts.bar_body_border_color))
+ ass:append(ass_alpha(user_opts.bar_body_border_alpha))
+ ass:append(remove_border())
+ ass:append(remove_shadow())
+ ass:draw_start()
+ ass:move_to(mark_x - half_base - bord, bar_top)
+ ass:line_to(mark_x + half_base + bord, bar_top)
+ ass:line_to(mark_x, bar_top + mark_height + bord)
+ ass:move_to(mark_x - half_base - bord, bar_bottom)
+ ass:line_to(mark_x + half_base + bord, bar_bottom)
+ ass:line_to(mark_x, bar_bottom - mark_height - bord)
+ ass:draw_stop()
+
+ -- triangle that hides the inside of the other triangle
+ ass:new_event()
+ ass:pos(0, 0)
+ ass:append(ass_reset())
+ ass:append(ass_color(user_opts.bar_outline_color))
+ ass:append(ass_alpha(user_opts.bar_outline_alpha))
+ ass:append(remove_border())
+ ass:append(remove_shadow())
+ ass:draw_start()
+ ass:move_to(mark_x - half_base, bar_top - g / 2)
+ ass:line_to(mark_x + half_base, bar_top - g / 2)
+ ass:line_to(mark_x, bar_top + mark_height)
+ ass:move_to(mark_x - half_base, bar_bottom + g / 2)
+ ass:line_to(mark_x + half_base, bar_bottom + g / 2)
+ ass:line_to(mark_x, bar_bottom - mark_height)
+ ass:draw_stop()
+end
+
+local function draw_thin_marker(ass, s_diagonal, s_width, s_height, x, y, marker_position)
+
+ local v_bar_height = scale_value(user_opts.bar_height, s_height)
+
+ local bord = scale_value(user_opts.text_border, s_diagonal)
+ local mark_x = x + (scale_value(user_opts.bar_width, s_width) * marker_position) + bord / 2
+ local mark_height = v_bar_height * user_opts.marker_height
+ local half_base = scale_value(user_opts.bar_width, s_width) * user_opts.marker_width
+
+ local bar_top = y
+ local bar_bottom = y + v_bar_height
+
+ ass:new_event()
+ ass:pos(0, 0)
+ ass:append(ass_reset())
+ ass:append(ass_color(user_opts.bar_outline_color))
+ ass:append(ass_alpha(user_opts.bar_outline_alpha))
+ ass:append(ass_border_color(user_opts.bar_outline_color))
+ ass:append(remove_border())
+ ass:append(remove_shadow())
+ ass:draw_start()
+
+ ass:move_to(mark_x - half_base, bar_top)
+ ass:line_to(mark_x + half_base, bar_top)
+ ass:line_to(mark_x, bar_top + mark_height)
+
+ ass:move_to(mark_x - half_base, bar_bottom)
+ ass:line_to(mark_x + half_base, bar_bottom)
+ ass:line_to(mark_x, bar_bottom - mark_height)
+
+ ass:draw_stop()
+end
+
+local function draw_bar(icon, label, value, min_value, max_value, marker_position)
+ local s_width, s_height = mp.get_osd_size()
+ local s_diagonal = math.sqrt(s_width * s_width + s_height * s_height)
+ local is_color_controls = (label == "Brightness" or label == "Saturation" or label == "Contrast")
+
+ local ratio = 0
+ if is_color_controls then
+ ratio = to_ratio(value + 100, math.abs(min_value) + max_value)
+ else
+ ratio = to_ratio(value, math.abs(min_value) + max_value)
+ end
+
+ local filled = scale_value(user_opts.bar_width, s_width) * ratio
+
+ local label_ratio = (label == "Volume" or is_color_controls) and (value / 100) or ratio
+
+ local x = (s_width - scale_value(user_opts.bar_width, s_width)) / 2 + scale_value(user_opts.bar_x_offset, s_width)
+ local y = s_height * user_opts.bar_y_pos
+ local lx = scale_value(user_opts.label_x_offset, s_width)
+ local ly = scale_value(user_opts.label_y_offset, s_height)
+
+ local ass = assdraw.ass_new()
+
+ if label == "Paused" or label == "Playing" then
+ draw_label(ass, s_diagonal, s_width, s_height, lx, ly, icon, label, -1)
+ if not user_opts.bar_when_pausing then
+ mp.set_osd_ass(s_width, s_height, ass.text)
+ schedule_clear()
+ return
+ end
+ else
+ draw_label(ass, s_diagonal, s_width, s_height, lx, ly, icon, label, label_ratio)
+ end
+
+ draw_icon(ass, s_diagonal, s_width, s_height, x, y, icon)
+ draw_outline(ass, s_diagonal, s_width, s_height, x, y)
+ draw_bar_body(ass, s_diagonal, s_width, s_height, x, y)
+ draw_fill(ass, s_diagonal, s_width, s_height, x, y, filled)
+
+ if marker_position then
+ if user_opts.show_bar_outline then
+ draw_marker(ass, s_diagonal, s_width, s_height, x, y, marker_position)
+ else
+ draw_thin_marker(ass, s_diagonal, s_width, s_height, x, y, marker_position)
+ end
+ end
+
+ mp.set_osd_ass(s_width, s_height, ass.text)
+ schedule_clear()
+end
+
+-- -----------------------------------------------
+-- OSD Actions
+-- -----------------------------------------------
+
+local function show_volume()
+ if not user_opts.show_volume then return end
+ local volume = mp.get_property_number("volume", 0)
+ local muted = mp.get_property_bool("mute", false)
+ local icon = (muted or volume == 0) and user_opts.icon_mute or user_opts.icon_volume
+ draw_bar(icon, "Volume", volume, 0, 130, 100 / 130)
+end
+
+local function show_seek()
+ if not user_opts.show_seek then return end
+ local time_pos = mp.get_property_number("time-pos", 0)
+ local duration = mp.get_property_number("duration", 0)
+ draw_bar(user_opts.icon_seek, "Seek", time_pos, 0, duration, nil)
+end
+
+local function show_pause_state()
+ if not user_opts.show_pause then return end
+ local paused = mp.get_property_bool("pause", false)
+ local time_pos = mp.get_property_number("time-pos", 0)
+ local duration = mp.get_property_number("duration", 0)
+
+ local icon = (paused) and user_opts.icon_pause or user_opts.icon_play
+ local label = (paused) and "Paused" or "Playing"
+
+ draw_bar(icon, label, time_pos, 0, duration, nil)
+end
+
+local function show_brightness()
+ if not user_opts.show_brightness then return end
+ local v = mp.get_property_number("brightness", 0)
+ draw_bar(user_opts.icon_brightness, "Brightness", v, -100, 100, 0.5)
+end
+
+local function show_saturation()
+ if not user_opts.show_saturation then return end
+ local v = mp.get_property_number("saturation", 0)
+ draw_bar(user_opts.icon_saturation, "Saturation", v, -100, 100, 0.5)
+end
+
+local function show_contrast()
+ if not user_opts.show_contrast then return end
+ local v = mp.get_property_number("contrast", 0)
+ draw_bar(user_opts.icon_contrast, "Contrast", v, -100, 100, 0.5)
+end
+
+
+
+-- Disable C OSD
+mp.set_property("osd-level", "0")
+
+
+-- Read options from config file and command-line with callback possibility
+opt.read_options(user_opts, "osd", function(changed)
+ validate_colors()
+end)
+
+validate_colors()
+
+
+
+
+-- -----------------------------------------------
+-- Hook to mpv
+-- -----------------------------------------------
+
+mp.observe_property("volume", "number", show_volume)
+mp.observe_property("mute", "bool", show_volume)
+mp.observe_property("pause", "bool", show_pause_state)
+mp.observe_property("brightness", "number", show_brightness)
+mp.observe_property("saturation", "number", show_saturation)
+mp.observe_property("contrast", "number", show_contrast)
+
+mp.register_event("seek", show_seek)
+
+msg.info("Lua OSD loaded")
+
+
diff --git a/player/scripting.c b/player/scripting.c
index 6ed582f14265a..edba6a5eee433 100644
--- a/player/scripting.c
+++ b/player/scripting.c
@@ -271,6 +271,7 @@ void mp_load_builtin_scripts(struct MPContext *mpctx)
load_builtin_script(mpctx, 6, mpctx->opts->lua_load_positioning, "@positioning.lua");
load_builtin_script(mpctx, 7, mpctx->opts->lua_load_commands, "@commands.lua");
load_builtin_script(mpctx, 8, mpctx->opts->lua_load_context_menu, "@context_menu.lua");
+ load_builtin_script(mpctx, 9, mpctx->opts->lua_load_osd, "@osd.lua");
}
bool mp_load_scripts(struct MPContext *mpctx)
diff --git a/test/libmpv_test_file_loading_lua_osd.c b/test/libmpv_test_file_loading_lua_osd.c
new file mode 100644
index 0000000000000..b53d4ec59e787
--- /dev/null
+++ b/test/libmpv_test_file_loading_lua_osd.c
@@ -0,0 +1,77 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see .
+ */
+
+#include "libmpv_common.h"
+
+static void test_file_loading(char *file)
+{
+ const char *cmd[] = {"loadfile", file, NULL};
+ command(cmd);
+ bool loaded = false;
+ bool finished = false;
+ bool c_osd_suppressed = false;
+ while (!finished) {
+ mpv_event *event = wrap_wait_event();
+ switch (event->event_id) {
+ case MPV_EVENT_FILE_LOADED:
+ // make sure it loads before exiting
+ loaded = true;
+
+ // make sure we're not using the C OSD and only the Lua one
+ if (!strcmp(mpv_get_property_string(ctx, "osd-level"), "0")) {
+ c_osd_suppressed = true;
+ }
+
+ break;
+ case MPV_EVENT_END_FILE:
+ if (loaded)
+ finished = true;
+ break;
+ }
+ }
+
+ if(!c_osd_suppressed)
+ fail("Failed to suppress the C OSD");
+
+ if (!finished)
+ fail("Unable to load test file!\n");
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ return 1;
+
+ ctx = mpv_create();
+ if (!ctx)
+ return 1;
+
+ atexit(exit_cleanup);
+
+ mpv_set_option_string(ctx, "osd-lua", "yes");
+ initialize();
+
+ const char *fmt = "================ TEST: %s ================\n";
+ printf(fmt, "test_file_loading for Lua OSD");
+ test_file_loading(argv[1]);
+ printf("================ SHUTDOWN ================\n");
+
+ command_string("quit");
+ while (wrap_wait_event()->event_id != MPV_EVENT_SHUTDOWN) {}
+
+ return 0;
+}
diff --git a/test/libmpv_test_lua_osd.c b/test/libmpv_test_lua_osd.c
new file mode 100644
index 0000000000000..81c0fae0d6d97
--- /dev/null
+++ b/test/libmpv_test_lua_osd.c
@@ -0,0 +1,343 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see .
+ */
+
+#include "libmpv_common.h"
+
+
+struct osdTestValues {
+ char* initial_volume;
+ char* initial_mute;
+ char* started_paused;
+ char* initial_brightness;
+ char* initial_saturation;
+ char* initial_contrast;
+
+ char* volume_after_change;
+ char* second_mute;
+ char* second_paused;
+ char* brightness_after_change;
+ char* saturation_after_change;
+ char* contrast_after_change;
+};
+
+
+static void set_option_string(const char *name, const char *value)
+{
+ int ret = mpv_set_option_string(ctx, name, value);
+ if (ret < 0)
+ fail("mpv API error while setting option '%s' to '%s' (%s)\n", name, value, mpv_error_string(ret));
+}
+
+
+static void free_osd_values(struct osdTestValues* C_osd_values, struct osdTestValues* Lua_osd_values) {
+ // Free everything
+ mpv_free(C_osd_values->initial_volume);
+ mpv_free(C_osd_values->initial_mute);
+ mpv_free(C_osd_values->started_paused);
+ mpv_free(C_osd_values->initial_brightness);
+ mpv_free(C_osd_values->initial_saturation);
+ mpv_free(C_osd_values->initial_contrast);
+
+ mpv_free(C_osd_values->volume_after_change);
+ mpv_free(C_osd_values->second_mute);
+ mpv_free(C_osd_values->second_paused);
+ mpv_free(C_osd_values->brightness_after_change);
+ mpv_free(C_osd_values->saturation_after_change);
+ mpv_free(C_osd_values->contrast_after_change);
+
+ mpv_free(Lua_osd_values->initial_volume);
+ mpv_free(Lua_osd_values->initial_mute);
+ mpv_free(Lua_osd_values->started_paused);
+ mpv_free(Lua_osd_values->initial_brightness);
+ mpv_free(Lua_osd_values->initial_saturation);
+ mpv_free(Lua_osd_values->initial_contrast);
+
+ mpv_free(Lua_osd_values->volume_after_change);
+ mpv_free(Lua_osd_values->second_mute);
+ mpv_free(Lua_osd_values->second_paused);
+ mpv_free(Lua_osd_values->brightness_after_change);
+ mpv_free(Lua_osd_values->saturation_after_change);
+ mpv_free(Lua_osd_values->contrast_after_change);
+}
+
+
+static void test_c_osd(struct osdTestValues* C_osd_values)
+{
+
+ C_osd_values->initial_volume = mpv_get_property_string(ctx, "volume");
+ printf("Initial volume: %s\n", C_osd_values->initial_volume);
+
+ C_osd_values->initial_mute = mpv_get_property_string(ctx, "mute");
+ printf("Initial mute flag: %s\n", C_osd_values->initial_mute);
+
+ C_osd_values->started_paused = mpv_get_property_string(ctx, "pause");
+ printf("Started paused: %s\n", C_osd_values->started_paused);
+
+ C_osd_values->initial_brightness = mpv_get_property_string(ctx, "brightness");
+ printf("Initial brightness: %s\n", C_osd_values->initial_brightness);
+
+ C_osd_values->initial_saturation = mpv_get_property_string(ctx, "saturation");
+ printf("Initial saturation: %s\n", C_osd_values->initial_saturation);
+
+ C_osd_values->initial_contrast = mpv_get_property_string(ctx, "contrast");
+ printf("Initial contrast: %s\n", C_osd_values->initial_contrast);
+
+
+
+ mpv_set_property_string(ctx, "volume", "60");
+ C_osd_values->volume_after_change = mpv_get_property_string(ctx, "volume");
+ printf("Volume after change: %s\n", C_osd_values->volume_after_change);
+
+ mpv_set_property_string(ctx, "mute", "yes");
+ C_osd_values->second_mute = mpv_get_property_string(ctx, "mute");
+ printf("Mute flag after changing it: %s\n", C_osd_values->second_mute);
+
+ mpv_set_property_string(ctx, "pause", "yes");
+ C_osd_values->second_paused = mpv_get_property_string(ctx, "pause");
+ printf("Paused after pause command: %s\n", C_osd_values->second_paused);
+
+ mpv_set_property_string(ctx, "brightness", "-70");
+ C_osd_values->brightness_after_change = mpv_get_property_string(ctx, "brightness");
+ printf("Brightness after change: %s\n", C_osd_values->brightness_after_change);
+
+ mpv_set_property_string(ctx, "saturation", "50");
+ C_osd_values->saturation_after_change = mpv_get_property_string(ctx, "saturation");
+ printf("Saturation after change: %s\n", C_osd_values->saturation_after_change);
+
+ mpv_set_property_string(ctx, "contrast", "4");
+ C_osd_values->contrast_after_change = mpv_get_property_string(ctx, "contrast");
+ printf("Contrast after change: %s\n", C_osd_values->contrast_after_change);
+
+
+
+}
+
+
+
+static void test_lua_osd(struct osdTestValues* Lua_osd_values)
+{
+
+ Lua_osd_values->initial_volume = mpv_get_property_string(ctx, "volume");
+ printf("Initial volume: %s\n", Lua_osd_values->initial_volume);
+
+ Lua_osd_values->initial_mute = mpv_get_property_string(ctx, "mute");
+ printf("Initial mute flag: %s\n", Lua_osd_values->initial_mute);
+
+ Lua_osd_values->started_paused = mpv_get_property_string(ctx, "pause");
+ printf("Started paused: %s\n", Lua_osd_values->started_paused);
+
+ Lua_osd_values->initial_brightness = mpv_get_property_string(ctx, "brightness");
+ printf("Initial brightness: %s\n", Lua_osd_values->initial_brightness);
+
+ Lua_osd_values->initial_saturation = mpv_get_property_string(ctx, "saturation");
+ printf("Initial saturation: %s\n", Lua_osd_values->initial_saturation);
+
+ Lua_osd_values->initial_contrast = mpv_get_property_string(ctx, "contrast");
+ printf("Initial contrast: %s\n", Lua_osd_values->initial_contrast);
+
+
+ mpv_set_property_string(ctx, "volume", "60");
+ Lua_osd_values->volume_after_change = mpv_get_property_string(ctx, "volume");
+ printf("Volume after change: %s\n", Lua_osd_values->volume_after_change);
+
+ mpv_set_property_string(ctx, "mute", "yes");
+ Lua_osd_values->second_mute = mpv_get_property_string(ctx, "mute");
+ printf("Mute flag after changing it: %s\n", Lua_osd_values->second_mute);
+
+ mpv_set_property_string(ctx, "pause", "yes");
+ Lua_osd_values->second_paused = mpv_get_property_string(ctx, "pause");
+ printf("Paused after pause command: %s\n", Lua_osd_values->second_paused);
+
+ mpv_set_property_string(ctx, "brightness", "-70");
+ Lua_osd_values->brightness_after_change = mpv_get_property_string(ctx, "brightness");
+ printf("Brightness after change: %s\n", Lua_osd_values->brightness_after_change);
+
+ mpv_set_property_string(ctx, "saturation", "50");
+ Lua_osd_values->saturation_after_change = mpv_get_property_string(ctx, "saturation");
+ printf("Saturation after change: %s\n", Lua_osd_values->saturation_after_change);
+
+ mpv_set_property_string(ctx, "contrast", "4");
+ Lua_osd_values->contrast_after_change = mpv_get_property_string(ctx, "contrast");
+ printf("Luaontrast after change: %s\n", Lua_osd_values->contrast_after_change);
+
+
+
+
+}
+
+static void test_compare_values(struct osdTestValues* C_osd_values, struct osdTestValues* Lua_osd_values)
+{
+
+ int number_of_matches = 0;
+ const char *matched = "%s: Matched.\n";
+ const char *no_matched = "%s: DID NOT MATCH (C OSD = %s; Lua OSD = %s)\n";
+
+ if (strcmp(C_osd_values->initial_volume, Lua_osd_values->initial_volume)) {
+ printf(no_matched, "Initial volume", C_osd_values->initial_volume, Lua_osd_values->initial_volume);
+ } else {
+ printf(matched, "Initial volume");
+ number_of_matches++;
+ }
+
+ if (strcmp(C_osd_values->initial_mute, Lua_osd_values->initial_mute)) {
+ printf(no_matched, "Initial mute flag", C_osd_values->initial_mute, Lua_osd_values->initial_mute);
+ } else {
+ printf(matched, "Initial mute flag");
+ number_of_matches++;
+ }
+
+ if (strcmp(C_osd_values->started_paused, Lua_osd_values->started_paused)) {
+ printf(no_matched, "Started paused", C_osd_values->started_paused, Lua_osd_values->started_paused);
+ } else {
+ printf(matched, "Started paused");
+ number_of_matches++;
+ }
+
+ if (strcmp(C_osd_values->initial_brightness, Lua_osd_values->initial_brightness)) {
+ printf(no_matched, "Initial brightness", C_osd_values->initial_brightness, Lua_osd_values->initial_brightness);
+ } else {
+ printf(matched, "Initial brightness");
+ number_of_matches++;
+ }
+
+ if (strcmp(C_osd_values->initial_saturation, Lua_osd_values->initial_saturation)) {
+ printf(no_matched, "Initial saturation", C_osd_values->initial_saturation, Lua_osd_values->initial_saturation);
+ } else {
+ printf(matched, "Initial saturation");
+ number_of_matches++;
+ }
+
+ if (strcmp(C_osd_values->initial_contrast, Lua_osd_values->initial_contrast)) {
+ printf(no_matched, "Initial contrast", C_osd_values->initial_contrast, Lua_osd_values->initial_contrast);
+ } else {
+ printf(matched, "Initial contrast");
+ number_of_matches++;
+ }
+
+
+
+ if (strcmp(C_osd_values->volume_after_change, Lua_osd_values->volume_after_change)) {
+ printf(no_matched, "Volume after change", C_osd_values->volume_after_change, Lua_osd_values->volume_after_change);
+ } else {
+ printf(matched, "Volume after change");
+ number_of_matches++;
+ }
+
+
+ if (strcmp(C_osd_values->second_mute, Lua_osd_values->second_mute)) {
+ printf(no_matched, "Mute flag after changing it", C_osd_values->second_mute, Lua_osd_values->second_mute);
+ } else {
+ printf(matched, "Mute flag after changing it");
+ number_of_matches++;
+ }
+
+
+ if (strcmp(C_osd_values->second_paused, Lua_osd_values->second_paused)) {
+ printf(no_matched, "Paused after pause command", C_osd_values->second_paused, Lua_osd_values->second_paused);
+ } else {
+ printf(matched, "Paused after pause command");
+ number_of_matches++;
+ }
+
+ if (strcmp(C_osd_values->brightness_after_change, Lua_osd_values->brightness_after_change)) {
+ printf(no_matched, "Brightness after change", C_osd_values->brightness_after_change, Lua_osd_values->brightness_after_change);
+ } else {
+ printf(matched, "Brightness after change");
+ number_of_matches++;
+ }
+
+ if (strcmp(C_osd_values->saturation_after_change, Lua_osd_values->saturation_after_change)) {
+ printf(no_matched, "Saturation after change", C_osd_values->saturation_after_change, Lua_osd_values->saturation_after_change);
+ } else {
+ printf(matched, "Saturation after change");
+ number_of_matches++;
+ }
+
+ if (strcmp(C_osd_values->contrast_after_change, Lua_osd_values->contrast_after_change)) {
+ printf(no_matched, "Contrast after change", C_osd_values->contrast_after_change, Lua_osd_values->contrast_after_change);
+ } else {
+ printf(matched, "Contrast after change");
+ number_of_matches++;
+ }
+
+
+ fflush(stdout); // to ensure correct order of prints
+ if (number_of_matches != 12) {
+ fail("\nSome values of Lua's OSD did not match the intended values from the C OSD.\n");
+ }
+
+
+ free_osd_values(C_osd_values, Lua_osd_values);
+}
+
+
+
+
+int main(void)
+{
+ struct osdTestValues C_osd_values;
+ struct osdTestValues Lua_osd_values;
+
+
+ ctx = mpv_create();
+ if (!ctx)
+ return 1;
+
+ atexit(exit_cleanup);
+
+ set_option_string("osd-lua", "no");
+ initialize();
+
+ printf("================ TEST: Lua OSD ================\n");
+
+ printf("--------------- Running C OSD to register expected values ---------------\n");
+ test_c_osd(&C_osd_values);
+ printf("--------------- Finished running C OSD ---------------\n");
+
+ command_string("quit");
+
+ while (wrap_wait_event()->event_id != MPV_EVENT_SHUTDOWN) {}
+
+// -----------------------------------------------------------------------------------------
+
+ ctx = mpv_create();
+ if (!ctx)
+ return 1;
+
+ atexit(exit_cleanup);
+
+ set_option_string("osd-lua", "yes");
+ initialize();
+
+ printf("--------------- Running Lua OSD to register values ---------------\n");
+ test_lua_osd(&Lua_osd_values);
+ printf("--------------- Finished running Lua OSD ---------------\n");
+
+ command_string("quit");
+ while (wrap_wait_event()->event_id != MPV_EVENT_SHUTDOWN) {}
+
+ printf("================ SHUTDOWN ================\n");
+
+// -----------------------------------------------------------------------------------------
+
+ printf("--------------- Comparing Lua OSD values with C OSD values ---------------\n");
+ test_compare_values(&C_osd_values, &Lua_osd_values);
+ printf("--------------- Finished comparing values ---------------\n");
+
+ return 0;
+}
+
diff --git a/test/meson.build b/test/meson.build
index 156d2c433b8e4..a87928f2dc7da 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -122,6 +122,10 @@ if get_option('libmpv')
include_directories: incdir, dependencies: libmpv_dep)
test('libmpv-test-file-loading', exe, args: file, suite: 'libmpv')
+ exe = executable('libmpv-test-file-loading-lua-osd', 'libmpv_test_file_loading_lua_osd.c',
+ include_directories: incdir, dependencies: libmpv_dep)
+ test('libmpv-test-file-loading-lua-osd', exe, args: file, suite: 'libmpv')
+
exe = executable('libmpv-test-lavfi-complex', 'libmpv_test_lavfi_complex.c',
include_directories: incdir, dependencies: libmpv_dep)
test('libmpv-test-lavfi-complex', exe, args: file, suite: 'libmpv')
@@ -130,6 +134,10 @@ if get_option('libmpv')
include_directories: incdir, dependencies: libmpv_dep)
test('libmpv-test-options', exe, suite: 'libmpv')
+ exe = executable('libmpv-test-lua-osd', 'libmpv_test_lua_osd.c',
+ include_directories: incdir, dependencies: libmpv_dep)
+ test('libmpv-test-lua-osd', exe, suite: 'libmpv')
+
# Old versions of ffmpeg are bugged when setting forced tracks and older
# versions of meson don't support the custom version checking argument.
if meson.version().version_compare('>= 1.5.0')