Video.jsでのHLS形式動画の再生を通してS3とCloudFrontのCORS設定について確認してみた

2020.02.29

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

はじめに

清水です。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を経由した)再生リクエストのみ許可する設定ができる
    • 設定ではhttpshttpが区別される
  • CloudFront+S3の構成にする場合にはCloudFrontでOriginヘッダをS3に転送する必要がある

いつもCORS設定がHLS側?html(Video.js)側?で混乱してしまうのですが……、整理ができたと思います!

参考