-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathseamlessloop.lua
More file actions
179 lines (167 loc) · 5.23 KB
/
seamlessloop.lua
File metadata and controls
179 lines (167 loc) · 5.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
-- Does not work with losslessly concatenated files (h264/hevc in mp4 at least)
-- If on hwdec use copyback for optimal results (hwdec=auto-copy)
-- TODO: Find a way to detect concatenated files and disable functionality on those files.
local options = {
autoloop_duration = 60, -- Seconds (1 min)
seamlessloop = true, -- Enable filter based seamless looping
allowsoftseamless = true, -- Even when seamless looping is disabled, try to make looping seamless with a softer approach when applicable
allowseek = true, -- Allow seeking (seeking breaks seamless looping).
ignoresubtitled = true -- Disable seamless looping for videos with softsubs (seamless looping breaks the subs).
}
mp.options = require "mp.options"
mp.options.read_options(options)
local maxaudio = 2.14748e+09
local maxframes = 32767
local duration = 0
local frames = 0
local cpuaccess = false
local subtitles = false
local looping = nil
local time = 0.0
local lasttime = 0.0
local seamlessmode = false
local seekbuffer = 0.05 -- Expected delay between executions, if you have a slow cpu you may need to increase this.
local allowreset = true
function disableseamless()
local filters = mp.get_property("vf") .. mp.get_property("af")
if string.match(filters,"seamlessloop") then
mp.command("no-osd vf remove @seamlessloop")
mp.command("no-osd af remove @seamlessloop")
end
if string.match(mp.get_property("lavfi-complex"), "loop") then
mp.set_property("lavfi-complex", "[vid1]null[vo]")
end
end
function softseamless()
if not options.allowsoftseamless then
return
end
if cpuaccess then
mp.command("no-osd vf set @seamlessloop:loop=-1")
--mp.command("no-osd af set @seamlessloop:aloop=-1")
else
mp.set_property("lavfi-complex", "[vid1]loop=-1[vo]")
--mp.command("no-osd af set @seamlessloop:aloop=-1")
end
seamlessmode = false
end
function update()
if tonumber(mp.get_property("sub")) then
subtitles = true
end
local hwdec = mp.get_property("hwdec-current")
if hwdec ~= nil then
if string.match(hwdec, "copy") or hwdec == "no" then
cpuaccess = true
else
cpuaccess = false
end
else
hwdec = mp.get_property("hwdec")
if string.match(hwdec, "copy") or hwdec == "no" then
cpuaccess = true
else
cpuaccess = false
end
end
looping = mp.get_property_native("loop-file")
duration = mp.get_property_native("duration")
if mp.get_property("container-fps") ~= nil then
frames = math.ceil(mp.get_property("container-fps") * duration) -- Not sure if this really matters
else
frames = maxframes
end
time = mp.get_property_native("time-pos")
seamlessmode = string.match(mp.get_property("vf"), '@seamlessloop.loop.-1.size')
if not seamlessmode then
seamlessmode = string.match(mp.get_property("lavfi-complex"), "loop.-1.size")
end
if time == nil then
time = 0.000
end
end
function seamlessloop(loop_property, status)
update()
looping = status
if not looping then
disableseamless()
return
elseif subtitles and options.ignoresubtitled then
return
elseif not options.seamlessloop then
return
elseif frames > maxframes then
return
elseif seamlessmode then
return
end
if looping and frames <= maxframes then
if allowreset then
allowrset = false
mp.command("no-osd seek 0 absolute")
end
if cpuaccess then
mp.command("no-osd vf set @seamlessloop:loop=-1:size=" .. frames)
mp.command("no-osd af set @seamlessloop:aloop=-1:size=" .. maxaudio)
else
mp.set_property("lavfi-complex", string.format("[vid1]loop=-1:size=%d[vo]", frames))
mp.command("no-osd af set @seamlessloop:aloop=-1:size=" .. maxaudio)
end
end
end
function autoloop()
if options.autoloop_duration == 0 then
return
end
if not duration then
mp.set_property_native("loop-file", false)
return
end
if not looping and duration <= options.autoloop_duration then
mp.set_property_native("loop-file", true)
mp.set_property_bool("file-local-options/save-position-on-quit", false)
if not options.seamlessloop then -- try to mitigate pause at end of loops, doesn't always work.
mp.command("no-osd seek 0 absolute")
softseamless()
end
elseif looping and duration > options.autoloop_duration then
mp.set_property_native("loop-file", false)
elseif looping then
if not options.seamlessloop then -- try to mitigate pause at end of loops, doesn't always work.
mp.command("no-osd seek 0 absolute")
softseamless()
end
end
end
function initialize()
disableseamless()
update()
if duration <= options.autoloop_duration then
mp.command("no-osd seek 0 absolute")
end
autoloop(looping)
-- This is how we hijack the loop function in mpv and toggle seamless looping in tandem with it.
mp.observe_property("loop-file", "native", seamlessloop)
mp.observe_property("loop", "native", seamlessloop)
end
mp.register_event("file-loaded", initialize)
mp.register_event("playback-restart", function()
update()
if math.abs(time - lasttime) < seekbuffer or not looping then
return
elseif options.seamlessloop then
if not options.allowseek then
disableseamless()
allowreset = true
seamlessloop("loop-file", looping)
elseif time < seekbuffer * 2 then
allowreset = true
seamlessloop("loop-file", looping)
else
disableseamless()
softseamless()
end
end
update()
lasttime = time
end)