HLSのHTTPヘッダを見てみたいというリクエストがあったのでHLSのヘッダを見てみる。
HLS
HLSはHTTP Live Streamingの略で、Appleが提案したストリーミング配信の仕組み。ネットワークの帯域を考慮して配信する動画の画質を配信中に切り替えられ、現在はニコ動やYoutube、Abemaなどで広く利用されている。
HLSは2種類のファイルから構成される
- m3u8:取得するセグメントを記したインデックスファイル
- ts:動画を断片化したセグメントファイル
Chromeの開発者ツールでAbemaのそれぞれのファイルのHTTPヘッダを確認した。
m3u8ファイル
HTTP/1.1 200 OK Content-Encoding: gzip Content-Length: 217 X-Akamai-Path-Stats: [3:1068:47932],[1:9793:7207],[1:16613:387],[1:14838:31162],[1:20680:4294957616] Cache-Control: max-age=30 Date: Thu, 05 Nov 2020 14:22:55 GMT Connection: keep-alive Vary: Accept-Encoding Akamai-Mon-Iucid-Del: 626621 Content-Type: application/x-mpegURL Access-Control-Max-Age: 86400 Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: Server,range,hdntl,hdnts,Akamai-Mon-Iucid-Ing,Akamai-Mon-Iucid-Del,Akamai-Request-BC Access-Control-Allow-Headers: origin,range,hdntl,hdnts Access-Control-Allow-Methods: GET,OPTIONS Access-Control-Allow-Origin: *
HTTPバージョン
HTTP/1.1 200 OK
AbemaはHTTP/2で通信をしているが、m3u8ファイルとtsファイルはHTTP/1.1で取得していた。AbemaのHLSファイルはAkamaiのCDNから配信されているため、CDNが単にHTTP/2対応してなかった可能性もあるが、HLSとHTTP/2は相性が悪いのではないかとも思った。
HTTP/2はファイルダウンロードの並列数を増やして全体的なパフォーマンスをあげる代わりに、1つのファイルあたりのダウンロード時間は長くなっている。ストリーミング配信ではページを開いてから再生できるまでの時間が短いほうがよいわけで、1ファイルのダウンロードに時間がかかるのは相性が悪いのかもしれない。
あるいは、HTTP/2のパケロスによるパフォーマンス低下をケアしたのかもしれない。HTTP/2は単一TCPコネクションの中で擬似的に複数のファイルストリームを作り、並列にダウンロードできる。しかし、単一TCPコネクションであるがゆえに、どこかのストリームでパケロスするとTCP輻輳窓が落ち、すべてのストリームで送信速度が低下する。後ろで再生するtsファイルのパケロスで今欲しいtsファイルのダウンロードが遅れては悲しすぎる。
同じ疑問がStackoverFlowで投稿されていた。
With video streaming, there are not that many concurrent small requests - instead the client fetches larger chunks piece by piece. This should work pretty fine on top of HTTP/1.1 connections with keep-alive
(動画ストリーミングにおいては、小さなリクエストが並列に実行されるわけではなく、大きなチャンクで1つずつ取得される。この場合HTTP/1.1のkeep-aliveのほうがより優れた性能を発揮するはずだ。)
こちらの回答がそれっぽいが、なぜ優れた性能を発揮するのか理由がよくわからなかった。性能としては同じくらいにならないのだろうか。
Content-Encoding
Content-Encoding: gzip
コンテンツの圧縮方式をgzipで指定している。jpg画像などはすでに圧縮されているため、さらに圧縮すると逆に容量が増えてしまうため注意が必要。
Cache-Control
Cache-Control: max-age=30
30秒だけキャッシュしてよいとしている。動画は変わるものではないのだから、30秒といわず1日キャッシュして良い気がするが問題があるのだろうか。
Connection
Connection: keep-alive
HTTPのコネクションがKeep-Aliveであることを意味する。動画の再生に応じてtsファイルをダウンロードする必要があるのだから、コネクションは毎回やり直したくないという意味。なお、HTTP/2の場合keep-aliveしてはいけない*1。
Content-Type
Content-Type: application/x-mpegURL
RFC8216では、必ずapplication/vnd.apple.mpegurl
かaudio/mpegurl
でなくてはいけないとあるが、Abemaでは独自にMIMEを定義しているようだった。x-
というのはかつて非公式なHTTPヘッダにつけられていたサフィックスと同じで、独自MIMEをあえてつけていることになにか理由があるのかも知れない。
In the second, the HTTP Content-Type MUST be "application/vnd.apple.mpegurl" or "audio/mpegurl".
CORS
Access-Control-Max-Age: 86400 Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: Server,range,hdntl,hdnts,Akamai-Mon-Iucid-Ing,Akamai-Mon-Iucid-Del,Akamai-Request-BC Access-Control-Allow-Headers: origin,range,hdntl,hdnts Access-Control-Allow-Methods: GET,OPTIONS Access-Control-Allow-Origin: *
CORSはオリジン間リソース共有のことで、XSSとCSRF対策の1つ。仕組みは徳丸先生の動画がとても参考になったためそちらに譲る。
- これらのヘッダはおそらくプリフライトリクエストのあとのレスポンスヘッダだと思われる。
- Access-Control-Allow-Origin
- すべてのオリジン(からダウンロードされたスクリプト)からのファイルアクセスを許可している。
- 誰でも見れる動画ファイルなのでそうなっている。
- Access-Control-Allow-Methods
- GETとOPTIONリクエストだけ許可している。つまり更新系のメソッドは許可していない。
- 動画ファイルにGETとOPTION以外使わない。
- Access-Control-Allow-Headers
- 指定された4つ+CORS セーフリストリクエストヘッダーはリクエストのヘッダに含めてもよいことを表す。
- しかし、ここで指定されていないAccept-Encodingなどがリクエストに含められているが問題ないのだろうか。
- Access-Control-Expose-Headers
- 指定されたヘッダ+CORS セーフリストレスポンスヘッダーは、ブラウザ(を介するスクリプト)がアクセスしても問題ないと判断される。
- 逆に指定されていないヘッダの値にブラウザはアクセスできない。
- Access-Control-Allow-Credentials
- Access-Control-Max-Age
- プリフライトの結果を24時間キャッシュすることを許可している。
理解しきれていないので間違っているかもしれない。
tsファイル
HTTP/1.1 200 OK Content-Length: 421888 Date: Fri, 06 Nov 2020 16:54:18 GMT Connection: keep-alive Akamai-Mon-Iucid-Del: 655733 Content-Type: video/MP2T Access-Control-Max-Age: 86400 Access-Control-Allow-Credentials: true Access-Control-Expose-Headers: Server,range,hdntl,hdnts,Akamai-Mon-Iucid-Ing,Akamai-Mon-Iucid-Del,Akamai-Request-BC Access-Control-Allow-Headers: origin,range,hdntl,hdnts Access-Control-Allow-Methods: GET,OPTIONS Access-Control-Allow-Origin: *
m3u8との相違点
- Content-Encodingで圧縮を指定していない。
- 【小ネタ】CloudFront のファイル圧縮機能で HLS コンテンツが圧縮されるのか調べてみた | Developers.IO
- こちらの記事によれば、tsファイルは圧縮効果がほとんど見込めないとのことで、Abemaも圧縮をしていないと思われる
- Cache-Controlでキャッシュ設定がない。
- キャッシュ設定は基本するべきな気がするがなぜ。
- Content-Typeがvideo/MP2T。
まとめ
HLSの場合HTTP/2を使わないというのも、こうやって調べないと気づけなかった。また、何でもかんでも圧縮すればいいわけではないこともわかった。勉強になる。
参考
HTTP Live Streaming - Wikipedia
オリジン間リソース共有 (CORS) - HTTP | MDN
【小ネタ】CloudFront のファイル圧縮機能で HLS コンテンツが圧縮されるのか調べてみた | Developers.IO