Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpecte/confusing output when subclip() tries to clip beyond end of file #2116

Open
abingham opened this issue Feb 14, 2024 · 3 comments
Open
Labels
bug Issues that report (apparent) bugs. video Related to VideoClip and related classes, or handling of video in general.

Comments

@abingham
Copy link

abingham commented Feb 14, 2024

This little program uses subclip() to take more than the available data in an input file, and then write the result to file:

from moviepy.video.io.VideoFileClip import VideoFileClip

clip = VideoFileClip("movie.mov")
clip = clip.subclip(0, clip.duration + 1)
clip.write_videofile("movie2.mp4")

In the file writing phase I end up with an exception:

<elided>
File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/audio/io/readers.py", line 170, in get_frame
    raise IOError("Error in file %s, "%(self.filename)+
OSError: Error in file output/good-name-movie-0/good-name.mov, Accessing time t=3.43-3.48 seconds, with clip duration=3 seconds,

Expected Behavior

The ultimate problem with the program is that I'm trying to subclip more data than is available. So I would expect for subclip() to complain when I ask for the impossible.

If it's actually the case that out-of-bounds subclip() calls are desirable, then I'd expect a more informative exception when the file writing eventually fails. That is, I'd like an error reporting the actual mistake I made (subclipping too much data) rather than some implementation detail (the OSError above).

Actual Behavior

As listed above, I get a relatively obscure and not terribly helpful exception when I do this. Here's the stack trace (trimmed to my call into moviepy code):

  File "/Users/austin/repos/sixty-north/demonstrable/deca/source/deca/ext/build_steps/timeline/step.py", line 164, in _build
    composite.write_videofile(
  File "<decorator-gen-73>", line 2, in write_videofile
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/decorators.py", line 54, in requires_duration
    return f(clip, *a, **k)
           ^^^^^^^^^^^^^^^^
  File "<decorator-gen-72>", line 2, in write_videofile
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/decorators.py", line 135, in use_clip_fps_by_default
    return f(clip, *new_a, **new_kw)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<decorator-gen-71>", line 2, in write_videofile
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/decorators.py", line 22, in convert_masks_to_RGB
    return f(clip, *a, **k)
           ^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/video/VideoClip.py", line 293, in write_videofile
    self.audio.write_audiofile(audiofile, audio_fps,
  File "<decorator-gen-63>", line 2, in write_audiofile
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/decorators.py", line 54, in requires_duration
    return f(clip, *a, **k)
           ^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/audio/AudioClip.py", line 206, in write_audiofile
    return ffmpeg_audiowrite(self, filename, fps, nbytes, buffersize,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<decorator-gen-27>", line 2, in ffmpeg_audiowrite
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/decorators.py", line 54, in requires_duration
    return f(clip, *a, **k)
           ^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/audio/io/ffmpeg_audiowriter.py", line 166, in ffmpeg_audiowrite
    for chunk in clip.iter_chunks(chunksize=buffersize,
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/audio/AudioClip.py", line 85, in iter_chunks
    yield self.to_soundarray(tt, nbytes=nbytes, quantize=quantize,
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "<decorator-gen-62>", line 2, in to_soundarray
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/decorators.py", line 54, in requires_duration
    return f(clip, *a, **k)
           ^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/audio/AudioClip.py", line 127, in to_soundarray
    snd_array = self.get_frame(tt)
                ^^^^^^^^^^^^^^^^^^
  File "<decorator-gen-29>", line 2, in get_frame
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/decorators.py", line 89, in wrapper
    return f(*new_a, **new_kw)
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/Clip.py", line 93, in get_frame
    return self.make_frame(t)
           ^^^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/audio/AudioClip.py", line 296, in make_frame
    sounds = [c.get_frame(t - c.start)*np.array([part]).T
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/audio/AudioClip.py", line 296, in <listcomp>
    sounds = [c.get_frame(t - c.start)*np.array([part]).T
              ^^^^^^^^^^^^^^^^^^^^^^^^
  File "<decorator-gen-29>", line 2, in get_frame
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/decorators.py", line 89, in wrapper
    return f(*new_a, **new_kw)
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/Clip.py", line 93, in get_frame
    return self.make_frame(t)
           ^^^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/Clip.py", line 136, in <lambda>
    newclip = self.set_make_frame(lambda t: fun(self.get_frame, t))
                                            ^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/Clip.py", line 187, in <lambda>
    return self.fl(lambda gf, t: gf(t_func(t)), apply_to,
                                 ^^^^^^^^^^^^^
  File "<decorator-gen-29>", line 2, in get_frame
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/decorators.py", line 89, in wrapper
    return f(*new_a, **new_kw)
           ^^^^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/Clip.py", line 93, in get_frame
    return self.make_frame(t)
           ^^^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/audio/io/AudioFileClip.py", line 77, in <lambda>
    self.make_frame = lambda t: self.reader.get_frame(t)
                                ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/austin/.virtualenvs/deca-dev/lib/python3.11/site-packages/moviepy/audio/io/readers.py", line 170, in get_frame
    raise IOError("Error in file %s, "%(self.filename)+
OSError: Error in file output/good-name-movie-0/good-name.mov, Accessing time t=3.43-3.48 seconds, with clip duration=3 seconds, 

Steps to Reproduce the Problem

Run the little program at the top, mutatis mutandis the filenames.

Specifications

  • Python Version: 3.11.0
  • MoviePy Version: 1.0.3
  • Platform Name: macOS
  • Platform Version: Ventura 13.4.1
@abingham abingham added the bug Issues that report (apparent) bugs. label Feb 14, 2024
@bzczb
Copy link

bzczb commented Feb 14, 2024

Idea to fix problems like this:

ffmpeg_reader should be very forgiving for out of bounds reads.

  • for video, reads before the start get the first frame, reads after the end get the last frame
  • for audio, out-of-bounds reads get 0-samples

The enforcement of duration should be in subclip(). subclip() could take a parameter elasticity with the following values:

  • elasticity=='infinite':
    ignores whether it's in bounds of the original duration.
    result: new_clip.duration == end_time - start_time

  • elasticity=='strict':
    assert original_clip.duration >= end_time - start_time, plus a small epsilon to take floating-point error into account.
    result: new_clip.duration == end_time - start_time

  • elasticity=='truncate':
    truncate the ends so it fits in the old clip.
    result: new_clip.duration <= end_time - start_time

  • elasticity=='truncatewarn':
    same as truncate, but does a warn() if it truncates more than an epsilon.

The default should be truncatewarn

@abingham
Copy link
Author

abingham commented Feb 14, 2024

Something along these lines would work well for my purposes.

Just one minor issue: in the truncate case, the new_clip.duration == min(original_clip.duration, end_time - start_time) comparison doesn't seem correct. If my [start_time, end_time] range overlaps the end of the data, then I'd expect new_clip.duration to be original_clip.duration - start_time which may be shorter than either of the arguments to min().

@bzczb
Copy link

bzczb commented Feb 14, 2024

Something along these lines would work well for my purposes.

Just one minor issue: in the truncate case, the new_clip.duration == min(original_clip.duration, end_time - start_time) comparison doesn't seem correct. If my [start_time, end_time] range overlaps the end of the data, then I'd expect new_clip.duration to be original_clip.duration - start_time which may be shorter than either of the arguments to min().

Thanks, my formulation was wrong. corrected

@keikoro keikoro added the video Related to VideoClip and related classes, or handling of video in general. label Feb 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issues that report (apparent) bugs. video Related to VideoClip and related classes, or handling of video in general.
Projects
None yet
Development

No branches or pull requests

3 participants