ライブ配信システムの構成
映像データは下記のように流れる。
カメラ、マイク -> ローランドの映像ミキサー -> LiveShell X -> MediaLive -> MediaStore -> CloudFront -> 各プラットフォームのプレーヤー
この手順では、MediaLive、MediaStore、CloudFrontの設定について行う。
このうち、MediaLiveには
- インプット( LiveShell X からの RTMP を受け取る )
- チャンネル( 映像データをエンコードする ) で構成されている。
またMediaStoreはストレージで、MediaLiveでエンコードされたデータを保存する領域になっている( MediaLive だけでは映像を保存できない、かならず別の保存する領域に転送する必要がある )
CloudFrontは大規模配信で使われるCDNで、 複数ユーザにキャッシュを見せる事で、MediaStoreの負荷を下げるために用いている。つまりCloudFront無しでも配信は可能だが、CloudFront無しだとライブ授業のユーザー数には耐えられないため、導入しておいた方がよい。
MediaStore( 映像チャンク置き場 )を作る
MediaStoreは「MediaLiveでエンコードされた映像のチャンクを置く場所」として必要( MediaLiveにはストレージが無いので、別途つくる必要がある ) MediaLiveを作る際にMediaStoreのアドレスを指定する必要があるので、事前に作成する。
チャンネルを作成する
MediaLiveで映像データをエンコードするチャンネルを作成する
管理画面 からログイン
設定例
入力名: dev 入力タイプ: RTMP (プッシュ) Network mode: Public 入力セキュリティグループ: 既存の使用、0.0.0.0/0 入力の送信先: SINGLE_PIPELINE
-
[チャンネルの作成] をクリック
-
( その環境にMediaLive用のロールが無ければ作る必要があるので )テンプレートからロールを作成する
チャンネルテンプレートは[ Live Action ]をベースに作成する事にした。 ( このテンプレートの内容は時代によって変わるようだ。以前よりテンプレートも増えているし内容も異なる )
[ Channel class ] は [ SINGLE PIPELINE ]を選択する。 標準では[ STANDARD ]になっているが、正しく冗長化を試みるとなると、インターネット回線を2つ用意するような話になるので、シンプルに[ SINGLE PIPELINE ]で設定する。
- RTMP入力を作成する
名称、RTMP(プッシュ)、ネットワークモードはPublicを選択。 入力セキュリティグループはプロバイダから得られたIPアドレスが変わるリスクを考えて 0.0.0.0 としているが、 この箇所へのDOS攻撃を考えると、RTMPのパスワードを検討する必要がある。 [入力の送信先]は[ SINGLE PIPELINE ]で、 [ アプリケーション名とインスタンス ] は
- アプリケーション名: dev
- アプリケーションインスタンス: 無し で[ 作成 ]をクリック。
この [ アプリケーション名とインスタンス ] はRTMPで接続する際( 例えば LiveShell X で接続先の設定をする際の パスとストリームキーの設定になる
- MediaLive -> MediaStore へのファイルアップロード設定を行なう 事前にMediaStoreの設定は済んでいると思うので、下記のような設定を行なう。
MediaStoreのURLは冒頭部分に mediastoressl:// を付ける。 mediastoressl://h3uuhq63cs6j5m.data.mediastore.ap-northeast-1.amazonaws.com/z/live
720p60は削除など簡単な調整を行なう。
- [ チャンネルの作成 ]をクリック 割とすぐ作成される。
CLIから作成する
MediaStore領域を作成する
aws –profile AWSアカウント名 mediastore create-container –container-name コンテナ名
実行例)
aws mediastore create-container --container-name dev1
出力結果
{
"Container": {
"CreationTime": "2021-02-12T16:59:20+09:00",
"ARN": "arn:aws:mediastore:ap-northeast-1:222222222:container/dev1",
"Name": "dev1",
"Status": "CREATING",
"AccessLoggingEnabled": false
}
}
MediaStoreのコンテナのポリシー( コンテナへのアクセス権限の設定 )
先に下記のようなjsonを用意する
mediastore_container_policy.json
AWSアカウントID、Resourceの箇所は必要に応じて変更する
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "MediaStoreFullAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::1111111111:root"
},
"Action": "mediastore:*",
"Resource": "arn:aws:mediastore:ap-northeast-1:1111111111:container/broadcast-endpoint-1/*",
"Condition": {
"Bool": {
"aws:SecureTransport": "true"
}
}
},
{
"Sid": "PublicReadOverHttps",
"Effect": "Allow",
"Principal": "*",
"Action": [
"mediastore:GetObject",
"mediastore:DescribeObject"
],
"Resource": "arn:aws:mediastore:ap-northeast-1:1111111111:container/broadcast-endpoint-1/*",
"Condition": {
"Bool": {
"aws:SecureTransport": "true"
}
}
}
]
}
下記のように設定
aws mediastore put-container-policy --container-name broadcast-endpoint-1 --policy file://tmp/mediastore_container_policy.json
MediaStoreのCORS設定
CORSの設定を行い、CloudFrontからアクセス可能にする。先に下記のようなjsonを用意する
corsPolicy.json
[
{
"AllowedOrigins": ["*"],
"AllowedMethods": ["GET", "HEAD"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3000
}
]
下記のコマンドで適用する
aws mediastore put-cors-policy \
--container-name dev-inoue-20210105 \
--cors-policy file://corsPolicy.json
MediaLiveのインプットエンドポイントを作成
先にjsonファイルを作成する。
- StreamName: ストリーム名
- InputSecurityGroups: 各AWSアカウントでセキュリティーグループのIDは異なるので注意
- Name: AWS管理画面で見える名前
cat medialive_input-dev1.json
{
"Destinations": [
{
"StreamName": "studio-dev1"
}
],
"InputDevices": [],
"InputSecurityGroups": [
"8167665"
],
"MediaConnectFlows": [],
"Name": "studio-dev1",
"Sources": [],
"Tags": {},
"Type": "RTMP_PUSH"
}
下記コマンドで作成する。 –type RTMP_PUSH で作成するタイプを指定している。またデフォルトだとシンプル構成で構成される。
aws medialive create-input –type RTMP_PUSH –cli-input-json file://medialive_input-dev1.json
下記のような出力を得られる
- Input.Destinations.Url このURLでRTMPを受け付ける
{
"Input": {
"Arn": "arn:aws:medialive:ap-northeast-1:222222222:input:6794009",
"AttachedChannels": [],
"Destinations": [
{
"Ip": "52.194.8.54",
"Port": "1935",
"Url": "rtmp://52.194.8.54:1935/studio-dev1"
}
],
"Id": "6794009",
"InputClass": "SINGLE_PIPELINE",
"InputDevices": [],
"InputSourceType": "STATIC",
"MediaConnectFlows": [],
"Name": "studio-dev1",
"SecurityGroups": [
"8167665"
],
"Sources": [],
"State": "DETACHED",
"Tags": {},
"Type": "RTMP_PUSH"
}
}
MediaLiveのチャンネルを作成する
割と大きなjsonファイルになるが、下記のようなjsonファイルを用意する。 AWS管理画面から、このjsonの雛形のようなものはダウロード可能だが、出力される内容にバグがあり、jsonのkeyは先頭文字を大文字に変更する必要がある。 また下記の要素は都度、変更する必要がある
- Name
- InputId
- Destinations.Settings.Url
- RoleArn: AWSアカウントによってIAM の ARNが異なる
{
"Name": "inoue-20210105",
"InputAttachments": [
{
"InputId": "7549717",
"InputAttachmentName": "mp4",
"InputSettings": {
"SourceEndBehavior": "CONTINUE",
"InputFilter": "AUTO",
"FilterStrength": 1,
"DeblockFilter": "DISABLED",
"DenoiseFilter": "DISABLED",
"Smpte2038DataPreference": "IGNORE",
"AudioSelectors": [],
"CaptionSelectors": []
}
}
],
"Destinations": [
{
"Id": "destination1",
"Settings": [
{
"Url": "mediastoressl://cdjdkfgqompc4q.data.mediastore.ap-northeast-1.amazonaws.com/live/test"
}
],
"MediaPackageSettings": []
}
],
"EncoderSettings": {
"AudioDescriptions": [
{
"AudioSelectorName": "Default",
"CodecSettings": {
"AacSettings": {
"InputType": "NORMAL",
"Bitrate": 192000,
"CodingMode": "CODING_MODE_2_0",
"RawFormat": "NONE",
"Spec": "MPEG4",
"Profile": "LC",
"RateControlMode": "CBR",
"SampleRate": 48000
}
},
"AudioTypeControl": "FOLLOW_INPUT",
"LanguageCodeControl": "FOLLOW_INPUT",
"Name": "audio_2"
}
],
"CaptionDescriptions": [],
"OutputGroups": [
{
"OutputGroupSettings": {
"HlsGroupSettings": {
"IncompleteSegmentBehavior": "AUTO",
"DiscontinuityTags": "INSERT",
"AdMarkers": [],
"CaptionLanguageSetting": "OMIT",
"CaptionLanguageMappings": [],
"HlsCdnSettings": {
"HlsMediaStoreSettings": {
"NumRetries": 10,
"ConnectionRetryInterval": 1,
"RestartDelay": 15,
"FilecacheDuration": 300,
"MediaStoreStorageClass": "TEMPORAL"
}
},
"InputLossAction": "EMIT_OUTPUT",
"ManifestCompression": "NONE",
"Destination": {
"DestinationRefId": "destination1"
},
"IvInManifest": "INCLUDE",
"IvSource": "FOLLOWS_SEGMENT_NUMBER",
"ClientCache": "ENABLED",
"TsFileMode": "SEGMENTED_FILES",
"ManifestDurationFormat": "INTEGER",
"SegmentationMode": "USE_SEGMENT_DURATION",
"RedundantManifest": "DISABLED",
"OutputSelection": "MANIFESTS_AND_SEGMENTS",
"StreamInfResolution": "INCLUDE",
"IFrameOnlyPlaylists": "DISABLED",
"IndexNSegments": 10,
"ProgramDateTime": "EXCLUDE",
"ProgramDateTimePeriod": 600,
"KeepSegments": 21,
"SegmentLength": 6,
"TimedMetadataId3Frame": "PRIV",
"TimedMetadataId3Period": 10,
"HlsId3SegmentTagging": "DISABLED",
"CodecSpecification": "RFC_4281",
"DirectoryStructure": "SINGLE_DIRECTORY",
"SegmentsPerSubdirectory": 10000,
"Mode": "LIVE"
}
},
"Name": "HD",
"Outputs": [
{
"OutputSettings": {
"HlsOutputSettings": {
"NameModifier": "_720p30",
"SegmentModifier": "mp4test",
"HlsSettings": {
"StandardHlsSettings": {
"M3u8Settings": {
"AudioFramesPerPes": 4,
"AudioPids": "492-498",
"EcmPid": "8182",
"NielsenId3Behavior": "NO_PASSTHROUGH",
"PcrControl": "PCR_EVERY_PES_PACKET",
"PmtPid": "480",
"ProgramNum": 1,
"Scte35Pid": "500",
"Scte35Behavior": "NO_PASSTHROUGH",
"TimedMetadataPid": "502",
"TimedMetadataBehavior": "NO_PASSTHROUGH",
"VideoPid": "481"
},
"AudioRenditionSets": "program_audio"
}
},
"H265PackagingType": "HVC1"
}
},
"OutputName": "mp4",
"VideoDescriptionName": "video_720p30",
"AudioDescriptionNames": ["audio_2"],
"CaptionDescriptionNames": []
}
]
}
],
"TimecodeConfig": {
"Source": "EMBEDDED"
},
"VideoDescriptions": [
{
"CodecSettings": {
"H264Settings": {
"AfdSignaling": "NONE",
"ColorMetadata": "INSERT",
"AdaptiveQuantization": "HIGH",
"Bitrate": 3000000,
"EntropyEncoding": "CABAC",
"FlickerAq": "ENABLED",
"ForceFieldPictures": "DISABLED",
"FramerateControl": "SPECIFIED",
"FramerateNumerator": 30,
"FramerateDenominator": 1,
"GopBReference": "DISABLED",
"GopClosedCadence": 1,
"GopNumBFrames": 1,
"GopSize": 60,
"GopSizeUnits": "FRAMES",
"SubgopLength": "FIXED",
"ScanType": "PROGRESSIVE",
"Level": "H264_LEVEL_AUTO",
"LookAheadRateControl": "HIGH",
"NumRefFrames": 3,
"ParControl": "INITIALIZE_FROM_SOURCE",
"Profile": "HIGH",
"RateControlMode": "CBR",
"Syntax": "DEFAULT",
"SceneChangeDetect": "ENABLED",
"Slices": 1,
"SpatialAq": "ENABLED",
"TemporalAq": "ENABLED",
"TimecodeInsertion": "DISABLED"
}
},
"Height": 720,
"Name": "video_720p30",
"RespondToAfd": "NONE",
"Sharpness": 100,
"ScalingBehavior": "DEFAULT",
"Width": 1280
}
]
},
"RoleArn": "arn:aws:iam::898115053391:role/MediaLiveAccessRole",
"InputSpecification": {
"Codec": "AVC",
"Resolution": "HD",
"MaximumBitrate": "MAX_20_MBPS"
},
"LogLevel": "DISABLED",
"ChannelClass": "SINGLE_PIPELINE"
}