Amazon IVSのマニフェストファイルを眺めてみた

Amazon IVSのマニフェストファイルやそのヘッダ情報などをcurlコマンドを使って眺めてみました。サーバ情報にはじまり、ABRやCORSに関する情報、そして各ファイルのリクエスト先DNS名が異なり、一部DNS名は条件により変動することなどなどが確認できました。
2020.09.30

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

清水です。簡単にセットアップでき、しかも超低遅延な配信ができるAWSのライブストリーミングソリューションAmazon Interactive Video Service (Amazon IVS)、本エントリではあえて?動画自体ではなくマニフェストファイル(拡張子.m3u8のファイル)に焦点を当て、その中身やヘッダ情報などからわかることをまとめてみました。

なお本エントリでは、Amazon IVSのChannelはオレゴンリージョンに作成し、注釈などない限りマネジメントコンソールからデフォルト設定で作成したChannelで確認した結果となります。

マニフェストファイルを眺めてみる

トップレベルマニフェストファイルのヘッダ情報

まずはIVSのChannelで払い出されるPlayback URL、つまりトップレベルのマニフェストファイルから確認していきます。マニフェストファイルの内容を確認する前に、レスポンスヘッダの情報をみてみましょう。curlコマンドに-Iオプションをつけて実行します。

% curl -I https://XXXXXXXXXXXX.us-west-2.playback.live-video.net/api/video/v1/us-west-2.123456789012.channel.XXXXXXXXsS0f.m3u8
HTTP/1.1 200 OK
Server: nginx/1.14.1
Date: Tue, 29 Sep 2020 10:46:36 GMT
Content-Type: application/vnd.apple.mpegurl
Content-Length: 7828
Access-Control-Allow-Origin: *
Cache-Control: no-cache,no-store

レスポンスヘッダのうち、Serverヘッダにはnginx/1.14.1の記載があります。Amazon IVSの中ではNginxが動いている?のかもしれません。(Serverヘッダ情報のみでの推測ですが。そして仮にNginxが使われていても、いろいろカスタマイズなどはしているのだろうなぁと思います。)ちなみに、AWS Elemental MediaPackageでもNginxのServerヘッダが返ってきたことがあります。(例えばこちらのエントリなどから確認ができます。)

CORS設定の際に必要なAccess-Control-Allow-Originヘッダも気になりますが、こちらは後述します。続いてマニフェストファイルの中身をみてみましょう。

トップレベルマニフェストの中身

curlコマンドにPlayback URLを指定して、そのレスポンス(マニフェストファイルの中身)を確認してみます。するとちょっとギョッとするほどの文字列が現れますね。各ビットレートへのマニフェストファイルのファイル名が800文字超のランダムな文字列で構成されていて、整理すると以下のようになります。

% curl https://XXXXXXXXXXXX.us-west-2.playback.live-video.net/api/video/v1/us-west-2.123456789012.channel.XXXXXXXXsS0f.m3u8
#EXTM3U
#EXT-X-SESSION-DATA:DATA-ID="NODE",VALUE="video-edge-755d74.tyo01"
#EXT-X-SESSION-DATA:DATA-ID="MANIFEST-NODE-TYPE",VALUE="weaver_cluster"
#EXT-X-SESSION-DATA:DATA-ID="MANIFEST-NODE",VALUE="video-weaver.tyo01"
#EXT-X-SESSION-DATA:DATA-ID="SUPPRESS",VALUE="true"
#EXT-X-SESSION-DATA:DATA-ID="SERVER-TIME",VALUE="1601376547.49"
#EXT-X-SESSION-DATA:DATA-ID="TRANSCODESTACK",VALUE="2017TranscodeQS_V2"
#EXT-X-SESSION-DATA:DATA-ID="USER-IP",VALUE="[アクセス元IPv4アドレス]"
#EXT-X-SESSION-DATA:DATA-ID="SERVING-ID",VALUE="[32文字のランダムな文字列]"
#EXT-X-SESSION-DATA:DATA-ID="CLUSTER",VALUE="tyo01"
#EXT-X-SESSION-DATA:DATA-ID="ABS",VALUE="false"
#EXT-X-SESSION-DATA:DATA-ID="VIDEO-SESSION-ID",VALUE="[19桁の数字]"
#EXT-X-SESSION-DATA:DATA-ID="BROADCAST-ID",VALUE="[11桁の数字]"
#EXT-X-SESSION-DATA:DATA-ID="STREAM-TIME",VALUE="195.485890"
#EXT-X-SESSION-DATA:DATA-ID="FUTURE",VALUE="true"
#EXT-X-SESSION-DATA:DATA-ID="MANIFEST-CLUSTER",VALUE="tyo01"
#EXT-X-SESSION-DATA:DATA-ID="ORIGIN",VALUE="sjc02"
#EXT-X-SESSION-DATA:DATA-ID="C",VALUE="[1000文字超のランダムな文字列]"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="chunked",NAME="1080p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=5550839,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2",VIDEO="chunked",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列1].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="720p30",NAME="720p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=2348396,RESOLUTION=1280x720,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="720p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列2].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="480p30",NAME="480p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=1403396,RESOLUTION=852x480,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="480p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列3].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="360p30",NAME="360p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=630000,RESOLUTION=640x360,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="360p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列4].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="160p30",NAME="160p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=230000,RESOLUTION=284x160,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="160p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列5].m3u8

まず#EXT-X-SESSION-DATAではじまる情報がたくさんありますね。17個。HLSなどの仕様書と照らし合わせるとおもしろいかもしれません。(私はきちんと仕様書を読んだことはなく、詳細はわからないのですが……)読み取れる情報を確認していきましょう。

DATA-ID="USER-IP"という項目があり、実際にアクセスもとのIPv4アドレスが記載されています。そのほかにもIDとなりそうな情報があり、またヘッダ情報でCache-Control: no-cache,no-storeがあることから、トップレベルマニフェストファイルについてはクライアントからのアクセスごと動的に生成しているのかな、と推測しています。

IVSのABR設定

続いて後半、#EXT-X-STREAM-INF#EXT-X-MEDIAが並ぶ部分に進んでいきましょう。こちらはAmazon IVSのAdaptive Bit Rate (ABR)についての情報が確認できますね。代表的な項目だけ抜き出してみました。

NAME BANDWIDTH RESOLUTION FRAME-RATE
1080p 5,550,839 1920x1080 30.000
720p 2,348,396 1280x720 30.000
480p 1,403,396 852x480 30.000
360p 630,000 640x360 30.000
160p 230,000 284x160 30.000

今回は1920x1080で映像を打ち上げています。ビットレートなどStreaming Softwareの設定や映像に起因する項目もあるかと思いますが、おそらく(1) 1920x1080で映像を打ち上げている、(2) Standard Channelである、という条件であれば上記のように5つのABRで構成されるのかなと思います。Amazon IVSの公式ページ、よくある質問に記載されている出力ストリームの種類にも一致しますね。 *1また最も小さい解像度で160p、230kbpsとネットワークが不安定な場所などでも、視聴ができそうです。

縦長Portraitなライブ配信でも確認してみました。1080x1920のフル解像度の映像を打ち上げています。

#EXTM3U
#EXT-X-SESSION-DATA:DATA-ID="NODE",VALUE="video-edge-5a748c.tyo01"
#EXT-X-SESSION-DATA:DATA-ID="MANIFEST-NODE-TYPE",VALUE="weaver_cluster"
#EXT-X-SESSION-DATA:DATA-ID="MANIFEST-NODE",VALUE="video-weaver.tyo01"
#EXT-X-SESSION-DATA:DATA-ID="SUPPRESS",VALUE="true"
#EXT-X-SESSION-DATA:DATA-ID="SERVER-TIME",VALUE="1601378231.04"
#EXT-X-SESSION-DATA:DATA-ID="TRANSCODESTACK",VALUE="2017TranscodeQS_V2"
#EXT-X-SESSION-DATA:DATA-ID="USER-IP",VALUE="[アクセス元IPv4アドレス]"
#EXT-X-SESSION-DATA:DATA-ID="SERVING-ID",VALUE="[32文字のランダムな文字列]"
#EXT-X-SESSION-DATA:DATA-ID="CLUSTER",VALUE="tyo01"
#EXT-X-SESSION-DATA:DATA-ID="ABS",VALUE="false"
#EXT-X-SESSION-DATA:DATA-ID="VIDEO-SESSION-ID",VALUE="[19桁の数字]"
#EXT-X-SESSION-DATA:DATA-ID="BROADCAST-ID",VALUE="[11桁の数字]"
#EXT-X-SESSION-DATA:DATA-ID="STREAM-TIME",VALUE="64.042630"
#EXT-X-SESSION-DATA:DATA-ID="FUTURE",VALUE="true"
#EXT-X-SESSION-DATA:DATA-ID="MANIFEST-CLUSTER",VALUE="tyo01"
#EXT-X-SESSION-DATA:DATA-ID="ORIGIN",VALUE="sjc02"
#EXT-X-SESSION-DATA:DATA-ID="C",VALUE="[100-文字超のランダムな文字列]"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="chunked",NAME="1920p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=9426231,RESOLUTION=1080x1920,CODECS="avc1.420028,mp4a.40.2",VIDEO="chunked",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列1].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="720p30",NAME="720p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=2312689,RESOLUTION=404x720,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="720p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列2].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="480p30",NAME="480p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=1367689,RESOLUTION=268x480,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="480p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列3].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="360p30",NAME="360p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=630000,RESOLUTION=200x360,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="360p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列4].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="160p30",NAME="160p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=230000,RESOLUTION=88x160,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="160p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列5].m3u8

こちらも#EXT-X-STREAM-INF#EXT-X-MEDIAが並ぶ部分から、ABRの情報を抜き出してみました。

NAME BANDWIDTH RESOLUTION FRAME-RATE
1920p 9,426,231 1080x1920 30.000
720p 2,312,689 404x720 30.000
480p 1,367,689 268x480 30.000
360p 630,000 200x360 30.000
160p 230,000 88x160 30.000

最もビットレート、解像度がよいものについては、1080x1920とフルHDの縦長Portrait状態と言えます。それ以外の解像度については、1280x720などではなく、横長Landscapeの垂直解像度、720や480などに合わせて水平解像度が決まっている点が興味深いですね。

ABRのマニフェストファイルの確認

ABRで一番ビットレート、解像度が高いものの、m3u8ファイルを参照してみます。まずはレスポンスヘッダです。

 % curl -I https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列1].m3u8
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Cache-Control: no-cache, no-store, private
Content-Type: application/vnd.apple.mpegurl
Vary: Accept-Encoding
Date: Wed, 30 Sep 2020 03:42:14 GMT

CORS設定用のAccess-Control-Allow-Originヘッダや、Cache-Controlヘッダでno-cache, no-store, privateとしていることなどが確認できますね。

続いてファイルの中身です。こちらもギョッとするほどの文字列が現れますが、整理すると以下のようになります。

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:54
#EXT-X-NET-LIVE-VIDEO-ELAPSED-SECS:107.995
#EXT-X-NET-LIVE-VIDEO-TOTAL-SECS:141.994
#EXT-X-DATERANGE:ID="source-1601437303",CLASS="live-video-net-stream-source",START-DATE="2020-09-30T03:41:43.386Z",END-ON-NEXT=YES,X-NET-LIVE-VIDEO-STREAM-SOURCE="live"
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:23.384Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列1].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:25.384Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列2].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:27.384Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列3].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:29.384Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列4].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:31.384Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列5].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:33.384Z
#EXTINF:1.999,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列6].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:35.383Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列7].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:37.383Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列8].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:39.383Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列9].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:41.383Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列10].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:43.383Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列11].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:45.383Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列12].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:47.383Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列13].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:49.383Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列14].ts
#EXT-X-PROGRAM-DATE-TIME:2020-09-30T03:42:51.383Z
#EXTINF:2.000,live
https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列15].ts
#EXT-X-PREFETCH:https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列16].ts
#EXT-X-PREFETCH:https://video-edge-7568a4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列17].ts

興味深いのは#EXT-X-PREFETCHが使われている点でしょうか。ざっと調べてみた限り、おそらくLow-latency HLS(LHLS)に使用されるヘッダかと思われます。(FFMPEG 4.1でLHLSを有効にするには?

またセグメントファイル(tsファイル)の参照先のDNS名についても注目してみましょう。ideo-edge-7568a4.tyo01.hls.live-video.netとなっています。Playback URLで示されているトップレベルマニフェストのDNS名、そしてそこから続くマニフェストファイル、セグメントファイル、それぞれで異なるDNS名からの配信となりますね。このDNS名については後ほどもう少し詳しく取り上げます。

Playback URLのCORS設定

Access-Control-Allow-Originヘッダ、CORS設定まわりについて確認してみましょう。おおまかですが、Access-Control-Allow-Originヘッダに記載がないドメインにAmazon IVS Playerを配置してもブラウザ側のJavaScriptの動作でエラーとなってしまい、正常にライブ動画が再生できない、となるかと思います。さてトップレベルマニフェストファイルのレスポンスヘッダでは、Access-Control-Allow-Origin: *となっていました。これは対象のドメインは限定しない(どのドメインでもよい)となります。そのため例えば、AWSマネジメントコンソール上でも再生が可能になっている状態かと思います。

このようにIVSのトップレベルマニフェストファイルでは、デフォルトの状態でCORS設定についてはすべてのドメインを許可している状態です。これだと逆に言えば、Amazon IVSのPlayback URLが入手できれば、他の第三者が自分のWebページにライブ動画を埋め込んでしまえる状態である、ともいえます。この再生許可ドメインを限定するには、Amazon IVSのPrivate Channel機能が利用できます。Private Channel再生時のtoken(JWT)作成時、payloadに以下のように再生を許可したいドメインを指定しましょう。

{
    "aws:channel-arn": <channel_arn>,
    "aws:access-control-allow-origin": "https://再生を許可したいドメイン",
    "exp": <unix timestamp>
}

実際にPython環境でJWTを作成、Privateに設定しているIVS Channelで確認してみます。JWT作成方法はこちらのエントリをご参照ください。今回は以下のようにpayloadを指定しました。

>>> payload = {
... "aws:channel-arn": "arn:aws:ivs:us-west-2:123456789012:channel/XXXXXXXXXXXX",
... "aws:access-control-allow-origin": "https://my-s3-bucket.s3-ap-northeast-1.amazonaws.com",
... "exp": int(expires.timestamp())
... }

payloadを指定してJWTを生成します。(encoded_jwtの中身、`で囲まれた部分になります。)

>>> encoded_jwt = jwt.encode(payload, secret, algorithm='ES384')
>>> encoded_jwt
b'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXImV4cCI6MTYwMTcwNTY0M30.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

このJWTでトップレベルマニフェストにアクセスしてみましょう。と言いつつ、まずは試しにPrivate ChannelのPlayback URLに対して、tokenなしでアクセスしてみます。

% curl -I https://XXXXXXXXXXXX.us-west-2.playback.live-video.net/api/video/v1/us-west-2.123456789012.channel.XXXXXXXXlctv.m3u8
HTTP/1.1 403 Forbidden
Server: nginx/1.14.1
Date: Wed, 30 Sep 2020 06:24:07 GMT
Content-Type: application/json
Content-Length: 187
Access-Control-Allow-Origin: *
Cache-Control: no-cache,no-store

レスポンスヘッダではステータスコード403が返っていることが分かりますね。レスポンスの中身もみてみましょう。

% curl https://XXXXXXXXXXXX.us-west-2.playback.live-video.net/api/video/v1/us-west-2.123456789012.channel.XXXXXXXXlctv.m3u8
[{"url":"/api/video/v1/us-west-2.123456789012.channel.XXXXXXXXlctv.m3u8","error":"token contains an invalid number of segments","error_code":"invalid_playback_auth_token","type":"error"}]

エラー内容についての記載があります。これはデバッグなどの際に活用できそうですね。

続いて本題、tokenを付与してアクセスしてみましょう。まずはリクエストヘッダです。

% curl -I "https://XXXXXXXXXXXX.us-west-2.playback.live-video.net/api/video/v1/us-west-2.123456789012.channel.XXXXXXXXlctv.m3u8?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzM4NCJ9.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXImV4cCI6MTYwMTcwNTY0M30.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
HTTP/1.1 200 OK
Server: nginx/1.14.1
Date: Wed, 30 Sep 2020 06:24:40 GMT
Content-Type: application/vnd.apple.mpegurl
Content-Length: 7814
Access-Control-Allow-Origin: https://my-s3-bucket.s3-ap-northeast-1.amazonaws.com
Cache-Control: no-cache,no-store

Access-Control-Allow-Originヘッダでドメインが指定されていることがわかりますね!Private Channelということでアクセスにtokenが必要であることに加え、Access-Control-Allow-Originヘッダでもアクセス元ドメインを限定できていることがわかります。

このトップレベルマニフェストの中身も見てみましょう。こちらはPrivateでないChannelとあまり大差ないように見受けられます。

#EXTM3U
#EXT-X-SESSION-DATA:DATA-ID="NODE",VALUE="video-edge-5cbaa4.tyo01"
#EXT-X-SESSION-DATA:DATA-ID="MANIFEST-NODE-TYPE",VALUE="weaver_cluster"
#EXT-X-SESSION-DATA:DATA-ID="MANIFEST-NODE",VALUE="video-weaver.tyo01"
#EXT-X-SESSION-DATA:DATA-ID="SUPPRESS",VALUE="true"
#EXT-X-SESSION-DATA:DATA-ID="SERVER-TIME",VALUE="1601447111.68"
#EXT-X-SESSION-DATA:DATA-ID="TRANSCODESTACK",VALUE="2017TranscodeQS_V2"
#EXT-X-SESSION-DATA:DATA-ID="USER-IP",VALUE="[アクセス元IPv4アドレス]"
#EXT-X-SESSION-DATA:DATA-ID="SERVING-ID",VALUE="[32文字のランダムな文字列]"
#EXT-X-SESSION-DATA:DATA-ID="CLUSTER",VALUE="tyo01"
#EXT-X-SESSION-DATA:DATA-ID="ABS",VALUE="false"
#EXT-X-SESSION-DATA:DATA-ID="VIDEO-SESSION-ID",VALUE="[19桁の数字]"
#EXT-X-SESSION-DATA:DATA-ID="BROADCAST-ID",VALUE="[11桁の数字]"
#EXT-X-SESSION-DATA:DATA-ID="STREAM-TIME",VALUE="247.676031"
#EXT-X-SESSION-DATA:DATA-ID="FUTURE",VALUE="true"
#EXT-X-SESSION-DATA:DATA-ID="MANIFEST-CLUSTER",VALUE="tyo01"
#EXT-X-SESSION-DATA:DATA-ID="ORIGIN",VALUE="sjc02"
#EXT-X-SESSION-DATA:DATA-ID="C",VALUE="[1000文字超のランダムな文字列]"
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="chunked",NAME="1080p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=5593120,RESOLUTION=1920x1080,CODECS="avc1.640028,mp4a.40.2",VIDEO="chunked",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列1].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="720p30",NAME="720p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=2341740,RESOLUTION=1280x720,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="720p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列2].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="480p30",NAME="480p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=1396740,RESOLUTION=852x480,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="480p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列3].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="360p30",NAME="360p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=630000,RESOLUTION=640x360,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="360p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列4].m3u8
#EXT-X-MEDIA:TYPE=VIDEO,GROUP-ID="160p30",NAME="160p",AUTOSELECT=YES,DEFAULT=YES
#EXT-X-STREAM-INF:BANDWIDTH=230000,RESOLUTION=284x160,CODECS="avc1.4D401F,mp4a.40.2",VIDEO="160p30",FRAME-RATE=30.000
https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列5].m3u8

各ABRに対応するm3u8ファイルについては、アクセス時のtokenは付与されていないようです。実際にアクセスしてみます。以下のようにtoken情報がなくてもアクセス可能なようでした。

 % curl -I https://video-weaver.tyo01.hls.live-video.net/v1/playlist/[800文字超のランダムな文字列1].m3u8
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Cache-Control: no-cache, no-store, private
Content-Type: application/vnd.apple.mpegurl
Vary: Accept-Encoding
Date: Wed, 30 Sep 2020 06:26:17 GMT

Access-Control-Allow-Originヘッダについてもドメインの指定はない状態です。さらに各ABRのm3u8、その一つの中身からtsファイルのヘッダ情報も確認してみます。m3u8の中身は先ほど例示したものと大差なさそうですので割愛します。

% curl -I https://video-edge-5cbaa4.tyo01.hls.live-video.net/v1/segment/[900文字超のランダムな文字列].ts
HTTP/1.1 200 OK
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Content-Length: 0
Content-Type: application/octet-stream
Date: Wed, 30 Sep 2020 06:26:55 GMT

こちらもtokenを付与しなくてもコード200のレスポンスが返り、Access-Control-Allow-Originヘッダはすべてのドメインを対象にしていました。

これら、つまりPlayback URLから先のリクエストについては、tokenが不要であることならびにAccess-Control-Allow-Originヘッダの内容が変わることは、念のため気に留めておく必要があるかと思います。とは言え、1000文字近いランダムな文字列で構成されているので、第三者が推測してアクセスするなどは難しいかなと思います。

各ファイルごとのリクエスト先DNS名について

トップレベルマニフェストファイル、各ABRごとのマニフェストファイル、セグメントファイルのリクエスト先DNS名がそれぞれ異なっている、という事象がありました。これについて少し振り返っておきます。

まずトップレベルマニフェストファイル、つまりPlayback URLで払い出されるのは以下のドメインです。

  • XXXXXXXXXXXX.us-west-2.playback.live-video.net

はじめの文字列はランダム(おそらくAWSアカウント内、かつ同じリージョンのChannelでは同一のようでしたが)、続いてリージョン情報、そしてplayback.live-video.netが続きます。

続いて、各ABRごとのマニフェストファイルのリクエスト先のドメインです。

  • video-weaver.tyo01.hls.live-video.net

そしてセグメントファイル(tsファイル)のリクエスト先ドメインは下記でした。

  • video-edge-7568a4.tyo01.hls.live-video.net

セグメントファイルのリクエスト応答は、エッジに分散させてリクエストを捌いているのでは?という推測ができますね。ところでtyo01の部分、気なりませんか?MANIFEST-NODEMANIFEST-CLUSTERでもtyo01の文字列がありました。なんとなくですが、tyoがtokyoを表しているのかな?な気もします。(空港コードでは東京地区はTYOですよね。)

アクセス元によって変わる情報なのかな?という推測のもと、試しに東京リージョンに起動したEC2と、バージニアリージョンで起動したEC2で確認してみました。ドメインのみ抜粋した結果が以下です。

  • 東京リージョンに起動したEC2からのリクエスト
    • ABRごとのマニフェストファイルのドメイン
      • video-weaver.sin01.hls.live-video.net
    • セグメントファイルのドメイン
      • video-edge-c68324.sin01.hls.live-video.net
  • バージニアリージョンに起動したEC2からのリクエスト
    • ABRごとのマニフェストファイルのドメイン
      • video-weaver.sea01.hls.live-video.net
    • セグメントファイルのドメイン
      • video-edge-8c7934.iad05.hls.live-video.net

東京リージョンでtyoとなれば推測通りかなと思ったのですが、sinでした。けどこれは、空港コードでシンガポールに該当します。(シンガポール・チャンギ国際空港 - Wikipedia)AWSも東京リージョンがまだない時はシンガポールリージョンを使うことも多かっただろうし、「ドメイン名の一部はアクセス元によって変わる、そのドメイン名には空港コードが使われている」という予想はいい線いっているのでは?ないでしょうか。

続いてバージニアリージョンからのリクエストです、こちらはseaとiad、空港コードで検索すると前者はシアトル・タコマ国際空港、後者はワシントン・ダレス国際空港に該当します。ワシントンは東海岸なので、バージニアに近いと言えると思います。対してシアトルは西海岸です。IVSのリソースがオレゴンにあることも影響しているのでしょうか。はたまた、ドメイン名が地理的な情報を表している、という推測が勘違いなのか……。

なお、Amazon IVSが動画を扱うソリューションということで、Amazon CloudFrontなどCDNとの連携を思いつくこともあるかと思います。そもそもAmazon IVSには専用のCDNが内包されているとのことでCDN連携をさせる必要はないと思うのですが、仮に連携を検討した場合、上記のようなリソースの違い、そして(おそらく)地理的条件によりDNS名が変わる、という点は考慮すべき点かと思います。(そしておそらく、「連携は難しい」という判断にならざるを得ないかとも思います。)

ライブ配信をしていないときのマニフェストファイル

ライブ配信をしていないとき(今回は映像を打ち上げておらず、IVS ChannelのStatusがOfflineのとき)のマニフェストファイルについても確認してみたのでまとめておきます。

まずはレスポンスヘッダの内容です。

% curl -I https://XXXXXXXXXXXX.us-west-2.playback.live-video.net/api/video/v1/us-west-2.123456789012.channel.XXXXXXXXsS0f.m3u8
HTTP/1.1 404 Not Found
Server: nginx/1.14.1
Date: Tue, 29 Sep 2020 10:39:45 GMT
Content-Type: application/json
Content-Length: 187
Access-Control-Allow-Origin: *
Cache-Control: no-cache,no-store

ステータスコードとして404が返ってきていることがわかります。続いて実際のレスポンスの内容です。

 % curl https://XXXXXXXXXXXX.us-west-2.playback.live-video.net/api/video/v1/us-west-2.123456789012.channel.XXXXXXXXsS0f.m3u8
[{"url":"/api/video/v1/us-west-2.123456789012.channel.XXXXXXXXsS0f.m3u8","error":"twirp error not_found: transcode does not exist","error_code":"transcode_does_not_exist","type":"error"}]%

トランスコードが存在しない(変換された映像がない)旨のエラーが返ります。(親切ですね!)

上記はいちどライブストリーミングを実施したチャンネルで停止した際に確認したのですが、Channel作成後いちどもライブストリーミングを行っていない(映像を打ち上げていない)Channelだと以下のようなエラーレスポンスが返ってきました。

% curl -I https://XXXXXXXXXXXX.us-west-2.playback.live-video.net/api/video/v1/us-west-2.123456789012.channel.TcYtHKkEO65t.m3u8
HTTP/1.1 404 Not Found
Server: nginx/1.14.1
Date: Tue, 29 Sep 2020 10:45:00 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 21
X-Content-Type-Options: nosniff
Cache-Control: no-cache,no-store

% curl https://XXXXXXXXXXXX.us-west-2.playback.live-video.net/api/video/v1/us-west-2.123456789012.channel.TcYtHKkEO65t.m3u8
Can not find channel

Can not find channel、Channelの使用状況でエラー内容が変わるのは少し興味深いですね。

Playback URLについて

最後に、Playback URL自体についても少し確認してみます。

live-video.netドメイン

Playback URLは以下の形式で払い出されますね。

https://XXXXXXXXXXXX.us-west-2.playback.live-video.net/api/video/v1/us-west-2.123456789012.channel.XXXXXXXX6CFp.m3u8

各ファイルごとのDNS名についても先ほど確認しましたが、ドメインとしては一貫してlive-video.netを使っているようです。ぱっと見AWSっぽくない(AWSやAmazonなどの文字列が含まれていない)、としながらもライブストリーミングソリューションにはぴったりのドメイン名だなと個人的には思いました。

ちなみに、一般固有名詞なドメインということから、すでに歴史があるようで?Internet ArchiveのWayback Machineで調べてみると、2017年時点では以下のようなロシア語、ブライダル関連と思われるサイトだったようです。この会社?が現在のAmazon IVSに繋がるのか、たまたま取得できたドメインをAWSが使っているのかは定かではありません。(個人的な印象としては後者なのかな?と思っていますが。)ちなみに2000年代もまた別の用途でドメインが使われていたようで(ドメインの所有者が別だったのでしょうか)、こちらはむやみに確認しないことをおすすめします。

Playback URLにAWSアカウントID

Playback URLドメイン名に続いて、パス部分にも注目してみます。

/api/video/v1/us-west-2.123456789012.channel.XXXXXXXX6CFp.m3u8

マニフェストファイル(m3u8ファイル)のファイル名、先頭にリージョン名がきて、続くのは12桁の数字ですね。この数字、確認してみるとAWSアカウントIDと一致していました。AWSアカウントIDについては、それを公開することで直接的な実害はないかと思いますが(他にも事情によりAWSアカウントIDを公開するという場面はいくつかあるかと思います)、例えばIAMユーザのAWSマネジメントコンソールへのログインの際の情報の一つであり、可能であれば秘匿したい情報かと考えます。リソース識別に使われる情報でもあり(ARNなど)、攻撃の手がかりになってしまう可能性もあります。もしこれらの点が気になるようであれば、Amazon IVS利用の際、専用のAWSアカウントの準備することも一つの手段かと思いました。

まとめ

ということで、Amazon IVSのマニフェストファイルを眺めながら、サーバ情報やABR情報、そしてCORS設定やアクセス先DNS名などについて確認してみました。なお今回確認した項目のうち、仕様やドキュメントなどに記載がない項目については、今後変更される可能性もあるのかなと思います。あくまで2020年9月の時点での情報であることにご留意ください。(例えばヘッダ情報で確認できたServer: nginx/1.14.1、同様の記載が確認できるAWS Elemental MediaPackageではServer: AWS Elemental MediaPackageという情報が確認できた時期もありました。)

脚注

  1. Amazon Interactive Video Service では、取り込んだ RTMPS の品質と解像度に基づき、出力トランスコードのための異なる適応ビットレート(ABR)セットを生成します。最高で 8.5Mbps、1080p60 のストリームを送信した場合、Amazon IVS は、8.5Mbps 1080p60、3Mbps 720p60、2Mbps 720p30、1.2Mbps 480p30、800Kbps 360p30、400Kbps 160p30 の各レンディションを 1 つの ABR ストリームの中に作成します。Amazon Interactive Video Service のよくある質問(2020/09/30)