【AWS】MediaConvertを用いてHLS VOD配信

# はじめに
今回はWEBアプリ作成にあたり動画をアップロードし、閲覧できる機能を実装したいため
**MediaConvert**を使用して**HLS（HTTP Live Streaming）**で配信する環境をAWSで構築しました！

[HLS VOD配信](https://aws-mediaservices-jp.workshop.aws/handson5.html)
AWSのハンズオンをほとんどを採用しているんですが笑
今回実装にあたりつまずいた点（ハンズオンで指示してるのに・・・）
や、少しカスタマイズした点があるのでその手順をアウトプットします。

カスタマイズしたのは処理した動画の出力先をバケットのトップではなく
動画がアップロードされた階層に出力するという点です。
**Lambda**へアップロードするファイルの記述を変更してます＾＾

またハンズオンの画面は古いものがあり迷った点がありました（＾ー＾；
今回は最新の画面で対応してます！！

# なぜHLSなのか
私も最初は大きすぎる動画ファイルを一定のサイズにするものかという認識でしたが、
それでは充分ではありませんでした。

通常の動画ファイルは一旦全てダウンロードした後に再生される仕組みです。

作成しているWEBアプリはスマホでの利用も想定しおり、
使用環境は様々でそれぞれの契約プランにも差がある。

もし通常の動画ファイルだと通信量が多くなり不便になることが容易に
考えられるため、`少しづつダウンロードしながら再生する`ということと、
`動画の解像度サイズを選べる`というHLSの形式が活きてきます！


なるほど！
ということでやっていきます＾＾

# アーキテクチャー
以下のような構成。
ハンズオンと同じです！
出力先のS3は最初のS3と同じバケットです。
![MediaConvert](https://user-images.githubusercontent.com/67918886/132083842-46dbad9f-2e69-4a80-a605-eb9d47e41f4f.png)

# IAMロール作成
**MediaConvert**が正常に動作するように前もって用意されているポリシーを用いてMediaConvert用ロールを作成します。

サービスから**IAM**を選択。

左側のメニュー一覧から**ロール**を選択。

そして**ロールの作成**を選択。

以下の画面に遷移し、**AWSサービス**を選択し、

<img width="1046" alt="09ba546b9eb363b0b9b23913e3ef503a" src="https://user-images.githubusercontent.com/67918886/132084109-24b255a6-d288-4914-ae62-d856ad923219.png">

下の図のように**MediaConvert**を選択し、次のステップへ。

<img width="1013" alt="9ac5e32e02b4f5b42a9c0f3d5e679e2c" src="https://user-images.githubusercontent.com/67918886/132084258-bf08d888-b474-44b6-ac4b-c84c654dcb89.png">

AmazonAPIGatewayInvokeFullAccessと
AmazonS3FullAccessのポリシーが付与されていること確認し、次にステップへ。

タグの作成は任意、次へ。

ロール名を入力し、**ロールの作成**で作成する。

<img width="1033" alt="4daec4a1de71c3174974de156dea2f54" src="https://user-images.githubusercontent.com/67918886/132084362-7f892f6b-eec4-4562-ade4-837507041f1a.png">

作成後、念のため検索して作成されてるか確認する。


# S3のバケット作成

サービスから**S3**を選択し、任意の名前のバケットを作成する。
特別な設定はここでしてません。

今回は「mediaconvert-movie」としました。

<img width="1039" alt="25d3b5ef158b53caf3a2bf2d61cc5545" src="https://user-images.githubusercontent.com/67918886/132084553-205ad734-c954-4479-87e3-9ab04de4f82e.png">

そして今回ハンズオンからカスタマイズした点は、処理した動画をトップに作成するのではなく、
動画があった階層にアウトプットフォルダを作成させる点です。

なので`movie`というフォルダを作成し、コンソールから動画をアップロードしました。

下図のようにフォルダを作成してそこへ動画をアップロードしました。

<img width="863" alt="6d979a4a56efc4c6caf9d497c62c1824" src="https://user-images.githubusercontent.com/67918886/132085022-67c79264-cbbc-4bb7-9c48-bbd3b25f7246.png">

# MediaConvertでジョブを作成する

下図のように`ジョブテンプレート`を選択。

<img width="1283" alt="ac9d0fe66ac8318206b95fcebebda1ee" src="https://user-images.githubusercontent.com/67918886/132085269-25e48f6e-1f9d-46b5-b685-415beff96bba.png">

`テンプレートをインポート`を選択し、設定用jsonファイルを選択する。
これはハンズオン用のファイルに`aws-mediaconvert-job-sample.json`というファイルが
あるのでそれを選択。


<img width="1027" alt="9b3c5042321f81fa9e94e9c2412bdadf" src="https://user-images.githubusercontent.com/67918886/132085324-9334c889-cd6a-46d5-b062-dfc75412d26f.png">

以下のような設定のJSONファイルです。

<img width="969" alt="db2e539f4e9ef5f757288189af77b13a" src="https://user-images.githubusercontent.com/67918886/132085484-9bd3911b-26be-4914-ac05-f48f21781ed1.png">


コード自体は以下。

<details><summary>テンプレートJSONコード</summary><div>

ハンズオンの資料と同じです。

```json:aws-mediaconvert-job-sample.json

{
  "Queue": "",
  "UserMetadata": {},
  "Role": "",
  "Settings": {
    "OutputGroups": [
      {
        "Name": "Apple HLS",
        "Outputs": [
          {
            "ContainerSettings": {
              "Container": "M3U8",
              "M3u8Settings": {
                "AudioFramesPerPes": 4,
                "PcrControl": "PCR_EVERY_PES_PACKET",
                "PmtPid": 480,
                "PrivateMetadataPid": 503,
                "ProgramNumber": 1,
                "PatInterval": 0,
                "PmtInterval": 0,
                "Scte35Source": "NONE",
                "NielsenId3": "NONE",
                "TimedMetadata": "NONE",
                "VideoPid": 481,
                "AudioPids": [
                  482,
                  483,
                  484,
                  485,
                  486,
                  487,
                  488,
                  489,
                  490,
                  491,
                  492
                ]
              }
            },
            "VideoDescription": {
              "Width": 1280,
              "ScalingBehavior": "DEFAULT",
              "Height": 720,
              "TimecodeInsertion": "DISABLED",
              "AntiAlias": "ENABLED",
              "Sharpness": 50,
              "CodecSettings": {
                "Codec": "H_264",
                "H264Settings": {
                  "InterlaceMode": "PROGRESSIVE",
                  "NumberReferenceFrames": 3,
                  "Syntax": "DEFAULT",
                  "Softness": 0,
                  "GopClosedCadence": 1,
                  "GopSize": 60,
                  "Slices": 1,
                  "GopBReference": "DISABLED",
                  "SlowPal": "DISABLED",
                  "SpatialAdaptiveQuantization": "ENABLED",
                  "TemporalAdaptiveQuantization": "ENABLED",
                  "FlickerAdaptiveQuantization": "DISABLED",
                  "EntropyEncoding": "CABAC",
                  "Bitrate": 15000000,
                  "FramerateControl": "INITIALIZE_FROM_SOURCE",
                  "RateControlMode": "CBR",
                  "CodecProfile": "MAIN",
                  "Telecine": "NONE",
                  "MinIInterval": 0,
                  "AdaptiveQuantization": "HIGH",
                  "CodecLevel": "AUTO",
                  "FieldEncoding": "PAFF",
                  "SceneChangeDetect": "ENABLED",
                  "QualityTuningLevel": "SINGLE_PASS",
                  "FramerateConversionAlgorithm": "DUPLICATE_DROP",
                  "UnregisteredSeiTimecode": "DISABLED",
                  "GopSizeUnits": "FRAMES",
                  "ParControl": "INITIALIZE_FROM_SOURCE",
                  "NumberBFramesBetweenReferenceFrames": 2,
                  "RepeatPps": "DISABLED",
                  "DynamicSubGop": "STATIC"
                }
              },
              "AfdSignaling": "NONE",
              "DropFrameTimecode": "ENABLED",
              "RespondToAfd": "NONE",
              "ColorMetadata": "INSERT"
            },
            "AudioDescriptions": [
              {
                "AudioTypeControl": "FOLLOW_INPUT",
                "CodecSettings": {
                  "Codec": "AAC",
                  "AacSettings": {
                    "AudioDescriptionBroadcasterMix": "NORMAL",
                    "Bitrate": 96000,
                    "RateControlMode": "CBR",
                    "CodecProfile": "LC",
                    "CodingMode": "CODING_MODE_2_0",
                    "RawFormat": "NONE",
                    "SampleRate": 48000,
                    "Specification": "MPEG4"
                  }
                },
                "LanguageCodeControl": "FOLLOW_INPUT"
              }
            ],
            "OutputSettings": {
              "HlsSettings": {
                "AudioGroupId": "program_audio",
                "AudioOnlyContainer": "AUTOMATIC",
                "IFrameOnlyManifest": "EXCLUDE"
              }
            },
            "NameModifier": "s1"
          },
          {
            "ContainerSettings": {
              "Container": "M3U8",
              "M3u8Settings": {
                "AudioFramesPerPes": 4,
                "PcrControl": "PCR_EVERY_PES_PACKET",
                "PmtPid": 480,
                "PrivateMetadataPid": 503,
                "ProgramNumber": 1,
                "PatInterval": 0,
                "PmtInterval": 0,
                "Scte35Source": "NONE",
                "NielsenId3": "NONE",
                "TimedMetadata": "NONE",
                "TimedMetadataPid": 502,
                "VideoPid": 481,
                "AudioPids": [
                  482,
                  483,
                  484,
                  485,
                  486,
                  487,
                  488,
                  489,
                  490,
                  491,
                  492
                ]
              }
            },
            "VideoDescription": {
              "Width": 640,
              "ScalingBehavior": "DEFAULT",
              "Height": 360,
              "TimecodeInsertion": "DISABLED",
              "AntiAlias": "ENABLED",
              "Sharpness": 50,
              "CodecSettings": {
                "Codec": "H_264",
                "H264Settings": {
                  "InterlaceMode": "PROGRESSIVE",
                  "NumberReferenceFrames": 3,
                  "Syntax": "DEFAULT",
                  "Softness": 0,
                  "GopClosedCadence": 1,
                  "GopSize": 60,
                  "Slices": 1,
                  "GopBReference": "DISABLED",
                  "SlowPal": "DISABLED",
                  "SpatialAdaptiveQuantization": "ENABLED",
                  "TemporalAdaptiveQuantization": "ENABLED",
                  "FlickerAdaptiveQuantization": "DISABLED",
                  "EntropyEncoding": "CABAC",
                  "Bitrate": 1000000,
                  "FramerateControl": "INITIALIZE_FROM_SOURCE",
                  "RateControlMode": "CBR",
                  "CodecProfile": "MAIN",
                  "Telecine": "NONE",
                  "MinIInterval": 0,
                  "AdaptiveQuantization": "HIGH",
                  "CodecLevel": "AUTO",
                  "FieldEncoding": "PAFF",
                  "SceneChangeDetect": "ENABLED",
                  "QualityTuningLevel": "SINGLE_PASS",
                  "FramerateConversionAlgorithm": "DUPLICATE_DROP",
                  "UnregisteredSeiTimecode": "DISABLED",
                  "GopSizeUnits": "FRAMES",
                  "ParControl": "INITIALIZE_FROM_SOURCE",
                  "NumberBFramesBetweenReferenceFrames": 2,
                  "RepeatPps": "DISABLED",
                  "DynamicSubGop": "STATIC"
                }
              },
              "AfdSignaling": "NONE",
              "DropFrameTimecode": "ENABLED",
              "RespondToAfd": "NONE",
              "ColorMetadata": "INSERT"
            },
            "AudioDescriptions": [
              {
                "AudioTypeControl": "FOLLOW_INPUT",
                "CodecSettings": {
                  "Codec": "AAC",
                  "AacSettings": {
                    "AudioDescriptionBroadcasterMix": "NORMAL",
                    "Bitrate": 96000,
                    "RateControlMode": "CBR",
                    "CodecProfile": "LC",
                    "CodingMode": "CODING_MODE_2_0",
                    "RawFormat": "NONE",
                    "SampleRate": 48000,
                    "Specification": "MPEG4"
                  }
                },
                "LanguageCodeControl": "FOLLOW_INPUT"
              }
            ],
            "OutputSettings": {
              "HlsSettings": {
                "AudioGroupId": "program_audio",
                "AudioOnlyContainer": "AUTOMATIC",
                "IFrameOnlyManifest": "EXCLUDE"
              }
            },
            "NameModifier": "s2"
          },
          {
            "ContainerSettings": {
              "Container": "M3U8",
              "M3u8Settings": {
                "AudioFramesPerPes": 4,
                "PcrControl": "PCR_EVERY_PES_PACKET",
                "PmtPid": 480,
                "PrivateMetadataPid": 503,
                "ProgramNumber": 1,
                "PatInterval": 0,
                "PmtInterval": 0,
                "Scte35Source": "NONE",
                "NielsenId3": "NONE",
                "TimedMetadata": "NONE",
                "TimedMetadataPid": 502,
                "VideoPid": 481,
                "AudioPids": [
                  482,
                  483,
                  484,
                  485,
                  486,
                  487,
                  488,
                  489,
                  490,
                  491,
                  492
                ]
              }
            },
            "VideoDescription": {
              "Width": 480,
              "ScalingBehavior": "DEFAULT",
              "Height": 270,
              "TimecodeInsertion": "DISABLED",
              "AntiAlias": "ENABLED",
              "Sharpness": 50,
              "CodecSettings": {
                "Codec": "H_264",
                "H264Settings": {
                  "InterlaceMode": "PROGRESSIVE",
                  "NumberReferenceFrames": 3,
                  "Syntax": "DEFAULT",
                  "Softness": 0,
                  "GopClosedCadence": 1,
                  "GopSize": 60,
                  "Slices": 1,
                  "GopBReference": "DISABLED",
                  "SlowPal": "DISABLED",
                  "SpatialAdaptiveQuantization": "ENABLED",
                  "TemporalAdaptiveQuantization": "ENABLED",
                  "FlickerAdaptiveQuantization": "DISABLED",
                  "EntropyEncoding": "CABAC",
                  "Bitrate": 800000,
                  "FramerateControl": "INITIALIZE_FROM_SOURCE",
                  "RateControlMode": "CBR",
                  "CodecProfile": "MAIN",
                  "Telecine": "NONE",
                  "MinIInterval": 0,
                  "AdaptiveQuantization": "HIGH",
                  "CodecLevel": "AUTO",
                  "FieldEncoding": "PAFF",
                  "SceneChangeDetect": "ENABLED",
                  "QualityTuningLevel": "SINGLE_PASS",
                  "FramerateConversionAlgorithm": "DUPLICATE_DROP",
                  "UnregisteredSeiTimecode": "DISABLED",
                  "GopSizeUnits": "FRAMES",
                  "ParControl": "INITIALIZE_FROM_SOURCE",
                  "NumberBFramesBetweenReferenceFrames": 2,
                  "RepeatPps": "DISABLED",
                  "DynamicSubGop": "STATIC"
                }
              },
              "AfdSignaling": "NONE",
              "DropFrameTimecode": "ENABLED",
              "RespondToAfd": "NONE",
              "ColorMetadata": "INSERT"
            },
            "AudioDescriptions": [
              {
                "AudioTypeControl": "FOLLOW_INPUT",
                "CodecSettings": {
                  "Codec": "AAC",
                  "AacSettings": {
                    "AudioDescriptionBroadcasterMix": "NORMAL",
                    "Bitrate": 96000,
                    "RateControlMode": "CBR",
                    "CodecProfile": "LC",
                    "CodingMode": "CODING_MODE_2_0",
                    "RawFormat": "NONE",
                    "SampleRate": 48000,
                    "Specification": "MPEG4"
                  }
                },
                "LanguageCodeControl": "FOLLOW_INPUT"
              }
            ],
            "OutputSettings": {
              "HlsSettings": {
                "AudioGroupId": "program_audio",
                "AudioOnlyContainer": "AUTOMATIC",
                "IFrameOnlyManifest": "EXCLUDE"
              }
            },
            "NameModifier": "s3"
          }
        ],
        "OutputGroupSettings": {
          "Type": "HLS_GROUP_SETTINGS",
          "HlsGroupSettings": {
            "ManifestDurationFormat": "INTEGER",
            "SegmentLength": 10,
            "TimedMetadataId3Period": 10,
            "CaptionLanguageSetting": "OMIT",
            "Destination": "",
            "TimedMetadataId3Frame": "PRIV",
            "CodecSpecification": "RFC_4281",
            "OutputSelection": "MANIFESTS_AND_SEGMENTS",
            "ProgramDateTimePeriod": 600,
            "MinSegmentLength": 0,
            "MinFinalSegmentLength": 0,
            "DirectoryStructure": "SINGLE_DIRECTORY",
            "ProgramDateTime": "EXCLUDE",
            "SegmentControl": "SEGMENTED_FILES",
            "ManifestCompression": "NONE",
            "ClientCache": "ENABLED",
            "StreamInfResolution": "INCLUDE"
          }
        }
      }
    ],
    "AdAvailOffset": 0,
    "Inputs": [
      {
        "AudioSelectors": {
          "Audio Selector 1": {
            "Offset": 0,
            "DefaultSelection": "DEFAULT",
            "ProgramSelection": 1
          }
        },
        "VideoSelector": {
          "ColorSpace": "FOLLOW",
          "Rotate": "DEGREE_0"
        },
        "FilterEnable": "AUTO",
        "PsiControl": "USE_PSI",
        "FilterStrength": 0,
        "DeblockFilter": "DISABLED",
        "DenoiseFilter": "DISABLED",
        "TimecodeSource": "EMBEDDED",
        "FileInput": ""
      }
    ]
  },
  "AccelerationSettings": {
    "Mode": "DISABLED"
  },
  "StatusUpdateInterval": "SECONDS_60",
  "Priority": 0
}


```

</div></details>

コードをアップロードしたら、名前を**convert-template**とし、作成する。

作成した**convert-template**をチェックし、ジョブの作成へすすむ。

<img width="1029" alt="609cfeada288282e1655be19bb66a1ab" src="https://user-images.githubusercontent.com/67918886/132085647-93423993-4fa1-4fa5-87cb-cd006ff16188.png">

入力ファイルURLにS3アップロード画像の`S3URL`を入力します。
次に左側にある`出力グループ`の**Apple HLS**を選択。

<img width="1269" alt="771f0e4be23b5398b4f7684440105887" src="https://user-images.githubusercontent.com/67918886/132085715-2dd19c7b-fb25-483c-990d-8acf88daba98.png">

`S3URL`自体は以下のように**S3**のオブジェクトからコピーします。

<img width="1068" alt="75979ef599c9b57e124a6c46afdc526b" src="https://user-images.githubusercontent.com/67918886/132085759-8b6adaf7-b3f4-4fc1-99ab-614802dbcc8e.png">

ここで出力先を選択します。
`参照`からバケット、ファルダを選択することができます。

<img width="840" alt="d056da3a575f121216c074c7aa3aa1af" src="https://user-images.githubusercontent.com/67918886/132085845-092378cd-7e3a-429f-bde7-c704fc12533b.png">

以下のように`AWSの統合`を選択し、
`既存のサービスロールを使用`を選択、
`MC-role`を選択。（さっき作成したロール）

画面下の`作成`をクリックして画像の処理をする。

<img width="1267" alt="506dd11d242bff5488336b02a6128240" src="https://user-images.githubusercontent.com/67918886/132085905-f50570ce-f3e1-43a2-8dda-ba8432503790.png">

ジョブのステータスが`COMPLETE`となれば処理完了。
以下のように**S3**の指定したフォルダへファイルが追加されていること確認（拡張子が.ts .m3u8）

<img width="1016" alt="f008fec9893a455d94c8f06a42978705" src="https://user-images.githubusercontent.com/67918886/132085992-1c703f99-6586-41ec-b491-8e0ed5f6ff17.png">

# CloudFrontの設定
先ほど処理した**S3**のコンテンツを**CloudFront**経由で配信するための設定です。

サービスから**CloudFront**を選択し、作成する。

`オリジンドメイン`で**S3**のバケットを選択する。

`S3バケットアクセス`で「はい」を選択。

`新しいOAIを作成`を選択し、自動で名前が生成される。

`バケットポリシー`は「はい、更新」を選択。

以下の図のようになる。
ここではS3 へのアクセスを「CloudFront からのみ」許可する設定をしてます。直接S3へは拒否となる。


<img width="789" alt="e437e15dd567ff468171a3ce79fc52cc" src="https://user-images.githubusercontent.com/67918886/132086352-3b81b427-9bf8-45c5-a1c6-a9197e6b659b.png">

`キャッシュポリシー`を`CachingOptimised`を選択。

`オリジンリクエストポリシー`を`CORS-S3Origin`を選択。

<img width="828" alt="882444112849a2185e47b46090255029" src="https://user-images.githubusercontent.com/67918886/132086527-17bd6dfb-8faf-474a-8eba-529450544b37.png">

以上の設定でディストリビューションの作成をする。

# S3のCORS設定

**S3**のコンソールへアクセスする。

該当の`バケット`を選択。

`アクセスの許可`を選択。

<img width="1022" alt="0a54984d79333ba97281aec1bae695e5" src="https://user-images.githubusercontent.com/67918886/132086791-20cea2cf-8639-420f-bf7f-89e0b0aeaa5e.png">


`Cross-Origin Resource Sharing`の編集をクリックして以下を入力する。

```json
[
    {
        "AllowedMethods": [
            "GET"
        ],
        "AllowedOrigins": [
            "*"
        ]
    }
]
```

下の図のように入力し、変更の保存をする。

<img width="834" alt="5e38ea8f2c76ba8a5087a5aa4f36fa29" src="https://user-images.githubusercontent.com/67918886/132086806-5d97305f-ca05-47ed-b6e0-018abe87a121.png">


# 視聴の確認

動画のURLは「https://{CloudFrontのディストリビューションドメイン名}/{動画ファイル名}.m3u8」です。

**CloudFront**の`ディストリビューションドメイン名`の確認方法は以下。

<img width="1311" alt="286070836bb58bceef48b5564cdf8e8d" src="https://user-images.githubusercontent.com/67918886/132086924-b14edd71-49d8-4ca0-93ac-0c3893b00220.png">


[再生確認ページはこちら](https://hls-js.netlify.app/demo/)

以下のように、「https://{CloudFrontのディストリビューションドメイン名}/{ファルダあれば/動画ファイル名}.m3u8」を入力し`Apply`を押すと動画が開始される。
下のほうにレートを選択できるのでそれぞれの解像度で見れることが確認できる！

![4f11d7a894c0c43b882dc481ece79770](https://user-images.githubusercontent.com/67918886/132087172-fad1120a-abc9-40b8-9c4b-c2b59e8d7ea5.jpeg)


ここまで構築できたら、あとは**Lambda**を設定して、動画がアップロードされたら、
自動で**MediaConvert**のジョブが自動で動くように設定します。

# Lambdaの設定

### Lambda用のロールを作成

コンソールから**IAM**を選択。

まずは専用のポリシーを自作します。
左の一覧から`ポリシー`を選択。

`ポリシー`の作成を選択。

`JSON`タブを選択し、以下のコードを入力する。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "mediaconvert:CreateJob",
                "logs:CreateLogStream",
                "logs:CreateLogGroup",
                "logs:PutLogEvents",
                "iam:PassRole"
            ],
            "Resource": "*"
        }
    ]
}

```
**MediaConvert**へのアクセスや**CloudWatch**へのログ書き込みを許可してます。

ポリシー名は「LambdaCustomPolicy」でポリシー作成します。

### Lambda用のロール作成
さきほどのポリシーでロール作成します。

`ロールの作成`を選択し、

一般的なユースケースから`Lambda`を選択。

ポリシー選択はさきぼどの`「LambdaCustomPolicy」`を検索しアタッチする。

ロール名を`「MC-Lambda-role」`としました。
名称は任意です。

### 関数作成

マネージメントコンソールよ**Lambda**を選択。

`関数作成`をクリック。

以下のように
任意の関数名を入力。

`ランタイム`は`Python3.8`を選択。

既存のロールから先ほど作成したLambda用のロールを選択。

<img width="1257" alt="c7a697bfbb23ce7fe0a9b32e49291926" src="https://user-images.githubusercontent.com/67918886/132087618-523287fc-6bf0-41df-aa57-14d4308d6dbd.png">

トリガーとして**S3**を選択します。
以下のように
`バケット`を選択し、

`サフィックス`に「.mp4」を入力。

再帰呼び出しの注意事項に同意し`追加`をクリック。

指定したS3バケットにmp4の拡張子のファイルがアップロードされたら実行するというトリガーです。


<img width="832" alt="e877958de804b06ba721105c91f616e8" src="https://user-images.githubusercontent.com/67918886/132087698-e3cb5453-3fb0-4729-bbb7-d25093725f51.png">

次に実行するソースをアップロードします。

コードタブを選択。

`アップロード元`から「.zipファイル」を選択しコードをアップロードします。


<img width="897" alt="d25e9736eeed1a9a1eca5ba43992c196" src="https://user-images.githubusercontent.com/67918886/132087866-fd64e6a9-30f0-47f3-b1f8-52d74cd45de8.png">


アップロードするコードは以下です。ほとんどハンズオンと同じです。
・job.json
・lambda_function.py
の二つでzip化してまとめてアップロードします。
※注意点は他の不要なファイルが入ってるとうまく動作しません！

```json:job.json
{
    "Inputs": [
        {
            "FileInput": ""
        }
    ],
    "OutputGroups": [
        {
            "OutputGroupSettings": {
                "HlsGroupSettings": {
                    "Destination": ""
                }
            }
        }
    ]
}
```

ハンズオンと違う点は以下のコードです。
ディレクトリーの情報を取得し、
出力するときの同じ階層となるように加工してます。
`print()`はデバック用です。

```python:lambda_function.py

import json
import urllib.parse
import boto3
import os
import pathlib
# import pprint

s3 = boto3.client('s3')

def lambda_handler(event, context):
    account = context.invoked_function_arn.split(':')[4]
    region = event['Records'][0]['awsRegion']
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')

　　　　　　　　#環境変数の値を代入
    template_name = os.environ['template_name']
    role_name = os.environ['role_name']

    inputFile = "s3://" + bucket + "/" + key
    print('inputFile=' + inputFile)

    pfile = pathlib.Path(inputFile)
    pdir = os.path.dirname(key)　#ディレクトリ名を取得
    print('pdir=' + pdir)　#確認

    # outputKey = "s3://" + bucket + "/output/"
    outputKey = "s3://" + bucket + "/" + pdir + "/output/"
    print('outputKey=' + outputKey)　#問題ないか確認

    mediaconvert = boto3.client(
        'mediaconvert',
        region_name=region,
        endpoint_url=os.environ['endpoint_url']
    )

    with open("job.json", "r") as jsonfile:
        job_object = json.load(jsonfile)

　　　　　　　　#jsonファイルへインプット元のファイルと出力先を代入してMCへ渡す
    job_object["OutputGroups"][0]["OutputGroupSettings"]["HlsGroupSettings"]["Destination"] = outputKey
    job_object["Inputs"][0]["FileInput"] = inputFile

    response = mediaconvert.create_job(
        JobTemplate='arn:aws:mediaconvert:%s:%s:jobTemplates/%s' % (region, account, template_name),
        Queue='arn:aws:mediaconvert:%s:%s:queues/Default' % (region, account),
        Role='arn:aws:iam::%s:role/%s' % (account, role_name),
        Settings=job_object
    )

    # pprint.pprint(event['Records'])
    # pprint.pprint(event['Records'][0]
    # pprint.pprint(event['Records'][0]['s3'])
    # pprint.pprint(event['Records'][0]['s3']['object'])
    # pprint.pprint(event['Records'][0]['s3']['object']['key'])
    # print(event['Records'])
    # print(os.environ)
    # print(context.invoked_function_arn.split(':'))
```

### ここで注意点

アップロードしただけだと下のように上位のフォルダがあり、うまく動作しません。
私は初めてのLambdaだったのでここがよくわからずつまりました（ToT）

トップに
・job.json
・lambda_function.py
をもってくるようにしましょう！（あたりまえ）

<img width="824" alt="545136af73ae5627f54973ce7fb8ec38" src="https://user-images.githubusercontent.com/67918886/132088229-90abf4af-0ee3-4145-b654-cd39055f2e5a.png">

### 環境変数の設定

`設定`ダブを選択。

`環境変数`を選択。


<img width="878" alt="92d307dd86177a111ce6535f8ad28be1" src="https://user-images.githubusercontent.com/67918886/132088350-6a624ab3-5af5-4c5e-8c07-b70662d283e5.png">

`編集`をクリックして、環境変数を追加します。
追加する内容は以下。

**endpoint_url**:
MediaConvert の API endpoint
「https://xxxxxx.mediaconvert.ap-northeast-1.amazonaws.com」

<details><summary>エンドポイント確認方法</summary><div>


**MediaConvert**のアカウントから確認。

<img width="898" alt="752ce136aca8421b5584db3336629b2a" src="https://user-images.githubusercontent.com/67918886/132088526-048f9e4e-bd93-4494-9be5-9b558a6381d2.png">

</div></details>

**role_name**:
MediaConvert用のrole

※ここでの注意点ですが、MediaConvert用のロールです！
私は間違ってLambdaのロールを入力し詰まりました・・・
Lambdaの編集だから・・・と。

**template_name**:
MediaConvertで作成したtemplate名「convert-template」

を`キー`、`値`として入力してください。

これで設定完了です！

**S3**に動画ファイル（.mp4）をアップロードすると
数秒後に`output`フォルダが作成されます！
ファルダの中に複数ファイルが作成されでます！

**CloudFront**経由で見れることを確認します。

私はこれを**CORS**でWEBアプリに反映されました＾＾

# うまく動作しないとき

ログを確認します。

**Lambda**の`モニタリング`タブをクリック。
 `CloudWatchのログを表示`をクリックするとログが確認できます。
ここでも問題なければ**MediaConvert**のジョブを確認すれば
何らしか失敗した原因が表示されてます！

<img width="950" alt="c7de6abe0b69e63e5c88c56b19975226" src="https://user-images.githubusercontent.com/67918886/132088961-bb23b1ff-371d-4e1e-8bc9-d8930dc01b87.png">