mirror of https://github.com/mpv-player/mpv
osc: use property notifications and a timer instead of "tick" events
Traditionally, the OSC used mpv's "tick" event, which was approximately sent once per video frame. It didn't try to track any other state, and just updated everything. This is sort of a problem in many corner cases and non-corner cases. For example, it would eat CPU in the paused state (probably to some degree also the mpv core's fault), or would waste power or even throw errors ("event queue overflows") on high FPS video. Change this to not using the tick event. Instead, react to a number of property change events. Rate-limit actual redrawing with a timer; the next update cannot happen sooner than the hardcoded 30ms OSC frame duration. This has also the effect that multiple successive updates are (mostly) coalesced. This means the OSC won't eat your CPU when the player is fucking paused. (It'll still update if e.g. the cache is growing, though.) There is some potential for bugs whenever it uses properties that are not explicitly observed. (In theory we could easily change this to a reactive concept to avoid such things, but whatever.)
This commit is contained in:
parent
ce9d2c9f8e
commit
48f906249e
|
@ -126,8 +126,9 @@ local state = {
|
||||||
message_text,
|
message_text,
|
||||||
message_timeout,
|
message_timeout,
|
||||||
fullscreen = false,
|
fullscreen = false,
|
||||||
timer = nil,
|
tick_timer = nil,
|
||||||
cache_idle = false,
|
tick_last_time = 0, -- when the last tick() was run
|
||||||
|
cache_state = nil,
|
||||||
idle = false,
|
idle = false,
|
||||||
enabled = true,
|
enabled = true,
|
||||||
input_enabled = true,
|
input_enabled = true,
|
||||||
|
@ -139,7 +140,7 @@ local state = {
|
||||||
}
|
}
|
||||||
|
|
||||||
local window_control_box_width = 80
|
local window_control_box_width = 80
|
||||||
|
local tick_delay = 0.03
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Helperfunctions
|
-- Helperfunctions
|
||||||
|
@ -1900,7 +1901,7 @@ function osc_init()
|
||||||
if user_opts.seekrangestyle == "none" then
|
if user_opts.seekrangestyle == "none" then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
local cache_state = mp.get_property_native("demuxer-cache-state", nil)
|
local cache_state = state.cache_state
|
||||||
if not cache_state then
|
if not cache_state then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
@ -1909,14 +1910,17 @@ function osc_init()
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
local ranges = cache_state["seekable-ranges"]
|
local ranges = cache_state["seekable-ranges"]
|
||||||
for _, range in pairs(ranges) do
|
|
||||||
range["start"] = 100 * range["start"] / duration
|
|
||||||
range["end"] = 100 * range["end"] / duration
|
|
||||||
end
|
|
||||||
if #ranges == 0 then
|
if #ranges == 0 then
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
return ranges
|
local nranges = {}
|
||||||
|
for _, range in pairs(ranges) do
|
||||||
|
nranges[#nranges + 1] = {
|
||||||
|
["start"] = 100 * range["start"] / duration,
|
||||||
|
["end"] = 100 * range["end"] / duration,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return nranges
|
||||||
end
|
end
|
||||||
ne.eventresponder["mouse_move"] = --keyframe seeking when mouse is dragged
|
ne.eventresponder["mouse_move"] = --keyframe seeking when mouse is dragged
|
||||||
function (element)
|
function (element)
|
||||||
|
@ -1980,8 +1984,8 @@ function osc_init()
|
||||||
ne = new_element("cache", "button")
|
ne = new_element("cache", "button")
|
||||||
|
|
||||||
ne.content = function ()
|
ne.content = function ()
|
||||||
local cache_state = mp.get_property_native("demuxer-cache-state", {})
|
local cache_state = state.cache_state
|
||||||
if not (cache_state["seekable-ranges"] and
|
if not (cache_state and cache_state["seekable-ranges"] and
|
||||||
#cache_state["seekable-ranges"] > 0) then
|
#cache_state["seekable-ranges"] > 0) then
|
||||||
-- probably not a network stream
|
-- probably not a network stream
|
||||||
return ""
|
return ""
|
||||||
|
@ -2119,7 +2123,7 @@ function hide_osc()
|
||||||
elseif (user_opts.fadeduration > 0) then
|
elseif (user_opts.fadeduration > 0) then
|
||||||
if not(state.osc_visible == false) then
|
if not(state.osc_visible == false) then
|
||||||
state.anitype = "out"
|
state.anitype = "out"
|
||||||
control_timer()
|
request_tick()
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
osc_visible(false)
|
osc_visible(false)
|
||||||
|
@ -2128,61 +2132,39 @@ end
|
||||||
|
|
||||||
function osc_visible(visible)
|
function osc_visible(visible)
|
||||||
state.osc_visible = visible
|
state.osc_visible = visible
|
||||||
control_timer()
|
request_tick()
|
||||||
update_margins()
|
update_margins()
|
||||||
end
|
end
|
||||||
|
|
||||||
function pause_state(name, enabled)
|
function pause_state(name, enabled)
|
||||||
state.paused = enabled
|
state.paused = enabled
|
||||||
control_timer()
|
request_tick()
|
||||||
end
|
end
|
||||||
|
|
||||||
function cache_state(name, idle)
|
function cache_state(name, st)
|
||||||
state.cache_idle = idle
|
state.cache_state = st
|
||||||
control_timer()
|
request_tick()
|
||||||
end
|
end
|
||||||
|
|
||||||
function control_timer()
|
-- Request that tick() is called (which typically re-renders the OSC).
|
||||||
if (state.paused) and (state.osc_visible) and
|
-- The tick is then either executed immediately, or rate-limited if it was
|
||||||
( not(state.cache_idle) or not (state.anitype == nil) ) then
|
-- called a small time ago.
|
||||||
|
function request_tick()
|
||||||
|
if state.tick_timer == nil then
|
||||||
|
state.tick_timer = mp.add_timeout(0, tick)
|
||||||
|
end
|
||||||
|
|
||||||
timer_start()
|
if not state.tick_timer:is_enabled() then
|
||||||
else
|
local now = mp.get_time()
|
||||||
timer_stop()
|
local timeout = tick_delay - (now - state.tick_last_time)
|
||||||
|
if timeout < 0 then
|
||||||
|
timeout = 0
|
||||||
|
end
|
||||||
|
state.tick_timer.timeout = timeout
|
||||||
|
state.tick_timer:resume()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function timer_start()
|
|
||||||
if not (state.timer_active) then
|
|
||||||
msg.trace("timer start")
|
|
||||||
|
|
||||||
if (state.timer == nil) then
|
|
||||||
-- create new timer
|
|
||||||
state.timer = mp.add_periodic_timer(0.03, tick)
|
|
||||||
else
|
|
||||||
-- resume existing one
|
|
||||||
state.timer:resume()
|
|
||||||
end
|
|
||||||
|
|
||||||
state.timer_active = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function timer_stop()
|
|
||||||
if (state.timer_active) then
|
|
||||||
msg.trace("timer stop")
|
|
||||||
|
|
||||||
if not (state.timer == nil) then
|
|
||||||
-- kill timer
|
|
||||||
state.timer:kill()
|
|
||||||
end
|
|
||||||
|
|
||||||
state.timer_active = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function mouse_leave()
|
function mouse_leave()
|
||||||
if user_opts.hidetimeout >= 0 then
|
if user_opts.hidetimeout >= 0 then
|
||||||
hide_osc()
|
hide_osc()
|
||||||
|
@ -2415,7 +2397,7 @@ function process_event(source, what)
|
||||||
if element_has_action(elements[n], action) then
|
if element_has_action(elements[n], action) then
|
||||||
elements[n].eventresponder[action](elements[n])
|
elements[n].eventresponder[action](elements[n])
|
||||||
end
|
end
|
||||||
tick()
|
request_tick()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2470,6 +2452,12 @@ function tick()
|
||||||
-- Flush OSD
|
-- Flush OSD
|
||||||
mp.set_osd_ass(osc_param.playresy, osc_param.playresy, "")
|
mp.set_osd_ass(osc_param.playresy, osc_param.playresy, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
state.tick_last_time = mp.get_time()
|
||||||
|
|
||||||
|
if state.anitype ~= nil then
|
||||||
|
request_tick()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function do_enable_keybindings()
|
function do_enable_keybindings()
|
||||||
|
@ -2539,17 +2527,16 @@ mp.observe_property("window-maximized", "bool",
|
||||||
mp.observe_property("idle-active", "bool",
|
mp.observe_property("idle-active", "bool",
|
||||||
function(name, val)
|
function(name, val)
|
||||||
state.idle = val
|
state.idle = val
|
||||||
tick()
|
request_tick()
|
||||||
end
|
end
|
||||||
)
|
)
|
||||||
mp.observe_property("pause", "bool", pause_state)
|
mp.observe_property("pause", "bool", pause_state)
|
||||||
mp.observe_property("cache-idle", "bool", cache_state)
|
mp.observe_property("demuxer-cache-state", "native", cache_state)
|
||||||
mp.observe_property("vo-configured", "bool", function(name, val)
|
mp.observe_property("vo-configured", "bool", function(name, val)
|
||||||
if val then
|
request_tick()
|
||||||
mp.register_event("tick", tick)
|
end)
|
||||||
else
|
mp.observe_property("playback-time", "number", function(name, val)
|
||||||
mp.unregister_event(tick)
|
request_tick()
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- mouse show/hide bindings
|
-- mouse show/hide bindings
|
||||||
|
|
Loading…
Reference in New Issue