Video.jsでのHLS形式動画の再生を通してS3とCloudFrontのCORS設定について確認してみた
はじめに
清水です。Video.js(などJavaScript製の動画プレイヤー)でHLS形式動画を再生するとときに、HTML関連のファイル(.htmlや.jsなど)とHLS形式のファイル(.m3u8や.ts)の配信ドメインが異なる場合には、後者にはCORS設定が必要となります。
CORSの詳細については以下エントリをご確認ください。
もちろん動画プレイヤーを使う場合に限らず、さまざまな場面でCORSの設定は必要になります。ただ今回は冒頭に示したようにHLS形式の動画をVideo.jsで再生するというケースに絞って、設定しないとどうなるか、どのように設定できるかなどをまとめてみます。なお配信環境としてはAmazon S3ならびにAmazon CloudFrontを利用するもと想定し、これらサービスでの設定箇所についてもまとめてみます。
S3にCORSを設定していない場合
まずはS3にCORS設定をしていない場合について確認してみます。このバケットを仮にnon-cors-bucket-1としましょう。パブリックリードのアクセスを許可し、パスhls/sample59.m3u8を起点としてHLSファイル一式をこのバケットに配置しておくとします。
htmlファイルとHLS形式ファイルが同じドメインから配信される場合
このS3バケット(non-cors-bucket-1)に、Video.jsでHLSを再生できるようにしたhtmlファイルを配置します。ファイルの内容は下記で、このS3バケット(non-cors-bucket-1)に配置されているHLS形式のファイルを再生するように指定しています。(Video.jsでHLS形式の動画を再生する方法は、こちらのエントリなどをご参照ください。)
<html> <head> <title>VHS de HLS</title> <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet"> </head> <body> <video-js id=example-video width=1280 height=720 class="vjs-default-skin" controls> <source src="https://no-cors-bucket-1.s3-ap-northeast-1.amazonaws.com/hls/sample59.m3u8" type="application/x-mpegURL"> </video-js> <script src="https://vjs.zencdn.net/7.4.1/video.js"></script> <script> var player = videojs('example-video'); </script> </body> </html>
このページをChromeで表示させてみます。以下のように問題なく動画が再生されました。Chromeのデベロッパーツールでも「faviconがない」というエラー以外は発生していません。
htmlファイルとHLS形式ファイルが異なるドメインから配信される場合
続いてHLS形式ファイルはこのCORS設定をしていないS3バケット(non-cors-bucket-1)に配置し、htmlファイルを別のドメインに配置した場合です。今回はこの「別のドメイン」については異なるS3バケットとしています(仮にhtml-host-domain-2としました)。htmlファイルは先ほどと同様です。(つまりnon-cors-bucket-1にあるHLS形式ファイルを参照しようとしています。)
Chromeブラウザで開くと、以下のようにエラーがでて再生されません。Chromeデベロッパーツールでもfavicon以外の無視できないエラーが発生しています。
Chromeデベロッパーツールでのエラー内容が下記です。htmlファイルをホストしている(HLS形式のファイルを要求するオリジンとなっている)html-host-domain-2.s3-ap-northeast-1.amazonaws.com
のドメインから、non-cors-bucket-1へのアクセスがCORSポリシーによってブロックされている、という内容ですね。
Access to XMLHttpRequest at 'https://non-cors-bucket-1.s3-ap-northeast-1.amazonaws.com/hls/sample59.m3u8' from origin 'https://html-host-domain-2.s3-ap-northeast-1.amazonaws.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. video.js:7390 GET https://non-cors-bucket-1.s3-ap-northeast-1.amazonaws.com/hls/sample59.m3u8 net::ERR_FAILED VIDEOJS: ERROR: (CODE:4 MEDIA_ERR_SRC_NOT_SUPPORTED) The media could not be loaded, either because the server or network failed or because the format is not supported.
S3にCORS設定をしてみた場合
まずはHLS形式ファイルを配信するS3にCORS設定をしていない場合は、htmlファイルが配信される(HLSファイルを呼び出す)ドメインが異なると、エラーとなってしまいVideo.jsでは正常に再生できない、ということが確認できました。正常に再生させるためには、HLS形式のファイルをホストするS3にCORS設定を行う必要があります。今回は確認のため、CORS設定をしたS3バケット別途準備し、さきほどのCORS設定をしていないバケット(non-cors-bucket-1)と同様のHLS形式のファイルを配置しました。パブリックリードのアクセスも許可しています。こちらのS3バケットを仮にcors-bucket-2としましょう。CORSは以下のように設定しています。
テキストだと下記になります。
<?xml version="1.0" encoding="UTF-8"?> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule> <AllowedOrigin>*</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <AllowedMethod>HEAD</AllowedMethod> <MaxAgeSeconds>3000</MaxAgeSeconds> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration>
なおS3のCORS設定詳細についは、以下のS3開発者ガイドをご参照ください。
Video.jsでの動画配信におけるCORSの設定の内容は、以下のAWS Elemental MediaStoreのユーザガイド「任意のドメインへの読み取りアクセス」を参考にしています。(こちらのリンク先のポリシー記載例はMediaStoreの形式なのでS3には適用できません。ご注意ください。)
以上の設定をしたS3バケットでホスティングしているHLS形式のファイルに対し、異なるドメインでホスティングしているhtmlファイル(Video.js)から参照してみます。先ほどのCORS設定をしていない状態ではエラーで再生できなかったパターンですね。またもやhtmlファイルのホスティングもS3を使っています、バケット名は仮にhtml-host-domain-3とします。
htmlファイルの中身は下記になります。(といってもハイライトしたsrc
部分しか変更はありません。)
<html> <head> <title>VHS de HLS</title> <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet"> </head> <body> <video-js id=example-video width=1280 height=720 class="vjs-default-skin" controls> <source src="https://cors-bucket-2.s3-ap-northeast-1.amazonaws.com/hls/sample59.m3u8" type="application/x-mpegURL"> </video-js> <script src="https://vjs.zencdn.net/7.4.1/video.js"></script> <script> var player = videojs('example-video'); </script> </body> </html>
このhtml-host-domain-3ドメインでホスティングしているhtmlファイルを、Chromeブラウザで開いてみます。先ほどのCORS設定をしていないS3バケットにHLS形式ファイルを配置しておいたときと同様の条件(htmlファイルとHLS形式ファイルが別ドメイン)でありますが、CORS設定をしているので問題なく動画視聴ができます。
S3のCORS設定で特定のドメインのみ許可
HLS形式のファイルをホスティングしているS3にCORS設定をしておけば、htmlファイルの配信が別ドメインでもVideo.jsでの動画再生ができることが確認できました。続いて、S3のCORS設定にはいろいろと項目がありますが、その中でもAllowedOrigin
の項目に注目してみます。
このAllowedOrigin
を適切に設定することで、htmlファイルをホスティングしているドメイン(つまり、HLS形式のファイルを呼び出すVideo.jsがあるドメイン)を限定することができます。自分たちで運用しているmovie.example.com
ドメインからはVideo.jsでの再生を許可するが、他ドメインでホストしているVideo.jsからは再生を許可しない、といったパターンですね。
こちらも確認のため別途S3バケットを用意しました。仮にcors-mydomain-only-3というバケット名としましょう。このバケットに先ほどと同様、パブリックリードのアクセスを許可し、パスhls/sample59.m3u8を起点としてHLSファイル一式を配置しておきます。
続いてこのHLS形式ファイルを参照するhtmlをホストするドメインを2種、準備します。これもS3でホスティングして確認しましたが、1つを「allowed-domain-1」、もういっぽうを「not-allowed-domain-2」としましょう。両方のhtmlファイルは同じで、以下のようにcors-mydomain-only-3バケットにあるHLS形式ファイルをsrc
に指定ししています。
<html> <head> <title>VHS de HLS</title> <link href="https://vjs.zencdn.net/7.4.1/video-js.css" rel="stylesheet"> </head> <body> <video-js id=example-video width=1280 height=720 class="vjs-default-skin" controls> <source src="https://cors-mydomain-only-3.s3-ap-northeast-1.amazonaws.com/hls/sample59.m3u8" type="application/x-mpegURL"> </video-js> <script src="https://vjs.zencdn.net/7.4.1/video.js"></script> <script> var player = videojs('example-video'); </script> </body> </html>
そしてこのS3バケットcors-mydomain-only-3のCORS設定です。以下のように、AllowedOrigin
要素でhttps://allowed-domain-1.s3-ap-northeast-1.amazonaws.com
(つまり「allowed-domain-1」のS3バケットのドメイン)を指定しておきます。
テキストだと下記になります。
<?xml version="1.0" encoding="UTF-8"?> <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/"> <CORSRule> <AllowedOrigin>https://allowed-domain-1.s3-ap-northeast-1.amazonaws.com</AllowedOrigin> <AllowedMethod>GET</AllowedMethod> <AllowedMethod>HEAD</AllowedMethod> <MaxAgeSeconds>3000</MaxAgeSeconds> <AllowedHeader>*</AllowedHeader> </CORSRule> </CORSConfiguration>
CORSで許可していないドメインの場合
まずはCORSで許可していないドメイン(not-allowed-domain-2)のhtmlファイルです。以下のように、S3にCORS設定をしなかったときと同様のエラーとなり、動画は再生できません。
エラー内容としても同様に、「not-allowed-domain-2」ドメインからのアクセスがCORSポリシーによっていブロックされているということです。
Access to XMLHttpRequest at 'https://cors-mydomain-only-3.s3-ap-northeast-1.amazonaws.com/hls/sample59.m3u8' from origin 'https://not-allowed-domain-2.s3-ap-northeast-1.amazonaws.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
CORSで許可したドメインの場合
続いてCORSで許可しているドメイン(allowed-domain-1)のhtmlファイルです。こちらは問題なく再生することができました。Chromeブラウザでアクセスしている先はhttps://allowed-domain-1.s3-ap-northeast-1.amazonaws.com/domain-3.html
です。
CORSで許可したドメインだけどhttpの場合
CORSのAllowedOrigin
にはドメイン名をhttps
で記載していました。ではhttp
の場合はどうなるでしょうか。結果は以下のように、再生できません。(httpについてはCORSポリシーで許可をしていないので。)
Chromeデベロッパーツールでは以下のエラーとなります。(許可されていないドメインと少し異なりますね。) Access to XMLHttpRequest at 'https://cors-mydomain-only-3.s3-ap-northeast-1.amazonaws.com/hls/sample59.m3u8' from origin 'http://allowed-domain-1.s3-ap-northeast-1.amazonaws.com' has been blocked by CORS policy: The 'Access-Control-Allow-Origin' header has a value 'https://allowed-domain-1.s3-ap-northeast-1.amazonaws.com' that is not equal to the supplied origin.
ここまでのS3のCORS設定まとめ
CORS設定が異なるS3バケットを用意し、それぞれで再生確認をしてみました。上記をいちどまとめてみます。
まずS3にCORSを設定していない場合について、同じドメイン(つまりHLSファイルと同じS3バケット)からhtmlファイルを配信する場合は、Video.jsでの再生が可能でした。ですがこのHLSファイルをホスティングするS3とは別のドメインからhtmlファイルを配信する場合、Video.jsでの再生はできませんでした。
HLSを配信するS3にCORS設定を行うことで、htmlファイルとHLSファイルの配信が別ドメインでもVideo.jsでの再生が可能になります。
S3へのCORS設定では任意のドメインを許可対象とするほか、許可するドメインを指定することも可能です。許可ドメインを指定した場合は、そのドメインでhtmlファイルをホスティングしていればVideo.jsでの再生は可能ですが、許可していないドメインでhtmlファイルをホスティングしている場合は再生不可となります。(図中では省略していますが、図の例ですとhtmlファイルの配信はhttps限定となります。)
なお、これらのHLSファイルの再生可否はあくまでVideo.js(などJavaScript製動画再生プレイヤー)をhtmlファイル上で扱う場合に限ります。例えばSafariブラウザなどHLSファイルをネイティブで再生できる環境で、HLSファイル(m3u8ファイル)を直接参照した場合、S3のCORS設定に関わらず再生が可能です。
CloudFrontで必要なCORS設定
ここまでCORS設定が異なるS3バケットを用意し、それぞれで再生可否を確認することで、HLS形式ファイルがホスティングされているS3には、Video.jsをホスティングしているドメインを許可するCORS設定が必要なことが確認できました。
さらにCloudFrontで必要なCORS設定についても簡単ですが確認しておきます。具体的な設定手順ベースとなりますが、S3をオリジンとしてるCloudFrontの場合、オリジンとなるS3にOrigin
ヘッダを転送するよう設定する必要があります。
詳細については以下エントリに記載がありますが、S3はリクエスト受ける際、Origin
ヘッダがないとCORSポリシーで許可するのに必要なAccess-Control-Allow-Origin
を返さないため、このOrigin
ヘッダのS3への転送が必要となります。
まとめ
ということで、CORS設定の異なるS3バケットを用意してつらつらと検証してみましたが、以下のことが再確認できました。
- Video.jsで読み込むHLSファイルのをホスティングしている環境でCORSを設定する必要がある
- S3でHLSファイルをホスティングする環境ならS3のCORS設定が必要
- S3のCORS設定では、特定のドメインからの(Video.jsを経由した)再生リクエストのみ許可する設定ができる
- 設定では
https
とhttp
が区別される
- 設定では
- CloudFront+S3の構成にする場合にはCloudFrontで
Origin
ヘッダをS3に転送する必要がある
いつもCORS設定がHLS側?html(Video.js)側?で混乱してしまうのですが……、整理ができたと思います!
参考
- CORS(Cross-Origin Resource Sharing)によるクロスドメイン通信の傾向と対策 | Developers.IO
- [新機能] Amazon CloudFrontがCORSに対応しました | Developers.IO
- Cross-Origin Resource Sharing (CORS) - Amazon Simple Storage Service
- AWS Elemental MediaStore のクロスオリジンリソース共有 (CORS) - AWS Elemental MediaStore
- Video.jsのvideojs-http-streaming(VHS)を使ってHLS形式のストリーミング配信を再生する最低限度の設定 | Developers.IO