Zulko / moviepy

Video editing with Python
https://zulko.github.io/moviepy/
MIT License
12.08k stars 1.51k forks source link

Seeking by frame is inexact and inconsistent #2115

Open bzczb opened 4 months ago

bzczb commented 4 months ago

Expected Behavior

clip.make_frame(0) returns frame 0, clip.make_frame(1 / clip.fps) returns frame 1, clip.make_frame(2 / clip.fps) returns frame 2, etc.

Actual Behavior

moviepy largely treats "get the frame at time x" as shorthand for "get the frame displayed at time x", so "get the frame at time 0.00001" should mean "get frame 0". But ffmpeg treats "get the frame at time x" as "skip all frames until reaching time x", so sometimes clip.make_frame(0.00001) will return frame 1, and sometimes it will return frame 0, depending on what's been cached in last_read.

Steps to Reproduce the Problem

Load a video clip which has a different image every frame. (Some clips will have the same image for multiple consecutive frames, which will make it seem like it works correctly)

Here is a clip you can use:

https://github.com/Zulko/moviepy/assets/114126196/9c03b087-5d36-4ef4-9b12-9de823678624

import moviepy.editor as edit

clip = edit.VideoFileClip(r"colorbars.mp4")

test1 = clip.make_frame(0)
test2 = clip.make_frame(0.001)
test3 = clip.make_frame(60)
test4 = clip.make_frame(0.001)
test5 = clip.make_frame(60)
test6 = clip.make_frame(0)
(test1 == test2).all()
True
(test1 == test6).all()
True
(test3 == test5).all()
False
(test2 == test4).all()
False

Specifications

Testing note

test_unusual_order_frame_pos() in test/test_ffmpeg_reader.py is insufficient to detect this bug, as the test video used runs at 1 frame per second exactly. The inconsistency between "moviepy seek" and "ffmpeg seek" does not appear if the timestamps of all frames can be represented exactly in decimal!