2015-04-03 15:22:32 +00:00
|
|
|
-- Display some stats.
|
|
|
|
--
|
2016-07-08 08:53:02 +00:00
|
|
|
-- Please consult the readme for information about usage and configuration:
|
|
|
|
-- https://github.com/Argon-/mpv-stats
|
2015-04-03 15:22:32 +00:00
|
|
|
--
|
|
|
|
-- Please note: not every property is always available and therefore not always
|
|
|
|
-- visible.
|
|
|
|
|
2016-07-12 15:46:40 +00:00
|
|
|
local mp = require 'mp'
|
2015-05-29 14:04:57 +00:00
|
|
|
local options = require 'mp.options'
|
2017-12-26 16:54:03 +00:00
|
|
|
local utils = require 'mp.utils'
|
2024-03-21 19:09:22 +00:00
|
|
|
local input = require 'mp.input'
|
2015-04-03 15:22:32 +00:00
|
|
|
|
2015-05-29 14:04:57 +00:00
|
|
|
-- Options
|
2015-04-03 15:22:32 +00:00
|
|
|
local o = {
|
2016-05-17 20:23:11 +00:00
|
|
|
-- Default key bindings
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
key_page_1 = "1",
|
|
|
|
key_page_2 = "2",
|
|
|
|
key_page_3 = "3",
|
2021-07-16 08:17:50 +00:00
|
|
|
key_page_4 = "4",
|
2024-06-08 16:19:00 +00:00
|
|
|
key_page_5 = "5",
|
2021-07-16 07:33:46 +00:00
|
|
|
key_page_0 = "0",
|
2020-04-10 22:20:02 +00:00
|
|
|
-- For pages which support scrolling
|
|
|
|
key_scroll_up = "UP",
|
|
|
|
key_scroll_down = "DOWN",
|
2024-03-21 19:09:22 +00:00
|
|
|
key_search = "/",
|
2024-10-17 07:20:04 +00:00
|
|
|
key_exit = "ESC",
|
2020-04-10 22:20:02 +00:00
|
|
|
scroll_lines = 1,
|
2016-05-17 20:23:11 +00:00
|
|
|
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
duration = 4,
|
2016-07-12 15:46:40 +00:00
|
|
|
redraw_delay = 1, -- acts as duration in the toggling case
|
2016-05-17 20:23:11 +00:00
|
|
|
ass_formatting = true,
|
2017-09-23 15:54:40 +00:00
|
|
|
persistent_overlay = false, -- whether the stats can be overwritten by other output
|
2024-05-12 01:37:38 +00:00
|
|
|
filter_params_max_length = 100, -- show one filter per line if list exceeds this length
|
2024-04-17 10:27:04 +00:00
|
|
|
file_tag_max_length = 128, -- only show file tags shorter than this length in bytes
|
|
|
|
file_tag_max_count = 16, -- only show the first x file tags
|
2024-05-12 01:37:38 +00:00
|
|
|
show_frame_info = false, -- whether to show the current frame info
|
2024-10-17 22:00:22 +00:00
|
|
|
term_clip = true,
|
2015-05-22 19:41:55 +00:00
|
|
|
debug = false,
|
2015-05-29 14:04:57 +00:00
|
|
|
|
2016-07-08 08:53:02 +00:00
|
|
|
-- Graph options and style
|
2016-09-12 00:30:09 +00:00
|
|
|
plot_perfdata = true,
|
|
|
|
plot_vsync_ratio = true,
|
|
|
|
plot_vsync_jitter = true,
|
2023-10-22 04:13:52 +00:00
|
|
|
plot_tonemapping_lut = false,
|
2016-07-12 15:46:40 +00:00
|
|
|
skip_frames = 5,
|
|
|
|
global_max = true,
|
2016-09-12 00:30:09 +00:00
|
|
|
flush_graph_data = true, -- clear data buffers when toggling
|
2016-06-10 22:04:14 +00:00
|
|
|
plot_bg_border_color = "0000FF",
|
|
|
|
plot_bg_color = "262626",
|
|
|
|
plot_color = "FFFFFF",
|
2024-10-29 15:19:17 +00:00
|
|
|
plot_bg_border_width = 1.25,
|
2016-06-10 22:04:14 +00:00
|
|
|
|
2015-05-29 14:04:57 +00:00
|
|
|
-- Text style
|
2023-11-13 07:46:29 +00:00
|
|
|
font = "",
|
2018-09-08 21:13:35 +00:00
|
|
|
font_mono = "monospace", -- monospaced digits are sufficient
|
2024-10-29 15:19:17 +00:00
|
|
|
font_size = 20,
|
2023-11-13 07:46:29 +00:00
|
|
|
font_color = "",
|
2024-11-17 06:01:55 +00:00
|
|
|
border_size = 1.65,
|
2023-11-13 07:46:29 +00:00
|
|
|
border_color = "",
|
2015-04-03 15:22:32 +00:00
|
|
|
shadow_x_offset = 0.0,
|
|
|
|
shadow_y_offset = 0.0,
|
2023-11-13 07:46:29 +00:00
|
|
|
shadow_color = "",
|
2015-04-03 15:22:32 +00:00
|
|
|
alpha = "11",
|
2024-05-16 14:56:46 +00:00
|
|
|
vidscale = "auto",
|
2015-04-28 00:13:27 +00:00
|
|
|
|
|
|
|
-- Custom header for ASS tags to style the text output.
|
|
|
|
-- Specifying this will ignore the text style values above and just
|
|
|
|
-- use this string instead.
|
|
|
|
custom_header = "",
|
|
|
|
|
2015-05-29 14:04:57 +00:00
|
|
|
-- Text formatting
|
|
|
|
-- With ASS
|
2017-07-04 22:31:44 +00:00
|
|
|
ass_nl = "\\N",
|
|
|
|
ass_indent = "\\h\\h\\h\\h\\h",
|
|
|
|
ass_prefix_sep = "\\h\\h",
|
|
|
|
ass_b1 = "{\\b1}",
|
|
|
|
ass_b0 = "{\\b0}",
|
2017-07-25 13:37:09 +00:00
|
|
|
ass_it1 = "{\\i1}",
|
|
|
|
ass_it0 = "{\\i0}",
|
2015-05-29 14:04:57 +00:00
|
|
|
-- Without ASS
|
2015-04-28 00:13:27 +00:00
|
|
|
no_ass_nl = "\n",
|
2024-10-21 06:39:13 +00:00
|
|
|
no_ass_indent = " ",
|
2015-10-14 07:53:01 +00:00
|
|
|
no_ass_prefix_sep = " ",
|
2015-07-21 19:53:23 +00:00
|
|
|
no_ass_b1 = "\027[1m",
|
|
|
|
no_ass_b0 = "\027[0m",
|
2017-07-25 13:37:09 +00:00
|
|
|
no_ass_it1 = "\027[3m",
|
|
|
|
no_ass_it0 = "\027[0m",
|
stats.lua: page 4 (keys): support help-like terminal printout
While --input-test is useful, and so is page 4 of stats, until now
there was no way to simply print the list in a help-like fashion.
This commit adds such printout, invoked by the script opt
stats-bindlist=yes, which uses the existing page 4 code. This prints
the list on startup and quits immediately - like any help page.
It's awkward to invoke compared to other help pages, and it does
require the stats page to be enabled (it is by default), however
it is a script-generated output, and currently there's no other
method to print a help page generated by a script.
The printout itself is performed using lua's io.write. While reliable,
it's not the standard way for mpv to print to the terminal.
Other possible printout methods are mp.msg.info - which also prints
"[stats]" prefix on each line (ugly), or forcing term-osd and setting
an osd-message which mpv will then print at the terminal - however
that's printed to stderr, and could also be subject to timing concerns
since we quit right afterwards.
In the future we can consider changing/integrating the invocation so
that mpv itself could print a help page generated by a script, thus
solving both the awkward invocation and printout-method issues.
2021-07-16 09:06:20 +00:00
|
|
|
|
|
|
|
bindlist = "no", -- print page 4 to the terminal on startup and quit mpv
|
2015-04-03 15:22:32 +00:00
|
|
|
}
|
2015-05-29 14:04:57 +00:00
|
|
|
options.read_options(o)
|
2015-04-03 15:22:32 +00:00
|
|
|
|
2016-07-08 08:53:02 +00:00
|
|
|
local format = string.format
|
2016-09-12 00:30:09 +00:00
|
|
|
local max = math.max
|
2017-07-24 20:46:01 +00:00
|
|
|
local min = math.min
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
|
2024-04-15 04:43:17 +00:00
|
|
|
-- Scaled metrics
|
|
|
|
local font_size = o.font_size
|
|
|
|
local border_size = o.border_size
|
|
|
|
local shadow_x_offset = o.shadow_x_offset
|
|
|
|
local shadow_y_offset = o.shadow_y_offset
|
|
|
|
local plot_bg_border_width = o.plot_bg_border_width
|
2016-07-19 07:11:51 +00:00
|
|
|
-- Function used to record performance data
|
2016-07-08 08:53:02 +00:00
|
|
|
local recorder = nil
|
2017-09-23 15:54:40 +00:00
|
|
|
-- Timer used for redrawing (toggling) and clearing the screen (oneshot)
|
|
|
|
local display_timer = nil
|
2019-06-22 22:30:56 +00:00
|
|
|
-- Timer used to update cache stats.
|
2024-05-12 01:37:38 +00:00
|
|
|
local cache_recorder_timer
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
-- Current page and <page key>:<page function> mappings
|
|
|
|
local curr_page = o.key_page_1
|
|
|
|
local pages = {}
|
2020-04-10 22:20:02 +00:00
|
|
|
local scroll_bound = false
|
2024-03-21 19:09:22 +00:00
|
|
|
local searched_text
|
2023-08-30 13:09:51 +00:00
|
|
|
local tm_viz_prev = nil
|
2016-07-19 07:11:51 +00:00
|
|
|
-- Save these sequences locally as we'll need them a lot
|
|
|
|
local ass_start = mp.get_property_osd("osd-ass-cc/0")
|
|
|
|
local ass_stop = mp.get_property_osd("osd-ass-cc/1")
|
2016-09-12 00:30:09 +00:00
|
|
|
-- Ring buffers for the values used to construct a graph.
|
|
|
|
-- .pos denotes the current position, .len the buffer length
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
-- .max is the max value in the corresponding buffer
|
2020-04-09 13:03:17 +00:00
|
|
|
local vsratio_buf, vsjitter_buf
|
2016-09-12 00:30:09 +00:00
|
|
|
local function init_buffers()
|
|
|
|
vsratio_buf = {0, pos = 1, len = 50, max = 0}
|
|
|
|
vsjitter_buf = {0, pos = 1, len = 50, max = 0}
|
|
|
|
end
|
2020-04-09 13:03:17 +00:00
|
|
|
local cache_ahead_buf, cache_speed_buf
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
local perf_buffers = {}
|
2024-10-17 07:20:04 +00:00
|
|
|
local process_key_binding
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
local function graph_add_value(graph, value)
|
|
|
|
graph.pos = (graph.pos % graph.len) + 1
|
|
|
|
graph[graph.pos] = value
|
|
|
|
graph.max = max(graph.max, value)
|
|
|
|
end
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
|
2016-07-12 17:19:34 +00:00
|
|
|
local function no_ASS(t)
|
stats.lua: fix ass-escape while persistent_overlay=yes
mpv has two methods to display output from text input:
- show-text (scripting: mp.osd_message) has ass disabled by default
(escaped), and the property osd-ass-cc can control escaping.
- osd-overlay (scripting: mp.set_osd_ass or mp.create_osd_overlay)
has ass enabled (unescaped), and osd-ass-cc is NOT supported.
By default, stats.lua uses mp.osd_message which does support escaping.
That's persistent_overlay=no.
When using persistent_overlay=yes then mp.set_osd_ass is used.
Due to this, the no_ASS(..) function - which is supposed to escape
ass, simply returned its input unmodified when persistent_overlay
is enabled.
This is not a new issue, and the filters on page 1 use no_ASS() to no
avail in persistent mode, however, this content (filter name and value
strings) rarely actually needs escaping, and users didn't notice.
However, the new page 4 (keys) does break visibly when no_ASS doesn't
work, because it tries to escape arbitrary key-names and command
strings, and at the very least the key '{' is bound by default, which
is displayed incorrectly if escaping doesn't work.
Fix this by rolling our own escaping when using mp.set_osd_ass,
similar to how the mpv code does it for mp.osd_message (substrings
replacements).
However, this means that the set_ASS(..) function can no longer
behave correctly because escaping requires going through the whole
content string rather than only inserting a marker.
Luckily, other than at no_ASS, set_ASS was only used at one place
(text_style), which is only called from two places:
- generate_graph() only needs to restore styles - not to enable ass.
- add_header() is only used at the begining of page output, and
uses set_ASS to enable ass initially when using mp.osd_message.
So remove the set_ASS function, and instead enable ass directly at
print_page using osd-ass-cc when mp.osd_message is used.
Fixes #9022
2021-07-20 15:03:29 +00:00
|
|
|
if not o.use_ass then
|
|
|
|
return t
|
|
|
|
elseif not o.persistent_overlay then
|
|
|
|
-- mp.osd_message supports ass-escape using osd-ass-cc/{0|1}
|
|
|
|
return ass_stop .. t .. ass_start
|
|
|
|
else
|
2022-05-01 05:29:32 +00:00
|
|
|
return mp.command_native({"escape-ass", tostring(t)})
|
stats.lua: fix ass-escape while persistent_overlay=yes
mpv has two methods to display output from text input:
- show-text (scripting: mp.osd_message) has ass disabled by default
(escaped), and the property osd-ass-cc can control escaping.
- osd-overlay (scripting: mp.set_osd_ass or mp.create_osd_overlay)
has ass enabled (unescaped), and osd-ass-cc is NOT supported.
By default, stats.lua uses mp.osd_message which does support escaping.
That's persistent_overlay=no.
When using persistent_overlay=yes then mp.set_osd_ass is used.
Due to this, the no_ASS(..) function - which is supposed to escape
ass, simply returned its input unmodified when persistent_overlay
is enabled.
This is not a new issue, and the filters on page 1 use no_ASS() to no
avail in persistent mode, however, this content (filter name and value
strings) rarely actually needs escaping, and users didn't notice.
However, the new page 4 (keys) does break visibly when no_ASS doesn't
work, because it tries to escape arbitrary key-names and command
strings, and at the very least the key '{' is bound by default, which
is displayed incorrectly if escaping doesn't work.
Fix this by rolling our own escaping when using mp.set_osd_ass,
similar to how the mpv code does it for mp.osd_message (substrings
replacements).
However, this means that the set_ASS(..) function can no longer
behave correctly because escaping requires going through the whole
content string rather than only inserting a marker.
Luckily, other than at no_ASS, set_ASS was only used at one place
(text_style), which is only called from two places:
- generate_graph() only needs to restore styles - not to enable ass.
- add_header() is only used at the begining of page output, and
uses set_ASS to enable ass initially when using mp.osd_message.
So remove the set_ASS function, and instead enable ass directly at
print_page using osd-ass-cc when mp.osd_message is used.
Fixes #9022
2021-07-20 15:03:29 +00:00
|
|
|
end
|
2015-04-03 15:22:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2024-05-12 01:37:38 +00:00
|
|
|
local function bold(t)
|
2016-07-12 17:19:34 +00:00
|
|
|
return o.b1 .. t .. o.b0
|
2016-06-10 22:04:14 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2017-07-25 13:37:09 +00:00
|
|
|
local function it(t)
|
|
|
|
return o.it1 .. t .. o.it0
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2016-07-12 17:19:34 +00:00
|
|
|
local function text_style()
|
2017-07-04 22:31:44 +00:00
|
|
|
if not o.use_ass then
|
2016-06-10 22:04:14 +00:00
|
|
|
return ""
|
2015-04-27 17:10:08 +00:00
|
|
|
end
|
2015-04-03 15:22:32 +00:00
|
|
|
if o.custom_header and o.custom_header ~= "" then
|
stats.lua: fix ass-escape while persistent_overlay=yes
mpv has two methods to display output from text input:
- show-text (scripting: mp.osd_message) has ass disabled by default
(escaped), and the property osd-ass-cc can control escaping.
- osd-overlay (scripting: mp.set_osd_ass or mp.create_osd_overlay)
has ass enabled (unescaped), and osd-ass-cc is NOT supported.
By default, stats.lua uses mp.osd_message which does support escaping.
That's persistent_overlay=no.
When using persistent_overlay=yes then mp.set_osd_ass is used.
Due to this, the no_ASS(..) function - which is supposed to escape
ass, simply returned its input unmodified when persistent_overlay
is enabled.
This is not a new issue, and the filters on page 1 use no_ASS() to no
avail in persistent mode, however, this content (filter name and value
strings) rarely actually needs escaping, and users didn't notice.
However, the new page 4 (keys) does break visibly when no_ASS doesn't
work, because it tries to escape arbitrary key-names and command
strings, and at the very least the key '{' is bound by default, which
is displayed incorrectly if escaping doesn't work.
Fix this by rolling our own escaping when using mp.set_osd_ass,
similar to how the mpv code does it for mp.osd_message (substrings
replacements).
However, this means that the set_ASS(..) function can no longer
behave correctly because escaping requires going through the whole
content string rather than only inserting a marker.
Luckily, other than at no_ASS, set_ASS was only used at one place
(text_style), which is only called from two places:
- generate_graph() only needs to restore styles - not to enable ass.
- add_header() is only used at the begining of page output, and
uses set_ASS to enable ass initially when using mp.osd_message.
So remove the set_ASS function, and instead enable ass directly at
print_page using osd-ass-cc when mp.osd_message is used.
Fixes #9022
2021-07-20 15:03:29 +00:00
|
|
|
return o.custom_header
|
2015-04-03 15:22:32 +00:00
|
|
|
else
|
2024-04-15 04:43:17 +00:00
|
|
|
local style = "{\\r\\an7\\fs" .. font_size .. "\\bord" .. border_size
|
2023-11-13 07:46:29 +00:00
|
|
|
|
|
|
|
if o.font ~= "" then
|
|
|
|
style = style .. "\\fn" .. o.font
|
|
|
|
end
|
|
|
|
|
|
|
|
if o.font_color ~= "" then
|
|
|
|
style = style .. "\\1c&H" .. o.font_color .. "&\\1a&H" .. o.alpha .. "&"
|
|
|
|
end
|
|
|
|
|
|
|
|
if o.border_color ~= "" then
|
|
|
|
style = style .. "\\3c&H" .. o.border_color .. "&\\3a&H" .. o.alpha .. "&"
|
|
|
|
end
|
|
|
|
|
|
|
|
if o.shadow_color ~= "" then
|
|
|
|
style = style .. "\\4c&H" .. o.shadow_color .. "&\\4a&H" .. o.alpha .. "&"
|
|
|
|
end
|
|
|
|
|
2024-04-15 04:43:17 +00:00
|
|
|
return style .. "\\xshad" .. shadow_x_offset ..
|
|
|
|
"\\yshad" .. shadow_y_offset .. "}"
|
2015-04-03 15:22:32 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2016-07-12 17:19:34 +00:00
|
|
|
local function has_vo_window()
|
2023-10-16 17:57:03 +00:00
|
|
|
return mp.get_property_native("vo-configured") and mp.get_property_native("video-osd")
|
2016-07-12 17:19:34 +00:00
|
|
|
end
|
2015-04-03 15:22:32 +00:00
|
|
|
|
2015-07-05 19:24:36 +00:00
|
|
|
|
2016-09-11 09:34:11 +00:00
|
|
|
-- Generate a graph from the given values.
|
|
|
|
-- Returns an ASS formatted vector drawing as string.
|
|
|
|
--
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
-- values: Array/table of numbers representing the data. Used like a ring buffer
|
2016-09-11 09:34:11 +00:00
|
|
|
-- it will get iterated backwards `len` times starting at position `i`.
|
|
|
|
-- i : Index of the latest data value in `values`.
|
|
|
|
-- len : The length/amount of numbers in `values`.
|
|
|
|
-- v_max : The maximum number in `values`. It is used to scale all data
|
|
|
|
-- values to a range of 0 to `v_max`.
|
2017-06-30 22:31:05 +00:00
|
|
|
-- v_avg : The average number in `values`. It is used to try and center graphs
|
|
|
|
-- if possible. May be left as nil
|
2016-09-11 09:34:11 +00:00
|
|
|
-- scale : A value that will be multiplied with all data values.
|
2017-06-30 19:46:16 +00:00
|
|
|
-- x_tics: Horizontal width multiplier for the steps
|
2017-06-30 22:31:05 +00:00
|
|
|
local function generate_graph(values, i, len, v_max, v_avg, scale, x_tics)
|
2016-09-12 00:30:09 +00:00
|
|
|
-- Check if at least one value exists
|
|
|
|
if not values[i] then
|
2016-07-08 08:53:02 +00:00
|
|
|
return ""
|
|
|
|
end
|
|
|
|
|
2016-09-11 01:34:44 +00:00
|
|
|
local x_max = (len - 1) * x_tics
|
2024-04-15 04:43:17 +00:00
|
|
|
local y_offset = border_size
|
|
|
|
local y_max = font_size * 0.66
|
2016-06-10 22:04:14 +00:00
|
|
|
local x = 0
|
|
|
|
|
stats.lua: graphs: fix bad rendering due to division by 0
This fixes two potential divisions by 0 at generate_graph(...):
- If v_avg is (given and) 0.
- if v_max is 0.
The former doesn't seem to happen in practice because v_avg is only
used at one generate_gpah call, where it's apparently not 0.
The latter triggers if all the graph values are 0 (hence v_max is
also 0).
The implication of these divisions by 0 is an invalid y-value which
ends up at the ASS coordinates string for the graph inner content.
On linux the value ends as "nan" (luajit) or "-nan" (lua 5.1/5.2), and
on Windows it's "nan" (luajit) or "-1.#IND00" (lua 5.1/5.2), maybe due
to msvcrt's snprintf.
All these strings are wrong as ASS numbers, but due to luck in how
libass parses the coordinates, "nan" and "-nan" result in empty graph
(which looks OK for "all values are 0"), while "-1.#IND00" is parsed
as -1, which breaks the graph rendering (affects other graphs too).
One example of "all values are 0" is at page 0 (internal performance
graphs) on Windows - because the cpu metrics don't work.
So this fixes at least page 0 on Windows with lua 5.1/5.2.
While at it, move the scale calculations to one place, which now
avoids division by 0, instead of duplicating this calculation.
In the future, we can consider improving the generate_graph API:
- It needs one peak value, but takes 3 (v_max, v_avg, scale) which
are meshed into one final value.
- v_avg is only used in 1 of 6 call sites, but at the middle of the
arguments, so all other call sites need to pass explicit "nil".
- "scale" is arbitrary and used to leave some space at the top of the
graph. 5 places use 0.8, one uses 0.9. Could probably be unified.
2022-02-21 11:16:30 +00:00
|
|
|
if v_max > 0 then
|
|
|
|
-- try and center the graph if possible, but avoid going above `scale`
|
|
|
|
if v_avg and v_avg > 0 then
|
|
|
|
scale = min(scale, v_max / (2 * v_avg))
|
|
|
|
end
|
|
|
|
scale = scale * y_max / v_max
|
|
|
|
end -- else if v_max==0 then all values are 0 and scale doesn't matter
|
2017-06-30 22:31:05 +00:00
|
|
|
|
stats.lua: graphs: fix bad rendering due to division by 0
This fixes two potential divisions by 0 at generate_graph(...):
- If v_avg is (given and) 0.
- if v_max is 0.
The former doesn't seem to happen in practice because v_avg is only
used at one generate_gpah call, where it's apparently not 0.
The latter triggers if all the graph values are 0 (hence v_max is
also 0).
The implication of these divisions by 0 is an invalid y-value which
ends up at the ASS coordinates string for the graph inner content.
On linux the value ends as "nan" (luajit) or "-nan" (lua 5.1/5.2), and
on Windows it's "nan" (luajit) or "-1.#IND00" (lua 5.1/5.2), maybe due
to msvcrt's snprintf.
All these strings are wrong as ASS numbers, but due to luck in how
libass parses the coordinates, "nan" and "-nan" result in empty graph
(which looks OK for "all values are 0"), while "-1.#IND00" is parsed
as -1, which breaks the graph rendering (affects other graphs too).
One example of "all values are 0" is at page 0 (internal performance
graphs) on Windows - because the cpu metrics don't work.
So this fixes at least page 0 on Windows with lua 5.1/5.2.
While at it, move the scale calculations to one place, which now
avoids division by 0, instead of duplicating this calculation.
In the future, we can consider improving the generate_graph API:
- It needs one peak value, but takes 3 (v_max, v_avg, scale) which
are meshed into one final value.
- v_avg is only used in 1 of 6 call sites, but at the middle of the
arguments, so all other call sites need to pass explicit "nil".
- "scale" is arbitrary and used to leave some space at the top of the
graph. 5 places use 0.8, one uses 0.9. Could probably be unified.
2022-02-21 11:16:30 +00:00
|
|
|
local s = {format("m 0 0 n %f %f l ", x, y_max - scale * values[i])}
|
2016-09-11 01:34:44 +00:00
|
|
|
i = ((i - 2) % len) + 1
|
2016-06-10 22:04:14 +00:00
|
|
|
|
2024-05-12 01:37:38 +00:00
|
|
|
for _ = 1, len - 1 do
|
2016-06-10 22:04:14 +00:00
|
|
|
if values[i] then
|
|
|
|
x = x - x_tics
|
stats.lua: graphs: fix bad rendering due to division by 0
This fixes two potential divisions by 0 at generate_graph(...):
- If v_avg is (given and) 0.
- if v_max is 0.
The former doesn't seem to happen in practice because v_avg is only
used at one generate_gpah call, where it's apparently not 0.
The latter triggers if all the graph values are 0 (hence v_max is
also 0).
The implication of these divisions by 0 is an invalid y-value which
ends up at the ASS coordinates string for the graph inner content.
On linux the value ends as "nan" (luajit) or "-nan" (lua 5.1/5.2), and
on Windows it's "nan" (luajit) or "-1.#IND00" (lua 5.1/5.2), maybe due
to msvcrt's snprintf.
All these strings are wrong as ASS numbers, but due to luck in how
libass parses the coordinates, "nan" and "-nan" result in empty graph
(which looks OK for "all values are 0"), while "-1.#IND00" is parsed
as -1, which breaks the graph rendering (affects other graphs too).
One example of "all values are 0" is at page 0 (internal performance
graphs) on Windows - because the cpu metrics don't work.
So this fixes at least page 0 on Windows with lua 5.1/5.2.
While at it, move the scale calculations to one place, which now
avoids division by 0, instead of duplicating this calculation.
In the future, we can consider improving the generate_graph API:
- It needs one peak value, but takes 3 (v_max, v_avg, scale) which
are meshed into one final value.
- v_avg is only used in 1 of 6 call sites, but at the middle of the
arguments, so all other call sites need to pass explicit "nil".
- "scale" is arbitrary and used to leave some space at the top of the
graph. 5 places use 0.8, one uses 0.9. Could probably be unified.
2022-02-21 11:16:30 +00:00
|
|
|
s[#s+1] = format("%f %f ", x, y_max - scale * values[i])
|
2016-06-10 22:04:14 +00:00
|
|
|
end
|
2016-09-11 01:34:44 +00:00
|
|
|
i = ((i - 2) % len) + 1
|
2016-06-10 22:04:14 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
s[#s+1] = format("%f %f %f %f", x, y_max, 0, y_max)
|
|
|
|
|
2024-04-15 04:45:18 +00:00
|
|
|
local bg_box = format("{\\bord%f}{\\3c&H%s&}{\\1c&H%s&}m 0 %f l %f %f %f 0 0 0",
|
2024-05-12 01:37:38 +00:00
|
|
|
plot_bg_border_width, o.plot_bg_border_color, o.plot_bg_color,
|
|
|
|
y_max, x_max, y_max, x_max)
|
|
|
|
return format("%s{\\rDefault}{\\pbo%f}{\\shad0}{\\alpha&H00}{\\p1}%s{\\p0}" ..
|
|
|
|
"{\\bord0}{\\1c&H%s}{\\p1}%s{\\p0}%s",
|
2016-07-13 08:11:18 +00:00
|
|
|
o.prefix_sep, y_offset, bg_box, o.plot_color, table.concat(s), text_style())
|
2016-07-08 08:53:02 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2017-12-26 16:54:03 +00:00
|
|
|
local function append(s, str, attr)
|
2018-03-24 21:36:01 +00:00
|
|
|
if not str then
|
|
|
|
return false
|
|
|
|
end
|
2017-12-26 16:54:03 +00:00
|
|
|
attr.prefix_sep = attr.prefix_sep or o.prefix_sep
|
|
|
|
attr.indent = attr.indent or o.indent
|
|
|
|
attr.nl = attr.nl or o.nl
|
|
|
|
attr.suffix = attr.suffix or ""
|
|
|
|
attr.prefix = attr.prefix or ""
|
|
|
|
attr.no_prefix_markup = attr.no_prefix_markup or false
|
2024-05-12 01:37:38 +00:00
|
|
|
attr.prefix = attr.no_prefix_markup and attr.prefix or bold(attr.prefix)
|
2024-05-23 01:24:58 +00:00
|
|
|
|
|
|
|
local index = #s + (attr.nl == "" and 0 or 1)
|
|
|
|
s[index] = s[index] or ""
|
|
|
|
s[index] = s[index] .. format("%s%s%s%s%s%s", attr.nl, attr.indent,
|
2017-12-26 16:54:03 +00:00
|
|
|
attr.prefix, attr.prefix_sep, no_ASS(str), attr.suffix)
|
2018-03-24 21:36:01 +00:00
|
|
|
return true
|
2017-12-26 16:54:03 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2016-09-12 00:30:09 +00:00
|
|
|
-- Format and append a property.
|
|
|
|
-- A property whose value is either `nil` or empty (hereafter called "invalid")
|
|
|
|
-- is skipped and not appended.
|
|
|
|
-- Returns `false` in case nothing was appended, otherwise `true`.
|
|
|
|
--
|
|
|
|
-- s : Table containing strings.
|
|
|
|
-- prop : The property to query and format (based on its OSD representation).
|
|
|
|
-- attr : Optional table to overwrite certain (formatting) attributes for
|
|
|
|
-- this property.
|
|
|
|
-- exclude: Optional table containing keys which are considered invalid values
|
|
|
|
-- for this property. Specifying this will replace empty string as
|
|
|
|
-- default invalid value (nil is always invalid).
|
|
|
|
local function append_property(s, prop, attr, excluded)
|
|
|
|
excluded = excluded or {[""] = true}
|
|
|
|
local ret = mp.get_property_osd(prop)
|
|
|
|
if not ret or excluded[ret] then
|
|
|
|
if o.debug then
|
|
|
|
print("No value for property: " .. prop)
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
2018-03-24 21:36:01 +00:00
|
|
|
return append(s, ret, attr)
|
2016-09-12 00:30:09 +00:00
|
|
|
end
|
|
|
|
|
2022-02-22 23:50:47 +00:00
|
|
|
local function sorted_keys(t, comp_fn)
|
|
|
|
local keys = {}
|
|
|
|
for k,_ in pairs(t) do
|
|
|
|
keys[#keys+1] = k
|
|
|
|
end
|
|
|
|
table.sort(keys, comp_fn)
|
|
|
|
return keys
|
|
|
|
end
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
|
2024-03-21 19:09:22 +00:00
|
|
|
local function scroll_hint(search)
|
|
|
|
local hint = format("(hint: scroll with %s/%s", o.key_scroll_up, o.key_scroll_down)
|
|
|
|
if search then
|
|
|
|
hint = hint .. " and search with " .. o.key_search
|
|
|
|
end
|
|
|
|
hint = hint .. ")"
|
2023-08-30 00:51:40 +00:00
|
|
|
if not o.use_ass then return " " .. hint end
|
2024-04-15 04:43:17 +00:00
|
|
|
return format(" {\\fs%s}%s{\\fs%s}", font_size * 0.66, hint, font_size)
|
2023-08-30 00:51:40 +00:00
|
|
|
end
|
|
|
|
|
2024-06-08 13:37:57 +00:00
|
|
|
local function append_perfdata(header, s, dedicated_page)
|
2017-06-30 19:46:16 +00:00
|
|
|
local vo_p = mp.get_property_native("vo-passes")
|
2016-07-08 08:53:02 +00:00
|
|
|
if not vo_p then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2017-07-24 20:46:01 +00:00
|
|
|
-- Sums of all last/avg/peak values
|
2017-06-30 19:46:16 +00:00
|
|
|
local last_s, avg_s, peak_s = {}, {}, {}
|
|
|
|
for frame, data in pairs(vo_p) do
|
|
|
|
last_s[frame], avg_s[frame], peak_s[frame] = 0, 0, 0
|
|
|
|
for _, pass in ipairs(data) do
|
|
|
|
last_s[frame] = last_s[frame] + pass["last"]
|
|
|
|
avg_s[frame] = avg_s[frame] + pass["avg"]
|
|
|
|
peak_s[frame] = peak_s[frame] + pass["peak"]
|
|
|
|
end
|
|
|
|
end
|
2016-06-10 22:04:14 +00:00
|
|
|
|
2017-09-16 23:21:44 +00:00
|
|
|
-- Pretty print measured time
|
|
|
|
local function pp(i)
|
2017-06-30 19:46:16 +00:00
|
|
|
-- rescale to microseconds for a saner display
|
2023-09-01 01:42:35 +00:00
|
|
|
return format("%5d", i / 1000)
|
2016-07-08 08:53:02 +00:00
|
|
|
end
|
|
|
|
|
2017-07-24 20:46:01 +00:00
|
|
|
-- Format n/m with a font weight based on the ratio
|
|
|
|
local function p(n, m)
|
|
|
|
local i = 0
|
|
|
|
if m > 0 then
|
|
|
|
i = tonumber(n) / m
|
|
|
|
end
|
|
|
|
-- Calculate font weight. 100 is minimum, 400 is normal, 700 bold, 900 is max
|
|
|
|
local w = (700 * math.sqrt(i)) + 200
|
2024-09-07 22:06:50 +00:00
|
|
|
if not o.use_ass then
|
|
|
|
local str = format("%3d%%", i * 100)
|
|
|
|
return w >= 700 and bold(str) or str
|
|
|
|
end
|
2024-06-13 18:44:36 +00:00
|
|
|
return format("{\\b%d}%3d%%{\\b0}", w, i * 100)
|
2017-07-24 20:46:01 +00:00
|
|
|
end
|
|
|
|
|
2024-09-07 22:06:50 +00:00
|
|
|
local font_small = o.use_ass and format("{\\fs%s}", font_size * 0.66) or ""
|
|
|
|
local font_normal = o.use_ass and format("{\\fs%s}", font_size) or ""
|
|
|
|
local font = o.use_ass and format("{\\fn%s}", o.font) or ""
|
|
|
|
local font_mono = o.use_ass and format("{\\fn%s}", o.font_mono) or ""
|
|
|
|
local indent = o.use_ass and "\\h" or " "
|
|
|
|
|
2020-05-17 11:55:38 +00:00
|
|
|
-- ensure that the fixed title is one element and every scrollable line is
|
|
|
|
-- also one single element.
|
2023-08-28 21:01:20 +00:00
|
|
|
local h = dedicated_page and header or s
|
2024-09-07 22:06:50 +00:00
|
|
|
h[#h+1] = format("%s%s%s%s%s%s%s%s",
|
2017-09-23 15:54:40 +00:00
|
|
|
dedicated_page and "" or o.nl, dedicated_page and "" or o.indent,
|
2024-09-07 22:06:50 +00:00
|
|
|
bold("Frame Timings:"), o.prefix_sep, font_small,
|
|
|
|
"(last/average/peak μs)", font_normal,
|
2023-08-30 00:51:40 +00:00
|
|
|
dedicated_page and scroll_hint() or "")
|
2016-07-12 15:46:40 +00:00
|
|
|
|
2022-02-22 23:50:47 +00:00
|
|
|
for _,frame in ipairs(sorted_keys(vo_p)) do -- ensure fixed display order
|
|
|
|
local data = vo_p[frame]
|
2024-09-07 22:06:50 +00:00
|
|
|
local f = "%s%s%s%s%s / %s / %s %s%s%s%s%s%s"
|
2016-07-12 15:46:40 +00:00
|
|
|
|
2024-06-08 13:37:57 +00:00
|
|
|
if dedicated_page then
|
2017-07-24 21:10:20 +00:00
|
|
|
s[#s+1] = format("%s%s%s:", o.nl, o.indent,
|
2024-05-12 01:37:38 +00:00
|
|
|
bold(frame:gsub("^%l", string.upper)))
|
2016-07-12 15:46:40 +00:00
|
|
|
|
2017-06-30 19:46:16 +00:00
|
|
|
for _, pass in ipairs(data) do
|
2017-07-24 21:10:20 +00:00
|
|
|
s[#s+1] = format(f, o.nl, o.indent, o.indent,
|
2024-09-07 22:06:50 +00:00
|
|
|
font_mono, pp(pass["last"]),
|
2017-09-16 23:21:44 +00:00
|
|
|
pp(pass["avg"]), pp(pass["peak"]),
|
2024-09-07 22:06:50 +00:00
|
|
|
o.prefix_sep .. indent, p(pass["last"], last_s[frame]),
|
|
|
|
font, o.prefix_sep, o.prefix_sep, pass["desc"])
|
2016-07-12 15:46:40 +00:00
|
|
|
|
2017-07-04 22:31:44 +00:00
|
|
|
if o.plot_perfdata and o.use_ass then
|
2020-05-17 11:55:38 +00:00
|
|
|
-- use the same line that was already started for this iteration
|
|
|
|
s[#s] = s[#s] ..
|
|
|
|
generate_graph(pass["samples"], pass["count"],
|
2017-06-30 19:46:16 +00:00
|
|
|
pass["count"], pass["peak"],
|
2017-06-30 22:31:05 +00:00
|
|
|
pass["avg"], 0.9, 0.25)
|
2017-06-30 19:46:16 +00:00
|
|
|
end
|
|
|
|
end
|
2016-07-12 15:46:40 +00:00
|
|
|
|
2017-07-24 20:46:01 +00:00
|
|
|
-- Print sum of timing values as "Total"
|
2017-07-24 21:10:20 +00:00
|
|
|
s[#s+1] = format(f, o.nl, o.indent, o.indent,
|
2024-09-07 22:06:50 +00:00
|
|
|
font_mono, pp(last_s[frame]),
|
2023-10-14 00:26:20 +00:00
|
|
|
pp(avg_s[frame]), pp(peak_s[frame]),
|
2024-09-07 22:06:50 +00:00
|
|
|
o.prefix_sep, bold("Total"), font, "", "", "")
|
2017-06-30 19:46:16 +00:00
|
|
|
else
|
|
|
|
-- for the simplified view, we just print the sum of each pass
|
2024-09-07 22:06:50 +00:00
|
|
|
s[#s+1] = format(f, o.nl, o.indent, o.indent, font_mono,
|
2017-09-16 23:21:44 +00:00
|
|
|
pp(last_s[frame]), pp(avg_s[frame]), pp(peak_s[frame]),
|
2024-09-07 22:06:50 +00:00
|
|
|
"", "", font, o.prefix_sep, o.prefix_sep,
|
2017-07-24 20:46:01 +00:00
|
|
|
frame:gsub("^%l", string.upper))
|
2017-06-30 19:46:16 +00:00
|
|
|
end
|
2016-07-13 08:11:18 +00:00
|
|
|
end
|
2016-06-10 22:04:14 +00:00
|
|
|
end
|
|
|
|
|
2021-07-16 08:17:50 +00:00
|
|
|
-- command prefix tokens to strip - includes generic property commands
|
|
|
|
local cmd_prefixes = {
|
|
|
|
osd_auto=1, no_osd=1, osd_bar=1, osd_msg=1, osd_msg_bar=1, raw=1, sync=1,
|
2024-10-13 18:30:06 +00:00
|
|
|
async=1, expand_properties=1, repeatable=1, nonrepeatable=1, nonscalable=1,
|
|
|
|
set=1, add=1, multiply=1, toggle=1, cycle=1, cycle_values=1, ["!reverse"]=1,
|
|
|
|
change_list=1,
|
2021-07-16 08:17:50 +00:00
|
|
|
}
|
|
|
|
-- commands/writable-properties prefix sub-words (followed by -) to strip
|
|
|
|
local name_prefixes = {
|
|
|
|
define=1, delete=1, enable=1, disable=1, dump=1, write=1, drop=1, revert=1,
|
2021-08-06 07:28:31 +00:00
|
|
|
ab=1, hr=1, secondary=1, current=1,
|
2021-07-16 08:17:50 +00:00
|
|
|
}
|
|
|
|
-- extract a command "subject" from a command string, by removing all
|
|
|
|
-- generic prefix tokens and then returning the first interesting sub-word
|
|
|
|
-- of the next token. For target-script name we also check another token.
|
|
|
|
-- The tokenizer works fine for things we care about - valid mpv commands,
|
|
|
|
-- properties and script names, possibly quoted, white-space[s]-separated.
|
|
|
|
-- It's decent in practice, and worst case is "incorrect" subject.
|
|
|
|
local function cmd_subject(cmd)
|
|
|
|
cmd = cmd:gsub(";.*", ""):gsub("%-", "_") -- only first cmd, s/-/_/
|
2021-07-28 10:43:57 +00:00
|
|
|
local TOKEN = '^%s*["\']?([%w_!]*)' -- captures+ends before (maybe) final "
|
2021-07-16 08:17:50 +00:00
|
|
|
local tok, sname, subw
|
|
|
|
|
2021-07-28 10:43:57 +00:00
|
|
|
repeat tok, cmd = cmd:match(TOKEN .. '["\']?(.*)')
|
2021-07-16 08:17:50 +00:00
|
|
|
until not cmd_prefixes[tok]
|
|
|
|
-- tok is the 1st non-generic command/property name token, cmd is the rest
|
|
|
|
|
|
|
|
sname = tok == "script_message_to" and cmd:match(TOKEN)
|
|
|
|
or tok == "script_binding" and cmd:match(TOKEN .. "/")
|
|
|
|
if sname and sname ~= "" then
|
|
|
|
return "script: " .. sname
|
|
|
|
end
|
|
|
|
|
|
|
|
-- return the first sub-word of tok which is not a useless prefix
|
|
|
|
repeat subw, tok = tok:match("([^_]*)_?(.*)")
|
|
|
|
until tok == "" or not name_prefixes[subw]
|
|
|
|
return subw:len() > 1 and subw or "[unknown]"
|
|
|
|
end
|
|
|
|
|
stats.lua: page 4 (keys): better alignment of non-ascii keys
Previously we assumed the key-name string occupies strlen(name) cells,
now we count codepoints instead.
This improves alignment of non-english key names. Still not perfect
because we don't know if the key name is single or double width, but
wcwidth not available to scripts, notoriously unreliable (depends on
locale, correct and updated tables, etc), and also not always
available (Windows).
Still, better than nothing, and we err by at most one cell - vs up to
three before this commit (4 bytes keyname codepoint).
In the future we could do the alignment using libass tags, however,
this both complicates the ass-output generation, and also not available
when we output for the terminal, so for now only count codepoints.
Also, if the key name was in a right-to-left language, then previously
the name/command were swapped visually. Now we inject a left-to-right
marker before the name to ensure direction. This works also when
harfbuzz is disabled for libass (--sub-ass-shaper=simple).
2021-07-30 06:23:56 +00:00
|
|
|
-- key names are valid UTF-8, ascii7 except maybe the last/only codepoint.
|
|
|
|
-- we count codepoints and ignore wcwidth. no need for grapheme clusters.
|
|
|
|
-- our error for alignment is at most one cell (if last CP is double-width).
|
|
|
|
-- (if k was valid but arbitrary: we'd count all bytes <0x80 or >=0xc0)
|
|
|
|
local function keyname_cells(k)
|
|
|
|
local klen = k:len()
|
|
|
|
if klen > 1 and k:byte(klen) >= 0x80 then -- last/only CP is not ascii7
|
|
|
|
repeat klen = klen-1
|
|
|
|
until klen == 1 or k:byte(klen) >= 0xc0 -- last CP begins at klen
|
|
|
|
end
|
|
|
|
return klen
|
|
|
|
end
|
|
|
|
|
2023-09-02 18:59:40 +00:00
|
|
|
local function get_kbinfo_lines()
|
2023-03-27 20:42:17 +00:00
|
|
|
-- active keys: only highest priority of each key, and not our (stats) keys
|
2021-07-16 08:17:50 +00:00
|
|
|
local bindings = mp.get_property_native("input-bindings", {})
|
|
|
|
local active = {} -- map: key-name -> bind-info
|
|
|
|
for _, bind in pairs(bindings) do
|
|
|
|
if bind.priority >= 0 and (
|
|
|
|
not active[bind.key] or
|
|
|
|
(active[bind.key].is_weak and not bind.is_weak) or
|
|
|
|
(bind.is_weak == active[bind.key].is_weak and
|
|
|
|
bind.priority > active[bind.key].priority)
|
2021-07-19 21:49:03 +00:00
|
|
|
) and not bind.cmd:find("script-binding stats/__forced_", 1, true)
|
2024-03-21 19:09:22 +00:00
|
|
|
and bind.section ~= "input_forced_console"
|
|
|
|
and (
|
|
|
|
searched_text == nil or
|
2024-10-01 12:26:54 +00:00
|
|
|
(bind.key .. bind.cmd .. (bind.comment or "")):lower():find(searched_text, 1, true)
|
2024-03-21 19:09:22 +00:00
|
|
|
)
|
2021-07-16 08:17:50 +00:00
|
|
|
then
|
|
|
|
active[bind.key] = bind
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- make an array, find max key len, add sort keys (.subject/.mods[_count])
|
|
|
|
local ordered = {}
|
|
|
|
local kspaces = "" -- as many spaces as the longest key name
|
|
|
|
for _, bind in pairs(active) do
|
|
|
|
bind.subject = cmd_subject(bind.cmd)
|
|
|
|
if bind.subject ~= "ignore" then
|
|
|
|
ordered[#ordered+1] = bind
|
|
|
|
_,_, bind.mods = bind.key:find("(.*)%+.")
|
|
|
|
_, bind.mods_count = bind.key:gsub("%+.", "")
|
|
|
|
if bind.key:len() > kspaces:len() then
|
|
|
|
kspaces = string.rep(" ", bind.key:len())
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function align_right(key)
|
stats.lua: page 4 (keys): better alignment of non-ascii keys
Previously we assumed the key-name string occupies strlen(name) cells,
now we count codepoints instead.
This improves alignment of non-english key names. Still not perfect
because we don't know if the key name is single or double width, but
wcwidth not available to scripts, notoriously unreliable (depends on
locale, correct and updated tables, etc), and also not always
available (Windows).
Still, better than nothing, and we err by at most one cell - vs up to
three before this commit (4 bytes keyname codepoint).
In the future we could do the alignment using libass tags, however,
this both complicates the ass-output generation, and also not available
when we output for the terminal, so for now only count codepoints.
Also, if the key name was in a right-to-left language, then previously
the name/command were swapped visually. Now we inject a left-to-right
marker before the name to ensure direction. This works also when
harfbuzz is disabled for libass (--sub-ass-shaper=simple).
2021-07-30 06:23:56 +00:00
|
|
|
return kspaces:sub(keyname_cells(key)) .. key
|
2021-07-16 08:17:50 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- sort by: subject, mod(ifier)s count, mods, key-len, lowercase-key, key
|
|
|
|
table.sort(ordered, function(a, b)
|
|
|
|
if a.subject ~= b.subject then
|
|
|
|
return a.subject < b.subject
|
|
|
|
elseif a.mods_count ~= b.mods_count then
|
|
|
|
return a.mods_count < b.mods_count
|
|
|
|
elseif a.mods ~= b.mods then
|
|
|
|
return a.mods < b.mods
|
|
|
|
elseif a.key:len() ~= b.key:len() then
|
|
|
|
return a.key:len() < b.key:len()
|
|
|
|
elseif a.key:lower() ~= b.key:lower() then
|
|
|
|
return a.key:lower() < b.key:lower()
|
|
|
|
else
|
|
|
|
return a.key > b.key -- only case differs, lowercase first
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
|
|
|
-- key/subject pre/post formatting for terminal/ass.
|
|
|
|
-- key/subject alignment uses spaces (with mono font if ass)
|
|
|
|
-- word-wrapping is disabled for ass, or cut at 79 for the terminal
|
2022-05-12 15:12:31 +00:00
|
|
|
local LTR = string.char(0xE2, 0x80, 0x8E) -- U+200E Left To Right mark
|
2021-07-16 08:17:50 +00:00
|
|
|
local term = not o.use_ass
|
stats.lua: page 4 (keys): better alignment of non-ascii keys
Previously we assumed the key-name string occupies strlen(name) cells,
now we count codepoints instead.
This improves alignment of non-english key names. Still not perfect
because we don't know if the key name is single or double width, but
wcwidth not available to scripts, notoriously unreliable (depends on
locale, correct and updated tables, etc), and also not always
available (Windows).
Still, better than nothing, and we err by at most one cell - vs up to
three before this commit (4 bytes keyname codepoint).
In the future we could do the alignment using libass tags, however,
this both complicates the ass-output generation, and also not available
when we output for the terminal, so for now only count codepoints.
Also, if the key name was in a right-to-left language, then previously
the name/command were swapped visually. Now we inject a left-to-right
marker before the name to ensure direction. This works also when
harfbuzz is disabled for libass (--sub-ass-shaper=simple).
2021-07-30 06:23:56 +00:00
|
|
|
local kpre = term and "" or format("{\\q2\\fn%s}%s", o.font_mono, LTR)
|
2021-07-16 08:17:50 +00:00
|
|
|
local kpost = term and " " or format(" {\\fn%s}", o.font)
|
|
|
|
local spre = term and kspaces .. " "
|
|
|
|
or format("{\\q2\\fn%s}%s {\\fn%s}{\\fs%d\\u1}",
|
2024-04-15 04:43:17 +00:00
|
|
|
o.font_mono, kspaces, o.font, 1.3*font_size)
|
2024-09-04 04:50:30 +00:00
|
|
|
local spost = term and "" or format("{\\u0\\fs%d}%s", font_size, text_style())
|
2021-07-16 08:17:50 +00:00
|
|
|
|
|
|
|
-- create the display lines
|
|
|
|
local info_lines = {}
|
|
|
|
local subject = nil
|
|
|
|
for _, bind in ipairs(ordered) do
|
|
|
|
if bind.subject ~= subject then -- new subject (title)
|
|
|
|
subject = bind.subject
|
|
|
|
append(info_lines, "", {})
|
|
|
|
append(info_lines, "", { prefix = spre .. subject .. spost })
|
|
|
|
end
|
|
|
|
if bind.comment then
|
|
|
|
bind.cmd = bind.cmd .. " # " .. bind.comment
|
|
|
|
end
|
2023-09-02 18:59:40 +00:00
|
|
|
append(info_lines, bind.cmd, { prefix = kpre .. no_ASS(align_right(bind.key)) .. kpost })
|
2021-07-16 08:17:50 +00:00
|
|
|
end
|
|
|
|
return info_lines
|
|
|
|
end
|
|
|
|
|
2023-08-28 21:01:20 +00:00
|
|
|
local function append_general_perfdata(s)
|
|
|
|
for i, data in ipairs(mp.get_property_native("perf-info") or {}) do
|
|
|
|
append(s, data.text or data.value, {prefix="["..tostring(i).."] "..data.name..":"})
|
|
|
|
|
|
|
|
if o.plot_perfdata and o.use_ass and data.value then
|
2024-05-12 01:37:38 +00:00
|
|
|
local buf = perf_buffers[data.name]
|
2023-08-28 21:01:20 +00:00
|
|
|
if not buf then
|
|
|
|
buf = {0, pos = 1, len = 50, max = 0}
|
|
|
|
perf_buffers[data.name] = buf
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
end
|
2023-08-28 21:01:20 +00:00
|
|
|
graph_add_value(buf, data.value)
|
|
|
|
s[#s] = s[#s] .. generate_graph(buf, buf.pos, buf.len, buf.max, nil, 0.8, 1)
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-06-10 22:04:14 +00:00
|
|
|
|
2017-01-18 20:35:59 +00:00
|
|
|
local function append_display_sync(s)
|
2016-09-12 00:30:09 +00:00
|
|
|
if not mp.get_property_bool("display-sync-active", false) then
|
|
|
|
return
|
2016-07-12 17:19:34 +00:00
|
|
|
end
|
|
|
|
|
2016-09-12 00:30:09 +00:00
|
|
|
local vspeed = append_property(s, "video-speed-correction", {prefix="DS:"})
|
|
|
|
if vspeed then
|
|
|
|
append_property(s, "audio-speed-correction",
|
|
|
|
{prefix="/", nl="", indent=" ", prefix_sep=" ", no_prefix_markup=true})
|
|
|
|
else
|
|
|
|
append_property(s, "audio-speed-correction",
|
|
|
|
{prefix="DS:" .. o.prefix_sep .. " - / ", prefix_sep=""})
|
|
|
|
end
|
2016-07-12 17:19:34 +00:00
|
|
|
|
2023-08-31 19:36:41 +00:00
|
|
|
append_property(s, "mistimed-frame-count", {prefix="Mistimed:", nl="",
|
|
|
|
indent=o.prefix_sep .. o.prefix_sep})
|
|
|
|
append_property(s, "vo-delayed-frame-count", {prefix="Delayed:", nl="",
|
|
|
|
indent=o.prefix_sep .. o.prefix_sep})
|
2017-08-12 12:08:35 +00:00
|
|
|
|
2017-01-21 13:31:46 +00:00
|
|
|
-- As we need to plot some graphs we print jitter and ratio on their own lines
|
2017-09-23 15:54:40 +00:00
|
|
|
if not display_timer.oneshot and (o.plot_vsync_ratio or o.plot_vsync_jitter) and o.use_ass then
|
2017-01-21 13:31:46 +00:00
|
|
|
local ratio_graph = ""
|
|
|
|
local jitter_graph = ""
|
2016-09-12 00:30:09 +00:00
|
|
|
if o.plot_vsync_ratio then
|
2024-05-12 01:37:38 +00:00
|
|
|
ratio_graph = generate_graph(vsratio_buf, vsratio_buf.pos,
|
|
|
|
vsratio_buf.len, vsratio_buf.max, nil, 0.8, 1)
|
2016-09-12 00:30:09 +00:00
|
|
|
end
|
|
|
|
if o.plot_vsync_jitter then
|
2024-05-12 01:37:38 +00:00
|
|
|
jitter_graph = generate_graph(vsjitter_buf, vsjitter_buf.pos,
|
|
|
|
vsjitter_buf.len, vsjitter_buf.max, nil, 0.8, 1)
|
2016-09-12 00:30:09 +00:00
|
|
|
end
|
2024-05-12 01:37:38 +00:00
|
|
|
append_property(s, "vsync-ratio", {prefix="VSync Ratio:",
|
|
|
|
suffix=o.prefix_sep .. ratio_graph})
|
|
|
|
append_property(s, "vsync-jitter", {prefix="VSync Jitter:",
|
|
|
|
suffix=o.prefix_sep .. jitter_graph})
|
2017-01-21 13:31:46 +00:00
|
|
|
else
|
|
|
|
-- Since no graph is needed we can print ratio/jitter on the same line and save some space
|
2023-08-31 19:36:41 +00:00
|
|
|
local vr = append_property(s, "vsync-ratio", {prefix="VSync Ratio:"})
|
|
|
|
append_property(s, "vsync-jitter", {prefix="VSync Jitter:",
|
|
|
|
nl=vr and "" or o.nl,
|
|
|
|
indent=vr and o.prefix_sep .. o.prefix_sep})
|
2016-09-12 00:30:09 +00:00
|
|
|
end
|
2015-04-03 15:22:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2017-07-25 13:37:09 +00:00
|
|
|
local function append_filters(s, prop, prefix)
|
|
|
|
local length = 0
|
|
|
|
local filters = {}
|
|
|
|
|
|
|
|
for _,f in ipairs(mp.get_property_native(prop, {})) do
|
|
|
|
local n = f.name
|
|
|
|
if f.enabled ~= nil and not f.enabled then
|
|
|
|
n = n .. " (disabled)"
|
|
|
|
end
|
|
|
|
|
2021-03-15 02:45:43 +00:00
|
|
|
if f.label ~= nil then
|
|
|
|
n = "@" .. f.label .. ": " .. n
|
|
|
|
end
|
|
|
|
|
2017-07-25 13:37:09 +00:00
|
|
|
local p = {}
|
2023-10-07 16:12:36 +00:00
|
|
|
for _,key in ipairs(sorted_keys(f.params)) do
|
|
|
|
p[#p+1] = key .. "=" .. f.params[key]
|
2017-07-25 13:37:09 +00:00
|
|
|
end
|
|
|
|
if #p > 0 then
|
|
|
|
p = " [" .. table.concat(p, " ") .. "]"
|
|
|
|
else
|
|
|
|
p = ""
|
|
|
|
end
|
|
|
|
|
|
|
|
length = length + n:len() + p:len()
|
|
|
|
filters[#filters+1] = no_ASS(n) .. it(no_ASS(p))
|
|
|
|
end
|
|
|
|
|
|
|
|
if #filters > 0 then
|
|
|
|
local ret
|
|
|
|
if length < o.filter_params_max_length then
|
|
|
|
ret = table.concat(filters, ", ")
|
|
|
|
else
|
|
|
|
local sep = o.nl .. o.indent .. o.indent
|
|
|
|
ret = sep .. table.concat(filters, sep)
|
|
|
|
end
|
2024-05-12 01:37:38 +00:00
|
|
|
s[#s+1] = o.nl .. o.indent .. bold(prefix) .. o.prefix_sep .. ret
|
2017-07-25 13:37:09 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2016-07-12 17:19:34 +00:00
|
|
|
local function add_header(s)
|
2017-07-10 21:12:03 +00:00
|
|
|
s[#s+1] = text_style()
|
2015-04-03 15:22:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2024-04-17 10:27:04 +00:00
|
|
|
local function add_file(s, print_cache, print_tags)
|
2018-06-08 21:54:22 +00:00
|
|
|
append(s, "", {prefix="File:", nl="", indent=""})
|
2018-03-24 21:43:04 +00:00
|
|
|
append_property(s, "filename", {prefix_sep="", nl="", indent=""})
|
2024-05-12 01:37:38 +00:00
|
|
|
if mp.get_property_osd("filename") ~= mp.get_property_osd("media-title") then
|
2016-07-12 17:19:34 +00:00
|
|
|
append_property(s, "media-title", {prefix="Title:"})
|
|
|
|
end
|
2017-08-25 21:57:17 +00:00
|
|
|
|
2024-04-17 10:27:04 +00:00
|
|
|
if print_tags then
|
|
|
|
local tags = mp.get_property_native("display-tags")
|
|
|
|
local tags_displayed = 0
|
|
|
|
for _, tag in ipairs(tags) do
|
|
|
|
local value = mp.get_property("metadata/by-key/" .. tag)
|
|
|
|
if tag ~= "Title" and tags_displayed < o.file_tag_max_count
|
|
|
|
and value and value:len() < o.file_tag_max_length then
|
|
|
|
append(s, value, {prefix=string.gsub(tag, "_", " ") .. ":"})
|
|
|
|
tags_displayed = tags_displayed + 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-04-15 17:22:35 +00:00
|
|
|
local editions = mp.get_property_number("editions")
|
|
|
|
local edition = mp.get_property_number("current-edition")
|
|
|
|
local ed_cond = (edition and editions > 1)
|
|
|
|
if ed_cond then
|
|
|
|
append_property(s, "edition-list/" .. tostring(edition) .. "/title",
|
|
|
|
{prefix="Edition:"})
|
|
|
|
append_property(s, "edition-list/count",
|
|
|
|
{prefix="(" .. tostring(edition + 1) .. "/", suffix=")", nl="",
|
|
|
|
indent=" ", prefix_sep=" ", no_prefix_markup=true})
|
|
|
|
end
|
|
|
|
|
2017-08-25 21:57:17 +00:00
|
|
|
local ch_index = mp.get_property_number("chapter")
|
|
|
|
if ch_index and ch_index >= 0 then
|
2020-04-15 17:22:35 +00:00
|
|
|
append_property(s, "chapter-list/" .. tostring(ch_index) .. "/title", {prefix="Chapter:",
|
|
|
|
nl=ed_cond and "" or o.nl})
|
2017-08-25 21:57:17 +00:00
|
|
|
append_property(s, "chapter-list/count",
|
2023-08-31 19:36:41 +00:00
|
|
|
{prefix="(" .. tostring(ch_index + 1) .. " /", suffix=")", nl="",
|
2017-08-25 21:57:17 +00:00
|
|
|
indent=" ", prefix_sep=" ", no_prefix_markup=true})
|
|
|
|
end
|
2017-12-26 16:54:03 +00:00
|
|
|
|
2020-04-15 17:27:37 +00:00
|
|
|
local fs = append_property(s, "file-size", {prefix="Size:"})
|
2023-08-31 19:36:41 +00:00
|
|
|
append_property(s, "file-format", {prefix="Format/Protocol:",
|
|
|
|
nl=fs and "" or o.nl,
|
|
|
|
indent=fs and o.prefix_sep .. o.prefix_sep})
|
2020-04-15 17:27:37 +00:00
|
|
|
|
2024-06-08 16:19:00 +00:00
|
|
|
if not print_cache then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2017-12-26 16:54:03 +00:00
|
|
|
local demuxer_cache = mp.get_property_native("demuxer-cache-state", {})
|
|
|
|
if demuxer_cache["fw-bytes"] then
|
|
|
|
demuxer_cache = demuxer_cache["fw-bytes"] -- returns bytes
|
|
|
|
else
|
|
|
|
demuxer_cache = 0
|
|
|
|
end
|
|
|
|
local demuxer_secs = mp.get_property_number("demuxer-cache-duration", 0)
|
2018-12-06 18:01:26 +00:00
|
|
|
if demuxer_cache + demuxer_secs > 0 then
|
|
|
|
append(s, utils.format_bytes_humanized(demuxer_cache), {prefix="Total Cache:"})
|
|
|
|
append(s, format("%.1f", demuxer_secs), {prefix="(", suffix=" sec)", nl="",
|
|
|
|
no_prefix_markup=true, prefix_sep="", indent=o.prefix_sep})
|
2016-07-12 17:19:34 +00:00
|
|
|
end
|
2015-04-28 00:13:27 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2023-08-31 21:57:51 +00:00
|
|
|
local function crop_noop(w, h, r)
|
|
|
|
return r["crop-x"] == 0 and r["crop-y"] == 0 and
|
|
|
|
r["crop-w"] == w and r["crop-h"] == h
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function crop_equal(r, ro)
|
|
|
|
return r["crop-x"] == ro["crop-x"] and r["crop-y"] == ro["crop-y"] and
|
|
|
|
r["crop-w"] == ro["crop-w"] and r["crop-h"] == ro["crop-h"]
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function append_resolution(s, r, prefix, w_prop, h_prop, video_res)
|
2023-08-13 15:20:03 +00:00
|
|
|
if not r then
|
|
|
|
return
|
|
|
|
end
|
2023-08-25 17:33:00 +00:00
|
|
|
w_prop = w_prop or "w"
|
|
|
|
h_prop = h_prop or "h"
|
|
|
|
if append(s, r[w_prop], {prefix=prefix}) then
|
|
|
|
append(s, r[h_prop], {prefix="x", nl="", indent=" ", prefix_sep=" ",
|
2023-08-13 15:20:03 +00:00
|
|
|
no_prefix_markup=true})
|
2023-11-11 04:57:18 +00:00
|
|
|
if r["aspect"] ~= nil and not video_res then
|
2023-08-31 19:36:41 +00:00
|
|
|
append(s, format("%.2f:1", r["aspect"]), {prefix="", nl="", indent="",
|
2023-08-13 15:20:03 +00:00
|
|
|
no_prefix_markup=true})
|
|
|
|
append(s, r["aspect-name"], {prefix="(", suffix=")", nl="", indent=" ",
|
|
|
|
prefix_sep="", no_prefix_markup=true})
|
|
|
|
end
|
2023-11-11 04:57:18 +00:00
|
|
|
if r["sar"] ~= nil and video_res then
|
|
|
|
append(s, format("%.2f:1", r["sar"]), {prefix="", nl="", indent="",
|
|
|
|
no_prefix_markup=true})
|
|
|
|
append(s, r["sar-name"], {prefix="(", suffix=")", nl="", indent=" ",
|
|
|
|
prefix_sep="", no_prefix_markup=true})
|
|
|
|
end
|
2023-08-31 19:36:41 +00:00
|
|
|
if r["s"] then
|
2023-08-25 19:30:13 +00:00
|
|
|
append(s, format("%.2f", r["s"]), {prefix="(", suffix="x)", nl="",
|
2023-08-31 19:36:41 +00:00
|
|
|
indent=o.prefix_sep, prefix_sep="",
|
2023-08-25 19:30:13 +00:00
|
|
|
no_prefix_markup=true})
|
|
|
|
end
|
2023-08-31 21:57:51 +00:00
|
|
|
-- We can skip crop if it is the same as video decoded resolution
|
|
|
|
if r["crop-w"] and (not video_res or
|
|
|
|
not crop_noop(r[w_prop], r[h_prop], r)) then
|
|
|
|
append(s, format("[x: %d, y: %d, w: %d, h: %d]",
|
|
|
|
r["crop-x"], r["crop-y"], r["crop-w"], r["crop-h"]),
|
2023-08-31 19:36:41 +00:00
|
|
|
{prefix="", nl="", indent="", no_prefix_markup=true})
|
2023-08-25 17:34:11 +00:00
|
|
|
end
|
2023-08-13 15:20:03 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2023-08-29 08:24:08 +00:00
|
|
|
local function pq_eotf(x)
|
|
|
|
if not x then
|
|
|
|
return x;
|
|
|
|
end
|
|
|
|
|
|
|
|
local PQ_M1 = 2610.0 / 4096 * 1.0 / 4
|
|
|
|
local PQ_M2 = 2523.0 / 4096 * 128
|
|
|
|
local PQ_C1 = 3424.0 / 4096
|
|
|
|
local PQ_C2 = 2413.0 / 4096 * 32
|
|
|
|
local PQ_C3 = 2392.0 / 4096 * 32
|
|
|
|
|
|
|
|
x = x ^ (1.0 / PQ_M2)
|
|
|
|
x = max(x - PQ_C1, 0.0) / (PQ_C2 - PQ_C3 * x)
|
|
|
|
x = x ^ (1.0 / PQ_M1)
|
|
|
|
x = x * 10000.0
|
|
|
|
|
|
|
|
return x
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2023-08-31 15:07:46 +00:00
|
|
|
local function append_hdr(s, hdr, video_out)
|
2023-08-29 08:24:08 +00:00
|
|
|
if not hdr then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2023-08-31 16:48:31 +00:00
|
|
|
local function should_show(val)
|
|
|
|
return val and val ~= 203 and val > 0
|
|
|
|
end
|
|
|
|
|
2023-08-31 15:07:46 +00:00
|
|
|
-- If we are printing video out parameters it is just display, not mastering
|
|
|
|
local display_prefix = video_out and "Display:" or "Mastering display:"
|
|
|
|
|
2023-08-31 19:36:41 +00:00
|
|
|
local indent = ""
|
|
|
|
|
2023-08-31 16:48:31 +00:00
|
|
|
if should_show(hdr["max-cll"]) or should_show(hdr["max-luma"]) then
|
2023-08-29 08:24:08 +00:00
|
|
|
append(s, "", {prefix="HDR10:"})
|
2023-08-31 16:48:31 +00:00
|
|
|
if hdr["min-luma"] and should_show(hdr["max-luma"]) then
|
2023-08-31 15:07:46 +00:00
|
|
|
-- libplacebo uses close to zero values as "defined zero"
|
|
|
|
hdr["min-luma"] = hdr["min-luma"] <= 1e-6 and 0 or hdr["min-luma"]
|
2023-08-29 08:24:08 +00:00
|
|
|
append(s, format("%.2g / %.0f", hdr["min-luma"], hdr["max-luma"]),
|
2023-08-31 19:36:41 +00:00
|
|
|
{prefix=display_prefix, suffix=" cd/m²", nl="", indent=indent})
|
|
|
|
indent = o.prefix_sep .. o.prefix_sep
|
2023-08-29 08:24:08 +00:00
|
|
|
end
|
2023-08-31 16:48:31 +00:00
|
|
|
if should_show(hdr["max-cll"]) then
|
2023-08-31 19:36:41 +00:00
|
|
|
append(s, hdr["max-cll"], {prefix="MaxCLL:", suffix=" cd/m²", nl="",
|
|
|
|
indent=indent})
|
|
|
|
indent = o.prefix_sep .. o.prefix_sep
|
2023-08-29 08:24:08 +00:00
|
|
|
end
|
|
|
|
if hdr["max-fall"] and hdr["max-fall"] > 0 then
|
2023-08-31 19:36:41 +00:00
|
|
|
append(s, hdr["max-fall"], {prefix="MaxFALL:", suffix=" cd/m²", nl="",
|
|
|
|
indent=indent})
|
2023-08-29 08:24:08 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-08-31 19:36:41 +00:00
|
|
|
indent = o.prefix_sep .. o.prefix_sep
|
|
|
|
|
2023-08-29 08:24:08 +00:00
|
|
|
if hdr["scene-max-r"] or hdr["scene-max-g"] or
|
|
|
|
hdr["scene-max-b"] or hdr["scene-avg"] then
|
|
|
|
append(s, "", {prefix="HDR10+:"})
|
|
|
|
append(s, format("%.1f / %.1f / %.1f", hdr["scene-max-r"] or 0,
|
|
|
|
hdr["scene-max-g"] or 0, hdr["scene-max-b"] or 0),
|
2023-08-31 19:36:41 +00:00
|
|
|
{prefix="MaxRGB:", suffix=" cd/m²", nl="", indent=""})
|
2023-08-29 08:24:08 +00:00
|
|
|
append(s, format("%.1f", hdr["scene-avg"] or 0),
|
2023-08-31 19:36:41 +00:00
|
|
|
{prefix="Avg:", suffix=" cd/m²", nl="", indent=indent})
|
2023-08-29 08:24:08 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
if hdr["max-pq-y"] and hdr["avg-pq-y"] then
|
|
|
|
append(s, "", {prefix="PQ(Y):"})
|
|
|
|
append(s, format("%.2f cd/m² (%.2f%% PQ)", pq_eotf(hdr["max-pq-y"]),
|
2023-08-31 19:36:41 +00:00
|
|
|
hdr["max-pq-y"] * 100), {prefix="Max:", nl="",
|
|
|
|
indent=""})
|
2023-08-29 08:24:08 +00:00
|
|
|
append(s, format("%.2f cd/m² (%.2f%% PQ)", pq_eotf(hdr["avg-pq-y"]),
|
2023-08-31 19:36:41 +00:00
|
|
|
hdr["avg-pq-y"] * 100), {prefix="Avg:", nl="",
|
|
|
|
indent=indent})
|
2023-08-29 08:24:08 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2023-08-31 15:07:46 +00:00
|
|
|
local function append_img_params(s, r, ro)
|
2018-03-24 21:36:01 +00:00
|
|
|
if not r then
|
2016-07-12 17:19:34 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2023-08-31 15:07:46 +00:00
|
|
|
append_resolution(s, r, "Resolution:", "w", "h", true)
|
|
|
|
if ro and (r["w"] ~= ro["dw"] or r["h"] ~= ro["dh"]) then
|
|
|
|
if ro["crop-w"] and (crop_noop(r["w"], r["h"], ro) or crop_equal(r, ro)) then
|
|
|
|
ro["crop-w"] = nil
|
|
|
|
end
|
|
|
|
append_resolution(s, ro, "Output Resolution:", "dw", "dh")
|
|
|
|
end
|
2021-06-24 15:09:05 +00:00
|
|
|
|
2023-08-31 19:36:41 +00:00
|
|
|
local indent = o.prefix_sep .. o.prefix_sep
|
2024-02-11 02:44:07 +00:00
|
|
|
r = ro or r
|
2023-08-31 19:36:41 +00:00
|
|
|
|
2023-09-01 11:25:46 +00:00
|
|
|
local pixel_format = r["hw-pixelformat"] or r["pixelformat"]
|
2023-10-13 17:19:34 +00:00
|
|
|
append(s, pixel_format, {prefix="Format:"})
|
2023-08-31 19:36:41 +00:00
|
|
|
append(s, r["colorlevels"], {prefix="Levels:", nl="", indent=indent})
|
2023-10-13 17:17:57 +00:00
|
|
|
if r["chroma-location"] and r["chroma-location"] ~= "unknown" then
|
|
|
|
append(s, r["chroma-location"], {prefix="Chroma Loc:", nl="", indent=indent})
|
|
|
|
end
|
2023-08-31 15:07:46 +00:00
|
|
|
|
|
|
|
-- Group these together to save vertical space
|
|
|
|
append(s, r["colormatrix"], {prefix="Colormatrix:"})
|
2023-08-31 19:36:41 +00:00
|
|
|
append(s, r["primaries"], {prefix="Primaries:", nl="", indent=indent})
|
|
|
|
append(s, r["gamma"], {prefix="Transfer:", nl="", indent=indent})
|
2023-08-31 15:07:46 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2023-09-01 01:19:47 +00:00
|
|
|
local function append_fps(s, prop, eprop)
|
|
|
|
local fps = mp.get_property_osd(prop)
|
|
|
|
local efps = mp.get_property_osd(eprop)
|
2024-06-08 16:19:00 +00:00
|
|
|
local single = eprop == "" or (fps ~= "" and efps ~= "" and fps == efps)
|
2023-09-01 01:19:47 +00:00
|
|
|
local unit = prop == "display-fps" and " Hz" or " fps"
|
|
|
|
local suffix = single and "" or " (specified)"
|
|
|
|
local esuffix = single and "" or " (estimated)"
|
2024-01-17 08:21:32 +00:00
|
|
|
local prefix = prop == "display-fps" and "Refresh Rate:" or "Frame Rate:"
|
2023-09-01 01:19:47 +00:00
|
|
|
local nl = o.nl
|
|
|
|
local indent = o.indent
|
|
|
|
|
|
|
|
if fps ~= "" and append(s, fps, {prefix=prefix, suffix=unit .. suffix}) then
|
|
|
|
prefix = ""
|
|
|
|
nl = ""
|
|
|
|
indent = ""
|
|
|
|
end
|
|
|
|
|
|
|
|
if not single and efps ~= "" then
|
|
|
|
append(s, efps,
|
|
|
|
{prefix=prefix, suffix=unit .. esuffix, nl=nl, indent=indent})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2023-08-31 15:07:46 +00:00
|
|
|
local function add_video_out(s)
|
|
|
|
local vo = mp.get_property_native("current-vo")
|
|
|
|
if not vo then
|
|
|
|
return
|
2016-07-12 17:19:34 +00:00
|
|
|
end
|
2023-08-31 15:07:46 +00:00
|
|
|
|
2024-05-23 01:24:58 +00:00
|
|
|
append(s, "", {prefix="Display:", nl=o.nl .. o.nl, indent=""})
|
2023-08-31 15:07:46 +00:00
|
|
|
append(s, vo, {prefix_sep="", nl="", indent=""})
|
|
|
|
append_property(s, "display-names", {prefix_sep="", prefix="(", suffix=")",
|
|
|
|
no_prefix_markup=true, nl="", indent=" "})
|
2023-12-21 04:17:28 +00:00
|
|
|
append(s, mp.get_property_native("current-gpu-context"),
|
|
|
|
{prefix="Context:", nl="", indent=o.prefix_sep .. o.prefix_sep})
|
2016-07-12 17:19:34 +00:00
|
|
|
append_property(s, "avsync", {prefix="A-V:"})
|
2023-09-01 01:19:47 +00:00
|
|
|
append_fps(s, "display-fps", "estimated-display-fps")
|
2023-10-14 10:49:19 +00:00
|
|
|
if append_property(s, "decoder-frame-drop-count",
|
2017-09-23 15:54:40 +00:00
|
|
|
{prefix="Dropped Frames:", suffix=" (decoder)"}) then
|
2023-10-14 10:49:19 +00:00
|
|
|
append_property(s, "frame-drop-count", {suffix=" (output)", nl="", indent=""})
|
2016-07-12 17:19:34 +00:00
|
|
|
end
|
2016-09-12 00:30:09 +00:00
|
|
|
append_display_sync(s)
|
2024-06-08 13:37:57 +00:00
|
|
|
append_perfdata(nil, s, false)
|
2016-07-12 17:19:34 +00:00
|
|
|
|
2024-01-22 03:38:50 +00:00
|
|
|
if mp.get_property_native("deinterlace-active") then
|
2023-08-31 15:07:46 +00:00
|
|
|
append_property(s, "deinterlace", {prefix="Deinterlacing:"})
|
2021-06-24 15:09:05 +00:00
|
|
|
end
|
2023-08-31 15:07:46 +00:00
|
|
|
|
2023-08-25 19:30:13 +00:00
|
|
|
local scale = nil
|
2023-08-13 15:16:41 +00:00
|
|
|
if not mp.get_property_native("fullscreen") then
|
2023-08-25 19:30:13 +00:00
|
|
|
scale = mp.get_property_native("current-window-scale")
|
|
|
|
end
|
|
|
|
|
2024-04-13 12:43:24 +00:00
|
|
|
local od = mp.get_property_native("osd-dimensions")
|
|
|
|
local rt = mp.get_property_native("video-target-params")
|
2024-05-12 01:37:38 +00:00
|
|
|
local r = rt or {}
|
2023-08-25 19:30:13 +00:00
|
|
|
|
2023-08-31 15:07:46 +00:00
|
|
|
-- Add window scale
|
|
|
|
r["s"] = scale
|
2024-04-13 12:43:24 +00:00
|
|
|
r["crop-x"] = od["ml"]
|
|
|
|
r["crop-y"] = od["mt"]
|
|
|
|
r["crop-w"] = od["w"] - od["ml"] - od["mr"]
|
|
|
|
r["crop-h"] = od["h"] - od["mt"] - od["mb"]
|
|
|
|
|
|
|
|
if not rt then
|
|
|
|
r["w"] = r["crop-w"]
|
|
|
|
r["h"] = r["crop-h"]
|
|
|
|
append_resolution(s, r, "Resolution:", "w", "h", true)
|
|
|
|
return
|
|
|
|
end
|
2023-08-31 15:07:46 +00:00
|
|
|
|
|
|
|
append_img_params(s, r)
|
|
|
|
append_hdr(s, r, true)
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
local function add_video(s)
|
|
|
|
local r = mp.get_property_native("video-params")
|
|
|
|
local ro = mp.get_property_native("video-out-params")
|
|
|
|
-- in case of e.g. lavfi-complex there can be no input video, only output
|
|
|
|
if not r then
|
|
|
|
r = ro
|
|
|
|
end
|
|
|
|
if not r then
|
|
|
|
return
|
2020-10-16 09:09:32 +00:00
|
|
|
end
|
2016-07-13 09:29:31 +00:00
|
|
|
|
2024-04-16 18:29:17 +00:00
|
|
|
local track = mp.get_property_native("current-tracks/video")
|
2024-06-27 14:32:53 +00:00
|
|
|
if track then
|
|
|
|
append(s, "", {prefix=track.image and "Image:" or "Video:", nl=o.nl .. o.nl, indent=""})
|
|
|
|
append(s, track["codec-desc"], {prefix_sep="", nl="", indent=""})
|
2024-04-16 18:29:17 +00:00
|
|
|
append(s, track["codec-profile"], {prefix="[", nl="", indent=" ", prefix_sep="",
|
2024-04-11 18:46:19 +00:00
|
|
|
no_prefix_markup=true, suffix="]"})
|
2024-04-15 19:13:33 +00:00
|
|
|
if track["codec"] ~= track["decoder"] then
|
|
|
|
append(s, track["decoder"], {prefix="[", nl="", indent=" ", prefix_sep="",
|
|
|
|
no_prefix_markup=true, suffix="]"})
|
|
|
|
end
|
2023-08-31 19:36:41 +00:00
|
|
|
append_property(s, "hwdec-current", {prefix="HW:", nl="",
|
|
|
|
indent=o.prefix_sep .. o.prefix_sep,
|
|
|
|
no_prefix_markup=false, suffix=""}, {no=true, [""]=true})
|
2023-08-31 15:07:46 +00:00
|
|
|
end
|
2023-09-01 02:12:52 +00:00
|
|
|
local has_prefix = false
|
|
|
|
if o.show_frame_info then
|
|
|
|
if append_property(s, "estimated-frame-number", {prefix="Frame:"}) then
|
|
|
|
append_property(s, "estimated-frame-count", {indent=" / ", nl="",
|
|
|
|
prefix_sep=""})
|
|
|
|
has_prefix = true
|
|
|
|
end
|
|
|
|
local frame_info = mp.get_property_native("video-frame-info")
|
|
|
|
if frame_info and frame_info["picture-type"] then
|
|
|
|
local attrs = has_prefix and {prefix="(", suffix=")", indent=" ", nl="",
|
|
|
|
prefix_sep="", no_prefix_markup=true}
|
|
|
|
or {prefix="Picture Type:"}
|
|
|
|
append(s, frame_info["picture-type"], attrs)
|
|
|
|
has_prefix = true
|
|
|
|
end
|
|
|
|
if frame_info and frame_info["interlaced"] then
|
|
|
|
local attrs = has_prefix and {indent=" ", nl="", prefix_sep=""}
|
|
|
|
or {prefix="Picture Type:"}
|
|
|
|
append(s, "Interlaced", attrs)
|
|
|
|
end
|
2024-09-23 02:09:17 +00:00
|
|
|
|
|
|
|
local timecodes = {
|
|
|
|
["gop-timecode"] = "GOP",
|
|
|
|
["smpte-timecode"] = "SMPTE",
|
|
|
|
["estimated-smpte-timecode"] = "Estimated SMPTE",
|
|
|
|
}
|
|
|
|
for prop, name in pairs(timecodes) do
|
|
|
|
if frame_info and frame_info[prop] then
|
|
|
|
local attrs = has_prefix and {prefix=name .. " Timecode:",
|
|
|
|
indent=o.prefix_sep .. o.prefix_sep, nl=""}
|
|
|
|
or {prefix=name .. " Timecode:"}
|
|
|
|
append(s, frame_info[prop], attrs)
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
2023-09-01 02:12:52 +00:00
|
|
|
end
|
|
|
|
|
2023-10-18 20:23:07 +00:00
|
|
|
if mp.get_property_native("current-tracks/video/image") == false then
|
|
|
|
append_fps(s, "container-fps", "estimated-vf-fps")
|
|
|
|
end
|
2023-08-31 15:07:46 +00:00
|
|
|
append_img_params(s, r, ro)
|
2023-08-31 14:28:04 +00:00
|
|
|
append_hdr(s, ro)
|
2024-06-27 20:04:36 +00:00
|
|
|
append_property(s, "video-bitrate", {prefix="Bitrate:"})
|
2017-07-25 13:37:09 +00:00
|
|
|
append_filters(s, "vf", "Filters:")
|
2015-04-03 15:22:32 +00:00
|
|
|
end
|
2015-04-28 00:13:27 +00:00
|
|
|
|
|
|
|
|
2016-07-12 17:19:34 +00:00
|
|
|
local function add_audio(s)
|
2018-03-24 21:36:01 +00:00
|
|
|
local r = mp.get_property_native("audio-params")
|
2021-08-15 08:31:13 +00:00
|
|
|
-- in case of e.g. lavfi-complex there can be no input audio, only output
|
2024-03-16 21:32:29 +00:00
|
|
|
local ro = mp.get_property_native("audio-out-params") or r
|
|
|
|
r = r or ro
|
2018-03-24 21:36:01 +00:00
|
|
|
if not r then
|
2016-07-12 17:19:34 +00:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2024-05-12 01:37:38 +00:00
|
|
|
local merge = function(rr, rro, prop)
|
|
|
|
local a = rr[prop] or rro[prop]
|
|
|
|
local b = rro[prop] or rr[prop]
|
2024-04-12 16:35:55 +00:00
|
|
|
return (a == b or a == nil) and a or (a .. " ➜ " .. b)
|
2024-03-16 21:32:29 +00:00
|
|
|
end
|
|
|
|
|
2024-05-23 01:24:58 +00:00
|
|
|
append(s, "", {prefix="Audio:", nl=o.nl .. o.nl, indent=""})
|
2024-04-16 18:29:17 +00:00
|
|
|
local track = mp.get_property_native("current-tracks/audio")
|
|
|
|
if track then
|
|
|
|
append(s, track["codec-desc"], {prefix_sep="", nl="", indent=""})
|
2024-06-27 17:06:10 +00:00
|
|
|
append(s, track["codec-profile"], {prefix="[", nl="", indent=" ", prefix_sep="",
|
|
|
|
no_prefix_markup=true, suffix="]"})
|
2024-04-15 19:13:33 +00:00
|
|
|
if track["codec"] ~= track["decoder"] then
|
|
|
|
append(s, track["decoder"], {prefix="[", nl="", indent=" ", prefix_sep="",
|
|
|
|
no_prefix_markup=true, suffix="]"})
|
|
|
|
end
|
2024-04-11 18:46:19 +00:00
|
|
|
end
|
2024-03-16 21:32:29 +00:00
|
|
|
append_property(s, "current-ao", {prefix="AO:", nl="",
|
|
|
|
indent=o.prefix_sep .. o.prefix_sep})
|
|
|
|
local dev = append_property(s, "audio-device", {prefix="Device:"})
|
|
|
|
local ao_mute = mp.get_property_native("ao-mute") and " (Muted)" or ""
|
|
|
|
append_property(s, "ao-volume", {prefix="AO Volume:", suffix="%" .. ao_mute,
|
|
|
|
nl=dev and "" or o.nl,
|
|
|
|
indent=dev and o.prefix_sep .. o.prefix_sep})
|
|
|
|
if math.abs(mp.get_property_native("audio-delay")) > 1e-6 then
|
|
|
|
append_property(s, "audio-delay", {prefix="A-V delay:"})
|
|
|
|
end
|
|
|
|
local cc = append(s, merge(r, ro, "channel-count"), {prefix="Channels:"})
|
|
|
|
append(s, merge(r, ro, "format"), {prefix="Format:", nl=cc and "" or o.nl,
|
2023-08-31 19:36:41 +00:00
|
|
|
indent=cc and o.prefix_sep .. o.prefix_sep})
|
2024-03-16 21:32:29 +00:00
|
|
|
append(s, merge(r, ro, "samplerate"), {prefix="Sample Rate:", suffix=" Hz"})
|
2024-06-27 20:04:36 +00:00
|
|
|
append_property(s, "audio-bitrate", {prefix="Bitrate:"})
|
2017-07-25 13:37:09 +00:00
|
|
|
append_filters(s, "af", "Filters:")
|
2015-04-03 15:22:32 +00:00
|
|
|
end
|
2015-04-28 00:13:27 +00:00
|
|
|
|
2016-07-08 08:53:02 +00:00
|
|
|
|
2017-09-23 15:54:40 +00:00
|
|
|
-- Determine whether ASS formatting shall/can be used and set formatting sequences
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
local function eval_ass_formatting()
|
2017-07-04 22:31:44 +00:00
|
|
|
o.use_ass = o.ass_formatting and has_vo_window()
|
|
|
|
if o.use_ass then
|
|
|
|
o.nl = o.ass_nl
|
|
|
|
o.indent = o.ass_indent
|
|
|
|
o.prefix_sep = o.ass_prefix_sep
|
|
|
|
o.b1 = o.ass_b1
|
|
|
|
o.b0 = o.ass_b0
|
2017-07-25 13:37:09 +00:00
|
|
|
o.it1 = o.ass_it1
|
|
|
|
o.it0 = o.ass_it0
|
2017-07-04 22:31:44 +00:00
|
|
|
else
|
2016-07-12 17:19:34 +00:00
|
|
|
o.nl = o.no_ass_nl
|
|
|
|
o.indent = o.no_ass_indent
|
|
|
|
o.prefix_sep = o.no_ass_prefix_sep
|
2020-04-23 13:29:18 +00:00
|
|
|
o.b1 = o.no_ass_b1
|
|
|
|
o.b0 = o.no_ass_b0
|
|
|
|
o.it1 = o.no_ass_it1
|
|
|
|
o.it0 = o.no_ass_it0
|
2015-07-21 19:53:23 +00:00
|
|
|
end
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
end
|
2016-07-12 17:19:34 +00:00
|
|
|
|
2023-08-28 21:39:34 +00:00
|
|
|
-- split str into a table
|
|
|
|
-- example: local t = split(s, "\n")
|
|
|
|
-- plain: whether pat is a plain string (default false - pat is a pattern)
|
|
|
|
local function split(str, pat, plain)
|
|
|
|
local init = 1
|
|
|
|
local r, i, find, sub = {}, 1, string.find, string.sub
|
|
|
|
repeat
|
|
|
|
local f0, f1 = find(str, pat, init, plain)
|
|
|
|
r[i], i = sub(str, init, f0 and f0 - 1), i+1
|
|
|
|
init = f0 and f1 + 1
|
|
|
|
until f0 == nil
|
|
|
|
return r
|
|
|
|
end
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
|
2023-08-28 20:43:29 +00:00
|
|
|
-- Composes the output with header and scrollable content
|
|
|
|
-- Returns string of the finished page and the actually chosen offset
|
|
|
|
--
|
2023-08-28 21:39:34 +00:00
|
|
|
-- header : table of the header where each entry is one line
|
|
|
|
-- content : table of the content where each entry is one line
|
|
|
|
-- apply_scroll: scroll the content
|
|
|
|
local function finalize_page(header, content, apply_scroll)
|
2024-10-18 07:53:22 +00:00
|
|
|
local term_height = mp.get_property_native("term-size/h", 24)
|
2023-08-28 21:39:34 +00:00
|
|
|
local from, to = 1, #content
|
2024-10-18 07:53:22 +00:00
|
|
|
if apply_scroll then
|
2023-08-28 21:39:34 +00:00
|
|
|
-- Up to 40 lines for libass because it can put a big performance toll on
|
|
|
|
-- libass to process many lines which end up outside (below) the screen.
|
|
|
|
-- In the terminal reduce height by 2 for the status line (can be more then one line)
|
|
|
|
local max_content_lines = (o.use_ass and 40 or term_height - 2) - #header
|
|
|
|
-- in the terminal the scrolling should stop once the last line is visible
|
|
|
|
local max_offset = o.use_ass and #content or #content - max_content_lines + 1
|
|
|
|
from = max(1, min((pages[curr_page].offset or 1), max_offset))
|
|
|
|
to = min(#content, from + max_content_lines - 1)
|
|
|
|
pages[curr_page].offset = from
|
|
|
|
end
|
|
|
|
local output = table.concat(header) .. table.concat(content, "", from, to)
|
2024-10-17 22:00:22 +00:00
|
|
|
if not o.use_ass and o.term_clip then
|
|
|
|
local clip = mp.get_property("term-clip-cc")
|
2023-08-28 21:39:34 +00:00
|
|
|
local t = split(output, "\n", true)
|
2024-10-17 22:00:22 +00:00
|
|
|
output = clip .. table.concat(t, "\n" .. clip)
|
2023-08-28 21:39:34 +00:00
|
|
|
end
|
|
|
|
return output, from
|
2023-08-28 20:43:29 +00:00
|
|
|
end
|
|
|
|
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
-- Returns an ASS string with "normal" stats
|
|
|
|
local function default_stats()
|
2017-07-02 21:24:51 +00:00
|
|
|
local stats = {}
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
eval_ass_formatting()
|
2017-07-02 21:24:51 +00:00
|
|
|
add_header(stats)
|
2024-04-17 10:27:04 +00:00
|
|
|
add_file(stats, true, false)
|
2023-08-31 15:07:46 +00:00
|
|
|
add_video_out(stats)
|
2017-07-02 21:24:51 +00:00
|
|
|
add_video(stats)
|
|
|
|
add_audio(stats)
|
2023-08-28 21:39:34 +00:00
|
|
|
return finalize_page({}, stats, false)
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- Returns an ASS string with extended VO stats
|
|
|
|
local function vo_stats()
|
2023-08-28 21:01:20 +00:00
|
|
|
local header, content = {}, {}
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
eval_ass_formatting()
|
2023-08-28 21:01:20 +00:00
|
|
|
add_header(header)
|
2024-06-08 13:37:57 +00:00
|
|
|
append_perfdata(header, content, true)
|
2023-08-28 21:01:20 +00:00
|
|
|
header = {table.concat(header)}
|
2024-04-09 13:22:45 +00:00
|
|
|
return finalize_page(header, content, true)
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
end
|
|
|
|
|
2021-07-16 08:17:50 +00:00
|
|
|
local kbinfo_lines = nil
|
2023-09-02 18:59:40 +00:00
|
|
|
local function keybinding_info(after_scroll, bindlist)
|
2021-07-16 08:17:50 +00:00
|
|
|
local header = {}
|
|
|
|
local page = pages[o.key_page_4]
|
|
|
|
eval_ass_formatting()
|
|
|
|
add_header(header)
|
2024-10-17 22:00:34 +00:00
|
|
|
local prefix = bindlist and page.desc or page.desc .. ":" .. scroll_hint(true)
|
|
|
|
append(header, "", {prefix=prefix, nl="", indent=""})
|
2023-08-28 20:43:29 +00:00
|
|
|
header = {table.concat(header)}
|
2021-07-16 08:17:50 +00:00
|
|
|
|
|
|
|
if not kbinfo_lines or not after_scroll then
|
2024-10-17 22:00:22 +00:00
|
|
|
kbinfo_lines = get_kbinfo_lines()
|
2021-07-16 08:17:50 +00:00
|
|
|
end
|
2023-08-28 20:43:29 +00:00
|
|
|
|
2023-09-02 18:59:40 +00:00
|
|
|
return finalize_page(header, kbinfo_lines, not bindlist)
|
2021-07-16 08:17:50 +00:00
|
|
|
end
|
|
|
|
|
2024-06-08 16:19:00 +00:00
|
|
|
local function float2rational(x)
|
|
|
|
local max_den = 100000
|
|
|
|
local m00, m01, m10, m11 = 1, 0, 0, 1
|
|
|
|
local a = math.floor(x)
|
|
|
|
local frac = x - a
|
|
|
|
while m10 * a + m11 <= max_den do
|
|
|
|
local temp = m00 * a + m01
|
|
|
|
m01 = m00
|
|
|
|
m00 = temp
|
|
|
|
temp = m10 * a + m11
|
|
|
|
m11 = m10
|
|
|
|
m10 = temp
|
|
|
|
|
|
|
|
if frac == 0 then
|
|
|
|
break
|
|
|
|
end
|
|
|
|
|
|
|
|
x = 1 / frac
|
|
|
|
a = math.floor(x)
|
|
|
|
frac = x - a
|
|
|
|
end
|
|
|
|
return m00, m10
|
|
|
|
end
|
|
|
|
|
|
|
|
local function add_track(c, t, i)
|
|
|
|
if not t then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2024-06-27 14:32:53 +00:00
|
|
|
local type = t.image and "Image" or t["type"]:sub(1, 1):upper() .. t["type"]:sub(2)
|
2024-06-08 16:19:00 +00:00
|
|
|
append(c, "", {prefix=type .. ":", nl=o.nl .. o.nl, indent=""})
|
|
|
|
append(c, t["title"], {prefix_sep="", nl="", indent=""})
|
|
|
|
append(c, t["id"], {prefix="ID:"})
|
|
|
|
append(c, t["src-id"], {prefix="Demuxer ID:", nl="", indent=o.prefix_sep .. o.prefix_sep})
|
|
|
|
append(c, t["program-id"], {prefix="Program ID:", nl="", indent=o.prefix_sep .. o.prefix_sep})
|
|
|
|
append(c, t["ff-index"], {prefix="FFmpeg Index:", nl="", indent=o.prefix_sep .. o.prefix_sep})
|
|
|
|
append(c, t["external-filename"], {prefix="File:"})
|
|
|
|
append(c, "", {prefix="Flags:"})
|
2024-06-27 18:00:31 +00:00
|
|
|
local flags = {"default", "forced", "dependent", "visual-impaired",
|
|
|
|
"hearing-impaired", "image", "albumart", "external"}
|
2024-06-08 16:19:00 +00:00
|
|
|
local any = false
|
|
|
|
for _, flag in ipairs(flags) do
|
|
|
|
if t[flag] then
|
|
|
|
append(c, flag, {prefix=any and ", " or "", nl="", indent="", prefix_sep=""})
|
|
|
|
any = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if not any then
|
|
|
|
table.remove(c)
|
|
|
|
end
|
|
|
|
if append(c, t["codec-desc"], {prefix="Format:"}) then
|
|
|
|
append(c, t["codec-profile"], {prefix="[", nl="", indent=" ", prefix_sep="",
|
|
|
|
no_prefix_markup=true, suffix="]"})
|
|
|
|
if t["codec"] ~= t["decoder"] then
|
|
|
|
append(c, t["decoder"], {prefix="[", nl="", indent=" ", prefix_sep="",
|
|
|
|
no_prefix_markup=true, suffix="]"})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
append(c, t["lang"], {prefix="Language:"})
|
|
|
|
append(c, t["demux-channel-count"], {prefix="Channels:"})
|
|
|
|
append(c, t["demux-channels"], {prefix="Channel Layout:"})
|
|
|
|
append(c, t["demux-samplerate"], {prefix="Sample Rate:", suffix=" Hz"})
|
|
|
|
local function B(b) return b and string.format("%.2f", b / 1024) end
|
|
|
|
local bitrate = append(c, B(t["demux-bitrate"]), {prefix="Bitrate:", suffix=" kbps"})
|
|
|
|
append(c, B(t["hls-bitrate"]), {prefix="HLS Bitrate:", suffix=" kbps",
|
|
|
|
nl=bitrate and "" or o.nl,
|
|
|
|
indent=bitrate and o.prefix_sep .. o.prefix_sep})
|
|
|
|
append_resolution(c, {w=t["demux-w"], h=t["demux-h"], ["crop-x"]=t["demux-crop-x"],
|
|
|
|
["crop-y"]=t["demux-crop-y"], ["crop-w"]=t["demux-crop-w"],
|
|
|
|
["crop-h"]=t["demux-crop-h"]}, "Resolution:")
|
|
|
|
if not t["image"] and t["demux-fps"] then
|
|
|
|
append_fps(c, "track-list/" .. i .. "/demux-fps", "")
|
|
|
|
end
|
|
|
|
append(c, t["demux-rotation"], {prefix="Rotation:"})
|
|
|
|
if t["demux-par"] then
|
|
|
|
local num, den = float2rational(t["demux-par"])
|
|
|
|
append(c, string.format("%d:%d", num, den), {prefix="Pixel Aspect Ratio:"})
|
|
|
|
end
|
|
|
|
local track_rg = t["replaygain-track-peak"] ~= nil or t["replaygain-track-gain"] ~= nil
|
|
|
|
local album_rg = t["replaygain-album-peak"] ~= nil or t["replaygain-album-gain"] ~= nil
|
|
|
|
if track_rg or album_rg then
|
|
|
|
append(c, "", {prefix="Replay Gain:"})
|
|
|
|
end
|
|
|
|
if track_rg then
|
|
|
|
append(c, "", {prefix="Track:", indent=o.indent .. o.prefix_sep, prefix_sep=""})
|
|
|
|
append(c, t["replaygain-track-gain"], {prefix="Gain:", suffix=" dB",
|
|
|
|
nl="", indent=o.prefix_sep})
|
|
|
|
append(c, t["replaygain-track-peak"], {prefix="Peak:", suffix=" dB",
|
|
|
|
nl="", indent=o.prefix_sep})
|
|
|
|
end
|
|
|
|
if album_rg then
|
|
|
|
append(c, "", {prefix="Album:", indent=o.indent .. o.prefix_sep, prefix_sep=""})
|
|
|
|
append(c, t["replaygain-album-gain"], {prefix="Gain:", suffix=" dB",
|
|
|
|
nl="", indent=o.prefix_sep})
|
|
|
|
append(c, t["replaygain-album-peak"], {prefix="Peak:", suffix=" dB",
|
|
|
|
nl="", indent=o.prefix_sep})
|
|
|
|
end
|
|
|
|
if t["dolby-vision-profile"] or t["dolby-vision-level"] then
|
|
|
|
append(c, "", {prefix="Dolby Vision:"})
|
|
|
|
append(c, t["dolby-vision-profile"], {prefix="Profile:", nl="", indent=""})
|
|
|
|
append(c, t["dolby-vision-level"], {prefix="Level:", nl="",
|
|
|
|
indent=t["dolby-vision-profile"] and
|
|
|
|
o.prefix_sep .. o.prefix_sep or ""})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function track_info()
|
|
|
|
local h, c = {}, {}
|
|
|
|
eval_ass_formatting()
|
|
|
|
add_header(h)
|
|
|
|
local desc = pages[o.key_page_5].desc
|
|
|
|
append(h, "", {prefix=format("%s:%s", desc, scroll_hint()), nl="", indent=""})
|
|
|
|
h = {table.concat(h)}
|
|
|
|
table.insert(c, o.nl .. o.nl)
|
2024-04-17 10:27:04 +00:00
|
|
|
add_file(c, false, true)
|
2024-06-08 16:19:00 +00:00
|
|
|
for i, track in ipairs(mp.get_property_native("track-list")) do
|
|
|
|
if track['selected'] then
|
|
|
|
add_track(c, track, i - 1)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return finalize_page(h, c, true)
|
|
|
|
end
|
|
|
|
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
local function perf_stats()
|
2023-08-28 21:01:20 +00:00
|
|
|
local header, content = {}, {}
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
eval_ass_formatting()
|
2023-08-28 21:01:20 +00:00
|
|
|
add_header(header)
|
2021-07-16 07:33:46 +00:00
|
|
|
local page = pages[o.key_page_0]
|
2023-08-30 00:51:40 +00:00
|
|
|
append(header, "", {prefix=format("%s:%s", page.desc, scroll_hint()), nl="", indent=""})
|
2023-08-28 21:01:20 +00:00
|
|
|
append_general_perfdata(content)
|
|
|
|
header = {table.concat(header)}
|
2023-08-28 21:39:34 +00:00
|
|
|
return finalize_page(header, content, true)
|
stats: some more performance graphs
Add an infrastructure for collecting performance-related data, use it in
some places. Add rendering of them to stats.lua.
There were two main goals: minimal impact on the normal code and normal
playback. So all these stats_* function calls either happen only during
initialization, or return immediately if no stats collection is going
on. That's why it does this lazily adding of stats entries etc. (a first
iteration made each stats entry an API thing, instead of just a single
stats_ctx, but I thought that was getting too intrusive in the "normal"
code, even if everything gets worse inside of stats.c).
You could get most of this information from various profilers (including
the extremely primitive --dump-stats thing in mpv), but this makes it
easier to see the most important information at once (at least in
theory), partially because we know best about the context of various
things.
Not very happy with this. It's all pretty primitive and dumb. At this
point I just wanted to get over with it, without necessarily having to
revisit it later, but with having my stupid statistics.
Somehow the code feels terrible. There are a lot of meh decisions in
there that could be better or worse (but mostly could be better), and it
just sucks but it's also trivial and uninteresting and does the job. I
guess I hate programming. It's so tedious and the result is always shit.
Anyway, enjoy.
2020-04-08 22:27:54 +00:00
|
|
|
end
|
|
|
|
|
2019-06-14 18:14:42 +00:00
|
|
|
local function opt_time(t)
|
|
|
|
if type(t) == type(1.1) then
|
|
|
|
return mp.format_time(t)
|
|
|
|
end
|
|
|
|
return "?"
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Returns an ASS string with stats about the demuxer cache etc.
|
|
|
|
local function cache_stats()
|
|
|
|
local stats = {}
|
|
|
|
|
2020-02-06 21:30:57 +00:00
|
|
|
eval_ass_formatting()
|
2019-06-14 18:14:42 +00:00
|
|
|
add_header(stats)
|
2024-01-17 08:21:32 +00:00
|
|
|
append(stats, "", {prefix="Cache Info:", nl="", indent=""})
|
2019-06-14 18:14:42 +00:00
|
|
|
|
|
|
|
local info = mp.get_property_native("demuxer-cache-state")
|
|
|
|
if info == nil then
|
|
|
|
append(stats, "Unavailable.", {})
|
2023-08-28 21:39:34 +00:00
|
|
|
return finalize_page({}, stats, false)
|
2019-06-14 18:14:42 +00:00
|
|
|
end
|
|
|
|
|
2019-06-22 22:30:56 +00:00
|
|
|
local a = info["reader-pts"]
|
|
|
|
local b = info["cache-end"]
|
|
|
|
|
2024-01-17 08:21:32 +00:00
|
|
|
append(stats, opt_time(a) .. " - " .. opt_time(b), {prefix = "Packet Queue:"})
|
2019-06-22 22:30:56 +00:00
|
|
|
|
|
|
|
local r = nil
|
2023-10-31 01:23:21 +00:00
|
|
|
if a ~= nil and b ~= nil then
|
2019-06-22 22:30:56 +00:00
|
|
|
r = b - a
|
|
|
|
end
|
|
|
|
|
|
|
|
local r_graph = nil
|
|
|
|
if not display_timer.oneshot and o.use_ass then
|
|
|
|
r_graph = generate_graph(cache_ahead_buf, cache_ahead_buf.pos,
|
|
|
|
cache_ahead_buf.len, cache_ahead_buf.max,
|
|
|
|
nil, 0.8, 1)
|
|
|
|
r_graph = o.prefix_sep .. r_graph
|
|
|
|
end
|
2024-01-17 08:21:32 +00:00
|
|
|
append(stats, opt_time(r), {prefix = "Readahead:", suffix = r_graph})
|
2019-06-14 18:14:42 +00:00
|
|
|
|
|
|
|
-- These states are not necessarily exclusive. They're about potentially
|
|
|
|
-- separate mechanisms, whose states may be decoupled.
|
|
|
|
local state = "reading"
|
|
|
|
local seek_ts = info["debug-seeking"]
|
|
|
|
if seek_ts ~= nil then
|
|
|
|
state = "seeking (to " .. mp.format_time(seek_ts) .. ")"
|
|
|
|
elseif info["eof"] == true then
|
|
|
|
state = "eof"
|
|
|
|
elseif info["underrun"] then
|
|
|
|
state = "underrun"
|
|
|
|
elseif info["idle"] == true then
|
|
|
|
state = "inactive"
|
|
|
|
end
|
|
|
|
append(stats, state, {prefix = "State:"})
|
|
|
|
|
2020-04-03 11:26:31 +00:00
|
|
|
local speed = info["raw-input-rate"] or 0
|
|
|
|
local speed_graph = nil
|
2019-06-22 22:30:56 +00:00
|
|
|
if not display_timer.oneshot and o.use_ass then
|
2020-04-03 11:26:31 +00:00
|
|
|
speed_graph = generate_graph(cache_speed_buf, cache_speed_buf.pos,
|
|
|
|
cache_speed_buf.len, cache_speed_buf.max,
|
2019-06-22 22:30:56 +00:00
|
|
|
nil, 0.8, 1)
|
2020-04-03 11:26:31 +00:00
|
|
|
speed_graph = o.prefix_sep .. speed_graph
|
2019-06-22 22:30:56 +00:00
|
|
|
end
|
2020-04-03 21:26:51 +00:00
|
|
|
append(stats, utils.format_bytes_humanized(speed) .. "/s", {prefix="Speed:",
|
|
|
|
suffix=speed_graph})
|
2020-04-03 11:26:31 +00:00
|
|
|
|
2019-06-14 18:14:42 +00:00
|
|
|
append(stats, utils.format_bytes_humanized(info["total-bytes"]),
|
2020-04-03 11:26:31 +00:00
|
|
|
{prefix = "Total RAM:"})
|
2019-06-14 18:14:42 +00:00
|
|
|
append(stats, utils.format_bytes_humanized(info["fw-bytes"]),
|
|
|
|
{prefix = "Forward RAM:"})
|
|
|
|
|
|
|
|
local fc = info["file-cache-bytes"]
|
|
|
|
if fc ~= nil then
|
|
|
|
fc = utils.format_bytes_humanized(fc)
|
|
|
|
else
|
|
|
|
fc = "(disabled)"
|
|
|
|
end
|
2024-01-17 08:21:32 +00:00
|
|
|
append(stats, fc, {prefix = "Disk Cache:"})
|
2019-06-14 18:14:42 +00:00
|
|
|
|
2024-01-17 08:21:32 +00:00
|
|
|
append(stats, info["debug-low-level-seeks"], {prefix = "Media Seeks:"})
|
|
|
|
append(stats, info["debug-byte-level-seeks"], {prefix = "Stream Seeks:"})
|
2019-06-14 18:14:42 +00:00
|
|
|
|
2024-05-23 01:24:58 +00:00
|
|
|
append(stats, "", {prefix="Ranges:", nl=o.nl .. o.nl, indent=""})
|
2019-06-14 18:14:42 +00:00
|
|
|
|
|
|
|
append(stats, info["bof-cached"] and "yes" or "no",
|
2024-01-17 08:21:32 +00:00
|
|
|
{prefix = "Start Cached:"})
|
2019-06-14 18:14:42 +00:00
|
|
|
append(stats, info["eof-cached"] and "yes" or "no",
|
2024-01-17 08:21:32 +00:00
|
|
|
{prefix = "End Cached:"})
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
|
2019-06-14 18:14:42 +00:00
|
|
|
local ranges = info["seekable-ranges"] or {}
|
2024-05-12 01:37:38 +00:00
|
|
|
for n, range in ipairs(ranges) do
|
|
|
|
append(stats, mp.format_time(range["start"]) .. " - " ..
|
|
|
|
mp.format_time(range["end"]),
|
2019-06-14 18:14:42 +00:00
|
|
|
{prefix = format("Range %s:", n)})
|
|
|
|
end
|
|
|
|
|
2023-08-28 21:39:34 +00:00
|
|
|
return finalize_page({}, stats, false)
|
2015-07-21 19:53:23 +00:00
|
|
|
end
|
2015-04-28 00:13:27 +00:00
|
|
|
|
2019-06-22 22:30:56 +00:00
|
|
|
-- Record 1 sample of cache statistics.
|
|
|
|
-- (Unlike record_data(), this does not return a function, but runs directly.)
|
|
|
|
local function record_cache_stats()
|
|
|
|
local info = mp.get_property_native("demuxer-cache-state")
|
|
|
|
if info == nil then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local a = info["reader-pts"]
|
|
|
|
local b = info["cache-end"]
|
2023-10-31 01:23:21 +00:00
|
|
|
if a ~= nil and b ~= nil then
|
2019-06-22 22:30:56 +00:00
|
|
|
graph_add_value(cache_ahead_buf, b - a)
|
|
|
|
end
|
|
|
|
|
2020-04-03 11:26:31 +00:00
|
|
|
graph_add_value(cache_speed_buf, info["raw-input-rate"] or 0)
|
2019-06-22 22:30:56 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
cache_recorder_timer = mp.add_periodic_timer(0.25, record_cache_stats)
|
|
|
|
cache_recorder_timer:kill()
|
2016-07-08 08:53:02 +00:00
|
|
|
|
2017-09-23 15:54:40 +00:00
|
|
|
-- Current page and <page key>:<page function> mapping
|
|
|
|
curr_page = o.key_page_1
|
|
|
|
pages = {
|
|
|
|
[o.key_page_1] = { f = default_stats, desc = "Default" },
|
2020-05-17 11:55:38 +00:00
|
|
|
[o.key_page_2] = { f = vo_stats, desc = "Extended Frame Timings", scroll = true },
|
2019-06-14 18:14:42 +00:00
|
|
|
[o.key_page_3] = { f = cache_stats, desc = "Cache Statistics" },
|
2024-01-17 08:21:32 +00:00
|
|
|
[o.key_page_4] = { f = keybinding_info, desc = "Active Key Bindings", scroll = true },
|
2024-06-08 16:19:00 +00:00
|
|
|
[o.key_page_5] = { f = track_info, desc = "Selected Tracks Info", scroll = true },
|
2024-01-17 08:21:32 +00:00
|
|
|
[o.key_page_0] = { f = perf_stats, desc = "Internal Performance Info", scroll = true },
|
2017-09-23 15:54:40 +00:00
|
|
|
}
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
|
|
|
|
|
|
|
|
-- Returns a function to record vsratio/jitter with the specified `skip` value
|
2016-09-12 00:30:09 +00:00
|
|
|
local function record_data(skip)
|
|
|
|
init_buffers()
|
|
|
|
skip = max(skip, 0)
|
2016-07-12 17:19:34 +00:00
|
|
|
local i = skip
|
|
|
|
return function()
|
|
|
|
if i < skip then
|
|
|
|
i = i + 1
|
|
|
|
return
|
|
|
|
else
|
|
|
|
i = 0
|
|
|
|
end
|
|
|
|
|
2016-09-12 00:30:09 +00:00
|
|
|
if o.plot_vsync_jitter then
|
2024-06-08 12:36:47 +00:00
|
|
|
local r = mp.get_property_number("vsync-jitter")
|
2016-09-12 00:30:09 +00:00
|
|
|
if r then
|
|
|
|
vsjitter_buf.pos = (vsjitter_buf.pos % vsjitter_buf.len) + 1
|
|
|
|
vsjitter_buf[vsjitter_buf.pos] = r
|
|
|
|
vsjitter_buf.max = max(vsjitter_buf.max, r)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
if o.plot_vsync_ratio then
|
2024-06-08 12:36:47 +00:00
|
|
|
local r = mp.get_property_number("vsync-ratio")
|
2016-09-12 00:30:09 +00:00
|
|
|
if r then
|
|
|
|
vsratio_buf.pos = (vsratio_buf.pos % vsratio_buf.len) + 1
|
|
|
|
vsratio_buf[vsratio_buf.pos] = r
|
|
|
|
vsratio_buf.max = max(vsratio_buf.max, r)
|
|
|
|
end
|
2016-07-12 17:19:34 +00:00
|
|
|
end
|
|
|
|
end
|
2015-04-03 15:22:32 +00:00
|
|
|
end
|
|
|
|
|
2017-09-23 15:54:40 +00:00
|
|
|
-- Call the function for `page` and print it to OSD
|
2021-07-16 07:37:41 +00:00
|
|
|
local function print_page(page, after_scroll)
|
stats.lua: fix ass-escape while persistent_overlay=yes
mpv has two methods to display output from text input:
- show-text (scripting: mp.osd_message) has ass disabled by default
(escaped), and the property osd-ass-cc can control escaping.
- osd-overlay (scripting: mp.set_osd_ass or mp.create_osd_overlay)
has ass enabled (unescaped), and osd-ass-cc is NOT supported.
By default, stats.lua uses mp.osd_message which does support escaping.
That's persistent_overlay=no.
When using persistent_overlay=yes then mp.set_osd_ass is used.
Due to this, the no_ASS(..) function - which is supposed to escape
ass, simply returned its input unmodified when persistent_overlay
is enabled.
This is not a new issue, and the filters on page 1 use no_ASS() to no
avail in persistent mode, however, this content (filter name and value
strings) rarely actually needs escaping, and users didn't notice.
However, the new page 4 (keys) does break visibly when no_ASS doesn't
work, because it tries to escape arbitrary key-names and command
strings, and at the very least the key '{' is bound by default, which
is displayed incorrectly if escaping doesn't work.
Fix this by rolling our own escaping when using mp.set_osd_ass,
similar to how the mpv code does it for mp.osd_message (substrings
replacements).
However, this means that the set_ASS(..) function can no longer
behave correctly because escaping requires going through the whole
content string rather than only inserting a marker.
Luckily, other than at no_ASS, set_ASS was only used at one place
(text_style), which is only called from two places:
- generate_graph() only needs to restore styles - not to enable ass.
- add_header() is only used at the begining of page output, and
uses set_ASS to enable ass initially when using mp.osd_message.
So remove the set_ASS function, and instead enable ass directly at
print_page using osd-ass-cc when mp.osd_message is used.
Fixes #9022
2021-07-20 15:03:29 +00:00
|
|
|
-- the page functions assume we start in ass-enabled mode.
|
|
|
|
-- that's true for mp.set_osd_ass, but not for mp.osd_message.
|
|
|
|
local ass_content = pages[page].f(after_scroll)
|
2017-09-23 15:54:40 +00:00
|
|
|
if o.persistent_overlay then
|
stats.lua: fix ass-escape while persistent_overlay=yes
mpv has two methods to display output from text input:
- show-text (scripting: mp.osd_message) has ass disabled by default
(escaped), and the property osd-ass-cc can control escaping.
- osd-overlay (scripting: mp.set_osd_ass or mp.create_osd_overlay)
has ass enabled (unescaped), and osd-ass-cc is NOT supported.
By default, stats.lua uses mp.osd_message which does support escaping.
That's persistent_overlay=no.
When using persistent_overlay=yes then mp.set_osd_ass is used.
Due to this, the no_ASS(..) function - which is supposed to escape
ass, simply returned its input unmodified when persistent_overlay
is enabled.
This is not a new issue, and the filters on page 1 use no_ASS() to no
avail in persistent mode, however, this content (filter name and value
strings) rarely actually needs escaping, and users didn't notice.
However, the new page 4 (keys) does break visibly when no_ASS doesn't
work, because it tries to escape arbitrary key-names and command
strings, and at the very least the key '{' is bound by default, which
is displayed incorrectly if escaping doesn't work.
Fix this by rolling our own escaping when using mp.set_osd_ass,
similar to how the mpv code does it for mp.osd_message (substrings
replacements).
However, this means that the set_ASS(..) function can no longer
behave correctly because escaping requires going through the whole
content string rather than only inserting a marker.
Luckily, other than at no_ASS, set_ASS was only used at one place
(text_style), which is only called from two places:
- generate_graph() only needs to restore styles - not to enable ass.
- add_header() is only used at the begining of page output, and
uses set_ASS to enable ass initially when using mp.osd_message.
So remove the set_ASS function, and instead enable ass directly at
print_page using osd-ass-cc when mp.osd_message is used.
Fixes #9022
2021-07-20 15:03:29 +00:00
|
|
|
mp.set_osd_ass(0, 0, ass_content)
|
2016-05-17 20:23:11 +00:00
|
|
|
else
|
stats.lua: fix ass-escape while persistent_overlay=yes
mpv has two methods to display output from text input:
- show-text (scripting: mp.osd_message) has ass disabled by default
(escaped), and the property osd-ass-cc can control escaping.
- osd-overlay (scripting: mp.set_osd_ass or mp.create_osd_overlay)
has ass enabled (unescaped), and osd-ass-cc is NOT supported.
By default, stats.lua uses mp.osd_message which does support escaping.
That's persistent_overlay=no.
When using persistent_overlay=yes then mp.set_osd_ass is used.
Due to this, the no_ASS(..) function - which is supposed to escape
ass, simply returned its input unmodified when persistent_overlay
is enabled.
This is not a new issue, and the filters on page 1 use no_ASS() to no
avail in persistent mode, however, this content (filter name and value
strings) rarely actually needs escaping, and users didn't notice.
However, the new page 4 (keys) does break visibly when no_ASS doesn't
work, because it tries to escape arbitrary key-names and command
strings, and at the very least the key '{' is bound by default, which
is displayed incorrectly if escaping doesn't work.
Fix this by rolling our own escaping when using mp.set_osd_ass,
similar to how the mpv code does it for mp.osd_message (substrings
replacements).
However, this means that the set_ASS(..) function can no longer
behave correctly because escaping requires going through the whole
content string rather than only inserting a marker.
Luckily, other than at no_ASS, set_ASS was only used at one place
(text_style), which is only called from two places:
- generate_graph() only needs to restore styles - not to enable ass.
- add_header() is only used at the begining of page output, and
uses set_ASS to enable ass initially when using mp.osd_message.
So remove the set_ASS function, and instead enable ass directly at
print_page using osd-ass-cc when mp.osd_message is used.
Fixes #9022
2021-07-20 15:03:29 +00:00
|
|
|
mp.osd_message((o.use_ass and ass_start or "") .. ass_content,
|
2021-07-16 07:37:41 +00:00
|
|
|
display_timer.oneshot and o.duration or o.redraw_delay + 1)
|
2016-05-17 20:23:11 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2024-10-12 17:00:21 +00:00
|
|
|
local function update_scale(osd_height)
|
2024-05-16 14:08:06 +00:00
|
|
|
local scale_with_video
|
|
|
|
if o.vidscale == "auto" then
|
|
|
|
scale_with_video = mp.get_property_native("osd-scale-by-window")
|
|
|
|
else
|
|
|
|
scale_with_video = o.vidscale == "yes"
|
|
|
|
end
|
|
|
|
|
2024-04-15 04:43:17 +00:00
|
|
|
-- Calculate scaled metrics.
|
2024-10-29 15:19:17 +00:00
|
|
|
-- Make font_size=n the same size as --osd-font-size=n.
|
|
|
|
local scale = 288 / 720
|
2024-10-12 17:00:21 +00:00
|
|
|
if not scale_with_video and osd_height > 0 then
|
2024-10-29 15:19:17 +00:00
|
|
|
scale = 288 / osd_height
|
2024-04-15 04:43:17 +00:00
|
|
|
end
|
|
|
|
font_size = o.font_size * scale
|
|
|
|
border_size = o.border_size * scale
|
|
|
|
shadow_x_offset = o.shadow_x_offset * scale
|
|
|
|
shadow_y_offset = o.shadow_y_offset * scale
|
|
|
|
plot_bg_border_width = o.plot_bg_border_width * scale
|
|
|
|
if display_timer:is_enabled() then
|
|
|
|
print_page(curr_page)
|
|
|
|
end
|
|
|
|
end
|
2016-05-17 20:23:11 +00:00
|
|
|
|
2024-10-12 17:00:21 +00:00
|
|
|
local function handle_osd_height_update(_, osd_height)
|
|
|
|
update_scale(osd_height)
|
2024-05-16 16:30:52 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
local function handle_osd_scale_by_window_update()
|
2024-10-12 17:00:21 +00:00
|
|
|
update_scale(mp.get_property_native("osd-height"))
|
2024-05-16 16:30:52 +00:00
|
|
|
end
|
|
|
|
|
2017-09-23 15:54:40 +00:00
|
|
|
local function clear_screen()
|
|
|
|
if o.persistent_overlay then mp.set_osd_ass(0, 0, "") else mp.osd_message("", 0) end
|
|
|
|
end
|
|
|
|
|
2020-04-10 22:20:02 +00:00
|
|
|
local function scroll_delta(d)
|
|
|
|
if display_timer.oneshot then display_timer:kill() ; display_timer:resume() end
|
|
|
|
pages[curr_page].offset = (pages[curr_page].offset or 1) + d
|
2021-07-16 07:37:41 +00:00
|
|
|
print_page(curr_page, true)
|
2020-04-10 22:20:02 +00:00
|
|
|
end
|
|
|
|
local function scroll_up() scroll_delta(-o.scroll_lines) end
|
|
|
|
local function scroll_down() scroll_delta(o.scroll_lines) end
|
|
|
|
|
|
|
|
local function reset_scroll_offsets()
|
|
|
|
for _, page in pairs(pages) do
|
|
|
|
page.offset = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local function bind_scroll()
|
|
|
|
if not scroll_bound then
|
2024-05-12 01:37:38 +00:00
|
|
|
mp.add_forced_key_binding(o.key_scroll_up, "__forced_" .. o.key_scroll_up,
|
|
|
|
scroll_up, {repeatable=true})
|
|
|
|
mp.add_forced_key_binding(o.key_scroll_down, "__forced_" .. o.key_scroll_down,
|
|
|
|
scroll_down, {repeatable=true})
|
2020-04-10 22:20:02 +00:00
|
|
|
scroll_bound = true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
local function unbind_scroll()
|
|
|
|
if scroll_bound then
|
2021-07-19 21:49:03 +00:00
|
|
|
mp.remove_key_binding("__forced_"..o.key_scroll_up)
|
|
|
|
mp.remove_key_binding("__forced_"..o.key_scroll_down)
|
2020-04-10 22:20:02 +00:00
|
|
|
scroll_bound = false
|
|
|
|
end
|
|
|
|
end
|
2024-03-21 19:09:22 +00:00
|
|
|
|
|
|
|
local function filter_bindings()
|
|
|
|
input.get({
|
|
|
|
prompt = "Filter bindings:",
|
|
|
|
opened = function ()
|
|
|
|
-- This is necessary to close the console if the oneshot
|
|
|
|
-- display_timer expires without typing anything.
|
|
|
|
searched_text = ""
|
|
|
|
end,
|
|
|
|
edited = function (text)
|
|
|
|
reset_scroll_offsets()
|
|
|
|
searched_text = text:lower()
|
|
|
|
print_page(curr_page)
|
|
|
|
if display_timer.oneshot then
|
|
|
|
display_timer:kill()
|
|
|
|
display_timer:resume()
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
submit = input.terminate,
|
|
|
|
closed = function ()
|
|
|
|
searched_text = nil
|
|
|
|
if display_timer:is_enabled() then
|
|
|
|
print_page(curr_page)
|
|
|
|
if display_timer.oneshot then
|
|
|
|
display_timer:kill()
|
|
|
|
display_timer:resume()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end,
|
2024-10-01 18:53:47 +00:00
|
|
|
dont_bind_up_down = true,
|
2024-03-21 19:09:22 +00:00
|
|
|
})
|
|
|
|
end
|
|
|
|
|
|
|
|
local function bind_search()
|
|
|
|
mp.add_forced_key_binding(o.key_search, "__forced_"..o.key_search, filter_bindings)
|
|
|
|
end
|
|
|
|
|
|
|
|
local function unbind_search()
|
|
|
|
mp.remove_key_binding("__forced_"..o.key_search)
|
|
|
|
end
|
|
|
|
|
2024-10-17 07:20:04 +00:00
|
|
|
local function bind_exit()
|
|
|
|
-- Don't bind in oneshot mode because if ESC is pressed right when the stats
|
|
|
|
-- stop being displayed, it would unintentionally trigger any user-defined
|
|
|
|
-- ESC binding.
|
|
|
|
if not display_timer.oneshot then
|
|
|
|
mp.add_forced_key_binding(o.key_exit, "__forced_" .. o.key_exit, function ()
|
|
|
|
process_key_binding(false)
|
|
|
|
end)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
local function unbind_exit()
|
|
|
|
mp.remove_key_binding("__forced_" .. o.key_exit)
|
|
|
|
end
|
|
|
|
|
2020-04-10 22:20:02 +00:00
|
|
|
local function update_scroll_bindings(k)
|
2023-10-31 01:23:21 +00:00
|
|
|
if pages[k].scroll then
|
2020-04-10 22:20:02 +00:00
|
|
|
bind_scroll()
|
|
|
|
else
|
|
|
|
unbind_scroll()
|
|
|
|
end
|
2024-03-21 19:09:22 +00:00
|
|
|
|
|
|
|
if k == o.key_page_4 then
|
|
|
|
bind_search()
|
|
|
|
else
|
|
|
|
unbind_search()
|
|
|
|
end
|
2020-04-10 22:20:02 +00:00
|
|
|
end
|
2017-09-23 15:54:40 +00:00
|
|
|
|
|
|
|
-- Add keybindings for every page
|
|
|
|
local function add_page_bindings()
|
|
|
|
local function a(k)
|
|
|
|
return function()
|
2020-04-10 22:20:02 +00:00
|
|
|
reset_scroll_offsets()
|
|
|
|
update_scroll_bindings(k)
|
2017-09-23 15:54:40 +00:00
|
|
|
curr_page = k
|
|
|
|
print_page(k)
|
|
|
|
if display_timer.oneshot then display_timer:kill() ; display_timer:resume() end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
for k, _ in pairs(pages) do
|
2021-07-19 21:49:03 +00:00
|
|
|
mp.add_forced_key_binding(k, "__forced_"..k, a(k), {repeatable=true})
|
2017-07-02 11:23:23 +00:00
|
|
|
end
|
2020-04-10 22:20:02 +00:00
|
|
|
update_scroll_bindings(curr_page)
|
2024-10-17 07:20:04 +00:00
|
|
|
bind_exit()
|
2017-07-02 11:23:23 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
|
2017-09-23 15:54:40 +00:00
|
|
|
-- Remove keybindings for every page
|
|
|
|
local function remove_page_bindings()
|
|
|
|
for k, _ in pairs(pages) do
|
2021-07-19 21:49:03 +00:00
|
|
|
mp.remove_key_binding("__forced_"..k)
|
2017-09-23 15:54:40 +00:00
|
|
|
end
|
2020-04-10 22:20:02 +00:00
|
|
|
unbind_scroll()
|
2024-03-21 19:09:22 +00:00
|
|
|
unbind_search()
|
2024-10-17 07:20:04 +00:00
|
|
|
unbind_exit()
|
2017-09-23 15:54:40 +00:00
|
|
|
end
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
|
|
|
|
|
2024-10-17 07:20:04 +00:00
|
|
|
process_key_binding = function(oneshot)
|
2020-04-10 22:20:02 +00:00
|
|
|
reset_scroll_offsets()
|
2017-09-23 15:54:40 +00:00
|
|
|
-- Stats are already being displayed
|
|
|
|
if display_timer:is_enabled() then
|
|
|
|
-- Previous and current keys were oneshot -> restart timer
|
|
|
|
if display_timer.oneshot and oneshot then
|
|
|
|
display_timer:kill()
|
|
|
|
print_page(curr_page)
|
|
|
|
display_timer:resume()
|
|
|
|
-- Previous and current keys were toggling -> end toggling
|
|
|
|
elseif not display_timer.oneshot and not oneshot then
|
|
|
|
display_timer:kill()
|
2019-06-22 22:30:56 +00:00
|
|
|
cache_recorder_timer:stop()
|
2023-08-30 13:09:51 +00:00
|
|
|
if tm_viz_prev ~= nil then
|
|
|
|
mp.set_property_native("tone-mapping-visualize", tm_viz_prev)
|
|
|
|
tm_viz_prev = nil
|
|
|
|
end
|
2017-09-23 15:54:40 +00:00
|
|
|
clear_screen()
|
|
|
|
remove_page_bindings()
|
|
|
|
if recorder then
|
2019-12-24 15:02:24 +00:00
|
|
|
mp.unobserve_property(recorder)
|
2017-09-23 15:54:40 +00:00
|
|
|
recorder = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
-- No stats are being displayed yet
|
|
|
|
else
|
|
|
|
if not oneshot and (o.plot_vsync_jitter or o.plot_vsync_ratio) then
|
|
|
|
recorder = record_data(o.skip_frames)
|
2019-12-24 15:02:24 +00:00
|
|
|
-- Rely on the fact that "vsync-ratio" is updated at the same time.
|
|
|
|
-- Using "none" to get a sample any time, even if it does not change.
|
|
|
|
-- Will stop working if "vsync-jitter" property change notification
|
|
|
|
-- changes, but it's fine for an internal script.
|
|
|
|
mp.observe_property("vsync-jitter", "none", recorder)
|
2020-04-09 13:03:17 +00:00
|
|
|
end
|
2023-08-30 13:09:51 +00:00
|
|
|
if not oneshot and o.plot_tonemapping_lut then
|
|
|
|
tm_viz_prev = mp.get_property_native("tone-mapping-visualize")
|
|
|
|
mp.set_property_native("tone-mapping-visualize", true)
|
|
|
|
end
|
2020-04-09 13:03:17 +00:00
|
|
|
if not oneshot then
|
|
|
|
cache_ahead_buf = {0, pos = 1, len = 50, max = 0}
|
|
|
|
cache_speed_buf = {0, pos = 1, len = 50, max = 0}
|
2019-06-22 22:30:56 +00:00
|
|
|
cache_recorder_timer:resume()
|
2017-09-23 15:54:40 +00:00
|
|
|
end
|
|
|
|
display_timer:kill()
|
|
|
|
display_timer.oneshot = oneshot
|
|
|
|
display_timer.timeout = oneshot and o.duration or o.redraw_delay
|
|
|
|
add_page_bindings()
|
|
|
|
print_page(curr_page)
|
|
|
|
display_timer:resume()
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
stats: support for multiple "pages" of stats
Please note that the latest version of this script needs a very recent
version of mpv (from yesterday, to be precise, see the readme).
For older versions, please go to "releases".
HOW IT WORKS:
While the stats are visible (i.e. text is printed to the OSD) a
subsequent click on a numeric key (1, 2, ...) will display the
corresponding "page".
This works no matter if the stats are toggled or just shown as a single
invocation. In case of a single invocation, the newly displayed page
will be shown for the full duration again.
The selected page will be remembered (not persistantly though).
So far, only 3 pages are available.
1: the default page, stats as they used to be
2: extensive VO performance stats (to be redesigned/changed soon)
3: dummy
In the future, many more pages are possible.
Implementation is likely to change again (functionality will stay
the same). A new timer had to be introduced to remove the forced
keybindings in the oneshot case. The toggle case can remove them without
a timer. Ensuring that each mode won't remove timers of the other mode
didn't really turn out neat.
Therefore, I intend to change this again, maybe by merging the
oneshot case into the toggle case.
2017-07-02 01:00:49 +00:00
|
|
|
|
2017-09-23 15:54:40 +00:00
|
|
|
-- Create the timer used for redrawing (toggling) or clearing the screen (oneshot)
|
|
|
|
-- The duration here is not important and always set in process_key_binding()
|
|
|
|
display_timer = mp.add_periodic_timer(o.duration,
|
2017-07-02 11:53:32 +00:00
|
|
|
function()
|
2017-09-23 15:54:40 +00:00
|
|
|
if display_timer.oneshot then
|
|
|
|
display_timer:kill() ; clear_screen() ; remove_page_bindings()
|
2024-03-21 19:09:22 +00:00
|
|
|
-- Close the console only if it was opened for searching bindings.
|
|
|
|
if searched_text then
|
|
|
|
input.terminate()
|
|
|
|
end
|
2017-09-23 15:54:40 +00:00
|
|
|
else
|
|
|
|
print_page(curr_page)
|
2017-07-02 11:53:32 +00:00
|
|
|
end
|
|
|
|
end)
|
2017-09-23 15:54:40 +00:00
|
|
|
display_timer:kill()
|
2016-07-12 17:19:34 +00:00
|
|
|
|
2016-07-08 08:53:02 +00:00
|
|
|
-- Single invocation key binding
|
2021-07-21 06:13:23 +00:00
|
|
|
mp.add_key_binding(nil, "display-stats", function() process_key_binding(true) end,
|
2017-09-23 15:54:40 +00:00
|
|
|
{repeatable=true})
|
|
|
|
|
|
|
|
-- Toggling key binding
|
2021-07-21 06:13:23 +00:00
|
|
|
mp.add_key_binding(nil, "display-stats-toggle", function() process_key_binding(false) end,
|
2017-09-23 15:54:40 +00:00
|
|
|
{repeatable=false})
|
2017-07-02 11:23:23 +00:00
|
|
|
|
|
|
|
for k, _ in pairs(pages) do
|
2024-10-01 19:47:48 +00:00
|
|
|
-- Single invocation key bindings for specific pages, e.g.:
|
|
|
|
-- "e script-binding stats/display-page-2"
|
|
|
|
mp.add_key_binding(nil, "display-page-" .. k, function()
|
|
|
|
curr_page = k
|
|
|
|
process_key_binding(true)
|
|
|
|
end, {repeatable=true})
|
|
|
|
|
|
|
|
-- Key bindings to toggle a specific page, e.g.:
|
|
|
|
-- "h script-binding stats/display-page-4-toggle".
|
|
|
|
mp.add_key_binding(nil, "display-page-" .. k .. "-toggle", function()
|
|
|
|
curr_page = k
|
|
|
|
process_key_binding(false)
|
|
|
|
end, {repeatable=true})
|
2017-07-02 11:23:23 +00:00
|
|
|
end
|
2016-07-08 08:53:02 +00:00
|
|
|
|
2017-07-02 11:23:23 +00:00
|
|
|
-- Reprint stats immediately when VO was reconfigured, only when toggled
|
2016-07-08 08:53:02 +00:00
|
|
|
mp.register_event("video-reconfig",
|
2017-09-23 15:54:40 +00:00
|
|
|
function()
|
|
|
|
if display_timer:is_enabled() then
|
|
|
|
print_page(curr_page)
|
|
|
|
end
|
|
|
|
end)
|
stats.lua: page 4 (keys): support help-like terminal printout
While --input-test is useful, and so is page 4 of stats, until now
there was no way to simply print the list in a help-like fashion.
This commit adds such printout, invoked by the script opt
stats-bindlist=yes, which uses the existing page 4 code. This prints
the list on startup and quits immediately - like any help page.
It's awkward to invoke compared to other help pages, and it does
require the stats page to be enabled (it is by default), however
it is a script-generated output, and currently there's no other
method to print a help page generated by a script.
The printout itself is performed using lua's io.write. While reliable,
it's not the standard way for mpv to print to the terminal.
Other possible printout methods are mp.msg.info - which also prints
"[stats]" prefix on each line (ugly), or forcing term-osd and setting
an osd-message which mpv will then print at the terminal - however
that's printed to stderr, and could also be subject to timing concerns
since we quit right afterwards.
In the future we can consider changing/integrating the invocation so
that mpv itself could print a help page generated by a script, thus
solving both the awkward invocation and printout-method issues.
2021-07-16 09:06:20 +00:00
|
|
|
|
|
|
|
if o.bindlist ~= "no" then
|
2024-10-12 01:49:51 +00:00
|
|
|
-- This is a special mode to print key bindings to the terminal,
|
|
|
|
-- Adjust the print format and level to make it print only the key bindings.
|
|
|
|
mp.set_property("msg-level", "all=no,statusline=status")
|
|
|
|
mp.set_property("term-osd", "force")
|
|
|
|
mp.set_property_bool("msg-module", false)
|
|
|
|
mp.set_property_bool("msg-time", false)
|
|
|
|
-- wait for all other scripts to finish init
|
|
|
|
mp.add_timeout(0, function()
|
|
|
|
if o.bindlist:sub(1, 1) == "-" then
|
|
|
|
o.no_ass_b0 = ""
|
|
|
|
o.no_ass_b1 = ""
|
|
|
|
end
|
stats.lua: page 4 (keys): support help-like terminal printout
While --input-test is useful, and so is page 4 of stats, until now
there was no way to simply print the list in a help-like fashion.
This commit adds such printout, invoked by the script opt
stats-bindlist=yes, which uses the existing page 4 code. This prints
the list on startup and quits immediately - like any help page.
It's awkward to invoke compared to other help pages, and it does
require the stats page to be enabled (it is by default), however
it is a script-generated output, and currently there's no other
method to print a help page generated by a script.
The printout itself is performed using lua's io.write. While reliable,
it's not the standard way for mpv to print to the terminal.
Other possible printout methods are mp.msg.info - which also prints
"[stats]" prefix on each line (ugly), or forcing term-osd and setting
an osd-message which mpv will then print at the terminal - however
that's printed to stderr, and could also be subject to timing concerns
since we quit right afterwards.
In the future we can consider changing/integrating the invocation so
that mpv itself could print a help page generated by a script, thus
solving both the awkward invocation and printout-method issues.
2021-07-16 09:06:20 +00:00
|
|
|
o.ass_formatting = false
|
|
|
|
o.no_ass_indent = " "
|
2024-10-12 01:49:51 +00:00
|
|
|
mp.osd_message(keybinding_info(false, true))
|
|
|
|
-- wait for next tick to print status line and flush it without clearing
|
|
|
|
mp.add_timeout(0, function()
|
|
|
|
mp.command("flush-status-line no")
|
|
|
|
mp.command("quit")
|
|
|
|
end)
|
stats.lua: page 4 (keys): support help-like terminal printout
While --input-test is useful, and so is page 4 of stats, until now
there was no way to simply print the list in a help-like fashion.
This commit adds such printout, invoked by the script opt
stats-bindlist=yes, which uses the existing page 4 code. This prints
the list on startup and quits immediately - like any help page.
It's awkward to invoke compared to other help pages, and it does
require the stats page to be enabled (it is by default), however
it is a script-generated output, and currently there's no other
method to print a help page generated by a script.
The printout itself is performed using lua's io.write. While reliable,
it's not the standard way for mpv to print to the terminal.
Other possible printout methods are mp.msg.info - which also prints
"[stats]" prefix on each line (ugly), or forcing term-osd and setting
an osd-message which mpv will then print at the terminal - however
that's printed to stderr, and could also be subject to timing concerns
since we quit right afterwards.
In the future we can consider changing/integrating the invocation so
that mpv itself could print a help page generated by a script, thus
solving both the awkward invocation and printout-method issues.
2021-07-16 09:06:20 +00:00
|
|
|
end)
|
|
|
|
end
|
2024-04-15 04:43:17 +00:00
|
|
|
|
2024-05-16 16:30:52 +00:00
|
|
|
mp.observe_property('osd-height', 'native', handle_osd_height_update)
|
|
|
|
mp.observe_property('osd-scale-by-window', 'native', handle_osd_scale_by_window_update)
|