この記事では、MediaConvertとLambdaを利用して動画ファイルをMP4形式に変換し、完了またはエラーのステータスに関する通知を電子メールで受け取る方法を共有します。開発フレームワークはAWS Amplifyです。
動画ファイルの変換と配信の効率化
動画ファイルのサイズを圧縮し、ストリーミング速度を向上させ、データ転送のコストを削減するためには、Amazon Elastic TranscoderまたはAWS Elemental MediaConvertなどの動画変換サービスを利用することができます。
AWS Elemental MediaConvertの概要
[AWS Elemental MediaConvert]って何?
AWS Elemental MediaConvertは、ファイルベースの動画変換サービスであり、放送レベルの機能を備えています。これにより、オンデマンド(VOD)の動画コンテンツを容易に作成し、大規模な多画面配信が可能となります。
AWS Elemental MediaConvertの主な機能は、以下のとおりです。
- 高度なビデオおよびオーディオ機能による高品質な動画変換
- 使用量に応じた課金によるコスト効率の向上
- 自動化による運用の簡素化
- セキュリティ機能の提供
- AWS Elemental MediaConvertのメリット
AWS Elemental MediaConvertのメリット
AWS Elemental MediaConvertは、Amazon Elastic Transcoderと比較して、以下のメリットがあります。
- コストが安価
- 高度なセキュリティ機能(DRM)が組み込まれている
- AWS Elemental MediaConvertによる動画変換ガイド
ここでは、AWS Elemental MediaConvertを使用して動画ファイルをMP4形式に変換する手順を、AWS Amplifyを活用して紹介します。
AWSアーキテクチャ概要
動画変換の処理流れ
- 管理者は、S3バケットの「/video/」フォルダーに動画ファイルをアップロードします。
- Event Bridgeは、S3バケットの「/video/」フォルダーに新しいファイルがアップロードされると、イベントをトリガーします。
- イベントがトリガーされると、AWS Lambda関数が実行されます。
- Lambda関数は、アップロードされた動画ファイルの情報を取得し、動画変換の初期設定を行います。
- Lambda関数は、Lambda Layer FFProbeを使用して、ビデオファイルのサイズ情報を取得します。
- Lambda関数は、MediaConvertでジョブの作成を起動します。
- MediaConvertは、ジョブ情報を基にして動画変換処理を実行します。
- 処理が成功した場合、結果はS3バケットの「/video-convert/」フォルダーにアップロードされます。
- Event Bridgeは、「Completed」または「Error」のイベントを監視します。
- イベントが発生すると、Event BridgeはAWS SNSサービスを介してユーザーに通知のメールを送信します。
- ユーザーは、CloudFront経由でS3バケットの「/video-convert/」フォルダー内の変換した動画ファイルを確認できます。
AWS開発開始
コマンド実行:amplify init
名前: env
AWSプロフィールを入力する
他のパラメーターはデフォルトにしても結構です。
コマンド実行:amplify add function
初期作成:Lambda 関数(serverless function)
機能名:videoConvertを入力する
開発言語:nodeJS
Do you want to configure advanced settings > No
mediaconvertのため、下記の2URLが必要です。
- Url MediaConvert endpoint(Endpoint)
- Default queue(ARN)
MediaConvertのURLを取得方法
AWS Console→リージョンを選択→MediaConvert→メニュー→アカウントを選択すると下記のイメージ画像が表示される。
Endpoint:https://xxxxxxxx.mediaconvert.ap-southeast-1.amazonaws.com
Default queueのURLを取得方法
AWS Console→リージョンを選択→MediaConvert→Queue→Defaultを押下すると下記の画面が表示されます。
ARN: arn:aws:mediaconvert:ap-southeast-1:573580132305:queues/Default
S3 バケット内に動画の保存場所を作成
AWS Console→リージョンを選択→S3→Create bucket→Bucket name
S3バケットへのアクセス権限とオブジェクトへのアクセス権限の設定を行います。
Bucket→onetech-demo-mediaconvert→Permissions→Bock public access→Edit→Uncheck Block all public access
Bucket → onetech-demo-mediaconvert → Permissions → Object Ownership → Edit
Event BridgeはS3バケットへのアクセス許可設定
onetech-demo-mediaconvertバケットをクリックする。
下記の画面に沿って設定を行う。
Properties → Event notifications → Amazon EventBridge → Edit → Turn ON → Save Changes
node JSを使って開発プログラム開始
node JSライブラリをインストールする。
"devDependencies": {
"aws-sdk": "^2.1416.0",
"fluent-ffmpeg": "^2.1.2",
"fs": "^0.0.1-security",
"path": "^0.12.7"
}
cd amplify/backend/function/videoConvert/src
npm install -D aws-sdk fluent-ffmpeg fs path ↓ Develop node_modules ※モック作成のため、サーバへアップロードすると情報が消えてします。
インストールが完了したらVisual studio codeでrootフォルダを開く 新しいファイルを作成:config/resize.json
下記のデータを入力する
{
"Queue": "xxxxxx",
"UserMetadata": {
"input": "s3://xxxxxx/videos/Video400mb.mp4",
"environment": "dev"
},
"Role": "xxxxxxxx",
"Settings": {
"TimecodeConfig": {
"Source": "ZEROBASED"
},
"OutputGroups": [
{
"CustomName": "MP4",
"Name": "File Group",
"Outputs": [
{
"ContainerSettings": {
"Container": "MP4",
"Mp4Settings": {}
},
"VideoDescription": {
"Width": 3840,
"Height": 2160,
"CodecSettings": {
"Codec": "H_264",
"H264Settings": {
"GopClosedCadence": 1,
"GopSize": 90,
"GopBReference": "DISABLED",
"MaxBitrate": 3000000,
"SpatialAdaptiveQuantization": "ENABLED",
"TemporalAdaptiveQuantization": "ENABLED",
"FlickerAdaptiveQuantization": "DISABLED",
"RateControlMode": "QVBR",
"QvbrSettings": {
"QvbrQualityLevel": 7
},
"CodecProfile": "MAIN",
"MinIInterval": 0,
"AdaptiveQuantization": "HIGH",
"SceneChangeDetect": "DISABLED",
"QualityTuningLevel": "SINGLE_PASS",
"GopSizeUnits": "FRAMES",
"NumberBFramesBetweenReferenceFrames": 2
}
}
},
"AudioDescriptions": [
{
"CodecSettings": {
"Codec": "AAC",
"AacSettings": {
"Bitrate": 96000,
"CodingMode": "CODING_MODE_2_0",
"SampleRate": 48000
}
}
}
],
"NameModifier": "Resize"
}
],
"OutputGroupSettings": {
"Type": "FILE_GROUP_SETTINGS",
"FileGroupSettings": {
"Destination": "s3://xxxxxx/convert-video/",
"DestinationSettings": {
"S3Settings": {
"AccessControl": {
"CannedAcl": "PUBLIC_READ"
}
}
}
}
}
}
],
"Inputs": [
{
"AudioSelectors": {
"Audio Selector 1": {
"DefaultSelection": "DEFAULT"
}
},
"VideoSelector": {
"Rotate": "AUTO"
},
"TimecodeSource": "ZEROBASED",
"FileInput": "s3://xxxxxx/videos/IMG_0961.MOV"
}
]
},
"AccelerationSettings": {
"Mode": "DISABLED"
},
"StatusUpdateInterval": "SECONDS_60",
"Priority": 0,
"Tags": {
"MediaConvert": "DemoMediaConvert"
}
}
index.jsファイルを開いて動画設定などを行います。
今回は下記の動画サイズを固定にします。
1920×1080、1080×1920
要求により変更設定をおこなってください。
const AWS = require('aws-sdk');
AWS.config.update({
region: process.env.REGION,
});
const S3 = new AWS.S3({
signatureVersion: 'v4'
});
//Chỗ này cần update lại endpoint mediaconvert
const MediaConvert = new AWS.MediaConvert({
apiVersion: '2017-08-29',
endpoint: 'https://xxxxx.mediaconvert.ap-southeast-1.amazonaws.com',
});
//Read file from local
const fs = require('fs');
//Get info video
const ffmpeg = require('fluent-ffmpeg');
exports.handler = async (event) => {
try {
if(!event.detail) {
return false;
}
//Chỗ này cần update lại s3 bucket name
const bucket = 'onetech-demo-mediaconvert';
let convertWidth = 1920;
let convertHeight = 1080;
const objectKey = event.detail.object.key;
const regex = /\/[^/]*$/;
let convertFolder = objectKey.replace(regex, '/');
convertFolder = convertFolder.replace('video/', 'convert-video/');
console.log('convertFolder:::', convertFolder);
console.log('objectKey:::', objectKey);
//Check video vertical or horizontal
try {
const s3Params = {
Bucket: bucket,
Key: objectKey
};
const s3Stream = S3.getObject(s3Params).createReadStream();
const info = await new Promise((resolve, reject) => {
ffmpeg(s3Stream).ffprobe((err, info) => {
if (err) {
console.error(err);
reject(err);
return;
}
resolve(info);
});
});
console.log('Video Information:', info);
for (const stream of info.streams) {
if (!stream.width) {
continue;
}
console.log('Video INFO Steam', stream);
if(+stream.width < +stream.height) {
convertWidth = 1080;
convertHeight = 1920;
}
if(stream.rotation && stream.rotation === '-90') {
convertWidth = 1080;
convertHeight = 1920;
}
break;
}
} catch (error) {
console.error(error);
}
console.log('convertWidth', convertWidth);
console.log('convertHeight', convertHeight);
//End check video vertical or horizontal
const jsonData = fs.readFileSync('./config/resize.json', 'utf8');
let convertData = JSON.parse(jsonData);
//Update queue
convertData.Queue = 'arn:aws:mediaconvert:ap-southeast-1:111111111111:queues/Default';
//Update role
convertData.Role = process.env.MEDIA_CONVERT_ROLE;
//Update input file
const inputSource = `s3://${bucket}/${objectKey}`;
const outSource = `s3://${bucket}/${convertFolder}`;
convertData.Settings.Inputs[0].FileInput = inputSource;
//Update output folder
convertData.Settings.OutputGroups[0].OutputGroupSettings.FileGroupSettings.Destination = outSource;
//Update video width and height
convertData.Settings.OutputGroups[0].Outputs[0].VideoDescription.Width = convertWidth;
convertData.Settings.OutputGroups[0].Outputs[0].VideoDescription.Height = convertHeight;
//Update UserMetadata
convertData.UserMetadata.input = objectKey;
convertData.UserMetadata.environment = process.env.ENV;
convertData.UserMetadata.output = convertFolder;
convertData.UserMetadata.s3_bucket = bucket;
console.log('JSON MEDIACONVERT DATA', convertData);
const response = await MediaConvert.createJob(convertData).promise();
console.log('MediaConvert Job created:', response.Job);
return true;
} catch (error) {
console.error(error);
return false;
}
};
プログラムが一旦完了します。これからインフラ構成を基づいてcloudformationを設定する
編集ファイル: amplify/backend/function/videoConvert/videoConvert-cloudformation-template.json
説明:
-
LambdaFunction: Lambda function ビルド
-
DemoVideoConvertEventBridgeS3TriggerRule: Event bridge trigger をビルドしてファイルが指定したフォルダへアップロードできるか
-
MyLambdaPermission: Event bridgeからイベントを受け取り
-
DemoVideoConvertEventBridgeProcessRule:MediaConvert処理が成功または失敗した場合にEvent Bridgeトリガーを構築するために使用されます
-
DemoVideoConvertSNSProcessTopic: Event Bridgeからmediaconvert処理が成功か失敗か通知メールする
-
DemoVideoConvertSNSProcessTopicPolicy: Event BridgeからのイベントをSNSが受け取るように許可します
-
MyEmailSubscription:成功または失敗したMediaConvertの通知を受信するためのメール設定をします。
-
MediaConvertExecutionRole: role Lambda function
-
MediaConvertExecutionPolicy: Lambda functionで実行権限ポリシー
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "Lambda Function resource stack creation using Amplify CLI",
"Parameters": {
"CloudWatchRule": {
"Type": "String",
"Default": "NONE",
"Description": " Schedule Expression"
},
"deploymentBucketName": {
"Type": "String"
},
"env": {
"Type": "String"
},
"s3Key": {
"Type": "String"
}
},
"Conditions": {
"ShouldNotCreateEnvResources": {
"Fn::Equals": [
{
"Ref": "env"
},
"NONE"
]
}
},
"Resources": {
"LambdaFunction": {
"Type": "AWS::Lambda::Function",
"Metadata": {
"aws:asset:path": "./src",
"aws:asset:property": "Code"
},
"Properties": {
"Code": {
"S3Bucket": {
"Ref": "deploymentBucketName"
},
"S3Key": {
"Ref": "s3Key"
}
},
"Handler": "index.handler",
"FunctionName": {
"Fn::If": [
"ShouldNotCreateEnvResources",
"demoVideoConvert",
{
"Fn::Join": [
"",
[
"demoVideoConvert",
"-",
{
"Ref": "env"
}
]
]
}
]
},
"Environment": {
"Variables": {
"ENV": {
"Ref": "env"
},
"REGION": {
"Ref": "AWS::Region"
},
"MEDIA_CONVERT_ROLE": {
"Fn::GetAtt": [
"MediaConvertExecutionRole",
"Arn"
]
}
}
},
"Role": {
"Fn::GetAtt": [
"LambdaExecutionRole",
"Arn"
]
},
"Runtime": "nodejs18.x",
"Layers": [],
"MemorySize": 2048,
"EphemeralStorage": {
"Size": 2048
},
"Timeout": 300
}
},
"DemoVideoConvertEventBridgeS3TriggerRule": {
"DependsOn": [
"LambdaFunction"
],
"Type": "AWS::Events::Rule",
"Properties": {
"Name": {
"Fn::If": [
"ShouldNotCreateEnvResources",
"DemoVideoConvertEventBridgeS3TriggerRule",
{
"Fn::Join": [
"",
[
"DemoVideoConvertEventBridgeS3TriggerRule",
"-",
{
"Ref": "env"
}
]
]
}
]
},
"Description": "Trigger Lambda from S3 events",
"EventPattern": {
"source": [
"aws.s3"
],
"detail-type": [
"Object Created"
],
"detail": {
"bucket": {
"name": [
"onetech-demo-mediaconvert"
]
},
"object": {
"key": [
{
"prefix": "video/"
}
]
}
}
},
"Targets": [
{
"Arn": {
"Fn::GetAtt": [
"LambdaFunction",
"Arn"
]
},
"Id": {
"Fn::If": [
"ShouldNotCreateEnvResources",
"VideoAppEventBridgeRuleTargetId",
{
"Fn::Join": [
"",
[
"VideoAppEventBridgeRuleTargetId",
"-",
{
"Ref": "env"
}
]
]
}
]
}
}
]
}
},
"MyLambdaPermission": {
"DependsOn": [
"DemoVideoConvertEventBridgeS3TriggerRule"
],
"Type": "AWS::Lambda::Permission",
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"LambdaFunction",
"Arn"
]
},
"Principal": "events.amazonaws.com",
"SourceArn": {
"Fn::GetAtt": [
"DemoVideoConvertEventBridgeS3TriggerRule",
"Arn"
]
}
}
},
"DemoVideoConvertEventBridgeProcessRule": {
"DependsOn": [
"DemoVideoConvertSNSProcessTopic"
],
"Type": "AWS::Events::Rule",
"Properties": {
"Name": {
"Fn::If": [
"ShouldNotCreateEnvResources",
"DemoVideoConvertEventBridgeProcessRule",
{
"Fn::Join": [
"",
[
"DemoVideoConvertEventBridgeProcessRule",
"-",
{
"Ref": "env"
}
]
]
}
]
},
"Description": "Trigger mediaconvert process",
"EventPattern": {
"source": [
"aws.mediaconvert"
],
"detail": {
"status": [
"COMPLETE",
"ERROR"
],
"userMetadata": {
"environment": [
{
"Ref": "env"
}
]
}
}
},
"Targets": [
{
"Arn": {
"Ref": "DemoVideoConvertSNSProcessTopic"
},
"Id": {
"Fn::If": [
"ShouldNotCreateEnvResources",
"VideoAppEventBridgeMediaconvertProcessRule",
{
"Fn::Join": [
"",
[
"VideoAppEventBridgeMediaconvertProcessRule",
"-",
{
"Ref": "env"
}
]
]
}
]
}
}
]
}
},
"DemoVideoConvertSNSProcessTopic": {
"DependsOn": [],
"Type": "AWS::SNS::Topic",
"Properties": {
"DisplayName": {
"Fn::If": [
"ShouldNotCreateEnvResources",
"VideoAppSNSMediaconvertProcessTopic",
{
"Fn::Join": [
"",
[
"VideoAppSNSMediaconvertProcessTopic",
"-",
{
"Ref": "env"
}
]
]
}
]
}
}
},
"DemoVideoConvertSNSProcessTopicPolicy": {
"DependsOn": [
"DemoVideoConvertSNSProcessTopic"
],
"Type": "AWS::SNS::TopicPolicy",
"Properties": {
"Topics": [
{
"Ref": "DemoVideoConvertSNSProcessTopic"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowEventBridgePublish",
"Effect": "Allow",
"Principal": {
"Service": "events.amazonaws.com"
},
"Action": "sns:Publish",
"Resource": {
"Ref": "DemoVideoConvertSNSProcessTopic"
}
}
]
}
}
},
"MyEmailSubscription": {
"DependsOn": [
"DemoVideoConvertSNSProcessTopic"
],
"Type": "AWS::SNS::Subscription",
"Properties": {
"Protocol": "email",
"Endpoint": "duy@onetech.vn",
"TopicArn": {
"Ref": "DemoVideoConvertSNSProcessTopic"
}
}
},
"MediaConvertExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName": {
"Fn::If": [
"ShouldNotCreateEnvResources",
"videoAppMediaConvertRole",
{
"Fn::Join": [
"",
[
"videoAppMediaConvertRole",
"-",
{
"Ref": "env"
}
]
]
}
]
},
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"mediaconvert.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
}
}
},
"MediaConvertExecutionPolicy": {
"DependsOn": [
"MediaConvertExecutionRole"
],
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "media-convert-execution-policy",
"Roles": [
{
"Ref": "MediaConvertExecutionRole"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*",
"s3-object-lambda:*"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke",
"execute-api:ManageConnections"
],
"Resource": "arn:aws:execute-api:*:*:*"
}
]
}
}
},
"LambdaExecutionRole": {
"DependsOn": [
"MediaConvertExecutionRole",
"MediaConvertExecutionPolicy"
],
"Type": "AWS::IAM::Role",
"Properties": {
"RoleName": {
"Fn::If": [
"ShouldNotCreateEnvResources",
"videoappconvertLambdaRole30cacecd",
{
"Fn::Join": [
"",
[
"videoappconvertLambdaRole30cacecd",
"-",
{
"Ref": "env"
}
]
]
}
]
},
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
}
}
},
"LambdaExecutionPolicy": {
"DependsOn": [
"LambdaExecutionRole"
],
"Type": "AWS::IAM::Policy",
"Properties": {
"PolicyName": "lambda-execution-policy",
"Roles": [
{
"Ref": "LambdaExecutionRole"
}
],
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": [
{
"Fn::Sub": [
"arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*",
{
"region": {
"Ref": "AWS::Region"
},
"account": {
"Ref": "AWS::AccountId"
},
"lambda": {
"Ref": "LambdaFunction"
}
}
]
}
]
},
{
"Action": [
"iam:PassRole"
],
"Resource": [
{
"Fn::GetAtt": [
"MediaConvertExecutionRole",
"Arn"
]
}
],
"Effect": "Allow",
"Sid": "PassRole"
},
{
"Action": [
"mediaconvert:*"
],
"Resource": [
"*"
],
"Effect": "Allow",
"Sid": "MediaConvertService"
},
{
"Sid": "AllowS3Access",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "*"
}
]
}
}
}
},
"Outputs": {
"Name": {
"Value": {
"Ref": "LambdaFunction"
}
},
"Arn": {
"Value": {
"Fn::GetAtt": [
"LambdaFunction",
"Arn"
]
}
},
"Region": {
"Value": {
"Ref": "AWS::Region"
}
},
"LambdaExecutionRole": {
"Value": {
"Ref": "LambdaExecutionRole"
}
}
}
}
ffmpegライブラリを実行するためのLambdaレイヤーをビルドします
コマンド実行:amplify add function
↓
Lambda layerを選択
↓
名前:videoConvertLayer
↓
Node JS
↓
Enter
↓
https://github.com/phuocduy1988/ffmpeg からffmpegライブラリをダウンロードする。
↓
bin/ へコピー
↓
amplify/backend/function/lambdamediaconvertvideoConvertLayer/opt/
↓
Node JSライブラリをインストールする
“devDependencies”: {
“aws-sdk”: “^2.1416.0”,
“fluent-ffmpeg”: “^2.1.2”,
“fs”: “^0.0.1-security”,
“path”: “^0.12.7”
}
cd amplify/backend/function/lambdamediaconvertvideoConvertLayer/lib/nodejs
npm install aws-sdk fluent-ffmpeg fs path
このライブラリをインストールする目的は、Lambda関数のコードが共通ライブラリとして使用できるようにすることです。
Lambda MediaConvertにLambdaレイヤーを追加します。
コマンド実行:amplify update function
Lambda function
videoConvert
→ Lambda layers configuration → Yes
スペースを押下
lambdamediaconvertvideoConvertLayerを選択
Amplify MediaConvertのソースをAWS Cloudにデプロイします。
MediaConvertの機能テスト
AWS Console → リージョン → S3 → onetech-demo-mediaconvert → videoフォルダ作成 → 動画をアップロード
Cloudwatch logを確認
CloudWatch → Log groups → /aws/lambda/demoVideoConvert-dev
convert-videoフォルダで確認
元々動画ファイルは128MBから9MBになりました。
この検証のために作成したAWSリソースを削除します。
AWS Console →リージョン→ AWS Amplify → lambdamediaconvert → Delete appを選択
S3 バケット内に削除する
Amazon S3 → Buckets → onetech-demo-mediaconvert → Empty bucket
まとめ
AWS Elemental MediaConvertを使用して動画をMP4形式に変換してAWSの開発者にとって欠かせないスキルです。
最新技術を使いこなして、ユーザーにとって魅力的な動画配信体験を提供しましょう。
この記事が、効果的な動画変換ソリューションをお探しの皆様にとって有益であることを願っています。
Source: https://onetech.jp/blog/aws-elemental-mediaconvertでのスムーズな動画変換ガイド-17623