2018年初頭 自社のライブ配信基盤を AdobeMdiaServer から MediaLive によるライブ配信に切り替えました。 2020年以降ですとVimeo Liveが有力候補です。
背景
- 動画配信システムだけで良いのでシンプルに立てたい( カメラの映像を手っ取り早くユーザに届けたい )
- 必要なときだけ配信システムを立てて終了したい。
- 似たような案件ができたときに、似たようなシステムをスピーディーに構築したい
- 現状、EC2 で AdobeMediaServer 立ててライブ配信している
- AdobeMediaServer のバージョンアップ? <- それに伴う検証したいか、というと検証工数を割きづらい…
- その点、AWS 上のサービスなら、後から機能追加があったりする。
- 今回は使っていませんが、CMAF 対応が追加された。機能追加の検証をクラウドベンダがやってくれて、我々はその時間をもっと別な事に使える。あるいは早く帰れてハッピー。
- 逆に「我々は配信方法で独自のチャレンジをして差別化を狙うぜ」には向いてない(現状、弊社的にそこでチャレンジはしてない)その観点でのチャレンジは EC2 上で構築した方が様々な戦術がとり得る
- 何か仕様変更に伴う負荷上昇(対応ビットレートの追加とか)に対する検証工数がビジネスに繋がりにくい
なぜ、MediaService?
- AWS で費用をまとめられる。あとトラフィックが増えた場合、ネットワーク費用のディスカウントができるかもという。
- lambda から配信システムの起動/終了とかできそう
- Azure も魅力的に見える。ただ CLI や API での操作ってどうやるんだろうか、という調査に時間とられそうだった(その点、AWS なら手軽に CLI で操作できるのは知っていった)
システム構成
カメラ -> エンコーダー -> MediaLive -> MediaStore -> CloudFront -> Safari
CDN でコストを下げつつ、ライブ配信したい構成ですね。
それぞれの役割は
-
MediaLive: RTMP などカメラからの情報を受け付ける。目的の動画フォーマットにエンコードする。ただ、これ自体にはストレージ機能は無い。
-
MediaStore: CloudFront と経由するためのストレージ
-
CloudFront: MediaStore に貯められた
それぞれを構築する順序だが、MediaLive の設定で「どこに動画データを貯めるのか」という設定が必要なため、 MediaStore の設定から行う
今回のケースでは、時間を戻っての再生は考慮せず、シンブルなライブ配信を想定しています。
以下が構築手順です。
MediaStore 作る
CDN のオリジンになるものを MediaStore ではコンテナと呼ばれます。このコンテナ名は私はスタジオ名にしました。
冗長化のとり方についてはいろいろ考え方があるかと思いますが、私は MediaStore の段階で Active/Standby 構成にしました。 これは CDN として使う CloudFront に障害が発生した場合に、別の MediaStore を見せたいと思ったためです。
MediaStore の管理画面( https://ap-northeast-1.console.aws.amazon.com/mediastore/ )にアクセスして
チャンネル名
- Active: studio_w_active
- Standby: studio_w_standby
2つのチャンネルを作るのは、CloudFront に障害が発生した際の切り替わり用。 コンテナが作成できたら、クリックし、公開するために
- Container policy
- Container CORS policy の設定を行う。
CLI
$ aws mediastore create-container --container-name 'studio_w_active'
$ aws mediastore create-container --container-name 'studio_w_standby'Web コンソール
Web コンソールの[サービス]->下の方にスクロールして[MediaStore]
[Create container]
コンテナ名を入力( 私はスタジオ名にしました )
Container policy
リージョン、ID とコンテナ名は変更すること 下記は active 用の設定。コンテナ名を変更してスタンバイ側の設定も行う
“Sid” : “MediaStoreFullAccess”, の設定はコンテナを作った段階で入っているので、 “Sid” : “PublicReadOverHttps”,側の設定を追加する形の方が良いかもしれない。
{
"Version" : "2012-10-17",
"Statement" : [ {
"Sid" : "MediaStoreFullAccess",
"Effect" : "Allow",
"Principal" : {
"AWS" : "arn:aws:iam::119346245616:root"
},
"Action" : "mediastore:*",
"Resource" : "arn:aws:mediastore:ap-northeast-1:111111111111111:container/studio_x_active/*",
"Condition" : {
"Bool" : {
"aws:SecureTransport" : "true"
}
}
}, {
"Sid" : "PublicReadOverHttps",
"Effect" : "Allow",
"Principal" : "*",
"Action" : [ "mediastore:GetObject", "mediastore:DescribeObject" ],
"Resource" : "arn:aws:mediastore:ap-northeast-1:1111111111111:container/studio_x_active/*",
"Condition" : {
"Bool" : {
"aws:SecureTransport" : "true"
}
}
} ]
}Container CORS policy
[
{
"AllowedHeaders": [
"*"
],
"AllowedMethods": [
"GET",
"HEAD"
],
"AllowedOrigins": [
"*"
],
"MaxAgeSeconds": 3000
}
]MediaLive を作る
カメラからの映像を受け入れ、エンコードして別のサービスに転送するサービス(MediaLive 自体では保存はしない)
セキュリティグループ作成
MediaLive 側で受け入れるソース IP アドレスの設定、aws cli 実行後の返り値を jq でパース、 次の手順で使うため、変数 SECURITY_GROUP_ID に入れています。
export SECURITY_GROUP_ID=$(aws medialive create-input-security-group --whitelist-rules "Cidr=$(curl ifconfig.io)/32" | jq -r '.SecurityGroup.Id')単純に(環境変数への export なしで)実行すると、下記のようになっています。
aws medialive create-input-security-group --whitelist-rules "Cidr=118.238.204.183/32"
{
"SecurityGroup": {
"Arn": "arn:aws:medialive:ap-northeast-1:1111111111111:inputSecurityGroup:973746",
"Id": "973746",
"Inputs": [],
"State": "IDLE",
"WhitelistRules": [
{
"Cidr": "118.238.240.240/32"
}
]
}
}- STREAM_A=‘a’
- STREAM_B=‘b’
- TYPE=‘RTMP_PUSH’
- NAME=‘test’
- NPUTSECURITYGROUPID=前の処理を受け継ぐ
下記を実行すると環境変数 CREATEINPUT にインプット用の設定が入る
export CREATEINPUT=$(cat <<EOS
{
"Destinations": [
{
"StreamName": "$STREAM_A"
},
{
"StreamName": "$STREAM_B"
}
],
"InputSecurityGroups": [
"$SECURITY_GROUP_ID"
],
"Name": "$NAME",
"Type": "$TYPE"
}
EOS
)上記の $CREATEINPUT をベースに MediaLive の Input を作る
M_INPUT=$(aws medialive create-input --cli-input-json "$CREATEINPUT")
export M_INPUT_ID=$(echo "$M_INPUT" | jq -r '.Input.Id')
export M_INPUT_INPUT_A=$(echo "$M_INPUT" | jq -r '.Input.Destinations[0].Url')
export M_INPUT_INPUT_B=$(echo "$M_INPUT" | jq -r '.Input.Destinations[1].Url')
export M_INPUT_IP_A=$(echo "$M_INPUT" | jq -r '.Input.Destinations[0].Ip')
export M_INPUT_IP_B=$(echo "$M_INPUT" | jq -r '.Input.Destinations[1].Ip')RTMP_A_DOMAIN=“studio-w-a” RTMP_A_JSON=”$(jo Changes=$(jo Action=UPSERT ResourceRecordSet=$(jo Name=”${RTMP_A_DOMAIN}” Type=A TTL=120 ResourceRecords=$(jo -a $(jo Value=${M_INPUT_IP_A})))))”
RTMP_B_DOMAIN=“studio-w-b” RTMP_B_JSON=”$(jo Changes=$(jo Action=UPSERT ResourceRecordSet=$(jo Name=”${RTMP_B_DOMAIN}” Type=A TTL=120 ResourceRecords=$(jo -a $(jo Value=${M_INPUT_IP_A})))))”
アーカイブする際のファイル名を変更する
参照:可変データの識別子 - AWS Elemental MediaLive
via) AWS Elemental MediaLive のアーカイブ出力機能でライブ配信と同時にファイルにも映像を書き出す
$dt$ YYYYMMDDTHHMMSS
$d$ YYYYMMDD
$t$ HHMMSS2018/06/06 時点の設定
studio-y_$d$_$t$CloudFront で CDN 作成
- origin に MediaStore の Active 側のドメインを設定
- [ Viewer Protocol Policy ]は[ Redirect HTTP to HTTPS ]に設定
- [ Object Caching ]は[ Customize ]
- TTL が変更可能になるので
- Maximum TTL 120
- Default TTL 120 に変更した。 恐らく古いキャッシュが残っていても(MediaStore は即時書き込みを保証しているので)問題は無いと思うが、前日の HLS ライブ配信のキャッシュが残っていたため、過去の動画が入る、といった事故は避けたい。
- [ Query String Forwarding and Caching ]は[ Forword all, cache base on all ]に変更した。この設定は CDN のキャッシュヒット率を上げるための設定だと思われる。HLS 通信の場合、クエリストリングは飛んで来ない、と思われる。が有効にした。
上記の設定で一旦作成、CloudFront + MediaStore での配信に必要な CORS 設定を追加する。 [ Origin Setting ]->[ Forward Headers ]->[ Origin ]を追加する 参考: https://dev.classmethod.jp/cloud/aws/aws-elemental-mediastore-cors-policy/ https://dev.classmethod.jp/cloud/aws/cloudfront-cross-origin-resource-sharing/