[小ネタ] CDK で MediaLive を作成するときにちょっと楽をする方法

MediaLive を IaC で管理しようとすると、パラメータが多くなかなか大変です。カスタムテンプレートのダウンロード機能を利用することで、コンソールで作成した MediaLive Channel のパラメータを流用することができます。
2022.04.15

こんにちは、大前です。

最近 CDK への入門がてら MediaLive を CDK で作っていたりするのですが、MediaLive はパラメータ数が膨大でなかなか大変です。

そこで、MediaLive のカスタムテンプレートのダウンロード機能を利用し、CDK で MediaLive を構築するためのソースコードをちょっと楽して作ってみました。今回は CDK を例に挙げていますが、CloudFormation などでも活用できるかと思います。

MediaLive のパラメータの多さに心挫ける

MediaLive を構成する要素は InputInputSecurityGroupChannel の 3つですが、このうち Channel のパラメータ数が膨大で、「MediaLive もいっちょ IaC しちゃいますか〜〜」とか思いながらドキュメントを覗くと心を打ち砕かれます。以下に CDK の公式ドキュメントに乗っている MediaLive Channel 作成のサンプルコードを載せておきます。開いてみるとこの気持ちが伝わると思います。

Example - class CfnChannel (construct)
// The code below shows an example of how to instantiate this type.
// The values are placeholders you should change.
import { aws_medialive as medialive } from 'aws-cdk-lib';

declare const tags: any;
const cfnChannel = new medialive.CfnChannel(this, 'MyCfnChannel', /* all optional props */ {
  cdiInputSpecification: {
    resolution: 'resolution',
  },
  channelClass: 'channelClass',
  destinations: [{
    id: 'id',
    mediaPackageSettings: [{
      channelId: 'channelId',
    }],
    multiplexSettings: {
      multiplexId: 'multiplexId',
      programName: 'programName',
    },
    settings: [{
      passwordParam: 'passwordParam',
      streamName: 'streamName',
      url: 'url',
      username: 'username',
    }],
  }],
  encoderSettings: {
    audioDescriptions: [{
      audioNormalizationSettings: {
        algorithm: 'algorithm',
        algorithmControl: 'algorithmControl',
        targetLkfs: 123,
      },
      audioSelectorName: 'audioSelectorName',
      audioType: 'audioType',
      audioTypeControl: 'audioTypeControl',
      audioWatermarkingSettings: {
        nielsenWatermarksSettings: {
          nielsenCbetSettings: {
            cbetCheckDigitString: 'cbetCheckDigitString',
            cbetStepaside: 'cbetStepaside',
            csid: 'csid',
          },
          nielsenDistributionType: 'nielsenDistributionType',
          nielsenNaesIiNwSettings: {
            checkDigitString: 'checkDigitString',
            sid: 123,
          },
        },
      },
      codecSettings: {
        aacSettings: {
          bitrate: 123,
          codingMode: 'codingMode',
          inputType: 'inputType',
          profile: 'profile',
          rateControlMode: 'rateControlMode',
          rawFormat: 'rawFormat',
          sampleRate: 123,
          spec: 'spec',
          vbrQuality: 'vbrQuality',
        },
        ac3Settings: {
          bitrate: 123,
          bitstreamMode: 'bitstreamMode',
          codingMode: 'codingMode',
          dialnorm: 123,
          drcProfile: 'drcProfile',
          lfeFilter: 'lfeFilter',
          metadataControl: 'metadataControl',
        },
        eac3Settings: {
          attenuationControl: 'attenuationControl',
          bitrate: 123,
          bitstreamMode: 'bitstreamMode',
          codingMode: 'codingMode',
          dcFilter: 'dcFilter',
          dialnorm: 123,
          drcLine: 'drcLine',
          drcRf: 'drcRf',
          lfeControl: 'lfeControl',
          lfeFilter: 'lfeFilter',
          loRoCenterMixLevel: 123,
          loRoSurroundMixLevel: 123,
          ltRtCenterMixLevel: 123,
          ltRtSurroundMixLevel: 123,
          metadataControl: 'metadataControl',
          passthroughControl: 'passthroughControl',
          phaseControl: 'phaseControl',
          stereoDownmix: 'stereoDownmix',
          surroundExMode: 'surroundExMode',
          surroundMode: 'surroundMode',
        },
        mp2Settings: {
          bitrate: 123,
          codingMode: 'codingMode',
          sampleRate: 123,
        },
        passThroughSettings: { },
        wavSettings: {
          bitDepth: 123,
          codingMode: 'codingMode',
          sampleRate: 123,
        },
      },
      languageCode: 'languageCode',
      languageCodeControl: 'languageCodeControl',
      name: 'name',
      remixSettings: {
        channelMappings: [{
          inputChannelLevels: [{
            gain: 123,
            inputChannel: 123,
          }],
          outputChannel: 123,
        }],
        channelsIn: 123,
        channelsOut: 123,
      },
      streamName: 'streamName',
    }],
    availBlanking: {
      availBlankingImage: {
        passwordParam: 'passwordParam',
        uri: 'uri',
        username: 'username',
      },
      state: 'state',
    },
    availConfiguration: {
      availSettings: {
        scte35SpliceInsert: {
          adAvailOffset: 123,
          noRegionalBlackoutFlag: 'noRegionalBlackoutFlag',
          webDeliveryAllowedFlag: 'webDeliveryAllowedFlag',
        },
        scte35TimeSignalApos: {
          adAvailOffset: 123,
          noRegionalBlackoutFlag: 'noRegionalBlackoutFlag',
          webDeliveryAllowedFlag: 'webDeliveryAllowedFlag',
        },
      },
    },
    blackoutSlate: {
      blackoutSlateImage: {
        passwordParam: 'passwordParam',
        uri: 'uri',
        username: 'username',
      },
      networkEndBlackout: 'networkEndBlackout',
      networkEndBlackoutImage: {
        passwordParam: 'passwordParam',
        uri: 'uri',
        username: 'username',
      },
      networkId: 'networkId',
      state: 'state',
    },
    captionDescriptions: [{
      captionSelectorName: 'captionSelectorName',
      destinationSettings: {
        aribDestinationSettings: { },
        burnInDestinationSettings: {
          alignment: 'alignment',
          backgroundColor: 'backgroundColor',
          backgroundOpacity: 123,
          font: {
            passwordParam: 'passwordParam',
            uri: 'uri',
            username: 'username',
          },
          fontColor: 'fontColor',
          fontOpacity: 123,
          fontResolution: 123,
          fontSize: 'fontSize',
          outlineColor: 'outlineColor',
          outlineSize: 123,
          shadowColor: 'shadowColor',
          shadowOpacity: 123,
          shadowXOffset: 123,
          shadowYOffset: 123,
          teletextGridControl: 'teletextGridControl',
          xPosition: 123,
          yPosition: 123,
        },
        dvbSubDestinationSettings: {
          alignment: 'alignment',
          backgroundColor: 'backgroundColor',
          backgroundOpacity: 123,
          font: {
            passwordParam: 'passwordParam',
            uri: 'uri',
            username: 'username',
          },
          fontColor: 'fontColor',
          fontOpacity: 123,
          fontResolution: 123,
          fontSize: 'fontSize',
          outlineColor: 'outlineColor',
          outlineSize: 123,
          shadowColor: 'shadowColor',
          shadowOpacity: 123,
          shadowXOffset: 123,
          shadowYOffset: 123,
          teletextGridControl: 'teletextGridControl',
          xPosition: 123,
          yPosition: 123,
        },
        ebuTtDDestinationSettings: {
          copyrightHolder: 'copyrightHolder',
          fillLineGap: 'fillLineGap',
          fontFamily: 'fontFamily',
          styleControl: 'styleControl',
        },
        embeddedDestinationSettings: { },
        embeddedPlusScte20DestinationSettings: { },
        rtmpCaptionInfoDestinationSettings: { },
        scte20PlusEmbeddedDestinationSettings: { },
        scte27DestinationSettings: { },
        smpteTtDestinationSettings: { },
        teletextDestinationSettings: { },
        ttmlDestinationSettings: {
          styleControl: 'styleControl',
        },
        webvttDestinationSettings: {
          styleControl: 'styleControl',
        },
      },
      languageCode: 'languageCode',
      languageDescription: 'languageDescription',
      name: 'name',
    }],
    featureActivations: {
      inputPrepareScheduleActions: 'inputPrepareScheduleActions',
    },
    globalConfiguration: {
      initialAudioGain: 123,
      inputEndAction: 'inputEndAction',
      inputLossBehavior: {
        blackFrameMsec: 123,
        inputLossImageColor: 'inputLossImageColor',
        inputLossImageSlate: {
          passwordParam: 'passwordParam',
          uri: 'uri',
          username: 'username',
        },
        inputLossImageType: 'inputLossImageType',
        repeatFrameMsec: 123,
      },
      outputLockingMode: 'outputLockingMode',
      outputTimingSource: 'outputTimingSource',
      supportLowFramerateInputs: 'supportLowFramerateInputs',
    },
    motionGraphicsConfiguration: {
      motionGraphicsInsertion: 'motionGraphicsInsertion',
      motionGraphicsSettings: {
        htmlMotionGraphicsSettings: { },
      },
    },
    nielsenConfiguration: {
      distributorId: 'distributorId',
      nielsenPcmToId3Tagging: 'nielsenPcmToId3Tagging',
    },
    outputGroups: [{
      name: 'name',
      outputGroupSettings: {
        archiveGroupSettings: {
          archiveCdnSettings: {
            archiveS3Settings: {
              cannedAcl: 'cannedAcl',
            },
          },
          destination: {
            destinationRefId: 'destinationRefId',
          },
          rolloverInterval: 123,
        },
        frameCaptureGroupSettings: {
          destination: {
            destinationRefId: 'destinationRefId',
          },
          frameCaptureCdnSettings: {
            frameCaptureS3Settings: {
              cannedAcl: 'cannedAcl',
            },
          },
        },
        hlsGroupSettings: {
          adMarkers: ['adMarkers'],
          baseUrlContent: 'baseUrlContent',
          baseUrlContent1: 'baseUrlContent1',
          baseUrlManifest: 'baseUrlManifest',
          baseUrlManifest1: 'baseUrlManifest1',
          captionLanguageMappings: [{
            captionChannel: 123,
            languageCode: 'languageCode',
            languageDescription: 'languageDescription',
          }],
          captionLanguageSetting: 'captionLanguageSetting',
          clientCache: 'clientCache',
          codecSpecification: 'codecSpecification',
          constantIv: 'constantIv',
          destination: {
            destinationRefId: 'destinationRefId',
          },
          directoryStructure: 'directoryStructure',
          discontinuityTags: 'discontinuityTags',
          encryptionType: 'encryptionType',
          hlsCdnSettings: {
            hlsAkamaiSettings: {
              connectionRetryInterval: 123,
              filecacheDuration: 123,
              httpTransferMode: 'httpTransferMode',
              numRetries: 123,
              restartDelay: 123,
              salt: 'salt',
              token: 'token',
            },
            hlsBasicPutSettings: {
              connectionRetryInterval: 123,
              filecacheDuration: 123,
              numRetries: 123,
              restartDelay: 123,
            },
            hlsMediaStoreSettings: {
              connectionRetryInterval: 123,
              filecacheDuration: 123,
              mediaStoreStorageClass: 'mediaStoreStorageClass',
              numRetries: 123,
              restartDelay: 123,
            },
            hlsS3Settings: {
              cannedAcl: 'cannedAcl',
            },
            hlsWebdavSettings: {
              connectionRetryInterval: 123,
              filecacheDuration: 123,
              httpTransferMode: 'httpTransferMode',
              numRetries: 123,
              restartDelay: 123,
            },
          },
          hlsId3SegmentTagging: 'hlsId3SegmentTagging',
          iFrameOnlyPlaylists: 'iFrameOnlyPlaylists',
          incompleteSegmentBehavior: 'incompleteSegmentBehavior',
          indexNSegments: 123,
          inputLossAction: 'inputLossAction',
          ivInManifest: 'ivInManifest',
          ivSource: 'ivSource',
          keepSegments: 123,
          keyFormat: 'keyFormat',
          keyFormatVersions: 'keyFormatVersions',
          keyProviderSettings: {
            staticKeySettings: {
              keyProviderServer: {
                passwordParam: 'passwordParam',
                uri: 'uri',
                username: 'username',
              },
              staticKeyValue: 'staticKeyValue',
            },
          },
          manifestCompression: 'manifestCompression',
          manifestDurationFormat: 'manifestDurationFormat',
          minSegmentLength: 123,
          mode: 'mode',
          outputSelection: 'outputSelection',
          programDateTime: 'programDateTime',
          programDateTimeClock: 'programDateTimeClock',
          programDateTimePeriod: 123,
          redundantManifest: 'redundantManifest',
          segmentationMode: 'segmentationMode',
          segmentLength: 123,
          segmentsPerSubdirectory: 123,
          streamInfResolution: 'streamInfResolution',
          timedMetadataId3Frame: 'timedMetadataId3Frame',
          timedMetadataId3Period: 123,
          timestampDeltaMilliseconds: 123,
          tsFileMode: 'tsFileMode',
        },
        mediaPackageGroupSettings: {
          destination: {
            destinationRefId: 'destinationRefId',
          },
        },
        msSmoothGroupSettings: {
          acquisitionPointId: 'acquisitionPointId',
          audioOnlyTimecodeControl: 'audioOnlyTimecodeControl',
          certificateMode: 'certificateMode',
          connectionRetryInterval: 123,
          destination: {
            destinationRefId: 'destinationRefId',
          },
          eventId: 'eventId',
          eventIdMode: 'eventIdMode',
          eventStopBehavior: 'eventStopBehavior',
          filecacheDuration: 123,
          fragmentLength: 123,
          inputLossAction: 'inputLossAction',
          numRetries: 123,
          restartDelay: 123,
          segmentationMode: 'segmentationMode',
          sendDelayMs: 123,
          sparseTrackType: 'sparseTrackType',
          streamManifestBehavior: 'streamManifestBehavior',
          timestampOffset: 'timestampOffset',
          timestampOffsetMode: 'timestampOffsetMode',
        },
        multiplexGroupSettings: { },
        rtmpGroupSettings: {
          adMarkers: ['adMarkers'],
          authenticationScheme: 'authenticationScheme',
          cacheFullBehavior: 'cacheFullBehavior',
          cacheLength: 123,
          captionData: 'captionData',
          inputLossAction: 'inputLossAction',
          restartDelay: 123,
        },
        udpGroupSettings: {
          inputLossAction: 'inputLossAction',
          timedMetadataId3Frame: 'timedMetadataId3Frame',
          timedMetadataId3Period: 123,
        },
      },
      outputs: [{
        audioDescriptionNames: ['audioDescriptionNames'],
        captionDescriptionNames: ['captionDescriptionNames'],
        outputName: 'outputName',
        outputSettings: {
          archiveOutputSettings: {
            containerSettings: {
              m2TsSettings: {
                absentInputAudioBehavior: 'absentInputAudioBehavior',
                arib: 'arib',
                aribCaptionsPid: 'aribCaptionsPid',
                aribCaptionsPidControl: 'aribCaptionsPidControl',
                audioBufferModel: 'audioBufferModel',
                audioFramesPerPes: 123,
                audioPids: 'audioPids',
                audioStreamType: 'audioStreamType',
                bitrate: 123,
                bufferModel: 'bufferModel',
                ccDescriptor: 'ccDescriptor',
                dvbNitSettings: {
                  networkId: 123,
                  networkName: 'networkName',
                  repInterval: 123,
                },
                dvbSdtSettings: {
                  outputSdt: 'outputSdt',
                  repInterval: 123,
                  serviceName: 'serviceName',
                  serviceProviderName: 'serviceProviderName',
                },
                dvbSubPids: 'dvbSubPids',
                dvbTdtSettings: {
                  repInterval: 123,
                },
                dvbTeletextPid: 'dvbTeletextPid',
                ebif: 'ebif',
                ebpAudioInterval: 'ebpAudioInterval',
                ebpLookaheadMs: 123,
                ebpPlacement: 'ebpPlacement',
                ecmPid: 'ecmPid',
                esRateInPes: 'esRateInPes',
                etvPlatformPid: 'etvPlatformPid',
                etvSignalPid: 'etvSignalPid',
                fragmentTime: 123,
                klv: 'klv',
                klvDataPids: 'klvDataPids',
                nielsenId3Behavior: 'nielsenId3Behavior',
                nullPacketBitrate: 123,
                patInterval: 123,
                pcrControl: 'pcrControl',
                pcrPeriod: 123,
                pcrPid: 'pcrPid',
                pmtInterval: 123,
                pmtPid: 'pmtPid',
                programNum: 123,
                rateMode: 'rateMode',
                scte27Pids: 'scte27Pids',
                scte35Control: 'scte35Control',
                scte35Pid: 'scte35Pid',
                segmentationMarkers: 'segmentationMarkers',
                segmentationStyle: 'segmentationStyle',
                segmentationTime: 123,
                timedMetadataBehavior: 'timedMetadataBehavior',
                timedMetadataPid: 'timedMetadataPid',
                transportStreamId: 123,
                videoPid: 'videoPid',
              },
              rawSettings: { },
            },
            extension: 'extension',
            nameModifier: 'nameModifier',
          },
          frameCaptureOutputSettings: {
            nameModifier: 'nameModifier',
          },
          hlsOutputSettings: {
            h265PackagingType: 'h265PackagingType',
            hlsSettings: {
              audioOnlyHlsSettings: {
                audioGroupId: 'audioGroupId',
                audioOnlyImage: {
                  passwordParam: 'passwordParam',
                  uri: 'uri',
                  username: 'username',
                },
                audioTrackType: 'audioTrackType',
                segmentType: 'segmentType',
              },
              fmp4HlsSettings: {
                audioRenditionSets: 'audioRenditionSets',
                nielsenId3Behavior: 'nielsenId3Behavior',
                timedMetadataBehavior: 'timedMetadataBehavior',
              },
              frameCaptureHlsSettings: { },
              standardHlsSettings: {
                audioRenditionSets: 'audioRenditionSets',
                m3U8Settings: {
                  audioFramesPerPes: 123,
                  audioPids: 'audioPids',
                  ecmPid: 'ecmPid',
                  nielsenId3Behavior: 'nielsenId3Behavior',
                  patInterval: 123,
                  pcrControl: 'pcrControl',
                  pcrPeriod: 123,
                  pcrPid: 'pcrPid',
                  pmtInterval: 123,
                  pmtPid: 'pmtPid',
                  programNum: 123,
                  scte35Behavior: 'scte35Behavior',
                  scte35Pid: 'scte35Pid',
                  timedMetadataBehavior: 'timedMetadataBehavior',
                  timedMetadataPid: 'timedMetadataPid',
                  transportStreamId: 123,
                  videoPid: 'videoPid',
                },
              },
            },
            nameModifier: 'nameModifier',
            segmentModifier: 'segmentModifier',
          },
          mediaPackageOutputSettings: { },
          msSmoothOutputSettings: {
            h265PackagingType: 'h265PackagingType',
            nameModifier: 'nameModifier',
          },
          multiplexOutputSettings: {
            destination: {
              destinationRefId: 'destinationRefId',
            },
          },
          rtmpOutputSettings: {
            certificateMode: 'certificateMode',
            connectionRetryInterval: 123,
            destination: {
              destinationRefId: 'destinationRefId',
            },
            numRetries: 123,
          },
          udpOutputSettings: {
            bufferMsec: 123,
            containerSettings: {
              m2TsSettings: {
                absentInputAudioBehavior: 'absentInputAudioBehavior',
                arib: 'arib',
                aribCaptionsPid: 'aribCaptionsPid',
                aribCaptionsPidControl: 'aribCaptionsPidControl',
                audioBufferModel: 'audioBufferModel',
                audioFramesPerPes: 123,
                audioPids: 'audioPids',
                audioStreamType: 'audioStreamType',
                bitrate: 123,
                bufferModel: 'bufferModel',
                ccDescriptor: 'ccDescriptor',
                dvbNitSettings: {
                  networkId: 123,
                  networkName: 'networkName',
                  repInterval: 123,
                },
                dvbSdtSettings: {
                  outputSdt: 'outputSdt',
                  repInterval: 123,
                  serviceName: 'serviceName',
                  serviceProviderName: 'serviceProviderName',
                },
                dvbSubPids: 'dvbSubPids',
                dvbTdtSettings: {
                  repInterval: 123,
                },
                dvbTeletextPid: 'dvbTeletextPid',
                ebif: 'ebif',
                ebpAudioInterval: 'ebpAudioInterval',
                ebpLookaheadMs: 123,
                ebpPlacement: 'ebpPlacement',
                ecmPid: 'ecmPid',
                esRateInPes: 'esRateInPes',
                etvPlatformPid: 'etvPlatformPid',
                etvSignalPid: 'etvSignalPid',
                fragmentTime: 123,
                klv: 'klv',
                klvDataPids: 'klvDataPids',
                nielsenId3Behavior: 'nielsenId3Behavior',
                nullPacketBitrate: 123,
                patInterval: 123,
                pcrControl: 'pcrControl',
                pcrPeriod: 123,
                pcrPid: 'pcrPid',
                pmtInterval: 123,
                pmtPid: 'pmtPid',
                programNum: 123,
                rateMode: 'rateMode',
                scte27Pids: 'scte27Pids',
                scte35Control: 'scte35Control',
                scte35Pid: 'scte35Pid',
                segmentationMarkers: 'segmentationMarkers',
                segmentationStyle: 'segmentationStyle',
                segmentationTime: 123,
                timedMetadataBehavior: 'timedMetadataBehavior',
                timedMetadataPid: 'timedMetadataPid',
                transportStreamId: 123,
                videoPid: 'videoPid',
              },
            },
            destination: {
              destinationRefId: 'destinationRefId',
            },
            fecOutputSettings: {
              columnDepth: 123,
              includeFec: 'includeFec',
              rowLength: 123,
            },
          },
        },
        videoDescriptionName: 'videoDescriptionName',
      }],
    }],
    timecodeConfig: {
      source: 'source',
      syncThreshold: 123,
    },
    videoDescriptions: [{
      codecSettings: {
        frameCaptureSettings: {
          captureInterval: 123,
          captureIntervalUnits: 'captureIntervalUnits',
        },
        h264Settings: {
          adaptiveQuantization: 'adaptiveQuantization',
          afdSignaling: 'afdSignaling',
          bitrate: 123,
          bufFillPct: 123,
          bufSize: 123,
          colorMetadata: 'colorMetadata',
          colorSpaceSettings: {
            colorSpacePassthroughSettings: { },
            rec601Settings: { },
            rec709Settings: { },
          },
          entropyEncoding: 'entropyEncoding',
          filterSettings: {
            temporalFilterSettings: {
              postFilterSharpening: 'postFilterSharpening',
              strength: 'strength',
            },
          },
          fixedAfd: 'fixedAfd',
          flickerAq: 'flickerAq',
          forceFieldPictures: 'forceFieldPictures',
          framerateControl: 'framerateControl',
          framerateDenominator: 123,
          framerateNumerator: 123,
          gopBReference: 'gopBReference',
          gopClosedCadence: 123,
          gopNumBFrames: 123,
          gopSize: 123,
          gopSizeUnits: 'gopSizeUnits',
          level: 'level',
          lookAheadRateControl: 'lookAheadRateControl',
          maxBitrate: 123,
          minIInterval: 123,
          numRefFrames: 123,
          parControl: 'parControl',
          parDenominator: 123,
          parNumerator: 123,
          profile: 'profile',
          qualityLevel: 'qualityLevel',
          qvbrQualityLevel: 123,
          rateControlMode: 'rateControlMode',
          scanType: 'scanType',
          sceneChangeDetect: 'sceneChangeDetect',
          slices: 123,
          softness: 123,
          spatialAq: 'spatialAq',
          subgopLength: 'subgopLength',
          syntax: 'syntax',
          temporalAq: 'temporalAq',
          timecodeInsertion: 'timecodeInsertion',
        },
        h265Settings: {
          adaptiveQuantization: 'adaptiveQuantization',
          afdSignaling: 'afdSignaling',
          alternativeTransferFunction: 'alternativeTransferFunction',
          bitrate: 123,
          bufSize: 123,
          colorMetadata: 'colorMetadata',
          colorSpaceSettings: {
            colorSpacePassthroughSettings: { },
            hdr10Settings: {
              maxCll: 123,
              maxFall: 123,
            },
            rec601Settings: { },
            rec709Settings: { },
          },
          filterSettings: {
            temporalFilterSettings: {
              postFilterSharpening: 'postFilterSharpening',
              strength: 'strength',
            },
          },
          fixedAfd: 'fixedAfd',
          flickerAq: 'flickerAq',
          framerateDenominator: 123,
          framerateNumerator: 123,
          gopClosedCadence: 123,
          gopSize: 123,
          gopSizeUnits: 'gopSizeUnits',
          level: 'level',
          lookAheadRateControl: 'lookAheadRateControl',
          maxBitrate: 123,
          minIInterval: 123,
          parDenominator: 123,
          parNumerator: 123,
          profile: 'profile',
          qvbrQualityLevel: 123,
          rateControlMode: 'rateControlMode',
          scanType: 'scanType',
          sceneChangeDetect: 'sceneChangeDetect',
          slices: 123,
          tier: 'tier',
          timecodeInsertion: 'timecodeInsertion',
        },
        mpeg2Settings: {
          adaptiveQuantization: 'adaptiveQuantization',
          afdSignaling: 'afdSignaling',
          colorMetadata: 'colorMetadata',
          colorSpace: 'colorSpace',
          displayAspectRatio: 'displayAspectRatio',
          filterSettings: {
            temporalFilterSettings: {
              postFilterSharpening: 'postFilterSharpening',
              strength: 'strength',
            },
          },
          fixedAfd: 'fixedAfd',
          framerateDenominator: 123,
          framerateNumerator: 123,
          gopClosedCadence: 123,
          gopNumBFrames: 123,
          gopSize: 123,
          gopSizeUnits: 'gopSizeUnits',
          scanType: 'scanType',
          subgopLength: 'subgopLength',
          timecodeInsertion: 'timecodeInsertion',
        },
      },
      height: 123,
      name: 'name',
      respondToAfd: 'respondToAfd',
      scalingBehavior: 'scalingBehavior',
      sharpness: 123,
      width: 123,
    }],
  },
  inputAttachments: [{
    automaticInputFailoverSettings: {
      errorClearTimeMsec: 123,
      failoverConditions: [{
        failoverConditionSettings: {
          audioSilenceSettings: {
            audioSelectorName: 'audioSelectorName',
            audioSilenceThresholdMsec: 123,
          },
          inputLossSettings: {
            inputLossThresholdMsec: 123,
          },
          videoBlackSettings: {
            blackDetectThreshold: 123,
            videoBlackThresholdMsec: 123,
          },
        },
      }],
      inputPreference: 'inputPreference',
      secondaryInputId: 'secondaryInputId',
    },
    inputAttachmentName: 'inputAttachmentName',
    inputId: 'inputId',
    inputSettings: {
      audioSelectors: [{
        name: 'name',
        selectorSettings: {
          audioHlsRenditionSelection: {
            groupId: 'groupId',
            name: 'name',
          },
          audioLanguageSelection: {
            languageCode: 'languageCode',
            languageSelectionPolicy: 'languageSelectionPolicy',
          },
          audioPidSelection: {
            pid: 123,
          },
          audioTrackSelection: {
            tracks: [{
              track: 123,
            }],
          },
        },
      }],
      captionSelectors: [{
        languageCode: 'languageCode',
        name: 'name',
        selectorSettings: {
          ancillarySourceSettings: {
            sourceAncillaryChannelNumber: 123,
          },
          aribSourceSettings: { },
          dvbSubSourceSettings: {
            ocrLanguage: 'ocrLanguage',
            pid: 123,
          },
          embeddedSourceSettings: {
            convert608To708: 'convert608To708',
            scte20Detection: 'scte20Detection',
            source608ChannelNumber: 123,
            source608TrackNumber: 123,
          },
          scte20SourceSettings: {
            convert608To708: 'convert608To708',
            source608ChannelNumber: 123,
          },
          scte27SourceSettings: {
            ocrLanguage: 'ocrLanguage',
            pid: 123,
          },
          teletextSourceSettings: {
            outputRectangle: {
              height: 123,
              leftOffset: 123,
              topOffset: 123,
              width: 123,
            },
            pageNumber: 'pageNumber',
          },
        },
      }],
      deblockFilter: 'deblockFilter',
      denoiseFilter: 'denoiseFilter',
      filterStrength: 123,
      inputFilter: 'inputFilter',
      networkInputSettings: {
        hlsInputSettings: {
          bandwidth: 123,
          bufferSegments: 123,
          retries: 123,
          retryInterval: 123,
          scte35Source: 'scte35Source',
        },
        serverValidation: 'serverValidation',
      },
      scte35Pid: 123,
      smpte2038DataPreference: 'smpte2038DataPreference',
      sourceEndBehavior: 'sourceEndBehavior',
      videoSelector: {
        colorSpace: 'colorSpace',
        colorSpaceSettings: {
          hdr10Settings: {
            maxCll: 123,
            maxFall: 123,
          },
        },
        colorSpaceUsage: 'colorSpaceUsage',
        selectorSettings: {
          videoSelectorPid: {
            pid: 123,
          },
          videoSelectorProgramId: {
            programId: 123,
          },
        },
      },
    },
  }],
  inputSpecification: {
    codec: 'codec',
    maximumBitrate: 'maximumBitrate',
    resolution: 'resolution',
  },
  logLevel: 'logLevel',
  name: 'name',
  roleArn: 'roleArn',
  tags: tags,
  vpc: {
    publicAddressAllocationIds: ['publicAddressAllocationIds'],
    securityGroupIds: ['securityGroupIds'],
    subnetIds: ['subnetIds'],
  },
});

MediaLive Channel のパラメータを楽して埋めたい

膨大なパラメータをひとつひとつ調べて埋めて、リソース作成にトライして、失敗して、、、を繰り返していたらいくら時間があっても足りませんのでちょっと楽をして MediaLive Channel のパラメータを埋めてみます。

MediaLive には カスタムテンプレート という機能があり、これを利用することで、作成されている MediaLive Channel のパラメータをダウンロードすることができます。今回はこれを使って、MediaLive Channel を生成するソースコードを効率的に作成していきます。

やってみた

今回は以下の流れで進めます。

  1. MediaLive Input、MediaLive InputSecurityGroup を CDK で作成
  2. MediaLive Channel をコンソール上で作成
  3. 作成した MediaLive Channel のカスタムテンプレートをダウンロード
  4. MediaLive Channel を作成するための CDK ソースを作成
  5. CDK で MediaLive をデプロイ


1. MediaLive Input、MediaLive InputSecurityGroup を CDK で作成

まず、MediaLive の InputInputSecurityGroup を CDK で作成します。次の手順で MediaLive Channel を作成するために、この 2つのリソースが必要です。

各種ドキュメントを参考に、RTMP PUSH を受け取るための Input を作成する以下のソースコードを作成しました。今回は同一ファイルに全てのリソースを含めていますが、必要に応じてファイル分割などを行ってください。

import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as medialive from 'aws-cdk-lib/aws-medialive';

export class MediaworkloadStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    // MediaLive Input SecurityGroup
    const cfnInputSecurityGroup = new medialive.CfnInputSecurityGroup(this, 'MediaLiveInputSecurityGroup', {
      tags: {
        Name: 'MediaWL-SG'
      },
      whitelistRules: [
        {
          cidr: '10.0.0.0/16'
        }
      ],
    });

    // MediaLive Input
    const cfnInput = new medialive.CfnInput(this, 'MediaLiveInput', {
      destinations: [
        {
          streamName: 'mediawl/stream-a',
        },
        {
          streamName: 'mediawl/stream-b',
        }
      ],
      inputSecurityGroups: [ cfnInputSecurityGroup.ref ],
      name: 'MediaWL-Input',
      tags: {
        Name: 'MediaWL-Input'
      },
      type: 'RTMP_PUSH',
    })
  }
}


この状態でデプロイを行います。

cdk deploy


下記画像の様に、MediaLive の Input と、Input Security Group が作成されていれば OK です。

2. MediaLive Channel をコンソール上で作成

続いて、コンソール上で MediaLive Channel を作成します。

MediaLive →「チャネルの作成」で下記の様な画面が開きますので、任意のチャネル名を入力します。


チャネル名を入力した後、そのまま少し下にスクロールすると「チャネルテンプレート」という設定項目があります。ここから Live event HLS を選択します。選択後、「出力グループ」にいくつか設定が追加されるかと思います。


続いて、「入力アタッチメント」の横にある「追加」をクリックし、「入力」にて先ほど作成した Media Live Input リソースを選択し、「確認」をクリック。


最後に、「出力グループ」内の一番上にある文字列(今回は "1.HD(HLS)")をクリックし、「HLS グループ送信先」という項目をそれぞれ埋めます。本来は出力先のサービス等を示す URL を指定する必要がありますが、今回は検証目的であることと、後から変更も可能であることもあり適当な値を入れています。


一通り設定したら、画面下の「チャネルの作成」をクリックします。エラーなくチャネルが作成されたら、OK です。

3. 作成した MediaLive Channel のカスタムテンプレートをダウンロード

MediaLive Channel が作成されたら、作成された Channel の詳細画面を開き、右上の「アクション」→ 「カスタムテンプレートのダウンロード」をクリックします。


ダウンロードされた JSON ファイルの中身を確認すると、以下の様な構成になっていることがわかります。MediaLive Channel の CDK ドキュメント と見比べると、使いまわせそうなパラメータが沢山ありそうなことがわかるかと思います。

{
  "name": "HTTP live streaming - clone",
  "id": "1234567",
  "arn": "arn:aws:medialive:ap-northeast-1:111122223333:channel:1234567",
  "inputAttachments": [(省略)],
  "state": "IDLE",
  "pipelinesRunningCount": 0,
  "destinations": [(省略)],
  "egressEndpoints": [(省略)],
  "encoderSettings": {(省略)},
  "roleArn": "",
  "inputSpecification": {(省略)},
  "logLevel": "DISABLED",
  "tags": {},
  "channelClass": "STANDARD",
  "pipelineDetails": [],
  "maintenanceWindow": "FRIDAY_0400",
  "maintenanceStatus": "",
  "maintenance": {(省略)}
}


CDK のパラメータとして使える様に、上記 JSON に対して幾つかの処理をします。m3u8Settings だけ、カスタムテンプレート内の表現が CDK 側で定義されている値とずれていたため、変換をかけています。

cat <ダウンロードしたJSONファイル> | sed -E "s/\"([a-zA-Z0-1]*)\":/\1:/g" > tmp.json
cat tmp.json | sed -E "s/\"/\'/g" >  tmp.json
cat tmp.json | sed -E "s/m3u8Settings/m3U8Settings/g" > output.json


上記コマンドを実行すると、下記の様に CDK のソースコードにパラメータとしてそのまま使える形式になります。

{
  name: 'HTTP live streaming - clone',
  id: '1234567',
  arn: 'arn:aws:medialive:ap-northeast-1:111122223333:channel:1234567',
  inputAttachments: [(省略)],
  state: 'IDLE',
  pipelinesRunningCount: 0,
  destinations: [(省略)],
  egressEndpoints: [(省略)],
  encoderSettings: {(省略)},
  roleArn: '',
  inputSpecification: {(省略)},
  logLevel: 'DISABLED',
  tags: {},
  channelClass: 'STANDARD',
  pipelineDetails: [],
  maintenanceWindow: 'FRIDAY_0400',
  maintenanceStatus: '',
  maintenance: {(省略)}
}


4. MediaLive Channel を作成するための CDK ソースを作成

上記で取得したパラメータ一覧を CDK の CfnChannel パラメータに当てはめたものが下記のソースコードです。(長いので折りたたんでいます)

以下パラメータについて、上記で取得した値をそのままコピペで使っています。

  • channelClass
  • destinations
  • encoderSettings
  • inputSpecification
  • logLevel
  • name
  • tags
  • roleArn

一方で、ダウンロードしたカスタムテンプレートには入力(inputAttachments)に関する情報は含まれない形になるため、以下の記述だけ追加しています。

inputAttachments: [{
  inputAttachmentName: 'MediaWL-Input',
  inputId: cfnInput.ref,
}]


作成したスタックファイルはこちら
import { Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as medialive from 'aws-cdk-lib/aws-medialive';

export class MediaworkloadStack extends Stack {
  constructor(scope: Construct, id: string, props?: StackProps) {
    super(scope, id, props);

    // MediaLive Input SecurityGroup
    const cfnInputSecurityGroup = new medialive.CfnInputSecurityGroup(this, 'MediaLiveInputSecurityGroup', {
      tags: {
        Name: 'MediaWL-SG'
      },
      whitelistRules: [
        {
          cidr: '10.0.0.0/16'
        }
      ],
    });

    // MediaLive Input
    const cfnInput = new medialive.CfnInput(this, 'MediaLiveInput', {
      destinations: [
        {
          streamName: 'mediawl/stream-a',
        },
        {
          streamName: 'mediawl/stream-b',
        }
      ],
      inputSecurityGroups: [ cfnInputSecurityGroup.ref ],
      name: 'MediaWL-Input',
      tags: {
        Name: 'MediaWL-Input'
      },
      type: 'RTMP_PUSH',
    })

    // MediaLive Channel
    const cfnChannel = new medialive.CfnChannel(this, 'MediaLiveChannel', {
      channelClass: 'STANDARD',
      destinations: [
        {
          id: 'destination1',
          settings: [
            {
              url: 'http://example.com/main'
            },
            {
              url: 'http://example.com/sby'
            }
          ],
          mediaPackageSettings: []
        }
      ],
      encoderSettings: {
        audioDescriptions: [
          {
            audioSelectorName: 'default',
            codecSettings: {
              aacSettings: {
                bitrate: 64000,
                rawFormat: 'NONE',
                spec: 'MPEG4'
              }
            },
            audioTypeControl: 'FOLLOW_INPUT',
            languageCodeControl: 'FOLLOW_INPUT',
            name: 'audio_1_aac64'
          },
          {
            audioSelectorName: 'default',
            codecSettings: {
              aacSettings: {
                bitrate: 64000,
                rawFormat: 'NONE',
                spec: 'MPEG4'
              }
            },
            audioTypeControl: 'FOLLOW_INPUT',
            languageCodeControl: 'FOLLOW_INPUT',
            name: 'audio_2_aac64'
          },
          {
            audioSelectorName: 'default',
            codecSettings: {
              aacSettings: {
                bitrate: 64000,
                rawFormat: 'NONE',
                spec: 'MPEG4'
              }
            },
            audioTypeControl: 'FOLLOW_INPUT',
            languageCodeControl: 'FOLLOW_INPUT',
            name: 'audio_3_aac64'
          },
          {
            audioSelectorName: 'default',
            codecSettings: {
              aacSettings: {
                bitrate: 96000,
                rawFormat: 'NONE',
                spec: 'MPEG4'
              }
            },
            audioTypeControl: 'FOLLOW_INPUT',
            languageCodeControl: 'FOLLOW_INPUT',
            name: 'audio_1_aac96'
          },
          {
            audioSelectorName: 'default',
            codecSettings: {
              aacSettings: {
                bitrate: 96000,
                rawFormat: 'NONE',
                spec: 'MPEG4'
              }
            },
            audioTypeControl: 'FOLLOW_INPUT',
            languageCodeControl: 'FOLLOW_INPUT',
            name: 'audio_2_aac96'
          },
          {
            audioSelectorName: 'default',
            codecSettings: {
              aacSettings: {
                bitrate: 96000,
                rawFormat: 'NONE',
                spec: 'MPEG4'
              }
            },
            audioTypeControl: 'FOLLOW_INPUT',
            languageCodeControl: 'FOLLOW_INPUT',
            name: 'audio_3_aac96'
          },
          {
            audioSelectorName: 'default',
            codecSettings: {
              aacSettings: {
                bitrate: 128000,
                rawFormat: 'NONE',
                spec: 'MPEG4'
              }
            },
            audioTypeControl: 'FOLLOW_INPUT',
            languageCodeControl: 'FOLLOW_INPUT',
            name: 'audio_1_aac128'
          },
          {
            audioSelectorName: 'default',
            codecSettings: {
              aacSettings: {
                bitrate: 128000,
                rawFormat: 'NONE',
                spec: 'MPEG4'
              }
            },
            audioTypeControl: 'FOLLOW_INPUT',
            languageCodeControl: 'FOLLOW_INPUT',
            name: 'audio_2_aac128'
          },
          {
            audioSelectorName: 'default',
            codecSettings: {
              aacSettings: {
                bitrate: 128000,
                rawFormat: 'NONE',
                spec: 'MPEG4'
              }
            },
            audioTypeControl: 'FOLLOW_INPUT',
            languageCodeControl: 'FOLLOW_INPUT',
            name: 'audio_3_aac128'
          }
        ],
        outputGroups: [
          {
            outputGroupSettings: {
              hlsGroupSettings: {
                incompleteSegmentBehavior: 'AUTO',
                discontinuityTags: 'INSERT',
                adMarkers: [],
                captionLanguageSetting: 'OMIT',
                captionLanguageMappings: [],
                hlsCdnSettings: {
                  hlsBasicPutSettings: {
                    numRetries: 5,
                    connectionRetryInterval: 30,
                    restartDelay: 5,
                    filecacheDuration: 300
                  }
                },
                inputLossAction: 'EMIT_OUTPUT',
                manifestCompression: 'NONE',
                destination: {
                  destinationRefId: 'destination1'
                },
                ivInManifest: 'INCLUDE',
                ivSource: 'FOLLOWS_SEGMENT_NUMBER',
                clientCache: 'ENABLED',
                tsFileMode: 'SEGMENTED_FILES',
                manifestDurationFormat: 'FLOATING_POINT',
                segmentationMode: 'USE_SEGMENT_DURATION',
                redundantManifest: 'DISABLED',
                outputSelection: 'MANIFESTS_AND_SEGMENTS',
                streamInfResolution: 'INCLUDE',
                iFrameOnlyPlaylists: 'DISABLED',
                indexNSegments: 10,
                programDateTime: 'INCLUDE',
                programDateTimePeriod: 600,
                keepSegments: 21,
                segmentLength: 6,
                timedMetadataId3Frame: 'PRIV',
                timedMetadataId3Period: 10,
                hlsId3SegmentTagging: 'DISABLED',
                codecSpecification: 'RFC_4281',
                directoryStructure: 'SINGLE_DIRECTORY',
                segmentsPerSubdirectory: 10000,
                mode: 'LIVE',
                programDateTimeClock: 'INITIALIZE_FROM_OUTPUT_TIMECODE'
              }
            },
            name: 'TN2224',
            outputs: [
              {
                outputSettings: {
                  hlsOutputSettings: {
                    nameModifier: '_960x540_2000k',
                    hlsSettings: {
                      standardHlsSettings: {
                        m3U8Settings: {
                          audioPids: '492-498',
                          ecmPid: '8182',
                          pcrControl: 'PCR_EVERY_PES_PACKET',
                          pmtPid: '480',
                          scte35Pid: '500',
                          scte35Behavior: 'NO_PASSTHROUGH',
                          timedMetadataPid: '502',
                          timedMetadataBehavior: 'NO_PASSTHROUGH',
                          videoPid: '481'
                        },
                        audioRenditionSets: 'program_audio'
                      }
                    }
                  }
                },
                videoDescriptionName: 'video_960_540',
                audioDescriptionNames: [
                  'audio_2_aac96'
                ],
                captionDescriptionNames: []
              },
              {
                outputSettings: {
                  hlsOutputSettings: {
                    nameModifier: '_1280x720_3300k',
                    hlsSettings: {
                      standardHlsSettings: {
                        m3U8Settings: {
                          audioPids: '492-498',
                          ecmPid: '8182',
                          pcrControl: 'PCR_EVERY_PES_PACKET',
                          pmtPid: '480',
                          scte35Pid: '500',
                          scte35Behavior: 'NO_PASSTHROUGH',
                          timedMetadataPid: '502',
                          timedMetadataBehavior: 'NO_PASSTHROUGH',
                          videoPid: '481'
                        },
                        audioRenditionSets: 'program_audio'
                      }
                    }
                  }
                },
                videoDescriptionName: 'video_1280_720_1',
                audioDescriptionNames: [
                  'audio_3_aac96'
                ],
                captionDescriptionNames: []
              },
              {
                outputSettings: {
                  hlsOutputSettings: {
                    nameModifier: '_1280x720_5000k',
                    hlsSettings: {
                      standardHlsSettings: {
                        m3U8Settings: {
                          audioPids: '492-498',
                          ecmPid: '8182',
                          pcrControl: 'PCR_EVERY_PES_PACKET',
                          pmtPid: '480',
                          scte35Pid: '500',
                          scte35Behavior: 'NO_PASSTHROUGH',
                          timedMetadataPid: '502',
                          timedMetadataBehavior: 'NO_PASSTHROUGH',
                          videoPid: '481'
                        },
                        audioRenditionSets: 'program_audio'
                      }
                    }
                  }
                },
                videoDescriptionName: 'video_1280_720_2',
                audioDescriptionNames: [
                  'audio_1_aac128'
                ],
                captionDescriptionNames: []
              },
              {
                outputSettings: {
                  hlsOutputSettings: {
                    nameModifier: '_1280x720_6500k',
                    hlsSettings: {
                      standardHlsSettings: {
                        m3U8Settings: {
                          audioPids: '492-498',
                          ecmPid: '8182',
                          pcrControl: 'PCR_EVERY_PES_PACKET',
                          pmtPid: '480',
                          scte35Pid: '500',
                          scte35Behavior: 'NO_PASSTHROUGH',
                          timedMetadataPid: '502',
                          timedMetadataBehavior: 'NO_PASSTHROUGH',
                          videoPid: '481'
                        },
                        audioRenditionSets: 'program_audio'
                      }
                    }
                  }
                },
                videoDescriptionName: 'video_1280_720_3',
                audioDescriptionNames: [
                  'audio_2_aac128'
                ],
                captionDescriptionNames: []
              },
              {
                outputSettings: {
                  hlsOutputSettings: {
                    nameModifier: '_1920x1080_8000k',
                    hlsSettings: {
                      standardHlsSettings: {
                        m3U8Settings: {
                          audioPids: '492-498',
                          ecmPid: '8182',
                          pcrControl: 'PCR_EVERY_PES_PACKET',
                          pmtPid: '480',
                          scte35Pid: '500',
                          scte35Behavior: 'NO_PASSTHROUGH',
                          timedMetadataPid: '502',
                          timedMetadataBehavior: 'NO_PASSTHROUGH',
                          videoPid: '481'
                        },
                        audioRenditionSets: 'program_audio'
                      }
                    }
                  }
                },
                videoDescriptionName: 'video_1920_1080',
                audioDescriptionNames: [
                  'audio_3_aac128'
                ],
                captionDescriptionNames: []
              },
              {
                outputSettings: {
                  hlsOutputSettings: {
                    nameModifier: '_416x234_200k',
                    hlsSettings: {
                      standardHlsSettings: {
                        m3U8Settings: {
                          audioPids: '492-498',
                          ecmPid: '8182',
                          pcrControl: 'PCR_EVERY_PES_PACKET',
                          pmtPid: '480',
                          scte35Pid: '500',
                          scte35Behavior: 'NO_PASSTHROUGH',
                          timedMetadataPid: '502',
                          timedMetadataBehavior: 'NO_PASSTHROUGH',
                          videoPid: '481'
                        },
                        audioRenditionSets: 'program_audio'
                      }
                    }
                  }
                },
                videoDescriptionName: 'video_416_234',
                audioDescriptionNames: [
                  'audio_1_aac64'
                ],
                captionDescriptionNames: []
              },
              {
                outputSettings: {
                  hlsOutputSettings: {
                    nameModifier: '_480x270_400k',
                    hlsSettings: {
                      standardHlsSettings: {
                        m3U8Settings: {
                          audioPids: '492-498',
                          ecmPid: '8182',
                          pcrControl: 'PCR_EVERY_PES_PACKET',
                          pmtPid: '480',
                          scte35Pid: '500',
                          scte35Behavior: 'NO_PASSTHROUGH',
                          timedMetadataPid: '502',
                          timedMetadataBehavior: 'NO_PASSTHROUGH',
                          videoPid: '481'
                        },
                        audioRenditionSets: 'program_audio'
                      }
                    }
                  }
                },
                videoDescriptionName: 'video_480_270',
                audioDescriptionNames: [
                  'audio_2_aac64'
                ],
                captionDescriptionNames: []
              },
              {
                outputSettings: {
                  hlsOutputSettings: {
                    nameModifier: '_640x360_800k',
                    hlsSettings: {
                      standardHlsSettings: {
                        m3U8Settings: {
                          audioPids: '492-498',
                          ecmPid: '8182',
                          pcrControl: 'PCR_EVERY_PES_PACKET',
                          pmtPid: '480',
                          scte35Pid: '500',
                          scte35Behavior: 'NO_PASSTHROUGH',
                          timedMetadataPid: '502',
                          timedMetadataBehavior: 'NO_PASSTHROUGH',
                          videoPid: '481'
                        },
                        audioRenditionSets: 'program_audio'
                      }
                    }
                  }
                },
                videoDescriptionName: 'video_640_360',
                audioDescriptionNames: [
                  'audio_3_aac64'
                ],
                captionDescriptionNames: []
              },
              {
                outputSettings: {
                  hlsOutputSettings: {
                    nameModifier: '_768x432_1200k',
                    hlsSettings: {
                      standardHlsSettings: {
                        m3U8Settings: {
                          audioPids: '492-498',
                          ecmPid: '8182',
                          pcrControl: 'PCR_EVERY_PES_PACKET',
                          pmtPid: '480',
                          scte35Pid: '500',
                          scte35Behavior: 'NO_PASSTHROUGH',
                          timedMetadataPid: '502',
                          timedMetadataBehavior: 'NO_PASSTHROUGH',
                          videoPid: '481'
                        },
                        audioRenditionSets: 'program_audio'
                      }
                    }
                  }
                },
                videoDescriptionName: 'video_768_432',
                audioDescriptionNames: [
                  'audio_1_aac96'
                ],
                captionDescriptionNames: []
              }
            ]
          }
        ],
        timecodeConfig: {
          source: 'SYSTEMCLOCK'
        },
        videoDescriptions: [
          {
            codecSettings: {
              h264Settings: {
                colorMetadata: 'INSERT',
                adaptiveQuantization: 'HIGH',
                bitrate: 200000,
                entropyEncoding: 'CAVLC',
                flickerAq: 'ENABLED',
                framerateControl: 'SPECIFIED',
                framerateNumerator: 15000,
                framerateDenominator: 1001,
                gopBReference: 'DISABLED',
                gopNumBFrames: 0,
                gopSize: 30,
                gopSizeUnits: 'FRAMES',
                level: 'H264_LEVEL_3',
                lookAheadRateControl: 'HIGH',
                parControl: 'INITIALIZE_FROM_SOURCE',
                profile: 'BASELINE',
                rateControlMode: 'CBR',
                syntax: 'DEFAULT',
                sceneChangeDetect: 'ENABLED',
                spatialAq: 'ENABLED',
                temporalAq: 'ENABLED'
              }
            },
            height: 236,
            name: 'video_416_234',
            scalingBehavior: 'DEFAULT',
            width: 416
          },
          {
            codecSettings: {
              h264Settings: {
                colorMetadata: 'INSERT',
                adaptiveQuantization: 'HIGH',
                bitrate: 400000,
                entropyEncoding: 'CAVLC',
                flickerAq: 'ENABLED',
                framerateControl: 'SPECIFIED',
                framerateNumerator: 15000,
                framerateDenominator: 1001,
                gopBReference: 'DISABLED',
                gopNumBFrames: 0,
                gopSize: 30,
                gopSizeUnits: 'FRAMES',
                level: 'H264_LEVEL_3',
                lookAheadRateControl: 'HIGH',
                parControl: 'INITIALIZE_FROM_SOURCE',
                profile: 'BASELINE',
                rateControlMode: 'CBR',
                syntax: 'DEFAULT',
                sceneChangeDetect: 'ENABLED',
                spatialAq: 'ENABLED',
                temporalAq: 'ENABLED'
              }
            },
            height: 272,
            name: 'video_480_270',
            scalingBehavior: 'DEFAULT',
            width: 480
          },
          {
            codecSettings: {
              h264Settings: {
                colorMetadata: 'INSERT',
                adaptiveQuantization: 'HIGH',
                bitrate: 800000,
                entropyEncoding: 'CABAC',
                flickerAq: 'ENABLED',
                framerateControl: 'SPECIFIED',
                framerateNumerator: 30000,
                framerateDenominator: 1001,
                gopBReference: 'ENABLED',
                gopNumBFrames: 3,
                gopSize: 60,
                gopSizeUnits: 'FRAMES',
                level: 'H264_LEVEL_3',
                lookAheadRateControl: 'HIGH',
                parControl: 'INITIALIZE_FROM_SOURCE',
                profile: 'MAIN',
                rateControlMode: 'CBR',
                syntax: 'DEFAULT',
                sceneChangeDetect: 'ENABLED',
                spatialAq: 'ENABLED',
                temporalAq: 'ENABLED'
              }
            },
            height: 360,
            name: 'video_640_360',
            scalingBehavior: 'DEFAULT',
            width: 640
          },
          {
            codecSettings: {
              h264Settings: {
                colorMetadata: 'INSERT',
                adaptiveQuantization: 'HIGH',
                bitrate: 1200000,
                entropyEncoding: 'CABAC',
                flickerAq: 'ENABLED',
                framerateControl: 'SPECIFIED',
                framerateNumerator: 30000,
                framerateDenominator: 1001,
                gopBReference: 'ENABLED',
                gopNumBFrames: 3,
                gopSize: 60,
                gopSizeUnits: 'FRAMES',
                level: 'H264_LEVEL_4_1',
                lookAheadRateControl: 'HIGH',
                parControl: 'INITIALIZE_FROM_SOURCE',
                profile: 'MAIN',
                rateControlMode: 'CBR',
                syntax: 'DEFAULT',
                sceneChangeDetect: 'ENABLED',
                spatialAq: 'ENABLED',
                temporalAq: 'ENABLED'
              }
            },
            height: 432,
            name: 'video_768_432',
            scalingBehavior: 'DEFAULT',
            width: 768
          },
          {
            codecSettings: {
              h264Settings: {
                colorMetadata: 'INSERT',
                adaptiveQuantization: 'HIGH',
                bitrate: 2200000,
                entropyEncoding: 'CABAC',
                flickerAq: 'ENABLED',
                framerateControl: 'SPECIFIED',
                framerateNumerator: 30000,
                framerateDenominator: 1001,
                gopBReference: 'ENABLED',
                gopNumBFrames: 3,
                gopSize: 60,
                gopSizeUnits: 'FRAMES',
                level: 'H264_LEVEL_4_1',
                lookAheadRateControl: 'HIGH',
                parControl: 'INITIALIZE_FROM_SOURCE',
                profile: 'HIGH',
                rateControlMode: 'CBR',
                syntax: 'DEFAULT',
                sceneChangeDetect: 'ENABLED',
                spatialAq: 'ENABLED',
                temporalAq: 'ENABLED'
              }
            },
            height: 540,
            name: 'video_960_540',
            scalingBehavior: 'DEFAULT',
            width: 960
          },
          {
            codecSettings: {
              h264Settings: {
                colorMetadata: 'INSERT',
                adaptiveQuantization: 'HIGH',
                bitrate: 3300000,
                entropyEncoding: 'CABAC',
                flickerAq: 'ENABLED',
                framerateControl: 'SPECIFIED',
                framerateNumerator: 30000,
                framerateDenominator: 1001,
                gopBReference: 'ENABLED',
                gopNumBFrames: 3,
                gopSize: 60,
                gopSizeUnits: 'FRAMES',
                level: 'H264_LEVEL_4_1',
                lookAheadRateControl: 'HIGH',
                parControl: 'INITIALIZE_FROM_SOURCE',
                profile: 'HIGH',
                rateControlMode: 'CBR',
                syntax: 'DEFAULT',
                sceneChangeDetect: 'ENABLED',
                spatialAq: 'ENABLED',
                temporalAq: 'ENABLED'
              }
            },
            height: 720,
            name: 'video_1280_720_1',
            scalingBehavior: 'DEFAULT',
            width: 1280
          },
          {
            codecSettings: {
              h264Settings: {
                colorMetadata: 'INSERT',
                adaptiveQuantization: 'HIGH',
                bitrate: 4700000,
                entropyEncoding: 'CABAC',
                flickerAq: 'ENABLED',
                framerateControl: 'SPECIFIED',
                framerateNumerator: 30000,
                framerateDenominator: 1001,
                gopBReference: 'ENABLED',
                gopNumBFrames: 3,
                gopSize: 60,
                gopSizeUnits: 'FRAMES',
                level: 'H264_LEVEL_4_1',
                lookAheadRateControl: 'HIGH',
                parControl: 'INITIALIZE_FROM_SOURCE',
                profile: 'HIGH',
                rateControlMode: 'CBR',
                syntax: 'DEFAULT',
                sceneChangeDetect: 'ENABLED',
                spatialAq: 'ENABLED',
                temporalAq: 'ENABLED'
              }
            },
            height: 720,
            name: 'video_1280_720_2',
            scalingBehavior: 'DEFAULT',
            width: 1280
          },
          {
            codecSettings: {
              h264Settings: {
                colorMetadata: 'INSERT',
                adaptiveQuantization: 'HIGH',
                bitrate: 6200000,
                entropyEncoding: 'CABAC',
                flickerAq: 'ENABLED',
                framerateControl: 'SPECIFIED',
                framerateNumerator: 30000,
                framerateDenominator: 1001,
                gopBReference: 'ENABLED',
                gopNumBFrames: 3,
                gopSize: 60,
                gopSizeUnits: 'FRAMES',
                level: 'H264_LEVEL_4_1',
                lookAheadRateControl: 'HIGH',
                parControl: 'INITIALIZE_FROM_SOURCE',
                profile: 'HIGH',
                rateControlMode: 'CBR',
                syntax: 'DEFAULT',
                sceneChangeDetect: 'ENABLED',
                spatialAq: 'ENABLED',
                temporalAq: 'ENABLED'
              }
            },
            height: 720,
            name: 'video_1280_720_3',
            scalingBehavior: 'DEFAULT',
            width: 1280
          },
          {
            codecSettings: {
              h264Settings: {
                colorMetadata: 'INSERT',
                adaptiveQuantization: 'HIGH',
                bitrate: 8000000,
                entropyEncoding: 'CABAC',
                flickerAq: 'ENABLED',
                framerateControl: 'SPECIFIED',
                framerateNumerator: 30000,
                framerateDenominator: 1001,
                gopBReference: 'DISABLED',
                gopNumBFrames: 1,
                gopSize: 60,
                gopSizeUnits: 'FRAMES',
                level: 'H264_LEVEL_4_1',
                lookAheadRateControl: 'HIGH',
                parControl: 'INITIALIZE_FROM_SOURCE',
                profile: 'HIGH',
                rateControlMode: 'CBR',
                syntax: 'DEFAULT',
                sceneChangeDetect: 'ENABLED',
                spatialAq: 'ENABLED',
                temporalAq: 'ENABLED'
              }
            },
            height: 1080,
            name: 'video_1920_1080',
            scalingBehavior: 'DEFAULT',
            width: 1920
          }
        ]
      },
      inputAttachments: [{
        inputAttachmentName: 'MediaWL-Input',
        inputId: cfnInput.ref,
      }],
      inputSpecification: {
        codec: 'AVC',
        resolution: 'HD',
        maximumBitrate: 'MAX_20_MBPS'
      },
      logLevel: 'DISABLED',
      name: 'HTTP live streaming - clone',
      roleArn: '',
      tags: {},
    });
  }
}

5. CDK で MediaLive をデプロイ

最後に、作成したソースコードで MediaLive がデプロイできるか確認します。

先に、先ほど作成した MediaLive Channel は削除しておきましょう。


CDK で作成したリソースも削除します。

cdk destroy


リソースの削除が完了したら、デプロイをかけます。

cdk deploy


無事にリソースが作成できれば OK です。カスタムテンプレートをダウンロードすることで、パラメータをうまく流用することができました。

おわりに

パラメータの多い MediaLive Channel を IaC(今回は CDK)で管理しようとした際に、一つずつパラメータを調べていくのは大変です。今回紹介したカスタムテンプレートのダウンロード機能を利用すれば、コンソールで作成した MediaLive Channel の設定をそのまま持って来れます。

また、一度 MediaLive Channel を作成できる状態になってしまえば、細かいパラメータの調整やファイルの分割などは如何様にもできると思います。

MediaLive の IaC 管理でお困りの方のお役に立てれば幸いです。

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

参考