0

I partially created an aws lambda function that utilize moviepy to concatenate images and videos in a single video file.

For example, I have “img1.jpeg”, “img2.jpeg”, “video1.mp4” and “video2.mp4”. After processing the above files the final clip (“mp4” file) will be a single mp4 file of: “img1.jpeg + video1.mp4 + img2.jpeg + video2.mp4”. The fresulted clip resolution is 640x480.

So I resize the videos and the images (moviepy.video.fx.all.resize - resize images after converting to videos) in the case that they are greater than the aforementioned resolution. If the media files are in the above size, I move to the concatenation process.

When the media files are 640x480 all the process goes fine. The issue is when the media are larger than 640x480 I get an error:

[Errno 32] Broken pipe

MoviePy error: FFMPEG encountered the following error while writing file 1_img_transTEMP_MPY_wvf_snd.mp3:

1_img_transTEMP_MPY_wvf_snd.mp3: Permission denied


The audio export failed, possily because the bitrate you specified was two high or too low for the video codec.: IOError
Traceback (most recent call last):
File "/var/task/media_merge.py", line 70, in handler
s.do_merge()
File "/var/task/mediamerge/stitch_video_and_images.py", line 320, in do_merge
self.convert_crop_media()
File "/var/task/mediamerge/stitch_video_and_images.py", line 310, in convert_crop_media
res_path = resize.resize_media()
File "/var/task/mediamerge/stitch_video_and_images.py", line 229, in resize_media
self.final_media_file, verbose=False)
File "<decorator-gen-51>", line 2, in write_videofile
File "/var/task/moviepy/decorators.py", line 54, in requires_duration
return f(clip, *a, **k)
File "<decorator-gen-50>", line 2, in write_videofile
File "/var/task/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 "/var/task/moviepy/decorators.py", line 22, in convert_masks_to_RGB
return f(clip, *a, **k)
File "/var/task/moviepy/video/VideoClip.py", line 331, in write_videofile
verbose=verbose)
File "<decorator-gen-73>", line 2, in write_audiofile
File "/var/task/moviepy/decorators.py", line 54, in requires_duration
return f(clip, *a, **k)
File "/var/task/moviepy/audio/AudioClip.py", line 204, in write_audiofile
verbose=verbose, ffmpeg_params=ffmpeg_params)
File "<decorator-gen-70>", line 2, in ffmpeg_audiowrite
File "/var/task/moviepy/decorators.py", line 54, in requires_duration
return f(clip, *a, **k)
File "/var/task/moviepy/audio/io/ffmpeg_audiowriter.py", line 162, in ffmpeg_audiowrite
writer.write_frames(chunk)
File "/var/task/moviepy/audio/io/ffmpeg_audiowriter.py", line 122, in write_frames
raise IOError(error)
IOError: [Errno 32] Broken pipe

MoviePy error: FFMPEG encountered the following error while writing file 1_img_transTEMP_MPY_wvf_snd.mp3:

1_img_transTEMP_MPY_wvf_snd.mp3: Permission denied


The audio export failed, possily because the bitrate you specified was two high or too low for the video codec.

The above is from aws lambda logs. The funny thing is, that when I ran it locally it works.

Has anyone ever faced a similar issue or is anyone able to give me some pointers on how to tackle this problem?

2 Answers2

0

With lambda file permissions can be funny. Use /tmp for any temp, working files etc.

Not sure if that will help but good to know.

SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
LIB_DIR = os.path.join(SCRIPT_DIR, 'lib')



lambda_tmp_dir = '/tmp' # Lambda fuction can use this directory.
image_path = "{0}/{1}".format(lambda_tmp_dir, "images")
video_path = "{0}/{1}".format(lambda_tmp_dir, "video")
video_name = "video.mp4"

# ffmpeg is stored with this script.
# When executing ffmpeg, execute permission is requierd.
# But Lambda source directory do not have permission to change it.
# So move ffmpeg binary to `/tmp` and add permission.
FFMPEG_BINARY = "{0}/ffmpeg".format(lambda_tmp_dir)
shutil.copyfile('/var/task/ffmpeg/ffmpeg', FFMPEG_BINARY)
FFPROBE_BINARY = "{0}/ffprobe".format(lambda_tmp_dir)
shutil.copyfile('/var/task/ffmpeg/ffprobe', FFPROBE_BINARY)

os.environ['FFPROBE'] = FFPROBE_BINARY
os.environ['FFMPEG'] = FFMPEG_BINARY
os.chmod(FFPROBE_BINARY, os.stat(FFPROBE_BINARY).st_mode | stat.S_IEXEC)
os.chmod(FFMPEG_BINARY, os.stat(FFMPEG_BINARY).st_mode | stat.S_IEXEC)
David Kierans
  • 1,599
  • 1
  • 16
  • 24
0
from __future__ import print_function
import uuid
import boto3
from botocore.exceptions import ClientError

from moviepy.config import change_settings

import os
from shutil import copyfile
import stat

lambda_tmp_dir = '/tmp'
FFMPEG_BINARY = "{0}/ffmpeg".format(lambda_tmp_dir)
change_settings({"FFMPEG_BINARY": FFMPEG_BINARY})
copyfile('/var/task/ffmpeg', FFMPEG_BINARY)
FFPROBE_BINARY = "{0}/ffprobe".format(lambda_tmp_dir)
copyfile('/var/task/ffprobe', FFPROBE_BINARY)
os.environ['FFPROBE'] = FFPROBE_BINARY
os.environ['FFMPEG'] = FFMPEG_BINARY
os.chmod(FFPROBE_BINARY, os.stat(FFPROBE_BINARY).st_mode | stat.S_IEXEC)
os.chmod(FFMPEG_BINARY, os.stat(FFMPEG_BINARY).st_mode | stat.S_IEXEC)


def lambda_handler(event, context):
    from moviepy.video.io.VideoFileClip import VideoFileClip
    from moviepy.video.io.ffmpeg_tools import ffmpeg_extract_subclip
    s3_client = boto3.client('s3')
    for record in event['Records']:
        bucket = record['s3']['bucket']['name']
        key = record['s3']['object']['key']
        print('{} - {}'.format(bucket, key))
        key_without_path = key.replace('/', '')
        source_file = '/tmp/{}{}'.format(uuid.uuid4(), key_without_path)
        try:
            s3_client.download_file(bucket, key, source_file)
        except ClientError as e:
            if e.response['Error']['Code'] == "404":
                return None
        try:
            short_clip_path = '/tmp/resized{}{}'.format(uuid.uuid4(), key_without_path)
            ffmpeg_extract_subclip(source_file, 2, 5, targetname=short_clip_path)
            clip = VideoFileClip(source_file)
            print('clip.duration = {}'.format(clip.duration))
            clip.save_frame("{}.jpg".format(short_clip_path), t=4.00)

            if clip.duration > 5:
                print('clip bigger then 5')
        except:
            return None
        s3_client.upload_file(short_clip_path, bucket.replace('-', '-thumb-'), key)
        s3_client.upload_file("{}.jpg".format(short_clip_path), bucket.replace('-', '-thumb-'), "{}.jpg".format(key))

Download 64 bit static and unzip it and add 2 files to the lambda zip https://www.johnvansickle.com/ffmpeg/

Yusuf
  • 1