Zulko / moviepy

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

MoviePy write_videofile uses too much memory, causing it to raise the BrokenPipeError on the server instance. #1892

Open tengerdata opened 1 year ago

tengerdata commented 1 year ago

I want to deploy my Flask app onto the server. I am trying to do it on DigitalOcean.

My Flask app edits and processes videos for the user; therefore, I use the library MoviePy to create it.

A minimal reproducible example would be something along the lines of:

app.py:

from moviepy.editor import *
from flask import *
app = Flask(__name__ , static_folder='static')

app.route('/')
def mainpage():
    return render_template('index.html', static_folder='static')

@app.route('/circle', methods=['GET', 'POST'])
def circle():
    if request.method == 'GET':
        return render_template('circle.html', static_folder='static')
    dct = request.get_json()
    with open(f'static/Time Codes/values.txt', 'r') as f:
        timecodesdct = literal_eval(f.read().strip())
    clip = VideoFileClip('static/video.mp4')
    clip_before = clip.subclip(timecodesdct['start_second'], timecodesdct['freeze_frame'])
    clip_after = clip.subclip(timecodesdct['freeze_frame'], timecodesdct['end_second'])
    im_freeze = clip.to_ImageClip(timecodesdct['freeze_frame'])
    circle_mvp = ImageClip("static/image.png" % dct['size']).set_position((dct['x'], dct['y']))
    final = CompositeVideoClip([im_freeze, circle_mvp])
    final_clip = concatenate_videoclips([clip_before, final.set_duration(3), clip_after], method='chain')
    final_clip.write_videofile('static/final.mp4', fps=clip.fps)
    return redirect('/preview')

if __name__ == '__main__':
    app.run(debug=True)

It works completely fine on my own PC, but once I deploy it onto DigitalOcean, I face the following error:

Traceback (most recent call last):
  File "/srv/data/web/vhosts/default/local/lib/python3.8/site-packages/moviepy/video/io/ffmpeg_writer.py", line 133, in write_frame
    self.proc.stdin.write(img_array.tobytes())
BrokenPipeError: [Errno 32] Broken pipe

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/srv/data/web/vhosts/default/local/lib/python3.8/site-packages/flask/app.py", line 2525, in wsgi_app
    response = self.full_dispatch_request()
  File "/srv/data/web/vhosts/default/local/lib/python3.8/site-packages/flask/app.py", line 1822, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/srv/data/web/vhosts/default/local/lib/python3.8/site-packages/flask/app.py", line 1820, in full_dispatch_request
    rv = self.dispatch_request()
  File "/srv/data/web/vhosts/default/local/lib/python3.8/site-packages/flask/app.py", line 1796, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
  File "./app.py", line 206, in circle
    final_clip.write_videofile('static/final.mp4', fps=clip.fps)
  File "<decorator-gen-51>", line 2, in write_videofile
  File "/srv/data/web/vhosts/default/local/lib/python3.8/site-packages/moviepy/decorators.py", line 54, in requires_duration
    return f(clip, *a, **k)
  File "<decorator-gen-50>", line 2, in write_videofile
  File "/srv/data/web/vhosts/default/local/lib/python3.8/site-packages/moviepy/decorators.py", line 137, in use_clip_fps_by_default
    return f(clip, *new_a, **new_kw)
  File "<decorator-gen-49>", line 2, in write_videofile
  File "/srv/data/web/vhosts/default/local/lib/python3.8/site-packages/moviepy/decorators.py", line 22, in convert_masks_to_RGB
    return f(clip, *a, **k)
  File "/srv/data/web/vhosts/default/local/lib/python3.8/site-packages/moviepy/video/VideoClip.py", line 319, in write_videofile
    ffmpeg_write_video(self, filename, fps, codec,
  File "/srv/data/web/vhosts/default/local/lib/python3.8/site-packages/moviepy/video/io/ffmpeg_writer.py", line 223, in ffmpeg_write_video
    writer.write_frame(frame)
  File "/srv/data/web/vhosts/default/local/lib/python3.8/site-packages/moviepy/video/io/ffmpeg_writer.py", line 177, in write_frame
    raise IOError(error)
OSError: [Errno 32] Broken pipe

MoviePy error: FFMPEG encountered the following error while writing file static/Previews/video.mp4:

 b''

I can see that the error is raised after around 15% of the video is written. I understand that the error is cause by the following line:

final_clip.write_videofile('static/final.mp4', fps=clip.fps)

This line uses a lot of memory; thus, causing the application to be restarted automatically after the error is raised. I know that it takes up a lot of memory to write this video, but there must be a away to reduce memory usage. I tried using gc.collect() before this line, but it doesn't help. It doesn't matter if the duration of rendering this video increases, I am just trying to reduce memory usage so that the no errors are raised and the video is outputted as expected.

Is there a way to limit RAM usage for this line of code to under 1GB? On 1 vCPU? Or, what is the closest I could get to it. If it's not possible with MoviePy, what other libraries should I try?

My virtual environment versions:

Python == 3.8.8
MoviePy == 2.0.0.dev2

Any help appreciated.

PasqualePuzio commented 1 year ago

I'm facing the same issue, even when creating relatively simple and short videos. For instance, 1GB of memory is not enough to write a video of about 30 seconds with a single ImageClip as background and 5 TextClip.

FujiwaraChoki commented 5 months ago

+1