16
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

AWS MediaConvert と CloudFront 署名付きCookie で HLS+AES動画配信システムを作る

Last updated at Posted at 2020-12-30

#0. はじめに
##0.1. 目的

  • DRM動画配信サービス的なシステムをAWSを使って安価に作ってみる。
  • このシステムのDRM的なこと
  • 動画ファイルを AES-128 で暗号化/復号する
  • ストリーミング動画と鍵を署名付きCookieで配信する

##0.2. 完成形
007.png

##0.3 構築ステップ

  1. 動画アップロード用とCloudFrontオリジン用のS3バケット作成 と MediaConvert の動作確認
  2. 動画配信用CloudFront作成、ACM作成、S3バケットOAI設定
  3. CloudFront 署名付きURL/Cookie 設定
  4. AES-128鍵用のS3バケットとCloudFrontを作成
  5. Webサーバ(Apache+PHP)を作成、 CORS設定、 Video.js で Web Player 作成
  6. アップロードした動画ファイルをHLS+AES動画へ自動変換するLambdaを作成

##0.4. AWS

  • AWSリージョン
  • ACM以外はすべて「アジアパシフィック (東京)ap-northeast-1」
  • ACMはCloudFrontで使用するため「米国東部 (バージニア北部)us-east-1」

##0.5. ローカル作業環境
####ローカル作業PC

  • Mac

####Terraform

  • terraform バージョン
    • v0.12.9
  • aws provider バージョン
    • 2.48.0
  • terraform 用 IAM ユーザーのポリシー
    • AdministratorAccess
    • IAMFullAccess
  • terraform 作業ディレクトリ (システム完成形時のツリー)
$ tree terraform/
terraform/
├── cloudfront_cdn-ni4-6ra-net.tf
├── cloudfront_drm-aeskey-ni4-6ra-net.tf
├── cloudfront_key
│   ├── policy.json
│   ├── private_key.pem
│   └── public_key.pem
├── cloudfront_public-key.tf
├── credentials
├── ec2_web.tf
├── iam-role_lambda_mediaconvert_ni4-6ra-net.tf
├── iam-role_mediaconvert_ni4-6ra-net.tf
├── provider.tf
├── s3-bucket_drm-ni4-6ra-net-aeskey.tf
├── s3-bucket_drm-ni4-6ra-net-cloudfront-origin.tf
├── s3-bucket_drm-ni4-6ra-net-upload-origin.tf
├── sg_ec2_web.tf
└── terraform.tfstate
provider.tf
provider "aws" {
  version                 = "2.48.0"
  shared_credentials_file = "credentials"
  profile                 = "terraform"
  region                  = "ap-northeast-1"
}
credentials
[terraform]
aws_access_key_id = XXXXXXXXXXXXXXXXXXXX
aws_secret_access_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

#1. ステップ1 - 動画アップロード用とCloudFrontオリジン用のS3バケット作成 と MediaConvert の動作確認
##1.1. ステップ1 のゴール

  • 動画アップロード用S3バケットに動画ファイルをアップロードして、MediaConvertでHLS動画に変換してCloudFrontオリジン用S3バケットへ出力する。
  • 出力された HLS動画ファイルをローカルPCにダウンロードして、ffmpeg を使って mp4 ファイルに変換して QuickTime Player で再生する。
  • 完成図
    001.png

##1.2. ステップ1 構築
###1.2.1. オリジナル動画ファイルアップロード用 S3 バケット作成して、movie フォルダを作成

####S3バケット: drm-ni4-6ra-net-upload-origin

s3-bucket_drm-ni4-6ra-net-upload-origin.tf
resource "aws_s3_bucket" "drm-ni4-6ra-net-upload-origin" {
  bucket        = "drm-ni4-6ra-net-upload-origin"
  acl           = "private"
  force_destroy = "false"
  region        = "ap-northeast-1"
  versioning {
    enabled = false
  }
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}
resource "aws_s3_bucket_public_access_block" "drm-ni4-6ra-net-upload-origin" {
  depends_on              = [aws_s3_bucket.drm-ni4-6ra-net-upload-origin]
  bucket                  = "drm-ni4-6ra-net-upload-origin"
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}
resource "aws_s3_bucket_object" "drm-ni4-6ra-net-upload-origin-movie" {
  depends_on             = [aws_s3_bucket.drm-ni4-6ra-net-upload-origin]
  bucket                 = aws_s3_bucket.drm-ni4-6ra-net-upload-origin.id
  acl                    = "private"
  key                    = "movie/"
  server_side_encryption = "AES256"
}

####作成したS3バケット
001.png
002.png

###1.2.2. CloudFrontオリジン用 S3 バケット作成して、movie フォルダを作成
####S3バケット: drm-ni4-6ra-net-cloudfront-origin

s3-bucket_drm-ni4-6ra-net-cloudfront-origin.tf
resource "aws_s3_bucket" "drm-ni4-6ra-net-cloudfront-origin" {
  bucket        = "drm-ni4-6ra-net-cloudfront-origin"
  acl           = "private"
  force_destroy = "false"
  region        = "ap-northeast-1"
  versioning {
    enabled = false
  }
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}
resource "aws_s3_bucket_public_access_block" "drm-ni4-6ra-net-cloudfront-origin" {
  depends_on              = [aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin]
  bucket                  = "drm-ni4-6ra-net-cloudfront-origin"
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}
resource "aws_s3_bucket_object" "drm-ni4-6ra-net-cloudfront-origin-movie" {
  depends_on             = [aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin]
  bucket                 = aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin.id
  acl                    = "private"
  key                    = "movie/"
  server_side_encryption = "AES256"
}

####作成したS3バケット
003.png
004.png

###1.2.3. MediaConvert で使用する IAMロールを作成
####IAMロール: mediaconvert_ni4-6ra-net

iam-role_mediaconvert_ni4-6ra-net.tf
data "aws_iam_policy_document" "mediaconvert-assume-role-policy" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["mediaconvert.amazonaws.com"]
    }
  }
}

data "aws_iam_policy_document" "iam_drm-ni4-6ra-net-upload-origin" {
  statement {
    effect = "Allow"
    actions = [
      "s3:GetObject"
    ]
    resources = [
      "arn:aws:s3:::drm-ni4-6ra-net-upload-origin/*",
      "arn:aws:s3:::drm-ni4-6ra-net-upload-origin"
    ]
  }
}
resource "aws_iam_policy" "iam_drm-ni4-6ra-net-upload-origin" {
  name   = "s3_drm-ni4-6ra-net-upload-origin"
  policy = data.aws_iam_policy_document.iam_drm-ni4-6ra-net-upload-origin.json
}

data "aws_iam_policy_document" "iam_drm-ni4-6ra-net-cloudfront-origin" {
  statement {
    effect = "Allow"
    actions = [
      "s3:PutObject"
    ]
    resources = [
      "arn:aws:s3:::drm-ni4-6ra-net-cloudfront-origin/*",
      "arn:aws:s3:::drm-ni4-6ra-net-cloudfront-origin"
    ]
  }
}
resource "aws_iam_policy" "iam_drm-ni4-6ra-net-cloudfront-origin" {
  name   = "s3_drm-ni4-6ra-net-cloudfront-origin"
  policy = data.aws_iam_policy_document.iam_drm-ni4-6ra-net-cloudfront-origin.json
}

resource "aws_iam_role" "mediaconvert_ni4-6ra-net" {
  name               = "mediaconvert_ni4-6ra-net"
  assume_role_policy = data.aws_iam_policy_document.mediaconvert-assume-role-policy.json
}

resource "aws_iam_role_policy_attachment" "mediaconvet_ni4-6ra-net_drm-ni4-6ra-net-upload-origin" {
  role       = aws_iam_role.mediaconvert_ni4-6ra-net.name
  policy_arn = aws_iam_policy.iam_drm-ni4-6ra-net-upload-origin.arn
}

resource "aws_iam_role_policy_attachment" "mediaconvert_ni4-6ra-net_drm-ni4-6ra-net-cloudfront-origin" {
  role       = aws_iam_role.mediaconvert_ni4-6ra-net.name
  policy_arn = aws_iam_policy.iam_drm-ni4-6ra-net-cloudfront-origin.arn
}

####作成したIAMロール
005.png
006.png
007.png
008.png

###1.2.4. デモ用動画を S3バケット drm-ni4-6ra-net-upload-origin にアップロード
####デモ用動画

####S3バケット drm-ni4-6ra-net-upload-origin の movie フォルダにアップロード

  • AWS マネジメントコンソールから直接
    009.png

###1.2.5. MediaConvert で MP4 ⇒ HLS へ変換
####MediaConvertでジョブを作成

  • AWS マネジメントコンソールから直接
    ジョブ
    010.png
    ジョブ作成
    011.png
    入力ファイルURL ⇒ S3://drm-ni4-6ra-net-upload-origin/movie/demo01.mp4 を選択
    012.png
    出力グループ追加
    013.png
    Apple HLS を選択
    014.png
    出力 Output 1
    015.png
    名前修飾子 必須項目 適当な名前を入れておく(ここでは _non-aes)
    016.png
    送信先 ⇒ S3://drm-ni4-6ra-net-cloudfront-origin/movie/ を選択
    022.png
    解像度 ⇒ 空にしておく(空にするとパススルー設定となり、オリジナルと同じ解像度になる)
    017.png
    ビットレート ⇒ 必須項目 適当に 2000kbps にしておく
    018.png
    ジョブの設定 ⇒ AWS の統合
    019.png
    IAMロール ⇒ MediaConvert で使う IAMロールを指定する
    020.png
    以上、設定が終わったら 作成

  • ジョブのステータス確認
    ステータス SUBMITTED ⇒ PROGRESSING ⇒ COMPLETE
    023.png

  • S3 バケット drm-ni4-6ra-net-cloudfront-origin/movie/ 確認
    024.png

###1.2.6. 再生確認
####次のファイルをローカル作業PCにダウンロードする

demo01_non-aes.m3u8
demo01_non-aes_00001.ts
demo01_non-aes_00002.ts

demo01_non-aes.m3u8 はテキストファイル
中身はこんな感じ

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:11
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:11,
demo01_non-aes_00001.ts
#EXTINF:9,
demo01_non-aes_00002.ts
#EXT-X-ENDLIST

####ffmpeg で HLS ⇒ MP4 に変換

  • ffmepg インストール
$ brew install ffmpeg
  • ダウンロードしたファイルのあるフォルダで下記コマンドを実行
$ ffmpeg -protocol_whitelist file,http,https,tcp,tls,crypto -i demo01_non-aes.m3u8 -c copy demo01_non-aes.mp4
  • 生成された demo01_non-aes.mp4 を QuickTime Player で再生してみる
$ open demo01_non-aes.mp4

#2. ステップ2 - 動画配信用CloudFront作成、ACM作成、S3バケットOAI設定
##2.1. ステップ2 のゴール

  • CloudFront経由で S3バケット drm-ni4-6ra-net-cloudfront-origin 内のオブジェクトにアクセスする。
  • 完成図
    002.png

##2.2. ステップ2 構築
###2.2.1. ACM作成

  • 「米国東部 (バージニア北部)us-east-1」に作成する
  • 手順は割愛
  • 作成したACM (ワイルドカード)

image.png

###2.2.2. CloudFront作成

  • S3バケット s3-bucket_drm-ni4-6ra-net-cloudfront-origin で「パブリックアクセスをすべてブロックする」設定を入れているので、OAI 設定がないと CloudFront から S3バケットにアクセスしたときに AccessDenied となる。

####CloudFront: E2L8V7QYSFINZX

cloudfront_cdn-ni4-6ra-net.tf
resource "aws_cloudfront_origin_access_identity" "drm-ni4-6ra-net-cloudfront-origin" {
  comment = "S3 drm-ni4-6ra-net-cloudfront-origin"
}
resource "aws_cloudfront_distribution" "cdn-ni4-6ra-net" {
  aliases = [
    "cdn.ni4-6ra.net"
  ]
  enabled         = true
  is_ipv6_enabled = true
  default_cache_behavior {
    allowed_methods        = ["GET", "HEAD", "OPTIONS"]
    cached_methods         = ["GET", "HEAD"]
    default_ttl            = "0"
    max_ttl                = "0"
    min_ttl                = "0"
    target_origin_id       = "drm-ni4-6ra-net-cloudfront-origin"
    viewer_protocol_policy = "https-only"
    forwarded_values {
      headers = [
        "Access-Control-Request-Headers",
        "Access-Control-Request-Method",
        "Origin"
      ]
      query_string            = true
      query_string_cache_keys = []
      cookies {
        forward           = "none"
        whitelisted_names = []
      }
    }
  }
  origin {
    domain_name = "drm-ni4-6ra-net-cloudfront-origin.s3.amazonaws.com"
    origin_id   = "drm-ni4-6ra-net-cloudfront-origin"
    s3_origin_config {
      origin_access_identity = aws_cloudfront_origin_access_identity.drm-ni4-6ra-net-cloudfront-origin.cloudfront_access_identity_path
    }
  }
  viewer_certificate {
    acm_certificate_arn            = "arn:aws:acm:us-east-1:000000000000:certificate/00000000-0000-0000-0000-000000000000"
    cloudfront_default_certificate = false
    minimum_protocol_version       = "TLSv1.2_2019"
    ssl_support_method             = "sni-only"
  }
  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }
}

####作成した CloudFront の設定抜粋

025.png
026.png
OAI (Origin Access Identity) 設定
027.png
動作確認中に変なキャッシュをしないようにキャッシュしない設定にしておく
028.png

###2.2.3. Route 53 設定
CloudFront の Domain Name と Alternate Domain Names の CNAME 設定
029.png
030.png

※この時点で https://cdn.ni4-6ra.net にアクセスしてみると 「Access Denied」 となる
031.png

###2.2.4. S3バケット drm-ni4-6ra-net-cloudfront-origin に OAI ポリシーを追加

####S3バケット: drm-ni4-6ra-net-cloudfront-origin
※さきほどの s3-bucket_drm-ni4-6ra-net-cloudfront-origin.tf に追記する

s3-bucket_drm-ni4-6ra-net-cloudfront-origin.tf
resource "aws_s3_bucket" "drm-ni4-6ra-net-cloudfront-origin" {
  bucket        = "drm-ni4-6ra-net-cloudfront-origin"
  acl           = "private"
  force_destroy = "false"
  region        = "ap-northeast-1"
  versioning {
    enabled = false
  }
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}
resource "aws_s3_bucket_public_access_block" "drm-ni4-6ra-net-cloudfront-origin" {
  depends_on              = [aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin]
  bucket                  = "drm-ni4-6ra-net-cloudfront-origin"
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}
resource "aws_s3_bucket_object" "drm-ni4-6ra-net-cloudfront-origin-movie" {
  depends_on             = [aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin]
  bucket                 = aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin.id
  acl                    = "private"
  key                    = "movie/"
  server_side_encryption = "AES256"
}

## 下記を追記
data "aws_iam_policy_document" "s3bucket_drm-ni4-6ra-net-cloudfront-origin" {
  version = "2012-10-17"
  statement {
    sid    = "Grant a CloudFront Origin Identity access"
    effect = "Allow"
    principals {
      type        = "AWS"
      identifiers = [aws_cloudfront_origin_access_identity.drm-ni4-6ra-net-cloudfront-origin.iam_arn]
    }
    actions = [
      "s3:GetObject",
      "s3:List*"
    ]
    resources = [
      aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin.arn,
      "${aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin.arn}/*"
    ]
  }
}
resource "aws_s3_bucket_policy" "drm-ni4-6ra-net-cloudfront-origin" {
  bucket = aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin.bucket
  policy = data.aws_iam_policy_document.s3bucket_drm-ni4-6ra-net-cloudfront-origin.json
}

####追加した設定
032.png
033.png

###2.2.5. OAIアクセス確認

https://cdn.ni4-6ra.net にアクセス
034.png

#3. ステップ3 - CloudFront 署名付きURL/Cookie 設定
##3.1. ステップ3 のゴール

  • 署名付きCookieリクエストでストリーミング動画ファイルにアクセスする
  • 完成図
    003.png

##3.3. ステップ3 構築
###3.2.1. CloudFront署名付き URL/Cookie 向け公開鍵を作成して、Key Groups を設定
(参考)
root ユーザー作業が不要に!Amazon CloudFront で署名付き URL/Cookie 向け公開鍵を IAM ユーザー権限で管理できるようになりました。

####キーペアを作成
ローカル作業PCのterraform作業ディレクトリで

$ mkdir cloudfront_key
$ cd cloudfront_key
$ openssl genrsa -out private_key.pem 2048
$ openssl rsa -pubout -in private_key.pem -out public_key.pem

秘密鍵 ・・・ private_key.pem
公開鍵 ・・・ public_key.pem

####公開鍵をCloudFrontにアップロードする

cloudfront_public-key.tf
resource "aws_cloudfront_public_key" "cloudfront-public-key" {
  comment     = "cloudfront public key"
  encoded_key = file("./cloudfront_key/public_key.pem")
  name        = "cloudfront-public-key"
}

####アップロードした公開鍵
ID 「K1A3BSF7L9SNGX」 は後で必要になるのでメモっておく
035.png

####Key Groups 設定
key group を作成する terraform リソースがまだないので、マネジメントコンソールから手動で設定する
036.png
037.png
038.png

###3.2.2. CloudFront署名付き URL/Cookie を設定
CloudFront trusted key groups を設定する terraform リソースがまだないので、マネジメントコンソールから手動で設定する
039.png
040.png
Yes, Edit
041.png

###3.2.3. 署名付きCookieリクエストを作成して動作確認

(参考)
署名付き Cookie を使用して HLS コンテンツを取得してみた
カスタムポリシーを使用する署名付き Cookie の設定
Linux コマンドおよび OpenSSL を使用した Base64 エンコードおよび暗号化

####カスタムポリシーを使用する署名付き Cookie のポリシーステートメントの作成
ローカル作業PCのさきほどキーペアを作成したディレクトリにpolicy.jsonファイルを作成する
EpochTime は下記のサイトで求める(今回は検証用なので1年後の日付にしている)
https://www.epochconverter.com/

policy.json
{
    "Statement": [
        {
            "Resource": "https://*.ni4-6ra.net/*",
            "Condition": {
                "DateLessThan": {
                    "AWS:EpochTime": 1640698309
                }
            }
        }
    ]
}

####ポリシーステートメントを base64 でエンコード・・・①

$ cat policy.json | tr -d "\n" | tr -d " \t\n\r" | openssl base64 | tr -- '+=/' '-_~' | tr -d "\n" | tr -d " \t\n\r"

eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly8qLm5pNC02cmEubmV0LyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2NDA2OTgzMDl9fX1dfQ__

####ポリシーステートメントをハッシュ化して署名・・・②

$ cat policy.json | tr -d "\n" | tr -d " \t\n\r" | openssl sha1 -sign private_key.pem | openssl base64 | tr -- '+=/' '-_~' | tr -d "\n" | tr -d " \t\n\r"

jTZDJUJBwV7qvim-baunYIZbkrQzVCu0Y8581PuStrYyjcki-5evNlpHqat1gTFSIGB8BcvhDyBRcBse~l6VKCoRw12HYNYpjYm1mAZjjmYjF0a7h4DrANDhAw~6B4VS5tNHntQ9zKr3xDZbzqwQEK-8D8xfMVWSHgdFn0OUef-zGeYp4mmty37tHWmXILHedrjjgLceXIkwIX-FPPMPwGMoPdV-Bu7cLckFZoXVxstHb64im-yp5yq0lzCjqK~VGMlD7Kh-qu9g2KKe15vIFZfDlpDc2y~E1uYmSVfwtbo6WaddaU26O02FCX0kKmu6CDcsR9XFvTwPru~L8PnKow__

####①と②を次のリクエストに置き換え、③はPublic key ID 「K1A3BSF7L9SNGX」に置き換える

$ curl https://cdn.ni4-6ra.net/movie/demo01_non-aes.m3u8 -b 'CloudFront-Policy=①; CloudFront-Signature=②; CloudFront-Key-Pair-Id=③' 

$ curl https://cdn.ni4-6ra.net/movie/demo01_non-aes.m3u8 -b 'CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly8qLm5pNC02cmEubmV0LyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2NDA2OTgzMDl9fX1dfQ__; CloudFront-Signature=jTZDJUJBwV7qvim-baunYIZbkrQzVCu0Y8581PuStrYyjcki-5evNlpHqat1gTFSIGB8BcvhDyBRcBse~l6VKCoRw12HYNYpjYm1mAZjjmYjF0a7h4DrANDhAw~6B4VS5tNHntQ9zKr3xDZbzqwQEK-8D8xfMVWSHgdFn0OUef-zGeYp4mmty37tHWmXILHedrjjgLceXIkwIX-FPPMPwGMoPdV-Bu7cLckFZoXVxstHb64im-yp5yq0lzCjqK~VGMlD7Kh-qu9g2KKe15vIFZfDlpDc2y~E1uYmSVfwtbo6WaddaU26O02FCX0kKmu6CDcsR9XFvTwPru~L8PnKow__; CloudFront-Key-Pair-Id=K1A3BSF7L9SNGX'

成功すれば、下記のレスポンスが返る

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:11
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-PLAYLIST-TYPE:VOD
#EXTINF:11,
demo01_non-aes_00001.ts
#EXTINF:9,
demo01_non-aes_00002.ts
#EXT-X-ENDLIST

#4. ステップ4 - AES-128鍵用のS3バケットとCloudFrontを作成
##4.1. ステップ4 のゴール

  • MediaConvertでHLS動画をAES暗号化する。
  • HLS+AES動画ファイルをローカルPCにダウンロードして、ffmpeg を使って復号して mp4 ファイルに変換して QuickTime Player で再生する。
  • 完成図
    004.png

##4.2. ステップ4 構築
###4.2.1. AES鍵用S3バケットを作成
####S3バケット: drm-ni4-6ra-net-aeskey

s3-bucket_drm-ni4-6ra-net-aeskey.tf
resource "aws_s3_bucket" "drm-ni4-6ra-net-aeskey" {
  bucket        = "drm-ni4-6ra-net-aeskey"
  acl           = "private"
  force_destroy = false
  region        = "ap-northeast-1"
  versioning {
    enabled = false
  }
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }

resource "aws_s3_bucket_public_access_block" "drm-ni4-6ra-net-aeskey" {
  depends_on              = [aws_s3_bucket.drm-ni4-6ra-net-aeskey]
  bucket                  = "${aws_s3_bucket.drm-ni4-6ra-net-aeskey.id}"
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

####作成したS3バケット
042.png

###4.2.2. AES鍵用CloudFrontを作成
####CloudFront: E28HDM2W6KJHQ2

cloudfront_drm-aeskey-ni4-6ra-net.tf
resource "aws_cloudfront_origin_access_identity" "drm-ni4-6ra-net-aeskey" {
  comment = "S3 drm-ni4-6ra-net-aeskey"
}
resource "aws_cloudfront_distribution" "drm-aeskey-ni4-6ra-net" {
  aliases = [
    "drm-aeskey.ni4-6ra.net"
  ]
  enabled         = true
  is_ipv6_enabled = true
  default_cache_behavior {
    allowed_methods        = ["GET", "HEAD", "OPTIONS"]
    cached_methods         = ["GET", "HEAD"]
    default_ttl            = "0"
    max_ttl                = "0"
    min_ttl                = "0"
    target_origin_id       = "drm-ni4-6ra-net-aeskey"
    viewer_protocol_policy = "https-only"
    forwarded_values {
      headers = [
        "Access-Control-Request-Headers",
        "Access-Control-Request-Method",
        "Origin"
      ]
      query_string            = true
      query_string_cache_keys = []
      cookies {
        forward           = "none"
        whitelisted_names = []
      }
    }
  }
  origin {
    domain_name = "drm-ni4-6ra-net-aeskey.s3.amazonaws.com"
    origin_id   = "drm-ni4-6ra-net-aeskey"
    s3_origin_config {
      origin_access_identity = aws_cloudfront_origin_access_identity.drm-ni4-6ra-net-aeskey.cloudfront_access_identity_path
    }
  }
  viewer_certificate {
    acm_certificate_arn            = "arn:aws:acm:us-east-1:000000000000:certificate/00000000-0000-0000-0000-000000000000"
    cloudfront_default_certificate = false
    minimum_protocol_version       = "TLSv1.2_2019"
    ssl_support_method             = "sni-only"
  }
  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }
}

####作成した CloudFront の設定抜粋
043.png
044.png
OAI (Origin Access Identity) 設定
045.png
動作確認中に変なキャッシュをしないようにキャッシュしない設定にしておく
046.png

###4.2.3. Route 53 設定
CloudFront の Domain Name と Alternate Domain Names の CNAME 設定
047.png
048.png

###4.2.4. S3バケット drm-ni4-6ra-net-aeskey に OAI ポリシーを追加
####S3バケット drm-ni4-6ra-net-aeskey
※さきほどの s3-bucket_drm-ni4-6ra-net-aeskey.tf に追記する

s3-bucket_drm-ni4-6ra-net-aeskey.tf
resource "aws_s3_bucket" "drm-ni4-6ra-net-aeskey" {
  bucket        = "drm-ni4-6ra-net-aeskey"
  acl           = "private"
  force_destroy = false
  region        = "ap-northeast-1"
  versioning {
    enabled = false
  }
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
}
resource "aws_s3_bucket_public_access_block" "drm-ni4-6ra-net-aeskey" {
  depends_on              = [aws_s3_bucket.drm-ni4-6ra-net-aeskey]
  bucket                  = "${aws_s3_bucket.drm-ni4-6ra-net-aeskey.id}"
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

##下記を追記
data "aws_iam_policy_document" "s3bucket_drm-ni4-6ra-net-aeskey" {
  version = "2012-10-17"
  statement {
    sid    = "Grant a CloudFront Origin Identity access"
    effect = "Allow"
    principals {
      type        = "AWS"
      identifiers = [aws_cloudfront_origin_access_identity.drm-ni4-6ra-net-aeskey.iam_arn]
    }
    actions = [
      "s3:GetObject",
      "s3:List*"
    ]
    resources = [
      aws_s3_bucket.drm-ni4-6ra-net-aeskey.arn,
      "${aws_s3_bucket.drm-ni4-6ra-net-aeskey.arn}/*"
    ]
  }
}
resource "aws_s3_bucket_policy" "drm-ni4-6ra-net-aeskey" {
  bucket = aws_s3_bucket.drm-ni4-6ra-net-aeskey.bucket
  policy = data.aws_iam_policy_document.s3bucket_drm-ni4-6ra-net-aeskey.json
}

####追加した設定
049.png
050.png

###4.2.5. CloudFront署名付き URL/Cookie を設定
CloudFront trusted key groups を設定する terraform リソースがまだないので、マネジメントコンソールから手動で設定する
051.png
040.png
Yes, Edit
052.png

※この時点で https://drm-aeskey.ni4-6ra.net/ にアクセスしてみると 「Missing Key-Pair-Id query parameter or cookie value」 となる
053.png

###4.2.6. AES鍵を作成して、S3バケット drm-ni4-6ra-net-aeskey へアップロードする
ローカル作業PCで

####静的キーを作成する

$ openssl rand 16 > aes.key

####作成した静的キーから「静的キーの値」を生成 (MediaConverrt DRM暗号化設定に必要な値)

$ xxd -ps aes.key
54a65de338c972d3f1dafe95ac936ad2

####「定数初期化ベクトル」を生成 (MediaConverrt DRM暗号化設定に必要な値)

$ openssl rand -hex 16
d190b98ad29394fd6883a5dbd062a205

####aes.keyファイルをS3バケット drm-ni4-6ra-net-aeskey へアップロード
054.png

###4.2.7. 署名付きCookieリクエストで動作確認

署名付きCookieリクエストで https://drm-aeskey.ni4-6ra.net/aes.key を取得

$ curl https://drm-aeskey.ni4-6ra.net/aes.key -b 'CloudFront-Policy=①; CloudFront-Signature=②; CloudFront-Key-Pair-Id=③' 

「3.2.3. 署名付きCookieリクエストを作成して動作確認」で作成した①と②をそのまま使う。
③もPublic key ID 「K1A3BSF7L9SNGX」を使う。

$ curl https://drm-aeskey.ni4-6ra.net/aes.key -b 'CloudFront-Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly8qLm5pNC02cmEubmV0LyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2NDA2OTgzMDl9fX1dfQ__; CloudFront-Signature=jTZDJUJBwV7qvim-baunYIZbkrQzVCu0Y8581PuStrYyjcki-5evNlpHqat1gTFSIGB8BcvhDyBRcBse~l6VKCoRw12HYNYpjYm1mAZjjmYjF0a7h4DrANDhAw~6B4VS5tNHntQ9zKr3xDZbzqwQEK-8D8xfMVWSHgdFn0OUef-zGeYp4mmty37tHWmXILHedrjjgLceXIkwIX-FPPMPwGMoPdV-Bu7cLckFZoXVxstHb64im-yp5yq0lzCjqK~VGMlD7Kh-qu9g2KKe15vIFZfDlpDc2y~E1uYmSVfwtbo6WaddaU26O02FCX0kKmu6CDcsR9XFvTwPru~L8PnKow__; CloudFront-Key-Pair-Id=K1A3BSF7L9SNGX'

aes.key はバイナリファイルなので、次のようなレスポンスが返る

T�]�8�r�������j�

###4.2.8. MediaConvertで MP4 ⇒ HLS+AES動画へ変換
####MediaConvertでジョブを作成
ジョブ
010.png
ジョブ作成
011.png
入力ファイルURL ⇒ S3://drm-ni4-6ra-net-upload-origin/movie/demo01.mp4 を選択
012.png
出力グループ 追加
013.png
Apple HLS を選択
014.png
送信先 ⇒ S3://drm-ni4-6ra-net-cloudfront-origin/movie/ を選択
022.png
DRM 暗号化
・定数初期化ベクトル: d190b98ad29394fd6883a5dbd062a205
・静的キーの値: 54a65de338c972d3f1dafe95ac936ad2
・URL: https://drm-aeskey.ni4-6ra.net/aes.key
055.png
出力 Output 1
015.png
名前修飾子 ⇒ 必須項目 適当な名前を入れておく (ここでは _aes)
056.png
解像度 ⇒ 空にしておく(空にするとパススルー設定となり、オリジナルと同じ解像度になる)
017.png
ビットレート ⇒ 必須項目 適当に 2000kbps にしておく
018.png
ジョブの設定 ⇒ AWS の統合
019.png
IAMロール ⇒ MediaConvert で使う IAMロールを指定する
020.png
以上、設定が終わったら 作成

  • ジョブのステータス確認
    ステータス SUBMITTED ⇒ PROGRESSING ⇒ COMPLETE

  • S3 バケット drm-ni4-6ra-net-cloudfront-origin/movie/ 確認
    057.png

###4.2.9. 再生確認
####次のファイルをローカル作業PCの aes.key があるフォルダにダウンロードする

demo01_aes.m3u8
demo01_aes_00001.ts
demo01_aes_00002.ts

demo01_aes.m3u8 の中身はこんな感じ

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:11
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-KEY:METHOD=AES-128,URI="https://drm-aeskey.ni4-6ra.net/aes.key",IV=0xD190B98AD29394FD6883A5DBD062A205
#EXTINF:11,
demo01_aes_00001.ts
#EXTINF:9,
demo01_aes_00002.ts
#EXT-X-ENDLIST

####ffmpeg で HLS ⇒ MP4 に変換
今回は、AES鍵はローカルにある鍵を使うので、demo01_aes.m3u8 ファイルの EXT-X-KEY 行を次のように編集する

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:11
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-PLAYLIST-TYPE:VOD
#EXT-X-KEY:METHOD=AES-128,URI="aes.key",IV=0xD190B98AD29394FD6883A5DBD062A205
#EXTINF:11,
demo01_aes_00001.ts
#EXTINF:9,
demo01_aes_00002.ts
#EXT-X-ENDLIST

下記のffmpegコマンドを実行

$ ffmpeg -allowed_extensions ALL -i "demo01_aes.m3u8" -c copy "demo01_aes.mp4"
  • 生成された demo01_aes.mp4 を QuickTime Player で再生してみる
$ open demo01_aes.mp4

#5. ステップ5 - Webサーバ(Apache+PHP)を作成、 CORS設定、 Video.js で Web Player 作成
##5.1. ステップ5 のゴール

  • Video.js を使って HLS+AES動画をストリーミング配信で再生
  • 完成図
    005.png

##5.2. ステップ5 構築
###5.2.1. Webサーバ作成

EC2 AmazonLinux2 で適当に作成

今回 AMI は ami-01748a72bed07727c を使った
058.png

Apache と PHP をインストール

# amazon-linux-extras enable php7.4
# amazon-linux-extras install php7.4
# yum install httpd
# systemctl httpd start

###5.2.2. Route 53 設定
EC2 の パブリック IPv4 アドレス と Webサーバのドメイン の Aレコード設定
059.png
060.png

###5.2.3. CORS設定

  • CORS設定がうまくいかない場合は、ブラウザの開発スールの Webコンソールなのでエラーを確認する

CloudFront の E28HDM2W6KJHQ2 と E2L8V7QYSFINZX のCORS用設定

  • それぞれの CloudFront 作成時に次の設定を入れているので、ここでは特に何もすることない
    061.png

S3バケット drm-ni4-6ra-net-aeskey のCORS設定

s3-bucket_drm-ni4-6ra-net-aeskey.tf に cors_rule を追記

s3-bucket_drm-ni4-6ra-net-aeskey.tf
resource "aws_s3_bucket" "drm-ni4-6ra-net-aeskey" {
  bucket        = "drm-ni4-6ra-net-aeskey"
  acl           = "private"
  force_destroy = false
  region        = "ap-northeast-1"
  versioning {
    enabled = false
  }
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
  ## 下記を追記
  cors_rule {
    allowed_headers = [
      "*",
    ]
    allowed_methods = [
      "GET"
    ]
    allowed_origins = [
      "http://drm.ni4-6ra.net"
    ]
    expose_headers  = []
    max_age_seconds = 3000
  }
}
resource "aws_s3_bucket_public_access_block" "drm-ni4-6ra-net-aeskey" {
  depends_on              = [aws_s3_bucket.drm-ni4-6ra-net-aeskey]
  bucket                  = "${aws_s3_bucket.drm-ni4-6ra-net-aeskey.id}"
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}
data "aws_iam_policy_document" "s3bucket_drm-ni4-6ra-net-aeskey" {
  version = "2012-10-17"
  statement {
    sid    = "Grant a CloudFront Origin Identity access"
    effect = "Allow"
    principals {
      type        = "AWS"
      identifiers = [aws_cloudfront_origin_access_identity.drm-ni4-6ra-net-aeskey.iam_arn]
    }
    actions = [
      "s3:GetObject",
      "s3:List*"
    ]
    resources = [
      aws_s3_bucket.drm-ni4-6ra-net-aeskey.arn,
      "${aws_s3_bucket.drm-ni4-6ra-net-aeskey.arn}/*"
    ]
  }
}
resource "aws_s3_bucket_policy" "drm-ni4-6ra-net-aeskey" {
  bucket = aws_s3_bucket.drm-ni4-6ra-net-aeskey.bucket
  policy = data.aws_iam_policy_document.s3bucket_drm-ni4-6ra-net-aeskey.json
}

062.png
063.png

S3バケット drm-ni4-6ra-net-cloudfront-origin のCORS設定

s3-bucket_drm-ni4-6ra-net-aeskey.tf に cors_rule を追記

s3-bucket_drm-ni4-6ra-net-cloudfront-origin.tf

resource "aws_s3_bucket" "drm-ni4-6ra-net-cloudfront-origin" {
  bucket        = "drm-ni4-6ra-net-cloudfront-origin"
  acl           = "private"
  force_destroy = "false"
  region        = "ap-northeast-1"
  versioning {
    enabled = false
  }
  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm = "AES256"
      }
    }
  }
  ## 下記を追記
  cors_rule {
    allowed_headers = [
      "*",
    ]
    allowed_methods = [
      "GET"
    ]
    allowed_origins = [
      "http://drm.ni4-6ra.net"
    ]
    expose_headers  = []
    max_age_seconds = 3000
  }
}
resource "aws_s3_bucket_public_access_block" "drm-ni4-6ra-net-cloudfront-origin" {
  depends_on              = [aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin]
  bucket                  = "drm-ni4-6ra-net-cloudfront-origin"
  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}
resource "aws_s3_bucket_object" "drm-ni4-6ra-net-cloudfront-origin-movie" {
  depends_on             = [aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin]
  bucket                 = aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin.id
  acl                    = "private"
  key                    = "movie/"
  server_side_encryption = "AES256"
}

data "aws_iam_policy_document" "s3bucket_drm-ni4-6ra-net-cloudfront-origin" {
  version = "2012-10-17"
  statement {
    sid    = "Grant a CloudFront Origin Identity access"
    effect = "Allow"
    principals {
      type        = "AWS"
      identifiers = [aws_cloudfront_origin_access_identity.drm-ni4-6ra-net-cloudfront-origin.iam_arn]
    }
    actions = [
      "s3:GetObject",
      "s3:List*"
    ]
    resources = [
      aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin.arn,
      "${aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin.arn}/*"
    ]
  }
}
resource "aws_s3_bucket_policy" "drm-ni4-6ra-net-cloudfront-origin" {
  bucket = aws_s3_bucket.drm-ni4-6ra-net-cloudfront-origin.bucket
  policy = data.aws_iam_policy_document.s3bucket_drm-ni4-6ra-net-cloudfront-origin.json
}

064.png
063.png

###5.2.4. Video.js で Webプレーヤー作成 - サンプル
(参考)
Video.jsのvideojs-http-streaming(VHS)を使ってHLS形式のストリーミング配信を再生する最低限度の設定

playler_sample.php
<?php
  setcookie("CloudFront-Policy", "①", time() + 300, "/" , ".ni4-6ra.net", 0);
  setcookie("CloudFront-Signature", "②", time() + 300, "/" , ".ni4-6ra.net", 0);
  setcookie("CloudFront-Key-Pair-Id", "③", time() + 300, "/" , ".ni4-6ra.net", 0);
?>
<html>
  <head>
    <title>Demo</title>
    <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet">
  </head>
  <body>
    <video-js id=example-video width=1280 height=720 class="vjs-default-skin" controls>
    </video-js>
    <script src="https://vjs.zencdn.net/7.4.1/video.js"></script>
    <script>
      var player = videojs('example-video');
        player.src({
        src: 'm3u8のURL',
        type: 'application/x-mpegURL',
      });
    </script>
  </body>
</html>
  • setcookieで署名付きCookieをセット

  • 「3.2.3. 署名付きCookieリクエストを作成して動作確認」で作成した①と②をそのまま使う

  • ③もPublic key ID 「K1A3BSF7L9SNGX」を使う

  • src に m3u8ファイル の URL を指定

###5.2.5. Video.js で Webプレーヤー作成 - AES暗号化されていない HLS動画を再生

src に 暗号化されていない HLS動画の m3u8 ファイル の URL https://cdn.ni4-6ra.net/movie/demo01_non-aes.m3u8 を指定

player_non-aes.php
<?php
  setcookie("CloudFront-Policy", "eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly8qLm5pNC02cmEubmV0LyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2NDA2OTgzMDl9fX1dfQ__", time() + 300, "/" , ".ni4-6ra.net", 0);
  setcookie("CloudFront-Signature", "jTZDJUJBwV7qvim-baunYIZbkrQzVCu0Y8581PuStrYyjcki-5evNlpHqat1gTFSIGB8BcvhDyBRcBse~l6VKCoRw12HYNYpjYm1mAZjjmYjF0a7h4DrANDhAw~6B4VS5tNHntQ9zKr3xDZbzqwQEK-8D8xfMVWSHgdFn0OUef-zGeYp4mmty37tHWmXILHedrjjgLceXIkwIX-FPPMPwGMoPdV-Bu7cLckFZoXVxstHb64im-yp5yq0lzCjqK~VGMlD7Kh-qu9g2KKe15vIFZfDlpDc2y~E1uYmSVfwtbo6WaddaU26O02FCX0kKmu6CDcsR9XFvTwPru~L8PnKow__
", time() + 300, "/" , ".ni4-6ra.net", 0);
  setcookie("CloudFront-Key-Pair-Id", "K1A3BSF7L9SNGX", time() + 300, "/" , ".ni4-6ra.net", 0);
?>
<html>
  <head>
    <title>Demo</title>
    <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet">
  </head>
  <body>
    <video-js id=example-video width=1280 height=720 class="vjs-default-skin" controls>
    </video-js>
    <script src="https://vjs.zencdn.net/7.4.1/video.js"></script>
    <script>
      var player = videojs('example-video');
        player.src({
        src: 'https://cdn.ni4-6ra.net/movie/demo01_non-aes.m3u8',
        type: 'application/x-mpegURL',
        withCredentials: true
      });
    </script>
  </body>
</html>

Mac Firefox で http://drm.ni4-6ra.net/player_non-aes.php にアクセス
065.png

###5.2.6. Video.js で Webプレーヤー作成 - AES暗号化された HLS動画を再生

src に AES暗号化されたHLS動画の m3u8 ファイル の URL https://cdn.ni4-6ra.net/movie/demo01_aes.m3u8 を指定

player_aes.php
<?php
  setcookie("CloudFront-Policy", "eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly8qLm5pNC02cmEubmV0LyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2NDA2OTgzMDl9fX1dfQ__", time() + 300, "/" , ".ni4-6ra.net", 0);
  setcookie("CloudFront-Signature", "jTZDJUJBwV7qvim-baunYIZbkrQzVCu0Y8581PuStrYyjcki-5evNlpHqat1gTFSIGB8BcvhDyBRcBse~l6VKCoRw12HYNYpjYm1mAZjjmYjF0a7h4DrANDhAw~6B4VS5tNHntQ9zKr3xDZbzqwQEK-8D8xfMVWSHgdFn0OUef-zGeYp4mmty37tHWmXILHedrjjgLceXIkwIX-FPPMPwGMoPdV-Bu7cLckFZoXVxstHb64im-yp5yq0lzCjqK~VGMlD7Kh-qu9g2KKe15vIFZfDlpDc2y~E1uYmSVfwtbo6WaddaU26O02FCX0kKmu6CDcsR9XFvTwPru~L8PnKow__
", time() + 300, "/" , ".ni4-6ra.net", 0);
  setcookie("CloudFront-Key-Pair-Id", "K1A3BSF7L9SNGX", time() + 300, "/" , ".ni4-6ra.net", 0);
?>
<html>
  <head>
    <title>Demo</title>
    <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet">
  </head>
  <body>
    <video-js id=example-video width=1280 height=720 class="vjs-default-skin" controls>
    </video-js>
    <script src="https://vjs.zencdn.net/7.4.1/video.js"></script>
    <script>
      var player = videojs('example-video');
        player.src({
        src: 'https://cdn.ni4-6ra.net/movie/demo01_aes.m3u8',
        type: 'application/x-mpegURL',
        withCredentials: true
      });
    </script>
  </body>
</html>

Mac Firefox で http://drm.ni4-6ra.net/player_aes.php にアクセス
066.png

#6. ステップ6 - アップロードした動画ファイルをHLS+AES動画へ自動変換するLambdaを作成
##6.1. ステップ6 のゴール

  • アップロードした動画ファイルをHLS+AES動画へ自動変換

  • S3バケット drm-ni4-6ra-net-upload-origin/movie/ に動画ファイルをアップロード
    * demo01.mp4

  • Lambda が起動して、MediaConvert ジョブテンプレートが実行される
    * テンプレート demo-lambda-autoconvert

  • 変換された動画ファイルが S3バケット drm-ni4-6ra-net-cloudfront-origin/movie/ に出力される
    * demo01_auto.m3u8
    * demo01_auto_00001.ts
    * demo01_auto_00002.ts

  • 完成図
    006.png

##6.2. ステップ6 構築
###6.2.1. MediaConvert ジョブテンプレート作成

  • 今回は 自動回転、サーバー側の暗号化 なども設定する
  • ジョブテンプレートでは、次のものは設定しない
  • 入力ファイルURL
  • 送信先
  • IAMロール

ジョブテンプレート ⇒ テンプレートの作成
067.png
一般設定 ⇒ 名前を適当に付ける
068.png
入力 追加
069.png
回転させる ⇒ 自動
070.png
出力グループ 追加
013.png
Apple HLS を選択
014.png
サーバー側の暗号化設定
071.png
DRM 暗号化設定
・定数初期化ベクトル: d190b98ad29394fd6883a5dbd062a205
・静的キーの値: 54a65de338c972d3f1dafe95ac936ad2
・URL: https://drm-aeskey.ni4-6ra.net/aes.key
055.png
出力 Output 1
015.png
名前修飾子 ⇒ 必須項目 適当な名前を入れておく (ここでは _auto)
072.png
解像度 ⇒ 空にしておく(空にするとパススルー設定となり、オリジナルと同じ解像度になる)
017.png
ビットレート ⇒ 必須項目 適当に 2000kbps にしておく
018.png
以上、設定が終わったら 作成

###6.2.2. Lambdaで使用するIAMロールを作成

####IAMロール名: lambda_mediaconvert_ni4-6ra-net

iam-role_lambda_mediaconvert_ni4-6ra-net.tf
data "aws_iam_policy_document" "lambda-assume-role-policy" {
  statement {
    actions = ["sts:AssumeRole"]
    principals {
      type        = "Service"
      identifiers = ["lambda.amazonaws.com"]
    }
  }
}
data "aws_iam_policy_document" "mediaconvert-createjob" {
  version = "2012-10-17"
  statement {
    effect = "Allow"
    actions = [
      "iam:PassRole",
      "mediaconvert:CreateJob"
    ]
    resources = [
      "*"
    ]
  }
}
resource "aws_iam_policy" "mediaconvert-createjob" {
  name   = "mediaconvert-createjob"
  policy = data.aws_iam_policy_document.mediaconvert-createjob.json
}
resource "aws_iam_role" "lambda_mediaconvert_ni4-6ra-net" {
  name               = "lambda_mediaconvert_ni4-6ra-net"
  assume_role_policy = data.aws_iam_policy_document.lambda-assume-role-policy.json
  path               = "/service-role/"
}
resource "aws_iam_role_policy_attachment" "lambda_mediaconvert_AWSLambdaBasicExecutionRole" {
  role       = aws_iam_role.lambda_mediaconvert_ni4-6ra-net.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}
resource "aws_iam_role_policy_attachment" "lambda_mediaconvert_mediaconvert" {
  role       = aws_iam_role.lambda_mediaconvert_ni4-6ra-net.name
  policy_arn = aws_iam_policy.mediaconvert-createjob.arn
}

####作成したIAMロール
073.png
074.png
075.png

###6.2.3. Lambda関数作成

  • ランタイムは Python3.7

関数の作成
076.png

一から作成
077.png

基本的な情報
・関数名は適当に(ここでは mediaconvert_ni4-6ra-net)
・ランタイムは Python 3.7
・既存のロール lambda_mediaconvert_ni4-6ra-net
078.png

関数の作成

トリガーを追加
079.png

トリガーの設定
080.png
081.png

Lambda環境変数設定
・AWS_ACCOUNT_ID ・・・ このシステムを構築している AWS アカウントのID(12桁)
・S3_BUCKET_UPLOAD ・・・ オリジナル動画ファイルをアップロードするS3バケット名(drm-ni4-6ra-net-upload-origin)
・S3_BUCKET_OUTPUT ・・・ MediaConvertで変換した動画ファイルを出力するS3バケット名(drm-ni4-6ra-net-cloudfront-origin)
・IAM_ROLE_MEDIACONVERT ・・・ MediaConvertで使用するIAMロール名(mediaconvert_ni4-6ra-net)
・MEDIACONVERT_ENDPOINT ・・・ MediaConvertのエンドポイント
・MEDIACONVERT_JOB_TEMPLATE ・・・ MediaConvertのジョブテンプレート名(demo-lambda-autoconvert)

(補足)
MediaConvertのエンドポイントは、MediaConvertのダッシュボードで確認できる
082.png
083.png

関数コード

job.json
{
  "OutputGroups": [
    {
        "Name": "Apple HLS",
        "OutputGroupSettings": {
          "Type": "HLS_GROUP_SETTINGS",
          "HlsGroupSettings": {
            "Destination": ""
        }
      }

    }
  ],
  "Inputs": [
    {
      "FileInput": ""
    }
  ]
}
lambda_function

import os
import json
import boto3
import urllib.parse
import logging

aws_account_id = os.environ['AWS_ACCOUNT_ID']
mediaconvert_endpoint = os.environ['MEDIACONVERT_ENDPOINT']
mediaconvert_job_template = os.environ['MEDIACONVERT_JOB_TEMPLATE']
s3_bucket_upload = os.environ['S3_BUCKET_UPLOAD']
s3_bucket_output = os.environ['S3_BUCKET_OUTPUT']
iam_role_mediaconvert = os.environ['IAM_ROLE_MEDIACONVERT']

logger = logging.getLogger()
logger.setLevel(logging.INFO)

s3 = boto3.client('s3')
mediaconvert = boto3.client('mediaconvert', region_name='ap-northeast-1', endpoint_url=mediaconvert_endpoint)

def lambda_handler(event, context):

    print(aws_account_id)
    print(mediaconvert_endpoint)
    print(mediaconvert_job_template)
    print(s3_bucket_upload)
    print(s3_bucket_output)
    print(iam_role_mediaconvert)

    key = urllib.parse.unquote_plus(event['Records'][0]['s3']['object']['key'], encoding='utf-8')
    filename = (key.split('/')[1])
    print("filename: " + filename)

    input_file = "s3://" + s3_bucket_upload + "/" + key
    print("input_file: " + input_file)
    destination = "s3://" + s3_bucket_output + "/movie/" 
    print("destination: " + destination)

    job_template = 'arn:aws:mediaconvert:ap-northeast-1:' + aws_account_id + ':jobTemplates/' + mediaconvert_job_template
    print("job_template: " + job_template)
    job_queue = 'arn:aws:mediaconvert:ap-northeast-1:' + aws_account_id + ':queues/Default'
    print("job_queue: " + job_queue)
    role = 'arn:aws:iam::' + aws_account_id + ':role/' + iam_role_mediaconvert
    print("role: " + role)

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

    job_object["Inputs"][0]["FileInput"] = input_file
    job_object["OutputGroups"][0]["OutputGroupSettings"]["HlsGroupSettings"]["Destination"] = destination
    response = mediaconvert.create_job(
        JobTemplate=job_template,
        Queue=job_queue,
        Role=role,
        Settings=job_object
    )
    logger.info(response)

084.png

テストイベントの設定

S3PubObject
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148868/5f278703-6177-c912-2a13-5be4133d17fb.png)
{
   "Records":[
      {
         "eventVersion":"2.1",
         "eventSource":"aws:s3",
         "awsRegion":"ap-northeast-1",
         "eventTime":"2020-12-29T00:00:0.000Z",
         "eventName":"ObjectCreated:Put",
         "userIdentity":{
            "principalId":"AWS:000000000000000000000"
         },
         "requestParameters":{
            "sourceIPAddress":"1.1.1.1"
         },
         "responseElements":{
            "x-amz-request-id":"0000000000000000",
            "x-amz-id-2":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
         },
         "s3":{
            "s3SchemaVersion":"1.0",
            "configurationId":"00000000-0000-0000-00000000000000000",
            "bucket":{
               "name":"drm-ni4-6ra-net-upload-origin",
               "ownerIdentity":{
                  "principalId":"00000000000000"
               },
               "arn":"arn:aws:s3:::drm-ni4-6ra-net-upload-origin"
            },
            "object":{
               "key":"/movie/demo01.mp4",
               "size":4,
               "eTag":"00000000000000000000000000000000",
               "versionId":"00000000000000000000000000000000",
               "sequencer":"000000000000000000"
            }
         }
      }
   ]
}

085.png

###6.2.4. Lambda関数テスト
MediaConvertで変換した動画ファイルを出力するS3バケットを空にしておく
086.png

Lambda テストを実行

  • テストイベントJSON の object key で変換する動画ファイルが設定されていることを確認する (今回は /movie/demo01.mp4)

087.png

  • Lambda実行結果
    088.png

  • MediaConvert ジョブステータス
    089.png

  • S3バケット drm-ni4-6ra-net-cloudfront-origin 確認
    090.png

###6.2.5. Web Player で再生確認

src に https://cdn.ni4-6ra.net/movie/demo01_auto.m3u8 を指定

player_auto.php
<?php
  setcookie("CloudFront-Policy", "eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly8qLm5pNC02cmEubmV0LyoiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE2NDA2OTgzMDl9fX1dfQ__", time() + 300, "/" , ".ni4-6ra.net", 0);
  setcookie("CloudFront-Signature", "jTZDJUJBwV7qvim-baunYIZbkrQzVCu0Y8581PuStrYyjcki-5evNlpHqat1gTFSIGB8BcvhDyBRcBse~l6VKCoRw12HYNYpjYm1mAZjjmYjF0a7h4DrANDhAw~6B4VS5tNHntQ9zKr3xDZbzqwQEK-8D8xfMVWSHgdFn0OUef-zGeYp4mmty37tHWmXILHedrjjgLceXIkwIX-FPPMPwGMoPdV-Bu7cLckFZoXVxstHb64im-yp5yq0lzCjqK~VGMlD7Kh-qu9g2KKe15vIFZfDlpDc2y~E1uYmSVfwtbo6WaddaU26O02FCX0kKmu6CDcsR9XFvTwPru~L8PnKow__
", time() + 300, "/" , ".ni4-6ra.net", 0);
  setcookie("CloudFront-Key-Pair-Id", "K1A3BSF7L9SNGX", time() + 300, "/" , ".ni4-6ra.net", 0);
?>
<html>
  <head>
    <title>Demo</title>
    <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet">
  </head>
  <body>
    <video-js id=example-video width=1280 height=720 class="vjs-default-skin" controls>
    </video-js>
    <script src="https://vjs.zencdn.net/7.4.1/video.js"></script>
    <script>
      var player = videojs('example-video');
        player.src({
        src: 'https://cdn.ni4-6ra.net/movie/demo01_auto.m3u8',
        type: 'application/x-mpegURL',
        withCredentials: true
      });
    </script>
  </body>
</html>

Mac Firefox で http://drm.ni4-6ra.net/player_auto.php にアクセス
091.png

###実際に動画ファイルをS3バケットにアップロードして動作確認

  • S3 バケット drm-ni4-6ra-net-upload-origin の movie フォルダ内を空にする
  • S3 バケット drm-ni4-6ra-net-cloudfront-origin の moview フォルダ内を空にする
  • AWSマネジメントコンソールで S3 バケット drm-ni4-6ra-net-upload-origin の moive フォルダに demo01.mp4 をアップロードする
  • Lambdaの実行ログ確認(CloudWatch ロググループの /aws/lambda/mediaconvert_ni4-6ra-net
  • MediaConvert のステータス確認
  • S3 バケット drm-ni4-6ra-net-cloudfront-origin の確認
  • Web Player で確認

#7. 実際の運用での注意点

##7.1 CloudFrontキャッシュ設定

  • ここでは動作検証のためキャッシュしない設定にしているので、検証が終わったら適切なキャッシュ設定を行う。

##7.2. 解像度と動画サイズ

  • HD (ハイビジョン画質)

  • 解像度:720 × 1280

  • フルHD(フルハイビジョン画質)

  • 解像度:1080 × 1920

  • スマホで録画した動画ファイルはフルHD

  • MediaConvertの解像度パススルー設定でコンバートすると、変換後の動画の解像度も 1080 × 1920 になる

  • 4K

  • 解像度:2160 x 3840

##7.2. 動画ファイル アップロードサイズ制限

  • CMSサーバ経由で動画ファイルをS3へアップロードする場合、CMSサーバのメモリサイズが十分か確認したり、M/Wのファイルアップロード制限のチューニングを行う必要がある。
  • 例えば php.ini

memory_limit = 2048M
post_max_size = 2048M
upload_max_filesize = 2048M
  • 動画ファイルサイズを2GBで制限をかける場合は、許可する動画の再生時間を予め決めておく。
16
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?