HLSのインデックスファイルをPOSTで取得する【Video.js】
この記事は2023/02/01に作成されました。
社内のプロジェクトにてLive映像の表示を行う機会がありました。
HLSという通信の仕組みとVideo.jsというjsライブラリを使用して実現していきます。
その際に、得た情報や実装をご紹介していきたいと思います。
順番に説明していきます。
HLS(HTTP Live Streaming)について
https://developer.apple.com/documentation/http_live_streaming
Appleによって開発されたHTTPベースの通信プロトコル。2009年リリースでリリースから10年以上が経つようです。ストリーミングに使用されるため、MP4ファイルのような動画ファイルではありません。
・セグメントファイル(.ts)
数秒のオーディオとビデオのファイル
・インデックスファイル(.m3u8)
セグメントファイルの順番やURLなどの情報を提供するファイル
この2種類のファイルを使用してライブストリーミングを実現しています。
クライアントはこのインデックスファイル(プレイリスト)にアクセスして、URL記述からセグメントファイルを順番に要求しています。今回の仕様では、POSTデータのみ受け付けるようにしてあるAPIを経由して、インデックスファイルを取得するという要件でした。video.jsはそのままで使用するとGETでsrcのデータを取得してしまいます。なので、クライアント(Video.js)でセグメントファイルを要求する前の段階、インデックスファイルの取得をカスタマイズしていくことになります。
Video.jsについて
HLSを標準対応しているブラウザはSafariとEdgeのみです。
それ以外のブラウザでHLSを再生するためにVideo.jsというライブラリを使用します。
npm,cdnでライブラリを読み込み、公式のドキュメントに従って実装します。
<video id=example-video width=600 height=300 class="video-js vjs-default-skin" controls>
<source
src="https://example.com/index.m3u8"
type="application/x-mpegURL">
</video>
<script src="video.js"></script>
<script src="videojs-contrib-hls.min.js"></script>
<script>
var player = videojs('example-video');
player.play();
</script>
ここでHLS特有の設定として、
・srcにm3u8のインデックスファイルを設定
・typeにapplication/x-mpegURL
を記述してください。
src設定をカスタマイズする
srcにインデックスファイルを返すURLを設定する場合どうなるでしょうか?
インデックスファイルの読み込みはされますが、インデックスファイル内のURL記述から各セグメントファイルの読み込みをするときに存在しないとして読み込みの失敗になってしまいました。
インデックスファイルを解析するときに、ファイルが存在する場所からセグメントファイルを探していることが原因のようです。なので
EXTINF:2.00
1.ts
EXTINF:2.00
2ts
のようなURLだとセグメントファイルを取得することに失敗してしまいます。
URLをフルパスで出力してあげると成功します。
では、POSTでインデックスファイルを返すURLを設定したい場合どうすればいいでしょうか?
srcはgetで取得しているため、postでの送信は行うことができません。
なので、video.js内部で行っているインデックスファイルを取得する機能をカスタマイズする必要があります。
Video.jsはインデックスファイルを取得するときに内部的にxhrリクエストをしており、
const player = videojs('vid1', {});
player.hls.xhr.beforeRequest = function(options) {
options.uri = options.uri.replace('example.com', 'foo.com');
return options;
};
のようにすればリクエストを変更できると公式にもありますが、このままでは動きませんでした。
最終的には以下のようなコードになりました。
videojs.Hls.xhr.beforeRequest = function (options) {
let hls = player.tech({IWillNotUseThisInPlugins: true}).hls;
//videojs内部で行われるxhrリクエスト前の処理を制御する
player.hls.xhr.beforeRequest = function (options) {
options.uri = '設定したいURL';
options.method = 'post';
formData.append('key', 'value');
options.body = formData;
};
return options;
};
video.js全体に対してリクエスト前の処理を制御するbeforeRequestを設定してあげないと、
各プレイヤーのhlsへのアクセスができませんでした。
ブラウザの対応
'-----------
Media Source Extensions API(MSE、メディアソース拡張機能 API)は、プラグイン不要で Web ベースのストリーミングメディアを可能にする機能を提供します。 MSE を使用すると、メディアストリームを JavaScript で作成し、<audio>
要素 や <video>
要素で再生できます。
MSE を使用すると、メディア要素に渡す通常の単一トラックの src
値を MediaSource
オブジェクトへの参照で置き換えることができます。
'----------
https://developer.mozilla.org/ja/docs/Web/API/Media_Source_Extensions_API
どうやらVideo.jsやhls.jsなどのライブラリでHLS非対応ブラウザでのHLS再生を実現する際にこのMSEを使っているようです。safari/iOSはMSEをサポートしていないため、video要素に渡すsrc値をカスタマイズするといった機能は利用できません。
まとめ
ストリーミングサービスの仕組みの一端を理解できると楽しみ方の幅も広がると思います。ブラウザに依存する部分も多いので、機能を作るときは対応しているかの確認をする必要があるので注意です。
以上になります、ありがとうございました!
プログラマー/K.D