15

I'm trying to convert an html5 video to mp4 video and am doing so by screen shooting through PhantomJS over time

I'm also cropping the images using PIL so eventually my code is roughly:

while time() < end_time:
    screenshot_list.append(phantom.get_screenshot_as_base64())
.
.
for screenshot in screenshot_list:
    im = Image.open(BytesIO(base64.b64decode(screenshot)))
    im = im.crop((left, top, right, bottom))

Right now I'm saving to disc all those images and using ffmpeg from saved files:

os.system('ffmpeg -r {fps} -f image2 -s {width}x{height} -i {screenshots_dir}%04d.png -vf scale={width}:-2 '
      '-vcodec libx264 -crf 25 -vb 20M -pix_fmt yuv420p {output}'.format(fps=fps, width=width,
                                                                  screenshots_dir=screenshots_dir,
                                                                  height=height, output=output))

But I want instead of using those saved files, to be able to pipe the PIL.Images directy to ffmpeg, how can I do that?

bluesummers
  • 11,365
  • 8
  • 72
  • 108
  • ffmpeg part should start like `ffmpeg -f image2pipe -vcodec png -i - -vf ....`. If the PIL outputs a proper PNG syntax, you shouldn't need to specify input image size. – Gyan Apr 27 '17 at 07:51
  • I specify because sometime it is not dividable by 2 and that creates problems so I handle it and then specify it – bluesummers Apr 27 '17 at 08:55
  • The scale filter argument takes care of the mod 2 requirement. That's not relevant to ingesting the images. – Gyan Apr 27 '17 at 09:05
  • Thanks, I'll fix that – bluesummers Apr 27 '17 at 09:06
  • 1
    Possible duplicate of [Can you "stream" images to ffmpeg to construct a video, instead of saving them to disk?](https://stackoverflow.com/questions/13294919/can-you-stream-images-to-ffmpeg-to-construct-a-video-instead-of-saving-them-t) – Aran-Fey Sep 10 '17 at 16:38

1 Answers1

15

Bounty is gone, but I found the solution.

After acquiring all of the screenshots as base64 strings I write them into a subprocess with the following code

import subprocess as sp

# Generating all of the screenshots as base64 
# in a variable called screenshot_list

cmd_out = ['ffmpeg',
           '-f', 'image2pipe',
           '-vcodec', 'png',
           '-r', '30',  # FPS 
           '-i', '-',  # Indicated input comes from pipe 
           '-vcodec', 'png',
           '-qscale', '0',
           '/home/user1/output_dir/video.mp4']

pipe = sp.Popen(cmd_out, stdin=sp.PIPE)

for screenshot in screenshot_list:
    im = Image.open(BytesIO(base64.b64decode(screenshot)))
    im.save(pipe.stdin, 'PNG')

pipe.stdin.close()
pipe.wait()

# Make sure all went well
if pipe.returncode != 0:
    raise sp.CalledProcessError(pipe.returncode, cmd_out)

If execution time is an issue, you can save the images as JPEGs instead, and use appropriate codecs for that, but the highest quality I managed to achieve was with these settings

themadmax
  • 2,344
  • 1
  • 31
  • 36
bluesummers
  • 11,365
  • 8
  • 72
  • 108
  • 2
    Thanks for this answer -- really helpful! I think in your cmd_out list, the second vcodec line (which applies to the output) should read `'-vcodec', 'libx264',` instead of `'-vcodec', 'png',`, no? – Pender Mar 31 '19 at 22:07