|
1 | 1 | from __future__ import annotations
|
2 | 2 |
|
3 | 3 | from fractions import Fraction
|
| 4 | +from math import ceil |
4 | 5 | from typing import TYPE_CHECKING, NamedTuple
|
5 | 6 |
|
6 | 7 | import numpy as np
|
@@ -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