13
13
from auto_editor .timeline import ASpace , TlAudio , TlVideo , VSpace , v1 , v3
14
14
from auto_editor .utils .func import mut_margin
15
15
from auto_editor .utils .types import Args , CoerceError , time
16
+ from math import ceil
16
17
17
18
if TYPE_CHECKING :
18
19
from numpy .typing import NDArray
@@ -231,21 +232,35 @@ def echunk(
231
232
chunks .append ((src , start , arr_length , speed_map [arr [j ]]))
232
233
return chunks
233
234
235
+ # Assert timeline is monotonic because non-monotonic timelines are incorrect
236
+ # here and causes back-seeking (performance issue) in video rendering.
237
+
238
+ # We don't properly check monotonicity for multiple sources, so skip those.
239
+
240
+ check_monotonic = len (sources ) == 1
241
+ last_i = 0
242
+
234
243
clips : list [Clip ] = []
235
- i = 0
236
244
start = 0
245
+
237
246
for chunk in echunk (speed_index , src_index ):
238
247
if chunk [3 ] != 99999 :
239
- dur = round ((chunk [2 ] - chunk [1 ]) / chunk [3 ])
248
+ dur = int ((chunk [2 ] - chunk [1 ]) / chunk [3 ])
240
249
if dur == 0 :
241
250
continue
242
251
243
- offset = int (chunk [1 ] / chunk [3 ])
252
+ offset = ceil (chunk [1 ] / chunk [3 ])
253
+
254
+ if check_monotonic :
255
+ max_end = start + dur - 1
256
+ this_i = round ((offset + max_end - start ) * chunk [3 ])
257
+ if this_i < last_i :
258
+ raise ValueError ("not monotonic" , sources , this_i , last_i )
259
+ last_i = this_i
260
+
261
+ clips .append (Clip (start , dur , offset , chunk [3 ], chunk [0 ]))
244
262
245
- if not (clips and clips [- 1 ].start == round (start )):
246
- clips .append (Clip (start , dur , offset , chunk [3 ], chunk [0 ]))
247
263
start += dur
248
- i += 1
249
264
250
265
vtl : VSpace = []
251
266
atl : ASpace = []
@@ -281,4 +296,22 @@ def chunkify(arr: NDArray, smap: dict[int, float]) -> Chunks:
281
296
else :
282
297
v1_compatiable = None
283
298
284
- return v3 (inp , tb , sr , res , args .background , vtl , atl , v1_compatiable )
299
+ tl = v3 (inp , tb , sr , res , args .background , vtl , atl , v1_compatiable )
300
+
301
+ # Additional monotonic check, o(n^2) time complexity so disable by default.
302
+
303
+ # if len(sources) != 1:
304
+ # return tl
305
+
306
+ # last_i = 0
307
+ # for index in range(tl.end):
308
+ # for layer in tl.v:
309
+ # for lobj in layer:
310
+ # if index >= lobj.start and index < (lobj.start + lobj.dur):
311
+ # _i = round((lobj.offset + index - lobj.start) * lobj.speed)
312
+ # if (_i < last_i):
313
+ # print(_i, last_i)
314
+ # raise ValueError("not monotonic")
315
+ # last_i = _i
316
+
317
+ return tl
0 commit comments