dmarx / video-killed-the-radio-star

Notebook and tools for end-to-end automation of music video production with generative AI
https://colab.research.google.com/github/dmarx/video-killed-the-radio-star/blob/main/Video_Killed_The_Radio_Star_Defusion.ipynb#scrollTo=oPbeyWtesAoh
MIT License
198 stars 35 forks source link

Video compilations seems to break after 4688 frames - BrokenPipeError: [Errno 32] Broken pipe #22

Closed morganmcg1 closed 2 years ago

morganmcg1 commented 2 years ago

At the end of Step 7 I have 22k PIL images in the frames list. When Step 8 starts to iterate through them it consistently hits an error with the PIL saving, BrokenPipeError: [Errno 32] Broken pipe, specifically after 4688 images - I've tested this 3 times, albeit with the same set of images. I also tested that the images in and around that index weren't corrupted (just by viewing them in colab), but then seem to be fine.

This is only on 1 video so far, I'm going to restart the colab and pick another video with the same settings and see if I can reproduce or if its just a weird artifact of the image creation.

Stacktrace:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
[/usr/local/lib/python3.7/dist-packages/PIL/ImageFile.py](https://localhost:8080/#) in _save(im, fp, tile, bufsize)
    495     try:
--> 496         fh = fp.fileno()
    497         fp.flush()

AttributeError: '_idat' object has no attribute 'fileno'

During handling of the above exception, another exception occurred:

BrokenPipeError                           Traceback (most recent call last)
5 frames
[<ipython-input-62-f956df09b414>](https://localhost:8080/#) in <module>
     31 for i,im in enumerate(frames[5000:10000]):
     32   print(i)
---> 33   im.save(p.stdin, 'PNG')
     34 p.stdin.close()
     35 

[/usr/local/lib/python3.7/dist-packages/PIL/Image.py](https://localhost:8080/#) in save(self, fp, format, **params)
   2132 
   2133         try:
-> 2134             save_handler(self, fp, filename)
   2135         finally:
   2136             # do what we can to clean up

[/usr/local/lib/python3.7/dist-packages/PIL/PngImagePlugin.py](https://localhost:8080/#) in _save(im, fp, filename, chunk, save_all)
   1289         _write_multiple_frames(im, fp, chunk, rawmode)
   1290     else:
-> 1291         ImageFile._save(im, _idat(fp, chunk), [("zip", (0, 0) + im.size, 0, rawmode)])
   1292 
   1293     chunk(fp, b"IEND", b"")

[/usr/local/lib/python3.7/dist-packages/PIL/ImageFile.py](https://localhost:8080/#) in _save(im, fp, tile, bufsize)
    509                 while True:
    510                     l, s, d = e.encode(bufsize)
--> 511                     fp.write(d)
    512                     if s:
    513                         break

[/usr/local/lib/python3.7/dist-packages/PIL/PngImagePlugin.py](https://localhost:8080/#) in write(self, data)
    994 
    995     def write(self, data):
--> 996         self.chunk(self.fp, b"IDAT", data)
    997 
    998 

[/usr/local/lib/python3.7/dist-packages/PIL/PngImagePlugin.py](https://localhost:8080/#) in putchunk(fp, cid, *data)
    981 
    982     fp.write(o32(len(data)) + cid)
--> 983     fp.write(data)
    984     crc = _crc32(data, _crc32(cid))
    985     fp.write(o32(crc))

BrokenPipeError: [Errno 32] Broken pipe
dmarx commented 2 years ago

Yeah I experienced a similar broken pipe error, not sure what the deal is. video encoding stuff isn't my wheel house. I'm thinking of swapping out the ffmpeg cli invocation with a python binding that might make writing frames to video more stable and possibly use less memory. not sure if memory is even the issue here, but yeah: this broken pipe thing is a problem for sure.

morganmcg1 commented 2 years ago

It happened just now with another video, but hit at 2380/3204 frames. Thing is it worked yesterday without a hitch. I tried undoing the change made in this commit on the random chance that might help, not no joy.

Thanks for the great notebook anyways, really enjoying it

morganmcg1 commented 2 years ago

Spent a good chunk of the last 2 days fighting ffmpeg but I think I have a more stable version using the python bindings from ffmpeg-python. This seems to be stable for me, I've tested it with a few different videos, up to about 2.5k frames. Its not pretty but it works :D

@dmarx let me know if you'd like a PR :) , this colab here is the same with just the video compilation cell replaced if you'd like to test

Install the ffmpeg python bindings: https://github.com/kkroening/ffmpeg-python

pip install -qqq ffmpeg-python

Then compile your video and audio like so:


import os
import ffmpeg

fps = storyboard.params.fps
input_audio = storyboard.params.audio_fpath
output_filename = storyboard.params.output_filename

# Create a directory to save the frames to
frames_directory = 'my_frames'
if not os.path.exists(frames_directory):
    os.makedirs(frames_directory)

# Save each PIL Image as a .png file to `frames_directory`
for i,f in enumerate(frames):
  f.save(f"{frames_directory}/{i}.png", format='png')

# Rename all the frames in the frames directory so that filenames include leading zeros and are listed in order
!rename -e 's/\d+/sprintf("%06d",$&)/e' -- $frames_directory/*.png

# Create the video without audio from the saved .png frames
ffmpeg.input(f'{frames_directory}/*.png', pattern_type='glob', framerate=fps).output(output_filename).overwrite_output().run()

# Merge the video and audio files
v = ffmpeg.input(output_filename)
a = ffmpeg.input(input_audio)

v.output(a, "final.mp4", shortest=None, vcodec="libx264", framerate=fps, pix_fmt="yuv420p", preset="veryslow").overwrite_output().run()
dmarx commented 2 years ago

I'd love a PR! Yes please, and thanks for finding a work around! :)

dmarx commented 2 years ago

I think the issue here had to do with how the notebook was occupying memory, which has been fixed. Closing this for now even though I haven't integrated the ffmpeg-python suggestion, but I'll try that solution if users continue to report similar issues

morganmcg1 commented 2 years ago

got it, glad that its working better now