CloudFormation で AWS Elemental MediaLive を作成する時に既存の AWS Elemental MediaPackage に紐付けるテンプレートを作成してみた

CloudFormationでMediaLiveを作成するときに既存のMediaPackageと紐付けたいと考えた事はありませんか?
2020.08.05

こんにちは、大前です。

 

AWS Elemental MediaLive(以下 MediaLive) は未使用の状態でも課金が発生してしまう為、使わなくなったチャンネル等は逐次削除する事が多いかと思います。

一方で、設定項目が多い MediaLive を一々手で作り直すのも手間なものです。

そこで以前は、MediaLive 等を AWS CloudFormation(以下 CloudFormation) で作成するブログを投稿しました。

 

今回は、MediaLive を CloudFormation で作成しつつ、既存の AWS Elemental MediaPackage(以下 MediaPackage) に紐付けるテンプレートを作成してみましたので紹介します。

CloudFormation で MediaPackage までまとめて作成できれば良いのですが、現時点では CloudFormation で MediaPackage を作成する事が出来ない為、既存の MediaPackage をうまく紐付けられる様にしてみました。(早く対応してほしいなー。。。)

 

余談ですが、AWS 公式のサンプルデプロイガイド等ではカスタムリソースを使用して MediaLive や MediaPackage を作成しているサンプルが多いです。興味ある方はそちらも見てみてください。

aws-samples / aws-media-services-simple-live-workflow

作成したテンプレート

早速ですが、作成したテンプレートを先に載せてしまいます。

今回は「既存の MediaPackage との紐付け」のみを目指してテンプレートを作成したので、細かいパラメータ調整などは行なっていません。

流用する時にはよしなに調整してご利用ください。

AWSTemplateFormatVersion: '2010-09-09'
Parameters:
  #ProjectName
  ProjectName:
    Description: "Name of MediaLiveInput"
    Default: "sample-prj"
    Type: String
  #StreamKey
  StreamKey:
    Description: "StreamKey"
    Type: String
    Default: "streamkey"
  #ChannelClass
  ChannelClass:
    Description: "ChannelClass"
    Type: String
    Default: "STANDARD"
    AllowedValues:
      - STANDARD
      - SINGLE_PIPELINE
  #SecurityGroupWhitelistCIDR
  SecurityGroupWhitelistCIDR:
    Description: "Whitelist CIDR of MediaLiveInputSecurityGroup."
    Type: String
    Default: "0.0.0.0/0"
  # MediaPackageChannelId
  MediaPackageChannelId:
    Description: "MediaPackage ChannelId"
    Type: String

Resources:
  # ----------------
  # MediaLiveInput
  # ----------------
  MediaLiveInput:
    Type: AWS::MediaLive::Input
    Properties:
      Destinations:
        - StreamName: !Sub "${ProjectName}/${StreamKey}-A"
        - StreamName: !Sub "${ProjectName}/${StreamKey}-B"
      InputSecurityGroups:
        - !Ref MediaLiveInputSecurityGroup
      Name: !Sub "${ProjectName}-input"
      Tags: { 'Name' : !Sub "${ProjectName}-input"}
      Type: "RTMP_PUSH"

  # -----------------------------
  # MediaLiveInputSecurityGroup
  # -----------------------------
  MediaLiveInputSecurityGroup:
    Type: AWS::MediaLive::InputSecurityGroup
    Properties:
      Tags: { 'Name' : !Sub "${ProjectName}-inputsg"}
      WhitelistRules:
        - Cidr: !Ref SecurityGroupWhitelistCIDR

  # ------------------
  # MediaLiveChannel
  # ------------------
  MediaLiveChannel:
    Type: AWS::MediaLive::Channel
    Properties:
      ChannelClass: !Ref ChannelClass
      Destinations:
        - Id: !Ref MediaPackageChannelId
          MediaPackageSettings:
            - ChannelId: !Ref MediaPackageChannelId
      EncoderSettings:
        AudioDescriptions:
          - AudioSelectorName: "default"
            CodecSettings:
              AacSettings:
                Bitrate: 96000
                RawFormat: "NONE"
                Spec: "MPEG4"
            AudioTypeControl: "FOLLOW_INPUT"
            LanguageCodeControl: "FOLLOW_INPUT"
            Name: "audio_3_aac96"
        OutputGroups:
          - OutputGroupSettings:
              MediaPackageGroupSettings:
                Destination:
                  DestinationRefId: !Ref MediaPackageChannelId
            Outputs:
              - OutputSettings:
                  MediaPackageOutputSettings:
                    OutputName: "1280_720_1"
                OutputName: "1280_720_1"
                VideoDescriptionName: "video_1280_720_1"
                AudioDescriptionNames:
                  - "audio_3_aac96"
        TimecodeConfig:
          Source: "SYSTEMCLOCK"
        VideoDescriptions:
          - CodecSettings:
              H264Settings:
                AfdSignaling: "NONE"
                ColorMetadata: "INSERT"
                AdaptiveQuantization: "HIGH"
                Bitrate: 1000000
                EntropyEncoding: "CABAC"
                FlickerAq: "ENABLED"
                FramerateControl: "SPECIFIED"
                FramerateNumerator: 30000
                FramerateDenominator: 1001
                GopBReference: "ENABLED"
                GopClosedCadence: 1
                GopNumBFrames: 3
                GopSize: 60
                GopSizeUnits: "FRAMES"
                SubgopLength: "FIXED"
                ScanType: "PROGRESSIVE"
                Level: "H264_LEVEL_4_1"
                LookAheadRateControl: "HIGH"
                NumRefFrames: 1
                ParControl: "SPECIFIED"
                Profile: "HIGH"
                RateControlMode: "CBR"
                Syntax: "DEFAULT"
                SceneChangeDetect: "ENABLED"
                SpatialAq: "ENABLED"
                TemporalAq: "ENABLED"
                TimecodeInsertion: "DISABLED"
            Height: 720
            Name: "video_1280_720_1"
            RespondToAfd: "NONE"
            Sharpness: 50
            ScalingBehavior: "DEFAULT"
            Width: 1280
      InputAttachments:
        - InputAttachmentName: !Sub "${ProjectName}-channelinput"
          InputId: !Ref MediaLiveInput
          InputSettings:
            DeblockFilter: "DISABLED"
            DenoiseFilter: "DISABLED"
            FilterStrength: 1
            InputFilter: "AUTO"
            SourceEndBehavior: "CONTINUE"
      InputSpecification:
          Codec: "AVC"
          MaximumBitrate: "MAX_20_MBPS"
          Resolution: "HD"
      Name: !Sub "${ProjectName}-channel"
      RoleArn: !GetAtt MediaLiveAccessRole.Arn
      Tags: { 'Name' : !Sub "${ProjectName}-channel"}

  # ---------------------
  # MediaLiveAccessRole
  # ---------------------
  MediaLiveAccessRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "medialive.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess
      Path: /
      Policies:
        - PolicyName: !Sub "${ProjectName}-MediaLiveCustomPolicy"
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action:
                  - s3:ListBucket
                  - s3:PutObject
                  - s3:GetObject
                  - s3:DeleteObject
                Resource: '*'
              - Effect: Allow
                Action:
                  - mediastore:ListContainers
                  - mediastore:PutObject
                  - mediastore:GetObject
                  - mediastore:DeleteObject
                  - mediastore:DescribeObject
                Resource: '*'
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                  - logs:DescribeLogStreams
                  - logs:DescribeLogGroups
                Resource: 'arn:aws:logs:*:*:*'
              - Effect: Allow
                Action:
                  - mediaconnect:ManagedDescribeFlow
                  - mediaconnect:ManagedAddOutput
                  - mediaconnect:ManagedRemoveOutput
                Resource: '*'
              - Effect: Allow
                Action:
                  - ec2:describeSubnets
                  - ec2:describeNetworkInterfaces
                  - ec2:createNetworkInterface
                  - ec2:createNetworkInterfacePermission
                  - ec2:deleteNetworkInterface
                  - ec2:deleteNetworkInterfacePermission
                  - ec2:describeSecurityGroups
                Resource: '*'
              - Effect: Allow
                Action:
                  - mediapackage:DescribeChannel
                Resource: '*'
      RoleName: !Sub "${ProjectName}-MediaLiveAccessRole"

ポイント

MediaLive を CloudFormation で作成する事自体そこそこ大変なのですが、今回は MediaPackage の紐付けに関連する箇所のみ説明を入れたいと思います。

Properties.Destinations

Destinations 内には紐付ける MediaPackage のチャンネル ID と、その設定に紐付く一意の ID(任意)を設定する必要があります。

MediaPackage のチャンネル ID を Parameters に設定する事で、スタック作成時に MediaPackage のチャンネル ID を指定する事で紐付けが出来る様になります。

      Destinations:
        - Id: !Ref MediaPackageChannelId
          MediaPackageSettings:
            - ChannelId: !Ref MediaPackageChannelId

Properties.EncoderSettings.OutputGroups

まず OutputGroupSettings には "MediaPackageGroupSettings" を記載し、上記 Destinations で定義した ID を DestinationRefId として記載します。

また、Outputs 内の記載で一番苦戦しました。

ここで何度もエラーが発生してしまいうまく行かなかったのですが、OutputSettings.MediaPackageOutputSettings と Outputs 配下にそれぞれ OutputName を記載する事で作成が可能になりました。(例えば OutputSettings.MediaPackageOutputSettings が空だとエラー)

ここに関してはこれが正しい記法なのか正直わかっていません。。。情報をお持ちの方いれば教えてください。。

        OutputGroups:
          - OutputGroupSettings:
              MediaPackageGroupSettings:
                Destination:
                  DestinationRefId: !Ref MediaPackageChannelId
            Outputs:
              - OutputSettings:
                  MediaPackageOutputSettings:
                    OutputName: "1280_720_1"
                OutputName: "1280_720_1"
                VideoDescriptionName: "video_1280_720_1"
                AudioDescriptionNames:
                  - "audio_3_aac96"

Properties.EncoderSettings.VideoDescriptions.CodecSettings.ParControl

細かい部分ですが、MediaLive から MediaPackage に映像を送出する際はこのパラメータを SPECIFIED にする必要があります。これはコンソール上から作成する際にも同様です。

        VideoDescriptions:
          - CodecSettings:
              H264Settings:
                (中略)
                ParControl: "SPECIFIED"
                (中略)

 

紐付けをやってみた

実際にこの CloudFormation テンプレートを使って既存の MediaPackage との紐付けをしてみたいと思います。

1. MediaPackage チャンネルの作成

まずは MediaPackage チャンネルをコンソールから作成します。

ちなみに、Terraform は MediaPackage を作成できます。一方で MediaLive が非対応なのでなかなか辛いところです。

 

MediaPacakge のチャンネル画面より Create をクリック

 

チャンネル ID を入力して Create をクリックします。今回は "TestChannel" としました。

 

これで MediaPackage の設定は完了です。本来配信を行う場合はエンドポイントの作成なども必要ですが、今回は紐付けだけを確認したいのでここまでで OK とします。

 

2. CloudFormation スタックの作成

続いて、上記で作成した CloudFormation テンプレートからスタックを作成していきます。

 

CloudFormation 画面のスタック作成より、テンプレートファイルをアップロードします。

 

続いて、各パラメータを入力します。

今回は「スタックの名前」と「MediaPackageChannelId」以外はデフォルトで大丈夫です。

MediaPackageChannelId には先ほど作成した MediaPackage のチャンネル ID を入力します。

 

その後のオプション等は特に設定せず、最後に IAM ロールの作成がある事に同意してスタックの作成を実行します。

 

特に問題なければ、スタックの作成が成功するはずです。

 

3. 作成された MediaLive チャンネルを確認

CloudFormation で作成された MediaLive チャンネルを確認しにいきます。

 

新しくチャンネルが作成されていることが確認できます。

 

チャンネルの詳細を開き、「送信先」を確認すると画面下部に指定した MediaPackage が紐づいている事が確認できます。

 

これにて、無事に CloudFormation を使って既存の MediaPackage を紐づける事が出来ました。

おわりに

MediaLive を CloudFormation で作成する際に既存の MediaPackage と紐づけるためのテンプレートを公開してみました。

MediaLive を作成する CloudFormation テンプレートを作成するのはなかなか大変ですが、一度作って置くと便利なのも事実です。うまく活用していきましょう。

 

確認後はリソースの削除をお忘れなく。

 

以上、AWS 事業本部の大前でした。

参考