Zulko / moviepy

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

AttributeError: 'NoneType' object has no attribute 'stdout' #1994

Closed tobiasBora closed 12 months ago

tobiasBora commented 1 year ago

Expected Behavior

I expect this code to work:

from moviepy.editor import *

clip1 = VideoFileClip("classe_inversee.mp4")
clip2 = ImageClip("myRobExt-3EC8291240A626FE802A5A28DB2022A8-img-1.png", duration=clip1.duration).with_position(("center", "center"))
final_clip = CompositeVideoClip([clip1, clip2])
final_clip.write_videofile("out.mp4", fps=24)

Actual Behavior

I get this error on master:

MoviePy - Building video out.mp4.
MoviePy - Writing audio in outTEMP_MPY_wvf_snd.mp3
chunk:   0%|                                                                                                                                                                    | 0/367 [00:00<?, ?it/s, now=None]Traceback (most recent call last):
  File "/home/leo/Documents/Cours/2023_-_Candidatures_postes/2023_03_-_Bordeaux/Presentation_orale/robustExternalize/test.py", line 7, in <module>
    final_clip.write_videofile("out.mp4", fps=24)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/decorators.py", line 52, in requires_duration
    return func(clip, *args, **kwargs)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/decorators.py", line 143, in use_clip_fps_by_default
    return func(clip, *new_args, **new_kwargs)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/decorators.py", line 23, in convert_masks_to_RGB
    return func(clip, *args, **kwargs)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/decorators.py", line 94, in wrapper
    return func(*new_args, **new_kwargs)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/video/VideoClip.py", line 369, in write_videofile
    self.audio.write_audiofile(
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/decorators.py", line 52, in requires_duration
    return func(clip, *args, **kwargs)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/decorators.py", line 94, in wrapper
    return func(*new_args, **new_kwargs)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 249, in write_audiofile
    return ffmpeg_audiowrite(
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/decorators.py", line 52, in requires_duration
    return func(clip, *args, **kwargs)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/audio/io/ffmpeg_audiowriter.py", line 205, in ffmpeg_audiowrite
    for chunk in clip.iter_chunks(
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 109, in iter_chunks
    yield self.to_soundarray(
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/decorators.py", line 52, in requires_duration
    return func(clip, *args, **kwargs)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 157, in to_soundarray
    snd_array = self.get_frame(tt)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/decorators.py", line 94, in wrapper
    return func(*new_args, **new_kwargs)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/Clip.py", line 82, in get_frame
    return self.make_frame(t)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 361, in make_frame
    sounds = [
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 362, in <listcomp>
    clip.get_frame(t - clip.start) * np.array([part]).T
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/decorators.py", line 94, in wrapper
    return func(*new_args, **new_kwargs)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/Clip.py", line 82, in get_frame
    return self.make_frame(t)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/audio/io/AudioFileClip.py", line 76, in <lambda>
    self.make_frame = lambda t: self.reader.get_frame(t)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/audio/io/readers.py", line 195, in get_frame
    self.buffer_around(fr_max)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/audio/io/readers.py", line 244, in buffer_around
    array = self.read_chunk(chunksize)
  File "/nix/store/vjgahh1d96mlpr4x7p2s5dcrs37d2s4a-python3-3.10.9-env/lib/python3.10/site-packages/moviepy/audio/io/readers.py", line 134, in read_chunk
    s = self.proc.stdout.read(self.nchannels * chunksize * self.nbytes)
AttributeError: 'NoneType' object has no attribute 'stdout'

Steps to Reproduce the Problem

First, get the latest version on master. If you want to get the same environment as mine and you have nix, you can just run:

$ nix develop --impure --expr 'let pkgs = (builtins.getFlake "github:NixOs/nixpkgs?rev=75a5ebf473cd60148ba9aec0d219f72e5cf52519").legacyPackages.x86_64-linux; in pkgs.mkShell { buildInputs = [ pkgs.ffmpeg (pkgs.python3.withPackages (ps: with ps; [ ipython (moviepy.overrideAttrs (old: {src = pkgs.fetchFromGitHub { owner = "Zulko"; repo = "moviepy"; rev = "b5bb086439cfd0960011a065f229eb7a7b4ebad5"; sha256 = "sha256-Y+rehn+fLR0id5jAmfOkYHj8mLMJgnMwld7wp5BxmuQ="; };})) ] )) ]; }'

to enter a shell with the expected dependencies.

Then, write in a file test.py:

from moviepy.editor import *

clip1 = VideoFileClip("classe_inversee.mp4")
clip2 = ImageClip("myRobExt-3EC8291240A626FE802A5A28DB2022A8-img-1.png", duration=clip1.duration).with_position(("center", "center"))
final_clip = CompositeVideoClip([clip1, clip2])
final_clip.write_videofile("out.mp4", fps=24)

create the 2 files using these two files (hopefully github will not alter them):

myRobExt-3EC8291240A626FE802A5A28DB2022A8-img-1

classe_inversee.mp4:

https://github.com/Zulko/moviepy/assets/2164118/c5a08e4d-1962-4af5-be61-c010fe26a2cd

and run

$ python3 test.py 

You should observe:

MoviePy - Building video out.mp4.
MoviePy - Writing audio in outTEMP_MPY_wvf_snd.mp3
chunk:   0%|                                                                                                                                                                    | 0/367 [00:00<?, ?it/s, now=None]Traceback (most recent call last):
  File "/home/leo/Documents/Cours/2023_-_Candidatures_postes/2023_03_-_Bordeaux/Presentation_orale/robustExternalize/test.py", line 7, in <module>
    final_clip.write_videofile("out.mp4", fps=24)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/decorators.py", line 52, in requires_duration
    return func(clip, *args, **kwargs)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/decorators.py", line 143, in use_clip_fps_by_default
    return func(clip, *new_args, **new_kwargs)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/decorators.py", line 23, in convert_masks_to_RGB
    return func(clip, *args, **kwargs)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/decorators.py", line 94, in wrapper
    return func(*new_args, **new_kwargs)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/video/VideoClip.py", line 369, in write_videofile
    self.audio.write_audiofile(
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/decorators.py", line 52, in requires_duration
    return func(clip, *args, **kwargs)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/decorators.py", line 94, in wrapper
    return func(*new_args, **new_kwargs)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 249, in write_audiofile
    return ffmpeg_audiowrite(
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/decorators.py", line 52, in requires_duration
    return func(clip, *args, **kwargs)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/audio/io/ffmpeg_audiowriter.py", line 205, in ffmpeg_audiowrite
    for chunk in clip.iter_chunks(
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 109, in iter_chunks
    yield self.to_soundarray(
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/decorators.py", line 52, in requires_duration
    return func(clip, *args, **kwargs)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 157, in to_soundarray
    snd_array = self.get_frame(tt)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/decorators.py", line 94, in wrapper
    return func(*new_args, **new_kwargs)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/Clip.py", line 82, in get_frame
    return self.make_frame(t)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 361, in make_frame
    sounds = [
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/audio/AudioClip.py", line 362, in <listcomp>
    clip.get_frame(t - clip.start) * np.array([part]).T
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/decorator.py", line 232, in fun
    return caller(func, *(extras + args), **kw)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/decorators.py", line 94, in wrapper
    return func(*new_args, **new_kwargs)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/Clip.py", line 82, in get_frame
    return self.make_frame(t)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/audio/io/AudioFileClip.py", line 76, in <lambda>
    self.make_frame = lambda t: self.reader.get_frame(t)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/audio/io/readers.py", line 195, in get_frame
    self.buffer_around(fr_max)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/audio/io/readers.py", line 244, in buffer_around
    array = self.read_chunk(chunksize)
  File "/nix/store/g6q4rn4zbjw4gp8gp6q3h3j3r2kaqcdn-python3-3.10.11-env/lib/python3.10/site-packages/moviepy/audio/io/readers.py", line 134, in read_chunk
    s = self.proc.stdout.read(self.nchannels * chunksize * self.nbytes)
AttributeError: 'NoneType' object has no attribute 'stdout'

Specifications

tobiasBora commented 1 year ago

So after a bit of investigation, it seems like the issue occurs only with some kinds of video (here a video exported via kdenlive), more precisely when moviepy tries to deal with the audio. The reason is that the file descriptor of self.proc (that contains a file descriptor to ffmpeg) is closed by calling this function:

https://github.com/Zulko/moviepy/blob/b5bb086439cfd0960011a065f229eb7a7b4ebad5/moviepy/audio/io/AudioFileClip.py#L79-L83

that destroys the proc to ffmpeg. Commenting these lines actually solve the problem, but this is a dirty solution since the proc will never be closed.

Now, why is it closed? A traceback gives:

  File "moviepy/Clip.py", line 639, in __del__
    self.close()
  File "moviepy/moviepy/video/io/VideoFileClip.py", line 167, in close
    self.audio.close()
  File "moviepy/audio/io/AudioFileClip.py", line 83, in close
    raise NameError("don't close me") <--- I added this line to debug

So it seems like at some point the clip is destroyed automatically by the garbage collector, that calls:

https://github.com/Zulko/moviepy/blob/b5bb086439cfd0960011a065f229eb7a7b4ebad5/moviepy/Clip.py#L637-L638

but this arrives before the make_frame defined here is called (which internally needs proc):

https://github.com/Zulko/moviepy/blob/b5bb086439cfd0960011a065f229eb7a7b4ebad5/moviepy/audio/io/AudioFileClip.py#L76

So proc does not exists when it is called later. I guess the garbage collector is not clever enough to determine that a function copy is still in use… This seems to be a well known issue however:

https://github.com/Zulko/moviepy/blob/b5bb086439cfd0960011a065f229eb7a7b4ebad5/moviepy/Clip.py#L541-L551

but I have no idea what is the clean way to solve it. Actually this line sayes that close should NOT be closed by __del__ (that makes sense now), and in the code, it is explicitely called by __del__ a few lines below. So I guess this should not be done?

@keikoro might have an idea?

tobiasBora commented 1 year ago

Ok, a quick git blame pointed to a recent commit from https://github.com/Zulko/moviepy/commit/57c279a30f7be6a20d2fe24e71b6d11039cbe0cd : @mgaitan , you confirm that it was not a good idea to call close in del (as suggested in the comment of close) and that it can therefore be undone?

tobiasBora commented 1 year ago

I just reverted the problematic modification of @mgaitan in this PR https://github.com/Zulko/moviepy/pull/1995