diff options
author | wm4 <wm4@nowhere> | 2019-12-19 12:41:01 +0100 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2019-12-19 12:41:01 +0100 |
commit | 48f906249ef359c66fc6c5bf4cc5885c8293bf8e (patch) | |
tree | f0d31632b957211f526f8d590be19ee611828bb2 /player | |
parent | ce9d2c9f8e6645c7a0e9b216d29f4d237355ce61 (diff) | |
download | mpv-48f906249ef359c66fc6c5bf4cc5885c8293bf8e.tar.bz2 mpv-48f906249ef359c66fc6c5bf4cc5885c8293bf8e.tar.xz |
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.)
Diffstat (limited to 'player')
-rw-r--r-- | player/lua/osc.lua | 107 |
1 files changed, 47 insertions, 60 deletions
diff --git a/player/lua/osc.lua b/player/lua/osc.lua index 872eead52d..93da61a759 100644 --- a/player/lua/osc.lua +++ b/player/lua/osc.lua @@ -126,8 +126,9 @@ local state = { message_text, message_timeout, fullscreen = false, - timer = nil, - cache_idle = false, + tick_timer = nil, + tick_last_time = 0, -- when the last tick() was run + cache_state = nil, idle = false, enabled = true, input_enabled = true, @@ -139,7 +140,7 @@ local state = { } local window_control_box_width = 80 - +local tick_delay = 0.03 -- -- Helperfunctions @@ -1900,7 +1901,7 @@ function osc_init() if user_opts.seekrangestyle == "none" then return nil end - local cache_state = mp.get_property_native("demuxer-cache-state", nil) + local cache_state = state.cache_state if not cache_state then return nil end @@ -1909,14 +1910,17 @@ function osc_init() return nil end 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 return nil 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 ne.eventresponder["mouse_move"] = --keyframe seeking when mouse is dragged function (element) @@ -1980,8 +1984,8 @@ function osc_init() ne = new_element("cache", "button") ne.content = function () - local cache_state = mp.get_property_native("demuxer-cache-state", {}) - if not (cache_state["seekable-ranges"] and + local cache_state = state.cache_state + if not (cache_state and cache_state["seekable-ranges"] and #cache_state["seekable-ranges"] > 0) then -- probably not a network stream return "" @@ -2119,7 +2123,7 @@ function hide_osc() elseif (user_opts.fadeduration > 0) then if not(state.osc_visible == false) then state.anitype = "out" - control_timer() + request_tick() end else osc_visible(false) @@ -2128,61 +2132,39 @@ end function osc_visible(visible) state.osc_visible = visible - control_timer() + request_tick() update_margins() end function pause_state(name, enabled) state.paused = enabled - control_timer() -end - -function cache_state(name, idle) - state.cache_idle = idle - control_timer() + request_tick() end -function control_timer() - if (state.paused) and (state.osc_visible) and - ( not(state.cache_idle) or not (state.anitype == nil) ) then - - timer_start() - else - timer_stop() - end +function cache_state(name, st) + state.cache_state = st + request_tick() 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 +-- Request that tick() is called (which typically re-renders the OSC). +-- The tick is then either executed immediately, or rate-limited if it was +-- called a small time ago. +function request_tick() + if state.tick_timer == nil then + state.tick_timer = mp.add_timeout(0, tick) 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() + if not state.tick_timer:is_enabled() then + local now = mp.get_time() + local timeout = tick_delay - (now - state.tick_last_time) + if timeout < 0 then + timeout = 0 end - - state.timer_active = false + state.tick_timer.timeout = timeout + state.tick_timer:resume() end end - - function mouse_leave() if user_opts.hidetimeout >= 0 then hide_osc() @@ -2415,7 +2397,7 @@ function process_event(source, what) if element_has_action(elements[n], action) then elements[n].eventresponder[action](elements[n]) end - tick() + request_tick() end end @@ -2470,6 +2452,12 @@ function tick() -- Flush OSD mp.set_osd_ass(osc_param.playresy, osc_param.playresy, "") end + + state.tick_last_time = mp.get_time() + + if state.anitype ~= nil then + request_tick() + end end function do_enable_keybindings() @@ -2539,17 +2527,16 @@ mp.observe_property("window-maximized", "bool", mp.observe_property("idle-active", "bool", function(name, val) state.idle = val - tick() + request_tick() end ) 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) - if val then - mp.register_event("tick", tick) - else - mp.unregister_event(tick) - end + request_tick() +end) +mp.observe_property("playback-time", "number", function(name, val) + request_tick() end) -- mouse show/hide bindings |