YouTubeに学ぶWebで動画を快適にブラウズする技術
最近業務で動画プレイヤーを実装する機会がありました。
実装においてはユーザーがよく触れているサービスと同じ挙動になっていることが重要だと考えYouTubeを大いに参考にしましたが、YouTubeには動画を快適にブラウズするための細かい実装がたくさん盛り込まれており、面白い発見が多かったので共有します。
video要素が提供する機能
まずはHTMLの<video>が提供してくれる機能をおさらいしておきます(YouTubeも動画部分には<video>を使用しています)。
- 再生、音量、シークなどの機能を制御するコントロール表示
-
Spaceによる再生/停止・←/→によるシーク - フルスクリーン表示
- 再生速度の変更
-
autoplay属性による自動再生(後述)
近年の動画再生サイトでラップされていない<video>が直接配置されていることはないと思いますが、<video>単体でも動画を閲覧するために必要な機能が提供されていることがわかります。
YouTubeが提供する機能
再生・停止ボタンなどのカスタムコントロール
YouTubeでも動画の再生には<video>を使用していますが、controls属性を省略することでデフォルトのコントロールは表示せずに自前実装のカスタムコントロールを表示しています。

カスタムコントロールによってvideoが元々持つ機能と連続再生有効/無効の切り替え・字幕の表示などのYouTube固有の機能がブランドに合わせた統一的なUIで実現されています。
見かけ上表示されるのは動画プレイヤー下部のカスタムコントロールだけですが、実際にはイベントハンドラを登録するためのコンテナ・字幕用のコンテナ・サムネイルプレビュー用のコンテナなど多くの要素を重ねて実装されていることがわかります。

ほとんどの動画再生サイトに実装されている「動画プレイヤーのどこをクリックしても再生/停止を切り替えられる」「ダブルクリックで10秒スキップ」「長押しで2倍速」などの機能は見えないコンテナにイベントハンドラを登録して実装されています。
ページを開いた時の自動再生
<video>要素にはautoplay属性があり、これを指定すると動画を自動再生することができると述べられていますが、YouTubeの<video>要素を見てみるとこの属性は指定されていません。
<video
tabindex="-1"
class="video-stream html5-main-video"
controlslist="nodownload"
style="..."
src="..."
>
その理由はブラウザのautoplay blockingによるものだと考えられます。
ページの読み込み後に音声を含む動画の再生を自動的に開始されることはユーザーにとって好ましくないと考えられており、多くのブラウザでは音声を含まない動画のみ自動再生が許可されています。
歴史のある動画プレイヤーライブラリであるvideo.jsでもautoplay属性による自動再生は推奨されておらず、Never assume autoplay will work.(autoplayが動作すると思わないでね)と述べられています。
YouTubeでもページの読み込み後にJSで再生開始の関数を発火させることで自動再生を実現しています(m=kevlar_base_module,kevlar_main_moduleというモジュールにshouldAutoPlayという真偽値に基づいて動画の再生を開始する関数を呼んでいる処理が見受けられます)。
キーボードアクション
通常の<video>もいくつかのキーボードアクションに対応していますが、YouTubeはより多彩なキーボードアクションに対応しています。

ここでも<video>に元々あるSpaceキーでの再生/停止やMキーでの消音などの機能と、YouTube固有の機能がシームレスに統合されています。
また、<video>でキーボードアクションを使用するにはフォーカスが<video>にある必要がありますが、YouTubeではdocumentに対してkeydownイベントを登録しており現在のフォーカスがインタラクティブな要素でなければキーボード操作で動画プレイヤーを操作できるようになっています。
ユーザー固有の設定値の保持
ユーザー固有の設定値はYouTubeではサーバー側に保存され、複数デバイス間でも同じ設定で動画を閲覧することができます。
これらの設定はブラウザのストレージでも管理されており、ログイン時にはサーバー側に保存された設定が優先しつつ未ログイン時にも毎回動画を開くたびに設定し直さなくて良いような最低限のUXを実現しています。
- LocalStorage(どの動画を再生していても共有されるべき設定)
- 字幕表示設定
- 再生する画質設定
- SessionStorage(再生している動画によって変えたい可能性のある設定)
- 次の動画を自動的に再生するかどうか
- 再生速度
動画ソースの配信方式
YouTubeでは独自実装のDASHによる動画のストリーミングを行っています。
DASHではアップロードされた動画を解像度ごとに細切れの短いファイルにエンコードしておき、ユーザーが現在再生に必要なファイルだけを少しずつ返します。
ネットワークタブを見てみると再生が進むごとに少しずつ小さな動画ファイルが返されているのが観測できます。再生位置をジャンプしたり解像度を切り替えたりした場合も、必ず新しいセグメントファイルをリクエストします。

DASHを利用する場合「どのセグメントファイルがどの解像度なのか」「どのセグメントファイルが動画のどの部分か」という情報を一元管理するファイル(.mpd)を返すことが一般的ですが、YouTubeの動画ページを開くといきなりセグメントファイルが返ってきており一元管理するファイルが見つかりません。

セグメントファイルのイニシエータはbase.jsとなっており、これは動画プレイヤーの初期化を担当する巨大なjsファイルです。
minifyされていますが中を覗いてみるとytInitialPlayerResponseというオブジェクトを操作してURLを取得しているようなコードが見て取れ、セグメントファイルの情報はファイルとして返さず初回のレスポンスに含めるYouTube独自のカスタム実装になっていることがわかります(SafariでもHLSを使用せずに同じ仕組みで動画ファイルを配信しています)。
サムネイルプレビュー
YouTubeではシークバー上にホバーすると該当位置のサムネイルが表示されます。

ネットワークタブを見るとシークバー上にホバーしたタイミングで、該当箇所のサムネイル画像のjpg画像をリクエストしていることが観測できます。
該当箇所だけではなく該当箇所付近の画像をまとめたスプライト画像を返すことで、シークバー上でカーソルを移動させても必要以上に多いリクエストを送らないようになっています。

まとめ
普段使っている時は気づきませんでしたが、開発者ツールでYouTubeの動作を掘り下げていくと様々な工夫が凝らされていることがわかります。
実際のプロダクト開発では<video>を一からカスタマイズせずに何らかのライブラリを使用することが多いと思いますが、重視したいユーザー体験を明確にしてからライブラリを選定しどこを自前で実装していくか考えるのが重要そうです。
Discussion