diff --git a/CHANGELOG.md b/CHANGELOG.md index 65103a973..99c6debdf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `IndexError` in `vfx.freeze`, `vfx.time_mirror` and `vfx.time_symmetrize` [#1124] - Using `rotate()` with a `ColorClip` no longer crashes [#1139] - `AudioFileClip` would not generate audio identical to the original file [#1108] +- Several issues resulting from incorrect time values due to floating point errors [#1195], for example: + - Blank frames at the end of clips [#210] + - Sometimes getting `IndexError: list index out of range` when using `concatenate_videoclips` [#646] ## [v1.0.3](https://github.com/zulko/moviepy/tree/v1.0.3) (2020-05-07) diff --git a/moviepy/Clip.py b/moviepy/Clip.py index e7427281d..1036dde38 100644 --- a/moviepy/Clip.py +++ b/moviepy/Clip.py @@ -466,7 +466,11 @@ def iter_frames(self, fps=None, with_times=False, logger=None, dtype=None): for frame in myclip.iter_frames()]) """ logger = proglog.default_bar_logger(logger) - for t in logger.iter_bar(t=np.arange(0, self.duration, 1.0 / fps)): + for frame_index in logger.iter_bar(t=np.arange(0, int(self.duration * fps))): + # int is used to ensure that floating point errors are rounded + # down to the nearest integer + t = frame_index / fps + frame = self.get_frame(t) if (dtype is not None) and (frame.dtype != dtype): frame = frame.astype(dtype) diff --git a/tests/test_compositing.py b/tests/test_compositing.py index 2f0495485..0bb900c98 100644 --- a/tests/test_compositing.py +++ b/tests/test_compositing.py @@ -38,3 +38,26 @@ def test_clips_array_duration(): video = clips_array([[red, green, blue]]).set_duration(5) video.write_videofile(join(TMP_DIR, "test_clips_array.mp4")) close_all_clips(locals()) + + +def test_concatenate_self(): + clip = BitmapClip([["AAA", "BBB"], ["CCC", "DDD"]]).set_fps(1) + target = BitmapClip([["AAA", "BBB"], ["CCC", "DDD"]]).set_fps(1) + + concatenated = concatenate_videoclips([clip]) + + concatenated.write_videofile(join(TMP_DIR, "test_concatenate_self.mp4")) + assert concatenated == target + + +def test_concatenate_floating_point(): + """ + >>> print("{0:.20f}".format(1.12)) + 1.12000000000000010658 + + This test uses duration=1.12 to check that it still works when the clip duration is + represented as being bigger than it actually is. Fixed in #1195. + """ + clip = ColorClip([100, 50], color=[255, 128, 64], duration=1.12).set_fps(25.0) + concat = concatenate_videoclips([clip]) + concat.write_videofile("concat.mp4", preset="ultrafast")