Skip to content

Commit e0ad4de

Browse files
committed
Converting to higher fps: fix flashing
1 parent f4aeab9 commit e0ad4de

File tree

1 file changed

+29
-4
lines changed

1 file changed

+29
-4
lines changed

src/render/video.nim

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ proc makeNewVideoFrames*(output: var OutputContainer, tl: v3, args: mainArgs):
270270
var frameIndex = -1
271271
var frame: ptr AVFrame = av_frame_clone(nullFrame)
272272
var objList: seq[VideoFrame] = @[]
273+
var lastProcessedFrame: ptr AVFrame = nil
274+
var lastFrameIndex = -1
273275

274276
return (encoderCtx, outputStream, iterator(): (ptr AVFrame, int) =
275277
# Process each frame in timeline order like Python version
@@ -279,21 +281,34 @@ proc makeNewVideoFrames*(output: var OutputContainer, tl: v3, args: mainArgs):
279281
for layer in tl.v:
280282
for obj in layer.clips:
281283
if index >= obj.start and index < (obj.start + obj.dur):
282-
let i = int(round(float(obj.offset + index - obj.start) * obj.speed))
284+
# Convert timeline position from target framerate to source framerate
285+
let timelinePos = obj.offset + index - obj.start
286+
let srcStream = cns[obj.src].video[0]
287+
let srcTb = srcStream.avg_frame_rate
288+
let sourceFramePos = int(round(float(timelinePos) * srcTb.float / tl.tb.float))
289+
let i = int(round(float(sourceFramePos) * obj.speed))
283290
objList.add VideoFrame(index: i, src: obj.src)
284291

285292
if tl.chunks.isSome:
286-
# When there can be valid gaps in the timeline.
293+
# When there can be valid gaps in the timeline and no objects for this frame.
287294
frame = av_frame_clone(nullFrame)
288-
# else, use the last frame
295+
# else, use the last frame or process objects
289296

290297
for obj in objList:
298+
# Check if we can reuse the last processed frame
299+
if obj.index == lastFrameIndex and lastProcessedFrame != nil:
300+
frame = av_frame_clone(lastProcessedFrame)
301+
continue
302+
291303
var myStream: ptr AVStream = cns[obj.src].video[0]
292304
if frameIndex > obj.index:
293305
debug(&"Seek: {frameIndex} -> 0")
294306
cns[obj.src].seek(0)
295307
avcodec_flush_buffers(decoders[obj.src])
296308

309+
# obj.index is already in source frame coordinates, no conversion needed
310+
let srcTb = myStream.avg_frame_rate
311+
297312
while frameIndex < obj.index:
298313
# Check if skipping ahead is worth it.
299314
if obj.index - frameIndex > seekCost[obj.src] and frameIndex > seekThreshold:
@@ -307,7 +322,7 @@ proc makeNewVideoFrames*(output: var OutputContainer, tl: v3, args: mainArgs):
307322
var foundFrame = false
308323
for decodedFrame in cns[obj.src].flushDecode(myStream.index.cint, decoder, frame):
309324
frame = decodedFrame
310-
frameIndex = int(round(decodedFrame.time(myStream.time_base) * tl.tb.float))
325+
frameIndex = int(round(decodedFrame.time(myStream.time_base) * srcTb.float))
311326
foundFrame = true
312327
break
313328

@@ -353,9 +368,19 @@ proc makeNewVideoFrames*(output: var OutputContainer, tl: v3, args: mainArgs):
353368
frame.pts = index.int64
354369
frame.time_base = av_inv_q(tl.tb)
355370
frame.duration = index.int64
371+
372+
# Update cache for frame reuse BEFORE yielding (which will unref the frame)
373+
if objList.len > 0:
374+
if lastProcessedFrame != nil:
375+
av_frame_free(addr lastProcessedFrame)
376+
lastProcessedFrame = av_frame_clone(frame)
377+
lastFrameIndex = objList[0].index
378+
356379
yield (frame, index)
357380

358381
if scaleGraph != nil:
359382
scaleGraph.cleanup()
360383
av_frame_free(addr nullFrame)
384+
if lastProcessedFrame != nil:
385+
av_frame_free(addr lastProcessedFrame)
361386
debug(&"Total frames saved seeking: {framesSaved}"))

0 commit comments

Comments
 (0)