Ruby on RailsでもHLS形式で動画を再生したい!
この記事は Ruby on Rails Advent Calendar 2021 二日目の記事です。
昨日は kakudaisuke さんの「I18nを使いながらPCでもスマホでも狙ったところでレスポンシブに改行する」でした!
この記事で話すこと
この記事では、動画を再生するときの形式の一つである HLS(HTTP ライブストリーミング)について簡単にまとめつつ、Ruby on Rails 製のウェブサイトで実装をするための勘所をお伝えします。
HLS って?何が嬉しいの?
HLS とは Web 上で動画や音声のストリーミング配信・再生を行なうためのプロトコルの一つです。
元は Apple が開発をした形式でしたが、現在は Apple 製品にとどまらずさまざまなデバイス・ブラウザにて利用することができます。
HLS を利用した動画の再生は、大まかにはこのような仕組みになっています:
- 配信側は、もとの動画を一定間隔で細かく分割した MPEG2-TS ファイル(
.ts
)と、その再生順などを記したインデックスファイル(.m3u8
)を用意する。 - 再生側はまず
.m3u8
ファイルを受信し、その内容に従って順々に.ts
ファイルを要求する。そして受信したものから順に再生する。
例えばサイトのファーストビューのメインビジュアルとして動画を利用するとき、素朴に MP4 の動画を読み込ませようとするとどうしても FCP スコア が下がってしまいます。
そのときに HLS を利用することで再生しながら動画の続きを読み込ませられるようになるため、ページの初期読み込み速度を抑えつつ動画を配信することができます。
HLS について、詳しくはこちらのサイトをご覧ください。
- HTTPライブストリーミングとは?| HLSストリーミング | Cloudflare
- オンデマンド動画配信を支える技術 - わかる!HLS編 - Qiita
- FV動画をHLS形式で実装した時の備忘録 | COFUS技術ブログ
HLS ってどうやって実装できるの?
HLS を使って動画の配信を実装するためには、大きく分けて三つの作業が必要です。
- 配信したい動画をもとに
.m3u8
ファイルや.ts
ファイルを生成する .m3u8
ファイルや.ts
ファイルを配信できるようにする- HLS 形式で動画を配信する仕組みを導入する
それぞれ順に見ていきます。
.m3u8
ファイルや .ts
ファイルを生成する
1. 配信したい動画をもとに HLS に必要なファイルたちの生成は ffmpeg
を利用すると簡単です。
例えばローカルにある video.mp4
から video.m3u8
というインデックスファイルと video000.ts
、video001.ts
、...のような動画ファイルを生成したいときは、このようなコマンドを実行すれば OK です。
ffmpeg -i video.mp4 -c:v copy -f hls -hls_time 5 -hls_playlist_type vod -hls_segment_filename "video%3d.ts" video.m3u8
オプションの意味
オプション | 意味 |
---|---|
-i video.mp4 |
元のファイルの指定。 |
-c:v copy |
コーデックの指定。copy とすると元のファイルと同じコーデックになります。 |
-f hls |
フォーマットの指定。 |
-hls_time 5 |
動画を分割する際の間隔の指定。単位は秒。 |
-hls_playlist_type vod |
HLSのタイプを指定します。 |
-hls_segment_filename "video%3d.ts" |
小さく分割した動画ファイルの名前の指定。%3d と指定していることで 000 、001 、...といった三桁のインデックスが割り振られます。 |
video.m3u8 |
インデックスファイルの名前の指定。 |
.m3u8
ファイルや .ts
ファイルを配信できるようにする
2. Ruby on Rails において、静的なファイルの配信はさまざまな方法で行うことができます。
Sprockets を使った昔ながらの方法、Webpacker を使ったイマドキ(?)な方法、/public
にあるファイルを参照する素朴な方法の三つが代表的なものでしょうか。
ここでは .m3u8
ファイル は Webpacker を使って配信し、.ts
ファイルは /public
に置いて配信してみます。
まずは Webpacker が動画ファイルを配信できるようにします。
app/assets/javascript/videos
というディレクトリを作り、そこに video.m3u8
を置きつつ、application.js
を次のようにします。
// この行があることで `asset_pack_path` で動画ファイルを参照できるようになる。
require.context("../videos", true);
import Rails from "@rails/ujs"
Rails.start()
次に動画を表示するための HTML を作っていきます。
詳細は割愛しますが、Rails の View ファイルにて先ほど配置した video.m3u8
を参照してみます。
<%= video_tag asset_pack_path('media/videos/video.m3u8') %>
これだけで iOS Safari などのブラウザでは HLS 形式の動画を読み込むことができます。
しかし実際に試してみると .ts
ファイルが見つからずに動画を再生することができません。
ファイルを探すときに Asset Pipeline の仕組みを使って探索に行ってしまうため、/public
にファイルを置いてあると見つけてくれないのです。
/public
に置いてあるファイルを直接参照させるために、video.m3u8
に手を加えましょう。
.ts
ファイルを呼び出している行の先頭全てに ↓ のように /
をつけてください。
-video000.ts
+/video000.ts
以上で、特別な設定が不要な iOS Safari などのブラウザでは HLS 形式の動画を読み込めるようになりました!
3. HLS 形式で動画を配信する仕組みを導入する
最後に、ネイティブにはサポートされていないその他のブラウザでも HLS 形式の動画を読み込めるようにしていきます。
ここではこの用途に特化した video-dev/hls.js
というライブラリを利用します。
基本的には README の通りに実装すれば正しく動きます。
せっかくなので今回は hls.js がサポートしていないブラウザ向けのフォールバックも含めて実装してみます。
app/views/home/show.html.erb の変更
HLS をサポートしていないブラウザ向けに MP4 動画も配置しつつ、その動画のパスも指定しておきます。
<%= video_tag '', class: 'js-hls-video', controls: true, data: { 'hls-src': asset_pack_path('media/videos/video.m3u8'), 'fallback-src': asset_pack_path('media/videos/video.mp4') } %>
package.json の変更
yarn add hls.js
を実行してください。
"dependencies": {
"@rails/ujs": "^6.0.0",
"@rails/webpacker": "5.4.3",
+ "hls.js": "^1.1.1",
"webpack": "^4.46.0",
"webpack-cli": "^3.3.12"
},
app/javascript/packs/application.js の変更
require.context("../videos", true);
import Rails from "@rails/ujs"
Rails.start()
+
+import "../src/loadHls"
app/javascript/src/loadHls.js の追加
import Hls from 'hls.js';
function loadHlsVideo (video) {
const videoSrc = video.getAttribute('data-hls-src');
const fallbackVideoSrc = video.getAttribute('data-fallback-src');
// hls.jsがサポートしているブラウザの場合。
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(videoSrc);
hls.attachMedia(video);
}
// HLSをサポートしているブラウザの場合はsrc属性に直接m3u8ファイルを指定すればよい。
else if (video.canPlayType('application/vnd.apple.mpegurl')) {
video.src = videoSrc;
// どうしようもないときはフォールバック用の動画を再生する。
} else {
video.src = fallbackVideoSrc;
}
}
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('video.js-hls-video').forEach((video) => loadHlsVideo(video))
})
こうすることで、hls.js がサポートする様々なブラウザ にて HLS 形式で動画を再生できるようになりました!
実装してみたコードは公開しています
今回実装してみたこれらのコードも含んだ、実際に動く Rails アプリのソースコードを公開しています。
理解・実践の参考にしていただけたら嬉しいです!
また Heroku でこの Rails アプリをデプロイしています。
実機での動作を確認したいときにお使いください!
開発者ツールでネットワークの状況を見てみると、少しずつ動画の続きが読み込まれていっているのがわかると思います。
感想:Rails にちょっと周辺領域の技術を組み合わせるのめっちゃ楽しい
この記事では、かなり枯れつつある Ruby on Rails に HLS というちょっと違った領域の技術を組み合わせてみました。
hls.js のおかげであまり複雑な実装をする必要はなくなっていますが、Rails ができることの広さをあらためて実感しました。
クラッソーネでは Ruby on Rails にいろんな技術を組み合わせるのが好きなエンジニアを大募集しています。
少しでもこの記事に興味を持っていただいた方、ぜひカジュアル面談でお話ししましょう!