AWS CLIでElastic Transcoderのジョブを作成してみた

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

清水です。今回はAWS CLIからAmazon Elastic Transcoderのジョブを作成してみます。 事の発端は、以前のエントリ でElastic Transcoderを使ってMPEG-DASHのAdaptive bitrate streamingをやってみました。 この際、ジョブ作成の際の出力設定(Output Details)が「異なるビットレートの3つ分+音声の1つ」と、 似た設定を4つ分繰り返すことになり、Management Consoleでの操作だと大変だなーと感じたことです。

この解決の一環として?AWS CLIでElastic Transcoderを操作してみたので、まとめてみたいと思います。

AWS CLIでの設定にあたり

Elastic Transcoderのジョブ作成には、以下のaws elastictranscoder create-jobコマンド を使用すれば良さそうです。

こちらのドキュメントをぱっと見た限り、CLI実行時のパラメータが多くなりそうな印象です。

--input (structure)

A section of the request body that provides information about the file that is being transcoded.

Shorthand Syntax:

Key=string,FrameRate=string,Resolution=string,AspectRatio=string,Interlaced=string,Container=string,Encryption={Mode=string,Key=string,KeyMd5=string,InitializationVector=string},DetectedProperties={Width=integer,Height=integer,FrameRate=string,FileSize=long,DurationMillis=long}

ちょっと大変そうなので、パラメータをJSONファイルで記述することとしました。具体的には、以下の手順で行っています。

  • --generate-cli-skeletonでcreate-jobのスケルトンJSONを生成
  • 既存のジョブの情報をAWS CLIでJSONとして出力、またManagement Consoleと比較しながら、この2つを参考にスケルトンJSONを加工
  • 加工したJSONを--cli-input-jsonの引数としてジョブを作成

Elastic Transcoderでの変換内容

変換内容は以前のエントリと同じく、元ファイルを Adaptive bitrate streaming(ABR)対応のMPEG-DASHに変換したいと思います。

ABRのビットレートについては、前回同様、高画質 2.4Mbps、中画質 1.2Mbps、低画質 600kbps、の3つとして、 画質の確認用に3色の円の画像をWatermarkとして挿入します。変換設定としては、これに音声 128kbpsを加えた4種類となります。

012-Original

やってみた

それでは、AWS CLIでElasticTranscoderからMPEG-DASHのABR変換のジョブを作成してみたいと思います。 入出力用のS3バケットや、パイプラインについては以前作成したものをそのまま使用しています。

スケルトンJSONの生成

まず、以下のコマンドでスケルトンJSONを生成します。

 $ aws elastictranscoder create-job --generate-cli-skeleton

以下のJSONが得られました。(長い。。)

{
    "PipelineId": "",
    "Input": {
        "Key": "",
        "FrameRate": "",
        "Resolution": "",
        "AspectRatio": "",
        "Interlaced": "",
        "Container": "",
        "Encryption": {
            "Mode": "",
            "Key": "",
            "KeyMd5": "",
            "InitializationVector": ""
        },
        "DetectedProperties": {
            "Width": 0,
            "Height": 0,
            "FrameRate": "",
            "FileSize": 0,
            "DurationMillis": 0
        }
    },
    "Output": {
        "Key": "",
        "ThumbnailPattern": "",
        "ThumbnailEncryption": {
            "Mode": "",
            "Key": "",
            "KeyMd5": "",
            "InitializationVector": ""
        },
        "Rotate": "",
        "PresetId": "",
        "SegmentDuration": "",
        "Watermarks": [
            {
                "PresetWatermarkId": "",
                "InputKey": "",
                "Encryption": {
                    "Mode": "",
                    "Key": "",
                    "KeyMd5": "",
                    "InitializationVector": ""
                }
            }
        ],
        "AlbumArt": {
            "MergePolicy": "",
            "Artwork": [
                {
                    "InputKey": "",
                    "MaxWidth": "",
                    "MaxHeight": "",
                    "SizingPolicy": "",
                    "PaddingPolicy": "",
                    "AlbumArtFormat": "",
                    "Encryption": {
                        "Mode": "",
                        "Key": "",
                        "KeyMd5": "",
                        "InitializationVector": ""
                    }
                }
            ]
        },
        "Composition": [
            {
                "TimeSpan": {
                    "StartTime": "",
                    "Duration": ""
                }
            }
        ],
        "Captions": {
            "MergePolicy": "",
            "CaptionSources": [
                {
                    "Key": "",
                    "Language": "",
                    "TimeOffset": "",
                    "Label": "",
                    "Encryption": {
                        "Mode": "",
                        "Key": "",
                        "KeyMd5": "",
                        "InitializationVector": ""
                    }
                }
            ],
            "CaptionFormats": [
                {
                    "Format": "",
                    "Pattern": "",
                    "Encryption": {
                        "Mode": "",
                        "Key": "",
                        "KeyMd5": "",
                        "InitializationVector": ""
                    }
                }
            ]
        },
        "Encryption": {
            "Mode": "",
            "Key": "",
            "KeyMd5": "",
            "InitializationVector": ""
        }
    },
    "Outputs": [
        {
            "Key": "",
            "ThumbnailPattern": "",
            "ThumbnailEncryption": {
                "Mode": "",
                "Key": "",
                "KeyMd5": "",
                "InitializationVector": ""
            },
            "Rotate": "",
            "PresetId": "",
            "SegmentDuration": "",
            "Watermarks": [
                {
                    "PresetWatermarkId": "",
                    "InputKey": "",
                    "Encryption": {
                        "Mode": "",
                        "Key": "",
                        "KeyMd5": "",
                        "InitializationVector": ""
                    }
                }
            ],
            "AlbumArt": {
                "MergePolicy": "",
                "Artwork": [
                    {
                        "InputKey": "",
                        "MaxWidth": "",
                        "MaxHeight": "",
                        "SizingPolicy": "",
                        "PaddingPolicy": "",
                        "AlbumArtFormat": "",
                        "Encryption": {
                            "Mode": "",
                            "Key": "",
                            "KeyMd5": "",
                            "InitializationVector": ""
                        }
                    }
                ]
            },
            "Composition": [
                {
                    "TimeSpan": {
                        "StartTime": "",
                        "Duration": ""
                    }
                }
            ],
            "Captions": {
                "MergePolicy": "",
                "CaptionSources": [
                    {
                        "Key": "",
                        "Language": "",
                        "TimeOffset": "",
                        "Label": "",
                        "Encryption": {
                            "Mode": "",
                            "Key": "",
                            "KeyMd5": "",
                            "InitializationVector": ""
                        }
                    }
                ],
                "CaptionFormats": [
                    {
                        "Format": "",
                        "Pattern": "",
                        "Encryption": {
                            "Mode": "",
                            "Key": "",
                            "KeyMd5": "",
                            "InitializationVector": ""
                        }
                    }
                ]
            },
            "Encryption": {
                "Mode": "",
                "Key": "",
                "KeyMd5": "",
                "InitializationVector": ""
            }
        }
    ],
    "OutputKeyPrefix": "",
    "Playlists": [
        {
            "Name": "",
            "Format": "",
            "OutputKeys": [
                ""
            ],
            "HlsContentProtection": {
                "Method": "",
                "Key": "",
                "KeyMd5": "",
                "InitializationVector": "",
                "LicenseAcquisitionUrl": "",
                "KeyStoragePolicy": ""
            },
            "PlayReadyDrm": {
                "Format": "",
                "Key": "",
                "KeyMd5": "",
                "KeyId": "",
                "InitializationVector": "",
                "LicenseAcquisitionUrl": ""
            }
        }
    ],
    "UserMetadata": {
        "KeyName": ""
    }
}

Management Consoleから作成したジョブ内容をJSONで確認

参考とするため、 以前のエントリでManagement Consoleから作成したジョブの内容をJSONで出力してみます。 ジョブIDはManagement Consoleから取得しました。

 $ aws elastictranscoder read-job --id "ジョブID"

取得できたJSONは下記です。こちらもなかなかの量がありますね。。

{
    "Job": {
        "Status": "Complete",
        "Playlists": [
            {
                "Name": "Sample5",
                "Format": "MPEG-DASH",
                "OutputKeys": [
                    "Sample5_Video_2400_Red.mp4",
                    "Sample5_Video_1200_Green.mp4",
                    "Sample5_Video_600_Blue.mp4",
                    "Sample5_Audio_128.mp4"
                ],
                "Status": "Complete"
            }
        ],
        "Outputs": [
            {
                "Status": "Complete",
                "Watermarks": [
                    {
                        "InputKey": "watermarks/Red.png",
                        "PresetWatermarkId": "BottomRight"
                    }
                ],
                "FrameRate": "30",
                "ThumbnailPattern": "",
                "FileSize": 18616667,
                "PresetId": "1351620000001-500030",
                "DurationMillis": 61533,
                "SegmentDuration": "6.0",
                "Height": 480,
                "Id": "1",
                "Width": 854,
                "Duration": 62,
                "Rotate": "auto",
                "Key": "Sample5_Video_2400_Red.mp4"
            },
            {
                "Status": "Complete",
                "Watermarks": [
                    {
                        "InputKey": "watermarks/Green.png",
                        "PresetWatermarkId": "BottomRight"
                    }
                ],
                "FrameRate": "30",
                "ThumbnailPattern": "",
                "FileSize": 9309008,
                "PresetId": "1351620000001-500040",
                "DurationMillis": 61533,
                "SegmentDuration": "6.0",
                "Height": 360,
                "Id": "2",
                "Width": 640,
                "Duration": 62,
                "Rotate": "auto",
                "Key": "Sample5_Video_1200_Green.mp4"
            },
            {
                "Status": "Complete",
                "Watermarks": [
                    {
                        "InputKey": "watermarks/Blue.png",
                        "PresetWatermarkId": "BottomRight"
                    }
                ],
                "FrameRate": "30",
                "ThumbnailPattern": "",
                "FileSize": 4683428,
                "PresetId": "1351620000001-500050",
                "DurationMillis": 61533,
                "SegmentDuration": "6.0",
                "Height": 238,
                "Id": "3",
                "Width": 426,
                "Duration": 62,
                "Rotate": "auto",
                "Key": "Sample5_Video_600_Blue.mp4"
            },
            {
                "Status": "Complete",
                "DurationMillis": 61602,
                "Duration": 62,
                "SegmentDuration": "6.0",
                "ThumbnailPattern": "",
                "FileSize": 1000342,
                "Id": "4",
                "Key": "Sample5_Audio_128.mp4",
                "Watermarks": [],
                "PresetId": "1351620000001-500060"
            }
        ],
        "Timing": {
            "SubmitTimeMillis": 1464412945026,
            "StartTimeMillis": 1464412946471,
            "FinishTimeMillis": 1464412970976
        },
        "OutputKeyPrefix": "MPEG-DASH-ABR/",
        "Input": {
            "FrameRate": "auto",
            "DetectedProperties": {
                "Width": 1920,
                "FileSize": 76423382,
                "DurationMillis": 61536,
                "FrameRate": "29.98",
                "Height": 1080
            },
            "Container": "auto",
            "Resolution": "auto",
            "AspectRatio": "auto",
            "Key": "Sample5.mov",
            "Interlaced": "auto"
        },
        "PipelineId": "パイプラインID",
        "Arn": "arn:aws:elastictranscoder:ap-northeast-1:"アカウントID":job/"ジョブID"",
        "Id": "ジョブID",
        "Output": {
            "Status": "Complete",
            "Watermarks": [
                {
                    "InputKey": "watermarks/Red.png",
                    "PresetWatermarkId": "BottomRight"
                }
            ],
            "FrameRate": "30",
            "ThumbnailPattern": "",
            "FileSize": 18616667,
            "PresetId": "1351620000001-500030",
            "DurationMillis": 61533,
            "SegmentDuration": "6.0",
            "Height": 480,
            "Id": "1",
            "Width": 854,
            "Duration": 62,
            "Rotate": "auto",
            "Key": "Sample5_Video_2400_Red.mp4"
        }
    }
}

ジョブ作成用のJSONの作成

スケルトンJSONや以前作成したジョブのJSON、さらに Management Consoleの画面を比べながら、ジョブ作成用のJSONを作成します。

まずはスケルトンJSONからInputの詳細や、出力のEncryptionやCaptions、 AlbumArtの項目など、今回使用しないものはバッサリと削除します。

{
    "PipelineId": "",
    "Input": {
        "Key": ""
    },
    "Outputs": [
        {
            "Key": "",
            "PresetId": "",
            "SegmentDuration": "",
            "Watermarks": [
                {
                    "PresetWatermarkId": "",
                    "InputKey": ""
                }
            ]
        }
    ],
    "OutputKeyPrefix": "",
    "Playlists": [
        {
            "Name": "",
            "Format": "",
            "OutputKeys": [
                ""
            ]
        }
    ]
}

だいぶスッキリしました。この状態でManagement Consoleと比較するとだいぶ入力内容の対応がわかりやすくなったと思います。

aws-cli-elastictranscoder-create-job-001

Management Consoleと比べて注意した点は以下です。

  • パイプラインID: Management Consoleから取得したパイプラインIDを記載します。
  • プリセットID: こちらもManagement Consoleで確認してプリセットIDを入力します。
  • 出力設定(Outputs): ABR用含めて、合計4つ(高画質、中画質、低画質、音声)の変換を行うので、詳細部分を4つに複製します。
  • PlaylisetsのOutputKeys: これも複数になるので、4つ分記載します。

実際に作成したものは以下のようになりました。

{
    "PipelineId": "パイプラインID",
    "Input": {
        "Key": "Sample4.mov"
    },
    "Outputs": [
        {
            "Key": "Sample4_Video_2400_Red.mp4",
            "PresetId": "1351620000001-500030",
            "SegmentDuration": "6",
            "Watermarks": [
                {
                    "PresetWatermarkId": "BottomRight",
                    "InputKey": "watermarks/Red.png"
                }
            ]
        },
        {
            "Key": "Sample4_Video_1200_Green.mp4",
            "PresetId": "1351620000001-500040",
            "SegmentDuration": "6",
            "Watermarks": [
                {
                    "PresetWatermarkId": "BottomRight",
                    "InputKey": "watermarks/Green.png"
                }
            ]
        },
        {
            "Key": "Sample4_Video_600_Blue.mp4",
            "PresetId": "1351620000001-500050",
            "SegmentDuration": "6",
            "Watermarks": [
                {
                    "PresetWatermarkId": "BottomRight",
                    "InputKey": "watermarks/Blue.png"
                }
            ]
        },
        {
            "Key": "Sample4_Audio_128.mp4",
            "PresetId": "1351620000001-500060",
            "SegmentDuration": "6"

        }
    ],
    "OutputKeyPrefix": "MPEG-DASH-ABR-CLI/",
    "Playlists": [
        {
            "Name": "Sample4",
            "Format": "MPEG-DASH",
            "OutputKeys": [
                "Sample4_Video_2400_Red.mp4",
                "Sample4_Video_1200_Green.mp4",
                "Sample4_Video_600_Blue.mp4",
                "Sample4_Audio_128.mp4"
            ]
        }
    ]
}

AWS CLIでジョブ作成

作成した上記のJSONファイルから、ジョブをAWS CLIで作成してみます。(実際に打ったコマンドは1行、JSON部分はAWS CLIによるOutputになります)

  $  aws elastictranscoder create-job --cli-input-json file://create-job-input.json
{
    "Job": {
        "Arn": "arn:aws:elastictranscoder:ap-northeast-1:"アカウントID":job/"ジョブID",
        "PipelineId": "パイプラインID",
        "Playlists": [
            {
                "OutputKeys": [
                    "Sample4_Video_2400_Red.mp4",
                    "Sample4_Video_1200_Green.mp4",
                    "Sample4_Video_600_Blue.mp4",
                    "Sample4_Audio_128.mp4"
                ],
                "Status": "Submitted",
                "Format": "MPEG-DASH",
                "Name": "Sample4"
            }
        ],
        "Timing": {
            "SubmitTimeMillis": 1467283547103
        },
        "Id": "ジョブID",
        "Input": {
            "Key": "Sample4.mov"
        },
        "OutputKeyPrefix": "MPEG-DASH-ABR-CLI/",
        "Status": "Submitted",
        "Outputs": [
            {
                "Key": "Sample4_Video_2400_Red.mp4",
                "Status": "Submitted",
                "PresetId": "1351620000001-500030",
                "Id": "1",
                "SegmentDuration": "6.0",
                "Watermarks": [
                    {
                        "InputKey": "watermarks/Red.png",
                        "PresetWatermarkId": "BottomRight"
                    }
                ]
            },
            {
                "Key": "Sample4_Video_1200_Green.mp4",
                "Status": "Submitted",
                "PresetId": "1351620000001-500040",
                "Id": "2",
                "SegmentDuration": "6.0",
                "Watermarks": [
                    {
                        "InputKey": "watermarks/Green.png",
                        "PresetWatermarkId": "BottomRight"
                    }
                ]
            },
            {
                "Key": "Sample4_Video_600_Blue.mp4",
                "Status": "Submitted",
                "PresetId": "1351620000001-500050",
                "Id": "3",
                "SegmentDuration": "6.0",
                "Watermarks": [
                    {
                        "InputKey": "watermarks/Blue.png",
                        "PresetWatermarkId": "BottomRight"
                    }
                ]
            },
            {
                "Key": "Sample4_Audio_128.mp4",
                "Status": "Submitted",
                "PresetId": "1351620000001-500060",
                "Id": "4",
                "SegmentDuration": "6.0",
                "Watermarks": []
            }
        ],
        "Output": {
            "Key": "Sample4_Video_2400_Red.mp4",
            "Status": "Submitted",
            "PresetId": "1351620000001-500030",
            "Id": "1",
            "SegmentDuration": "6.0",
            "Watermarks": [
                {
                    "InputKey": "watermarks/Red.png",
                    "PresetWatermarkId": "BottomRight"
                }
            ]
        }
    }
}

Management Consoleから確認すると、無事にジョブが作成され、トランスコードされていました。

aws-cli-elastictranscoder-create-job-002

再生確認

トランスコードが完了したら、再生確認をしてみます。今回もPlayerはMPEG-DASHのReference Client最新版を使用しました。

Enter your manifest URL hereのところに、S3に格納されているPlaylistファイルのURLを入力して、Loadボタンを押すと再生が始まります。

awscli-elastictranscoder-create-job-003-2

高画質2.4Mbpsが再生されました。再生タイミングなどにより、他のビットレートにも切り替わりました。

まとめ

AWS CLIを使ってElastic Transcoderのジョブを作成してみました。はじめにスケルトンJSONや、以前Management Consoleから作成したジョブのJSONデータを見たときはどうなることかと思いましたが、整理していくとスラっとジョブ作成用のJSONファイルが作成できた感じです。

またAWS CLIでジョブを作成することの効率については、Management Consoleの作成よりも断然良いと思いました。一度、変換するパターン(今回のAdaptive Bitrateのように、複数変換出力する場合の種類など)を決めてしまえば、後は入出力キー部分をテキストで変更するなどすれば別入力キーにも対応できます。(もっと簡単、自動化するにはAPIなどでコード化してしまうことも手段とは思いますが) ただ、Management Consoleでの経験無しでこのCLIを操作(JSONファイルの作成)するのは少し大変かなー、とも思いました。 一度目はManagement Consoleで実施、繰り返しが手間になってきたらAWS CLI、さらにはAPIなどでの自動化を考慮にいれる、というが良いのではないでしょうか。

引き続き、Elastic TranscoderのAWS CLIについてはパイプラインやプリセットについても確認してみたいと思います。