暗号化していない動画(.ts)は、ダウンロードして拡張子を.mpegとかにすれば細切れですが再生出来てしまいます。AESで暗号化すれば、そのままでは再生出来なくなります。
ただ、鍵(.key)で復号しながら再生しているので、ひと手間掛ければ再生できるように出来てしまいます。
コンテンツ保護はDRM使うのがベストですが、コストと再生できる環境が限られるので、要件や、コストとリスクで判断して、AESだけを選択する場合も多いです。

MediaConvertで暗号化(AES)する方法

残念ながら画面には項目がなく、選択するだけでは出来そうにありませんでした。
→ ジョブをインポートすれば出来ますが、鍵を毎回作成したいので、Lambdaを使います。

LambdaでJSONにパラメータを追加してジョブを作るのと、
対応する鍵(.key)をS3に保存してあげれば出来ます。

TODO: CMAFをAESで暗号化してみる
S3設置トリガーで自動変換と、MediaConvertでCMAFを暗号化(未解決)してみる

MediaConvertのジョブテンプレート作成

https://ap-northeast-1.console.aws.amazon.com/mediaconvert/home?region=ap-northeast-1#/templates/create

一般設定

ここで設定した名前をジョブ作成で使います。

入力

出力グループ

Apple HLS グループの設定

出力設定

名前修飾子
「_video」と入力。何でも良いけど設定しないと保存できない。
親と子のm3u8のファイル名が同じにならないようにとtsに入る。

Big Buck Bunny.m3u8
Big Buck Bunny_video.m3u8
Big Buck Bunny_video_00001.ts


Lambda関数

https://ap-northeast-1.console.aws.amazon.com/lambda/home?region=ap-northeast-1#/create/function

関数の作成


アクセス権限

鍵を作成するS3への書き込み権限(s3:Put)と、mediaconvert:CreateJob が必要になります。

前者は、MediaConvert_Default_Role_xxxxxxxx ←MediaConvertで変換して、ストリーミング配信を試してみる で作成
後者は、AWSElementalMediaConvertFullAccess を利用

IAM

コード

今回は元素材はベタに指定して、MediaConvertジョブ作成とkeyファイルをS3に保存して、MediaConvertのジョブIDを返却するようにしました。
TODO: S3設置トリガーで自動で変換したい
S3設置トリガーで自動変換と、MediaConvertでCMAFを暗号化(未解決)してみる
TODO: MediaInfoを実行して元素材の情報を残したい
EventBridgeでMediaConvertの進捗率を取得する
 ※Job State ChangeのINPUT_INFORMATIONである程度取れる。変換後のはCOMPLETEで取れる。

元素材が存在しなくてもエラーにならず、ジョブ作成は成功しちゃいました。
TODO: 進行状況を通知したい。完了か失敗か知りたい。
→  EventBridgeでMediaConvertの進捗率を取得する
TODO: Labmdaの例外やエラーを通知したい。

最新のコードこちら → https://dev.azure.com/nightonly/_git/lambda-origin?path=/createMediaConvertJob/lambda_function.py

import boto3
import os
import datetime
import secrets
import codecs

s3Client = boto3.client('s3')

OUTPUT_BUCKET = os.environ['OUTPUT_BUCKET']
KEY_BASE_URL = os.environ['KEY_BASE_URL'] ←相対パスに変更した為、削除
MEDIACONVERT_ENDPOINT_URL = os.environ['MEDIACONVERT_ENDPOINT_URL']
MEDIACONVERT_ROLE = os.environ['MEDIACONVERT_ROLE']
MEDIACONVERT_JOB_TEMPLATE = os.environ['MEDIACONVERT_JOB_TEMPLATE']
OUTPUT_SUFFIX_PATH = '_' + datetime.datetime.now().strftime('%Y%m%d%H%M%S')

def lambda_handler(event, context):
    input_bucket = 's3://input-media-cdn.nightonly.com/' # 対象動画のバケット
    input_file = 'Big Buck Bunny.mp4' # 対象動画のフォルダを含むファイル名
    output_path = os.path.splitext(input_file)[0] + OUTPUT_SUFFIX_PATH + '/' ←パスに拡張子も入れる。拡張子違いを別物扱いにする為
    output_path = input_file + OUTPUT_SUFFIX_PATH + '/'
    key_file = os.path.splitext(os.path.basename(input_file))[0] + '.key'
    print({ 'input_bucket': input_bucket, 'input_file': input_file, 'output_path': output_path, 'key_file': key_file })

    key_value = secrets.token_hex(16)
    settings = {
        'Inputs': [
            {
                'FileInput': input_bucket + input_file
            }
        ],
        'OutputGroups': [
            {
                'OutputGroupSettings': {
                    'HlsGroupSettings': {
                        'Destination': 's3://' + OUTPUT_BUCKET + '/' + output_path,
                        'Encryption': {
                            'EncryptionMethod': 'AES128',
                            'StaticKeyProvider': {
                                'StaticKeyValue': key_value,
                                'Url': KEY_BASE_URL + output_path + key_file ←相対パスに変更
                                'Url': os.path.basename(output_path + key_file)
                            },
                            'Type': 'STATIC_KEY'
                        }
                    }
                }
            }
        ]
    }
    print(settings)

    # MediaConvertジョブ作成
    mediaconvertClient = boto3.client('mediaconvert', endpoint_url = MEDIACONVERT_ENDPOINT_URL)
    result = mediaconvertClient.create_job(
        Role = MEDIACONVERT_ROLE,
        JobTemplate = MEDIACONVERT_JOB_TEMPLATE,
        Settings = settings
    )
    print(result)

    # keyファイル作成
    print(s3Client.put_object(
        Bucket = OUTPUT_BUCKET,
        Key = output_path + key_file,
        Body = codecs.decode(key_value, 'hex_codec')
    ))

    return result['Job']['Id']

ClodFrontのキャッシュクリアをしたくないのと、再変換で尺が短くなると不要なtsが残る為、OUTPUT_SUFFIX_PATHに日時(UTC)を入れる事にしました。
再変換した場合は、完了後にm3u8のパスを切り替える想定。切り替えたら元のをS3から削除すると容量削減できますね。

settingsのJSONは、ジョブテンプレート(MEDIACONVERT_JOB_TEMPLATE)のにマージされるので、追加・変更箇所のみ記載すればOK。
内容は「ジョブテンプレートの詳細 → ジョブの JSON を表示」で確認できる。

TODO: 変換に失敗するとkeyファイルが残るので、失敗したら削除したい
EventBridgeでMediaConvertの進捗率を取得する
 ※完了時に保存するように変更

環境変数

OUTPUT_BUCKET: 変換後の動画や鍵を置くバケット
KEY_BASE_URL: 鍵ファイルのURL(CloudFrontでOUTPUT_BUCKETにアクセスできるURL)
↑相対パスに変更した為、設定不要。ドメインやパス変更できる。
MEDIACONVERT_ENDPOINT_URL: MediaConvert > アカウント のAPIエンドポイント(下記)
MEDIACONVERT_ROLE: MediaConvert_Default_Role_xxxxxxxx のポリシーARN(下記)
MEDIACONVERT_JOB_TEMPLATE: 上で作成したジョブテンプレートの名前

https://ap-northeast-1.console.aws.amazon.com/mediaconvert/home?region=ap-northeast-1#/account

https://us-east-1.console.aws.amazon.com/iamv2/home#/policies

テスト

デプロイして、テスト実行!
テンプレートはs3-putにしましたが、今回は何でも良さそう。
ResponseにジョブIDが表示されれはリクエストは成功。

確認

MediaConvert

ジョブが作られている!
しばらく待ってから更新すると、PROGRESSINGからCOMPLETEになる。

https://ap-northeast-1.console.aws.amazon.com/mediaconvert/home?region=ap-northeast-1#/jobs/list

S3

HLSのファイルとkeyファイルが作られている!

https://s3.console.aws.amazon.com/s3/buckets?region=ap-northeast-1

再生してみる

hls.js demo
https://hls-js.netlify.app/demo/

今回は下記(しばらく残しておきます)
https://cdn.nightonly.com/Big%20Buck%20Bunny_HLS_AES/Big%20Buck%20Bunny.m3u8
https://cdn.nightonly.com/Big%20Buck%20Bunny_20220419235559/Big%20Buck%20Bunny.m3u8

MediaConvertでHLSをAESで暗号化してみる” に対して3件のコメントがあります。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です