History

skip-logo.lua: fix skipping in the first two frames

mpv typically decodes and filters at least 2 frames before starting playback. This happens during seeks, as well as when playback starts from the beginning of the file. skip-logo.lua receives notifications for all filtered frames, even during seeking. It should interrupt during seeking, so as a crude heuristic, it ignored all frames while the player was seeking. This does not mean all these frames are skipped due to seeking (thus it's a "crude hueristic"). In particular, it means that the first 2 frames of a video cannot be skipped, since they're filtered within the playback restart phase (equivalent to "seeking"). Fix this by making the heuristic slightly less crude. Since we observe the property as "none", the property is not actually read until we do it explicitly. By not reading it during seeking, we can let the frames internally queue up (vf_fingerprint discards them in a ringbuffer-like fashion if they're too many). Then, if seeking ends, we get the current playback timestamp, and check queued up frames that are at or after that timestamp. (In some ways, this duplicates what the player's seeking logic does.) A disadvantage is that this is racy. While playback-time is guaranteed to be set when seeking changes from false to true, playback could already have progressed to the next frame (or more) before the script gets time to react. In theory, we could add a seek restart hook or so, but I don't want to. A property that returns the last playback restart time would also do it, but feels to special. Not an important problem in practice anyway.

wm4 committed 7 years ago

Commit 68bbc551 parent 0a30a4a1 file · +36 −15Browse files

Unified · Split

TOOLS/lua/skip-logo.lua+36−15
@@ -79,6 +79,8 @@ local meta_property = string.format("vf-metadata/%s", label) · 78 unmodified lines
79local config = {}79local config = {}
80local cases = {}80local cases = {}
81local cur_bmp81local cur_bmp
82local seeking = false
83local playback_start_pts = nil
82 84
83-- Convert a hex string to an array. Convert each byte to a [0,1] float by85-- Convert a hex string to an array. Convert each byte to a [0,1] float by
84-- interpreting it as normalized uint8_t.86-- interpreting it as normalized uint8_t.
@@ -194,24 +196,12 @@ local function check_fingerprint(hex, pts) · 109 unmodified lines
194 return false196 return false
195end197end
196 198
197mp.observe_property(meta_property, "none", function()199local function read_frames()
198 local result = mp.get_property_native(meta_property)200 local result = mp.get_property_native(meta_property)
199 if result == nil then201 if result == nil then
200 return202 return
201 end203 end
202 204
203 -- Disable matching while seeking. This is not always ideal. For example,
204 -- the filter chain may filter frames ahead of where it will resume
205 -- playback (if something prefetches frames). On the other hand, the
206 -- skipping logic shouldn't activate when the user is trying to seek past
207 -- the skip frame anyway. You could be more fancy and here, and store all
208 -- seen frames, then apply the skipping when it's actually displayed (by
209 -- observing the playback time). But for now, the naive and not-always-
210 -- correct way seems to suffice.
211 if mp.get_property_bool("seeking", false) then
212 return
213 end
214
215 -- Try to get all entries. Out of laziness, assume that there are at most205 -- Try to get all entries. Out of laziness, assume that there are at most
216 -- 100 entries. (In fact, vf_fingerprint limits it to 10.)206 -- 100 entries. (In fact, vf_fingerprint limits it to 10.)
217 for i = 0, 99 do207 for i = 0, 99 do
@@ -223,10 +213,41 @@ mp.observe_property(meta_property, "none", function() · 5 unmodified lines
223 break213 break
224 end214 end
225 215
226 if check_fingerprint(hex, pts) then216 local skip = false -- blame Lua for not having "continue" or "goto", not me
227 break217
218 -- If seeking just stopped, there will be frames before the seek target,
219 -- ignore them by checking the timestamps.
220 if playback_start_pts ~= nil then
221 if pts >= playback_start_pts then
222 playback_start_pts = nil -- just for robustness
223 else
224 skip = true
225 end
226 end
227
228 if not skip then
229 if check_fingerprint(hex, pts) then
230 break
231 end
228 end232 end
229 end233 end
234end
235
236mp.observe_property(meta_property, "none", function()
237 -- Ignore frames that are decoded/filtered during seeking.
238 if seeking then
239 return
240 end
241
242 read_frames()
243end)
244
245mp.observe_property("seeking", "bool", function(name, val)
246 seeking = val
247 if seeking == false then
248 playback_start_pts = mp.get_property_number("playback-time")
249 read_frames()
250 end
230end)251end)
231 252
232local filters = mp.get_property_native("option-info/vf/choices", {})253local filters = mp.get_property_native("option-info/vf/choices", {})

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t