AWS Lambda

https://docs.aws.amazon.com/lambda/latest/dg/lambda-services.html

Python use Boto3 package in Lambda:

https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/mediaconvert.html#MediaConvert.Client.create_job

#!/usr/bin/env python

import glob
import json
import os
import uuid
import boto3
import datetime
import random

from botocore.client import ClientError

def handler(event, context):

    assetID = str(uuid.uuid4())
    sourceS3Bucket = event['Records'][0]['s3']['bucket']['name']
    sourceS3Key = event['Records'][0]['s3']['object']['key']
    sourceS3 = 's3://'+ sourceS3Bucket + sourceS3Key
    # os.path.basename(sourceS3) -> test.mp4
    # splitext() -> test
    sourceS3Basename = os.path.splitext(os.path.basename(sourceS3))[0]
    
    destConfig = event['Records'][0]['s3']['dest'];
    destURI = destConfig['uri']
    
    # s3://celeb-videos
    destinationS3 = 's3://' + destConfig['bucket']
    # celeb-videos
    destinationS3basename = os.path.splitext(os.path.basename(destinationS3))[0]
    mediaConvertRole = os.environ['MediaConvertRole']
    region = os.environ['AWS_DEFAULT_REGION']
    statusCode = 200
    body = {}
    
    
    
    # Use MediaConvert SDK UserMetadata to tag jobs with the assetID 
    # Events from MediaConvert will have the assetID in UserMedata
    jobMetadata = {'assetID': assetID, 'videoId': event['Records'][0]['videoId']}

    # print (json.dumps(event))
    # print (destinationS3basename)
    # return;
    
    try:
        # Job settings are in the lambda zip file in the current working directory
        with open('job.json') as json_data:
            jobSettings = json.load(json_data)
            # print(jobSettings)

        # get the account-specific mediaconvert endpoint for this region
        mc_client = boto3.client('mediaconvert', region_name=region)
        endpoints = mc_client.describe_endpoints()

        # add the account-specific endpoint to the client session 
        client = boto3.client('mediaconvert', region_name=region, endpoint_url=endpoints['Endpoints'][0]['Url'], verify=False)

        # Update the job settings with the source video from the S3 event and destination 
        # paths for converted videos
        jobSettings['Inputs'][0]['FileInput'] = sourceS3
        
        # Watermark destURI
        # S3KeyWatermark = 'assets/' + assetID + '/mp4/' + sourceS3Basename
        S3KeyWatermark = destURI + '/mp4/' + sourceS3Basename
        jobSettings['OutputGroups'][0]['OutputGroupSettings']['FileGroupSettings']['Destination'] \
            = destinationS3 + S3KeyWatermark
            
        # Thumbs
        # S3KeyThumbnails = 'assets/' + assetID + '/thumbs/' + sourceS3Basename
        S3KeyThumbnails = destURI + '/thumbs/' + sourceS3Basename
        jobSettings['OutputGroups'][1]['OutputGroupSettings']['FileGroupSettings']['Destination'] \
            = destinationS3 + S3KeyThumbnails
        
        # HLS Streaming
        # S3KeyHLS = 'assets/' + assetID + '/hls/' + sourceS3Basename
        S3KeyHLS = destURI + '/hls/' + sourceS3Basename
        jobSettings['OutputGroups'][2]['OutputGroupSettings']['HlsGroupSettings']['Destination'] \
            = destinationS3 + S3KeyHLS
         

        # print ('jobSettings:')
        # print (json.dumps(jobSettings))
        # return 'ok'

        # Convert the video using AWS Elemental MediaConvert
        job = client.create_job(Role=mediaConvertRole, UserMetadata=jobMetadata, Settings=jobSettings)
        print (json.dumps(job, default=str))
        print ('job:')
        print (job['Job'])
        
        body['job_id'] = job['Job']['Id']

    except Exception as e:
        print ('Exception: %s' % e)
        statusCode = 500
        raise

    finally:
        return {
            'statusCode': statusCode,
            'body': json.dumps(body),
            'headers': {'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'}
        }

How to get job ID

# Convert the video using AWS Elemental MediaConvert
job = client.create_job(Role=mediaConvertRole, UserMetadata=jobMetadata, Settings=jobSettings)
print (json.dumps(job, default=str))
print ('job:')
print (job['Job'])

body['job_id'] = job['Job']['Id']

Pass custom data to Lambda function

public function invoke_lambda($video, $version=5){
	$event = Storage::get('lambda_event_template.json');
	$event = json_decode($event, true);
	//logg($event);

	$event['Records'][0]['s3']['object']['key'] = $video->uri;
        $event['Records'][0]['s3']['dest']['uri'] = $video->base;

        //(string) is very important!
        $event['Records'][0]['videoId'] = (string)$video->id;

	$sharedConfig = [
		'region' => 'ap-northeast-1',
		'version' => 'latest', 
		'credentials' => [
			'key'    => env('AWS_ACCESS_KEY_ID'),
			'secret' => env('AWS_SECRET_ACCESS_KEY'),
		],
	];
	//logg($sharedConfig);

	// Create an SDK class used to share configuration across clients.
	$sdk = new Sdk($sharedConfig);

	// Create an Amazon S3 client using the shared configuration data.
	$client = $sdk->createLambda();

	$result = $client->invoke([
		'ClientContext' => '',
		'FunctionName' => 'arn:aws:lambda:ap-northeast-1:12341234:function:VODLambdaConvert:' . $version, // REQUIRED
		'InvocationType' => 'Event',
		'LogType' => 'Tail',
		'Payload' => json_encode($event),
		'Qualifier' => $version,
	]);

	//logg($result);
	return true;
}

Notice:

  • Version is very important!!
  • Make sure convert integer values to strings.
  • 有時候PHP會看起來沒有錯誤,但是MediaConvert卻沒有invoke成功,要去CloudWatch查是不是有Exception

SNS Listener

public function sns_listener(Request $req){
	$data = file_get_contents('php://input');
	$data = json_decode($data);
	//logg(json_decode($data, true));

	$log = new ApiLog;
	$log->title = 'SNS Listener';
	$log->request = json_encode($data);
	$log->save();

	//$headers = getallheaders();
	$message = json_decode($data->Message);
	$meta = $message->detail->userMetadata;
	$log->response = json_encode($meta);
	$log->save();

	if($message->detail->status == 'COMPLETE'){
		$video = VideoM::find($meta->videoId);
		if($video != false){
			$video->is_ready = 1;
			$video->cover = AwsLib::get_thumb($video);
			$video->save();
		}
	}

	echo 'ok';
}