1

/tmp/0003.mp4: Permission denied

  1. I have set AWSLambdaBasicExecutionRole for user role
  2. have following line in code:
        execSync(
            'touch /tmp/0001.mp4 /tmp/0003.mp4 /tmp/image.mp4 /tmp/dance.mp4',
            { stdio: 'ignore' }
        )
        execSync('chmod 777 /tmp')

Anyway the rest of the code is here:

import { execSync } from 'child_process'
import {
    S3Client,
    GetObjectCommand,
    PutObjectCommand,
} from '@aws-sdk/client-s3'
import * as fs from 'fs'

const s3Client = new S3Client({ region: 'eu-central-1' })

export const handler = async (event) => {
    try {
        const imageParams = {
            Bucket: 't44-admin',
            Key: 'dance.png',
        }

        const firstVideoParams = {
            Bucket: 't44-admin',
            Key: '0001.mp4',
        }

        const secondVideoParams = {
            Bucket: 't44-admin',
            Key: '0003.mp4',
        }

        const imageResponse = await s3Client.send(new GetObjectCommand(imageParams))
        const loopedImageChunks = []

        imageResponse.Body.on('data', (chunk) => {
            loopedImageChunks.push(chunk)
        })

        await new Promise((resolve, reject) => {
            imageResponse.Body.on('error', reject)
            imageResponse.Body.on('end', resolve)
        })

        const looedImageBuffer = Buffer.concat(loopedImageChunks)

        const firstVideoResponse = await s3Client.send(
            new GetObjectCommand(firstVideoParams)
        )
        const firstVideoChunks = []

        firstVideoResponse.Body.on('data', (chunk) => {
            firstVideoChunks.push(chunk)
        })

        await new Promise((resolve, reject) => {
            firstVideoResponse.Body.on('error', reject)
            firstVideoResponse.Body.on('end', resolve)
        })

        const firstVideoFileBuffer = Buffer.concat(firstVideoChunks)

        const secondVideoResponse = await s3Client.send(
            new GetObjectCommand(secondVideoParams)
        )
        const secondVideoChunks = []

        secondVideoResponse.Body.on('data', (chunk) => {
            secondVideoChunks.push(chunk)
        })

        await new Promise((resolve, reject) => {
            secondVideoResponse.Body.on('error', reject)
            secondVideoResponse.Body.on('end', resolve)
        })

        const secondVideoFileBuffer = Buffer.concat(secondVideoChunks)

        const imageFilePath = '/tmp/dance.png'
        const loopedImageFilePath = '/tmp/image.mp4'
        const firstVideoFilePath = '/tmp/0001.mp4'
        const secondVideoFilePath = '/tmp/0003.mp4'

        execSync(
            'touch /tmp/0001.mp4 /tmp/0003.mp4 /tmp/image.mp4 /tmp/dance.mp4',
            { stdio: 'ignore' }
        )
        execSync('chmod 777 /tmp')

        fs.writeFileSync(imageFilePath, looedImageBuffer)
        fs.writeFileSync(firstVideoFilePath, firstVideoFileBuffer)
        fs.writeFileSync(secondVideoFilePath, secondVideoFileBuffer)

        // Run ffmpeg command from the layer
        const ffmpegCommand1 = `/opt/nodejs/ffmpeg -loop 1 -i ${imageFilePath} -c:v libx264 -t 2 -pix_fmt yuv420p -vf "fade=t=in:st=0:d=0.2" -y ${loopedImageFilePath}`
        execSync(ffmpegCommand1)

        const ffmpegCommand2 = `/opt/nodejs/ffmpeg -i concat:${firstVideoFilePath}|${loopedImageFilePath}|${secondVideoFilePath} -c copy /tmp/output.mp4`
        execSync(ffmpegCommand2)

        // Upload the output video to S3
        const outputKey = 'output.mp4'
        const outputParams = {
            Bucket: 't44-admin',
            Key: outputKey,
            Body: fs.createReadStream('/tmp/output.mp4'),
            ACL: 'public-read',
            ContentType: 'video/mp4',
        }
        await s3Client.send(new PutObjectCommand(outputParams))

        // Upload the looped video to S3
        const loopedKey = 'looped.mp4'
        const loopedParams = {
            Bucket: 't44-admin',
            Key: loopedKey,
            Body: fs.createReadStream('/tmp/image.mp4'),
            ACL: 'public-read',
            ContentType: 'video/mp4',
        }
        await s3Client.send(new PutObjectCommand(loopedParams))

        return {
            statusCode: 200,
            body: 'Success',
        }
    } catch (error) {
        console.error('Error:', error)

        return {
            statusCode: 500,
            body: 'Error',
        }
    }
}
János
  • 32,867
  • 38
  • 193
  • 353
  • Why do you need to touch various files? Also, you don't own `/tmp` afaik. – jarmod Feb 20 '23 at 20:16
  • @jarmod You can use the /tmp directory for scratch work. See https://docs.aws.amazon.com/lambda/latest/dg/lambda-runtime-environment.html – Charles Yang Feb 20 '23 at 20:27
  • @jarmod how else you would store the inbetween states of the conversion? in buffer? – János Feb 20 '23 at 20:52
  • https://stackoverflow.com/questions/75514222/aws-lambda-bin-sh-tmp-image-mp4-cannot-execute-binary-file – János Feb 20 '23 at 21:18
  • Yes, you can use /tmp for temporary files but there's no need to touch files in advance or modify the permissions on /tmp. I've never seen someone go to the shell to do basic file handling when you can do it natively from your programming language e.g. using fs.openSync(). You might also look at Node.js wrappers for ffmpeg, rather than executing it directly from a shell, but either works fine. – jarmod Feb 20 '23 at 21:37
  • Can you explain why you think you need to `chmod 777 /tmp`? – jarmod Feb 20 '23 at 21:41
  • ChatGPT told it :D I tried with and without, none helped – János Feb 20 '23 at 22:01
  • If you have a Lambda layer with ffmpeg correctly installed then your general approach should work, assuming you have enough free diskspace in /tmp. By default, it only comes with 512MB. Here's a possibly simpler way to [download files](https://stackoverflow.com/a/67373050/271415) from S3 to /tmp. – jarmod Feb 20 '23 at 23:33
  • You don't need to chmod anything, with the possible exception of `chmod +x /opt/nodejs/ffmpeg`, but I suspect you can't, and don't need to, do that if your layer is correct. – jarmod Feb 20 '23 at 23:37

1 Answers1

0

You only modified the permissions for the directory, but not the files inside. Use chmod -R 666 /tmp/* to recursively give read-write (666) permissions for the contents.

Charles Yang
  • 330
  • 2
  • 10