node.jsamazon-web-servicesamazon-s3aws-lambdamediainfo

How to get MediaInfo in AWS lambda using nodeJs


I‘m currently working with video on demand process using aws lambda in nodeJs. I want to create video on demand with settings based on video file im storing inside S3 bucket. To make such a thing i would need to extract informations(like framerates, resolution, audio bit rate...) using MediaInfo.

I found this link https://aws.amazon.com/blogs/media/running-mediainfo-as-an-aws-lambda-function/ but it is done with python. Does anyone have idea or maybe other way how to do such a thing using nodeJs environment for lambda and not python ?


Solution

  • You can get this information using FFmpeg and FFprobe. In AWS Lambda you use the FFmpeg Lambda layer ffmpeg-lambda-layer for FFmpeg.

    Here is the code for index.js

    process.env.PATH = process.env.PATH + ':' + process.env['LAMBDA_TASK_ROOT']
    
    const AWS = require('aws-sdk')
    const { spawn, spawnSync } = require('child_process')
    const { createReadStream, createWriteStream } = require('fs')
    const s3 = new AWS.S3()
    const ffprobePath = '/opt/bin/ffprobe'
    const ffmpegPath = '/opt/bin/ffmpeg'
    const allowedTypes = ['mov', 'mpg', 'mpeg', 'mp4', 'wmv', 'avi', 'webm']
    const width = 300
    const height = 300
    module.exports.handler = async (event, context) => {
        const srcKey = decodeURIComponent(event.Records[0].s3.object.key).replace(/\+/g, ' ')
        const bucket = event.Records[0].s3.bucket.name
        const target = s3.getSignedUrl('getObject', { Bucket: bucket, Key: srcKey, Expires: 1000 })
        let fileType = srcKey.match(/\.\w+$/)
        if (!fileType) {
            throw new Error(`invalid file type found for key: ${srcKey}`)
        }
        fileType = fileType[0].slice(1)
        if (allowedTypes.indexOf(fileType) === -1) {
            throw new Error(`filetype: ${fileType} is not an allowed type`)
        }  
     
        function getImageMediainfo() {
            return new Promise((resolve, reject) => {
            var output = [];
            var ffprobe = spawn(ffprobePath, [
                '-v', 'error',
                '-select_streams', 'v',
                '-show_entries', 
                'stream', //stream=width,height:stream_tags=rotate
                '-of','json',
                target
            ]);
            ffprobe.stdout.on('data', function(data) {
                output.push(data);
            });
            ffprobe.on('error', function(err) {
                console.log('ffprobe error: ', err);
                reject()
            });
            ffprobe.on('close', function() {
                var outputStr = output.join('');
                var jsonOut = {};
                jsonOut = JSON.parse(outputStr);
                console.log('outputStr', outputStr)
                console.log('outputStr', jsonOut)
                let framerates = jsonOut.streams[0].r_frame_rate // jsonOut.streams[0].avg_frame_rate
                let resolution = {
                    width: jsonOut.streams[0].width,
                    height: jsonOut.streams[0].height
                }
                let audio_bit_rate = jsonOut.streams[0].bit_rate
                console.log('framerates', framerates)
                console.log('resolution', resolution)
                console.log('audio_bit_rate', audio_bit_rate)
                resolve()
            })
        })      
      }
      await getImageMediainfo();
      return console.log(`processed ${bucket}/${srcKey} successfully`)
    }
    

    Output Like This

    {
        "programs": [
    
        ],
        "streams": [
            {
                "index": 1,
                "codec_name": "h264",
                "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
                "profile": "High",
                "codec_type": "video",
                "codec_time_base": "299/36000",
                "codec_tag_string": "avc1",
                "codec_tag": "0x31637661",
                "width": 720,
                "height": 1280,
                "coded_width": 720,
                "coded_height": 1280,
                "has_b_frames": 1,
                "pix_fmt": "yuvj420p",
                "level": 32,
                "color_range": "pc",
                "color_space": "bt709",
                "color_transfer": "bt709",
                "color_primaries": "bt709",
                "chroma_location": "left",
                "refs": 1,
                "is_avc": "true",
                "nal_length_size": "4",
                "r_frame_rate": "60/1",
                "avg_frame_rate": "18000/299",
                "time_base": "1/600",
                "start_pts": 0,
                "start_time": "0.000000",
                "duration_ts": 2093,
                "duration": "3.488333",
                "bit_rate": "3284683",
                "bits_per_raw_sample": "8",
                "nb_frames": "210",
                "disposition": {
                    "default": 1,
                    "dub": 0,
                    "original": 0,
                    "comment": 0,
                    "lyrics": 0,
                    "karaoke": 0,
                    "forced": 0,
                    "hearing_impaired": 0,
                    "visual_impaired": 0,
                    "clean_effects": 0,
                    "attached_pic": 0,
                    "timed_thumbnails": 0
                },
                "tags": {
                    "rotate": "270",
                    "creation_time": "2021-07-15T23:03:31.000000Z",
                    "language": "und",
                    "handler_name": "Core Media Video",
                    "encoder": "H.264"
                },
                "side_data_list": [
                    {
                        "side_data_type": "Display Matrix",
                        "displaymatrix": "\n00000000:            0      -65536           0\n00000001:        65536           0           0\n00000002:            0    83886080  1073741824\n",
                        "rotation": 90
                    }
                ]
            }
        ]
    }