ユーザーブロマガは2021年10月7日(予定)をもちましてサービスを終了します

hls.jsでお手軽 動画・生配信視聴プレイヤー作ってみた 【お盆休みの自由研究2019その1】
閉じる
閉じる

新しい記事を投稿しました。シェアして読者に伝えましょう

×

hls.jsでお手軽 動画・生配信視聴プレイヤー作ってみた 【お盆休みの自由研究2019その1】

2019-08-12 19:00

    この記事はわりと細かいことまで書いていて長いです。時間のない方や,もうすでに知ってるわって人は飛ばし飛ばし読んでください。対象レベルはWebや動画に関して最低限の知識はあるけど,実際に作ったことはあまりない人程度にしたつもりです。

    簡単お手軽 低遅延な動画・生配信視聴プレイヤーです。

    ---- SKA’s blomaga written on 2019/08/12 ----

    はいみなさんこんにちは。SKA’s blomagaへようこそ。
    てことで今日はですね,「お盆休みの自由研究」と題した自宅鯖&Webアプリ開発日記の1つ目です。今回のテーマ(概要)は「hls.jsを用いて(実用レベルまでは行かないけど)簡単お手軽に低遅延で安定したHLS形式の映像配信視聴プレイヤーを作ってみた」って感じです。

    背景技術

     昔からニコ動とかニコ生とかYouTubeとかやってる人は分かると思いますが,昔(5〜15年くらい前)はWebで動画を流すにはAdobe Flashを用いたHTTPプログレッシブダウンロード(先頭からダウンロードし,ダウンロード終わったところのみ再生可能)が主流でした。生配信は同じくAdobe Flashを用いてRTMP方式(専用サーバー必要)で行っていました。今みたいにブラウザー単体で動画を扱えませんでした。しかし,iPhoneの登場,iOSがFlashをサポートしなかったこと,Flashのセキュリティー問題,さらにHTML5でvideoタグが追加されたこと(ブラウザー単体で動画再生可能に),それでもAdaptive Bitrate(自動画質調整)の付けられないプログレッシブダウンロードを特定の条件でしか再生できないようにiOSがしたことから,現在の動画配信は一般的にHLS(HTTP Live Streaming)形式(ニコ動・AbemaTV等で採用)や,それをより一般化・標準化したMPEG-DASH形式(YouTube等で採用)が使われるようになりました。

     ※ 個人的に長いカタカナ用語は好きではないので,以下 一般的名称ではありませんが,「プログレッシブダウンロード」を「順次ダウンロード」と記載します。また,わかりやすさ重視で「クライアント」を「客」,「サーバー」を「窓口」に見立てている部分があります。

    技術詳細

     順次ダウンロードはPCやスマホの動画再生アプリでPCやスマホ本体の動画を再生するのと同じように動画を先頭から順番にダウンロードしていきます。もちろん1本の動画では1ファイルです。

     しかし,HLSでは図のように10秒程度(Apple推奨値)に動画ファイルを分割します。その動画の順番と時間をプレイリストファイル(拡張子:m3u8)に書いておき,そのプレイリストの通りにファイルを順番にダウンロードしていくことにより再生します。あ,ちなみにニコ動は6秒ごとに分割してますね(2019年8月現在)。

     この方式の利点はたくさんあります。

     まず,先読み時間の制御がしやすく,ムダな通信を避けることができる点。例えば,10分の動画を1分で見飽きたとします。順次ダウンロードではどんどんダウンロードしていくので,8分ぶんダウンロードしてしまうかもしれません。しかし,HLSでは次の3ファイルまでしか読み込まないというようにしておけば,1分30秒ぶんしかダウンロードしていないわけです。これによりサーバー負荷もモバイルの通信量も抑えることができます。(逆に動画をダウンロードして保存したいときは時間もかかるし結合が面倒)

     次に,アクセスログにより簡単にユーザーの視聴時間が分かる点。例えば,先程の例だと9ファイル読み込まれて3ファイルは先読みなので実際に再生したのは6ファイル分で,時間にすると60秒という具合です。おそらくYouTubeはこの仕組みで視聴時間を算出し,広告料の配分をしていると考えられます。

     さらに,通信速度により最適な画質に調整できる点。先程の図のように,プレイリストのプレイリストを作ることができます。そこには「このプレイリストを使うとこのくらいの画質」ということが書いてあります。客側(ブラウザーやアプリ)が「やっべw この通信速度だと先読み3ファイルも用意できねえわwww」と判断すればサーバーに低画質の動画ファイルを要求して,「なにこの回線! 先読み一瞬で終わるんだが。たっのしー!」と判断すれば高画質の動画ファイルを要求するって感じです。つまり,客側が判断するだけで,無能お役人(一般的なWebサーバー)でも,自動画質調整ができてしまいます。

     そして,動画(オンデマンド)だけではなく,生配信(ライブ)すら無能な役人(一般的なWebサーバー)で対応可能です(10秒ごとに新しい動画ファイルが追加されて,プレイリストが更新されて,そのあとすぐにが来るので役人目線だと「普段より忙しいな」って感じでしょうか)。ただし,もちろん動画・生配信ともに約10秒ごとに動画ファイルを分割する他サーバーやアプリの協力は必要です。

    という感じに,HLS(あるいはMPEG-DASH)は現代の動画配信においてかなりメリットがあると思います。

    もちろんそのままブラウザーで再生できるんでしょ?

    いいえ! というと語弊があるかもしれませんが,Microsoft Edge(2019年現在,Chromium系になる前)とSafariは優しさ設計でvideoタグでHLSのプレイリストを指定してやれば,そのまま再生できてしまいます。が,Mozilla FirefoxやGoogle Chromeは未対応です。

    てことで,やっと本題です。OSがWIndowsでもmacでもAndroidでもiOSでも,ブラウザーがChromeでもFirefoxでも問題なくHLS形式で再生できるプレイヤーを作りましょう。

    やり方

     と言っても,やり方はいくらでもあります。自分で1からJavaScriptで実装しても良いですし,jQueryとかで頑張っても良いですし,video.jsというHLSに対応したJavaScriptの動画プレイヤーのライブラリーもすでにあります。ですが,今回はHTML5のvideoタグで色々なブラウザーでHLSを扱うことができるようになるライブラリー「hls.js」を使います。

     実は私は以前video.jsを使っていたのですが,パフォーマンスに問題がありました。まれにコマ落ちが発生したり,生配信の遅延が大きかったりなどです。それで,WebVTTによる章分け(チャプター)機能や,字幕機能にも対応していて,UIまで用意されていて圧倒的手軽さで実用的なvideo.jsですが,使うのはやめて,別のライブラリーを探してました。

     そして,見つけたのがhls.jsです。hls.jsはvideo.jsみたいにUIを持たず,本当にvideoタグでHLS形式の動画を読み込ませるだけです(自動画質調整はしてくれますが)。ですが,HTML5に対応している一般的なブラウザーはブラウザー自体が動画用のUIを持っているので,それを使えばシークや一時停止などは可能になります。また,自分のUIを使いたいのであれば,それこそ1から書いたり,jQueryでも使えばいいだけなので,hls.jsで全く問題ないと判断しました。

    では実装していきましょう。雰囲気を出すためにBGM用に てってってー を貼っておきます。

    実装

    前準備

     まず,今回作るお試しサイトのディレクトリー(フォルダー)構造を作っていきます。今回は以下のような構造にしましたが,まあお好みで。

    test-video-site/
      ├ js/
      | ├ hls.js
      | └ set-video.js
      ├ videos/
      | ├ hls/
      | | ├ video1.m3u8
      | | ├ video1-00000.ts
      | | ├ video1-00001.ts
      | | ├ video1-00002.ts
      | | └  …… 
      | └ video1.html
      ├ index.html
      ├ icon.ico
      └ style.css

    hls.jsは特にダウンロードせずにCDNの利用をオススメしますが,CDNを利用したくない場合GitHubの https://github.com/video-dev/hls.js/ からダウンロードするなりクローンするなりして,上のとおりjsディレクトリーにでも入れてください。

    icon.icoとstyle.cssはテキトーに用意してください。アイコンはGIMPとかで作成できます。スタイルはお好みで。別に作らなくてもいいです。index.htmlはvideo1へのリンクが貼ってあるページという想定ですが,なんでもいいです。テキトーに用意してください。
    これ以外のファイルについてはこれから順番に用意していきます。

    ちなみに,自動画質調整に対応させるならvideosの下のhls以下を次のようにします(ここもお好みで)。

    hls/
    ├ hd/
    | ├ video1-hd.m3u8
    | ├ video1-hd-00000.ts
    | ├ video1-hd-00001.ts
    | ├ video1-hd-00002.ts
    | └  …… 
    ├ sd/
    | ├ video1-sd.m3u8
    | ├ video1-sd-00000.ts
    | ├ video1-sd-00001.ts
    | ├ video1-sd-00002.ts
    | └  …… 
    └ video1.m3u8

    動画の準備

     上の方で解説したとおり,HLS形式の動画は10秒程度に分割したファイルを用います。分割に使うソフトは何を使っても良いですが,私はFFmpegを用いています。
    ただし,FFmpeg自体はフリーですが,商用利用でエンコードされる場合MPEG-LAとのライセンス契約が必要かと思われますのでご注意ください。
    FFmpegは自分でビルドすることをオススメしますが,時間がかかるのと,分割だけの場合 配布版で全く問題ないので,そこもおまかせします。

     次に,シェルでFFmpegが入っているディレクトリーに移動します。
    Linuxの人は自力で頑張ってください(シェルやデスクトップ環境によって違うので)。
    macの方はFinderでFFmpegが入っているフォルダーに移動し,ターミナルを起動し,「cd 」(最後は半角スペース)と打ったのち,Finderの一番上(赤丸 黄色丸 緑丸と同じy座標)のフォルダーマークをターミナルにドラッグアンドドロップして,文字が入ったらreturnキーを押します。
    WindowsではエクスプローラーでFFmpegが入っているフォルダーを開き,Shiftキーを押しながら右クリックすると「PowerShellでここを開く」的なメニューがあるので(日本語版Windows使ってないので正しい名称は分かりません),それを押します。

    ※ 以下 元の動画が,映像コーデックが「H.264」,音声コーデックが「AAC」であるという前提で進めます。

     その次に,自動画質調整しない場合は

    ./ffmpeg -i "(動画のパス)" -c:v copy -c:a copy -segment_format mpegts -hls_time 9.0 -break_non_keyframes 0 -hls_list_size 99999 -hls_segment_filename "(test-video-siteまでのパス)/test-video-site/videos/hls/video1-%05d.ts" "(test-video-siteまでのパス)/test-video-site/videos/hls/video1.m3u8"

    と入力しreturn(Enter)キーを押します。
    ただし,Windowsの場合,「ffmpeg」を「ffmpeg.exe」に置き換え,「/」(スラッシュ)を「\」(バックスラッシュ)に置き換えてください。場合によってはバックスラッシュが円記号(¥)に見えることがありますが,それはWindowsのクソ仕様なので無視してください。

    このコマンドの意味は
    このディレクトリーの「ffmpeg」を次の引数をとって実行します。その引数は入力ファイルが(動画のパス)で,映像コーデックはコピーして(再エンコードしない),音声コーデックもコピーして,分割後の形式(コンテナ)はMPEG-2 TSで,分割時間は9.0秒ごとで,「キーフレーム(Iフレーム)で分割しない」をオフ(0)にして,プレイリストの最大数は99999で,分割後のファイルは「(test-video-siteまでのパス)/test-video-site/videos/hls/video1-%05d.ts」として保存して,プレイリストは「(test-video-siteまでのパス)/test-video-site/videos/hls/video1.m3u8」として保存します。
    って感じです。
    分割時間は再エンコードなしで,「break_non_keyfreames」を0にすると,記載した時間よりも多少長めで分割されるので10.0ではなく,9.0を指定しています。分割後ファイル名には「%○○d」といった感じに数字のフォーマットを指定できます。%05dは0埋めあり(001みたいに先頭に0を付ける)の5桁の整数となります。

    よく分からない方はこのままお使いください。詳しい方は自分好みにカスタマイズしてご利用ください。

    自動画質調整する場合は映像をリサイズして再エンコードして,別ファイルに保存すればおkです。コマンドは…… メモしてたファイルをコピーし忘れてて今手元にないので,数日後更新して記載しておきます。

    set-video.jsの作成

    ※ set-video.jsという名前じゃなくても大丈夫です。

    hls.jsの機能を利用して,videoタグでHLSを読み込ませるset-video.jsと名付けたJavaScriptプログラムを書いていきます。AtomでもVS CodeでもJetBrainsさんのでも何でも良いですが,テキストエディターを起動して,test-video-siteのディレクトリーを開いて,「js」ディレクトリーに「set-video.js」というファイルを作成します。
    書く中身は先程の hls.jsのGitHub を参考に(本家とは少し違います)次のように書けばおkです。

    function setVideo(path)
    {
    var video = document.getElementById("video");
    if(video.canPlayType("application/vnd.apple.mpegurl"))
    {
    video.src = path;
    return;
    }
    if(Hls.isSupported( ))
    {
    var hls = new Hls( );
    hls.loadSource(path);
    hls.attachMedia(video);
    }
    return;
    }

    説明するまでもないと思いますが,軽く説明しておくと,
    次のような「path」を引数にとる関数setVideoを定義します。videoという変数にHTMLで「video」というIDが付与された要素を格納して,もしブラウザーが直接HLSを再生できれば,videoのsrc属性をpathにします。もしブラウザーがMSEに対応していれば(hls.jsの動作条件),hlsという変数に(hls.jsで定義されている)Hlsクラスのインスタンスを格納し,pathを読み込み,videoに結びつけます。(ブラウザーがMSEにもHLSにも対応していない場合 何もしません)

    video1.htmlの作成

     では次に,実際に動画を表示するHTMLを書いていきます。同じくテキストエディターで「video1.html」を作成します。中身はこんな感じです。「(○○)」「(ほげほげ)」は好きな文字に置き換えてください。

    <!DOCTYPE html>
    <html lang="ja">
    <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width,initial-scale=1" />
    <link rel="shortcut icon" type="image/vnd.microsoft.icon" href="../icon.ico" />
    <link rel="stylesheet" type="text/css" href="../style.css" />
    <!-- hls.jsをCDN経由で用いる場合 -->
    <script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
    <!-- hls.jsをダウンロードして用いる場合 -->
    <!-- <script src="../js/hls.js"></script> -->
    <script src="../js/set-video.js"></script>
    <title>(ほげほげ)</title>
    </head>
    <body>
    <h1>
    (動画タイトル)
    </h1>
    <p>
    (動画説明)
    </p>
    <video id="video" controls preload="metadata" width="854" height="480">
    <p>
    ご利用のブラウザーは当サイトの動画に対応していません。申し訳ありませんが,別のブラウザーソフトをお試しください。
    </p>
    </video>
    <script>setVideo("./hls/video1.m3u8");</script>
    </body>
    </html>

    って感じで,簡易的な動画サイトみたいな感じになります。videoタグの中身については,setVideo関数内でvideoというIDのついた要素を取得するようにしていたため,id="video",hls.jsは動画制御用UIを持たないので,ブラウザー内蔵のUIを用いるためcontrols,私は自動再生絶対許さないマンなのでpreload="metadata"(メタデータのみ先読みする),width(幅)とheight(高さ)はお好みの値で(動画の解像度に合わせる必要ありません)って感じですね。もしかしたら(もしかしなくても),widthとheightはvideoタグ内よりもCSSで指定した方がいいかもしれませんね。
    ちなみに,viewportの設定で,スマホを縦画面にしても表示が小さくならないようにしています。

    Webサーバーへの配置

     では最後に,Webサーバーへ配置しましょう。ファイルをブラウザーで直接開く場合,hls.jsが正しく動作しないことがあります。なので,HTTPかHTTPSのサービスをしているサーバーのところへ配置しましょう。
    サーバーのセットアップは次回の予定なので,今回は省略します。

    基本的にWebサーバーのドキュメントルートのディレクトリーにtest-video-siteディレクトリーの中身をコピーして,権限を変更すればおkです。

    だいたいこんな感じで終わりです。さっき てってってー を流した方はここで止めてください。

    動作確認

     ブラウザーを起動して,アドレスバーにWebサーバー上の「index.html」か「video1.html」のURIを入力してみましょう。正しく再生できましたか? ちなみに私はこのプレイヤーでGOP&分割時間0.5秒の生配信を視聴したら遅延時間が2〜4秒程度でした(LAN内でFirefoxの場合。Chromeはもう1秒程度遅れる感じでした)。コマ落ちも今のところ確認できていません。インターネット(WAN)上ではまだ試していませんが,パフォーマンスは良さそうです。


    -------- SKA’s blomaga --------

    はい,というわけで,今回はhls.jsを用いてお手軽なHLS形式の動画・生配信視聴プレイヤーを作ってみたってことで,いろいろと説明しました。
    さすがに長々と詳しく書きすぎて,これ書くのに丸1日かけてしまって,進捗0なので,次回からは簡潔に書いていきます。対象がやや上級者向けになるかもしれませんが,ご了承ください。そもそも内容も難しくなるので,そこは割り切ってしまおうと思います。

    また,今回の記事は私がやったことをベースに書いていますが,実際に私がやったこととは多少変えて書いています。そのため,間違っている部分があるかもしれません。何か間違いに気づいたり,この通りにやったのに動かないとかあればコメントお願いします。

    次回は「LinuxとNginxとFFmpegで動画配信サーバーを作ってみた」です。お楽しみに。 ではまた!

    今日のリンク と 参考文献

    • お盆休みの自由研究その0「お盆休みの自由研究と題して自宅鯖&Webアプリ構築するお(2019)」:ar1798085
    • お盆休みの自由研究その2「LinuxとNginxとFFmpegで動画配信サーバーを作ってみた」:(まだ書いていません)
    • プリパラ5周年 一挙放送 lv321267111 ←作業しながら見てました
    広告
    コメントを書く
    コメントをするには、
    ログインして下さい。