ウェブでの動画ストレージの管理

動画は管理が難しいアセットです。ストリーミングには多くの帯域幅が必要で、キャッシュ保存も簡単ではありません。これらの問題は、キオスク ディスプレイのように動画がループ再生される場合に悪化します。たとえば、1 日中 30 本の動画を繰り返し再生するデバイスが数百台ある場合、ネットワークがすぐに過負荷になる可能性があります。動画をストリーミングするのではなくキャッシュから配信することで、ダウンロード費用は 1 回のみで済み、その後の再生が高速化され、オフラインで再生できるようになります。これを行うには、ブラウザのストレージ機能を利用できます。このうち、動画ファイルの保存に最も適しているのは Cache Storage APIIndexedDB です。どちらも優れたオプションですが、ここでは一般的な サービス ワーカー ライブラリ Workbox との統合を理由に、Cache Storage API に焦点を当てます。

サービス ワーカーからの動画のキャッシュ保存

動画などの大きなアセットのダウンロードとキャッシュ保存は、特に時間とプロセッサを消費するタスクになる可能性があるため、メインスレッドから離れたバックグラウンドで行う必要があります。サービス ワーカーは、キャッシュ保存タスクのオフロードに特に役立ちます。ページとネットワーク間のプロキシとして機能し、リクエストをインターセプトして、ネットワーク レスポンスに追加のロジック(キャッシュ保存戦略など)を適用できます。

キャッシュ保存戦略は多数あり、それぞれ異なるユースケースに対応するように設計されています。たとえば、ファイルがキャッシュに存在する場合はキャッシュから提供し、存在しない場合はネットワークにフォールバックするには、次のコードを記述します。

self.addEventListener('fetch', function (event) {
  event.respondWith(
    caches.match(event.request).then(function (response) {
      return response || fetch(event.request);
    }),
  );
});

さまざまなアセットタイプや、異なるキャッシュ保存戦略を必要とする URL に対してこれを管理するのは、繰り返しが多く、エラーが発生しやすいプロセスです。Workbox は、ルーティング ヘルパーキャッシュ保存戦略など、サービス ワーカーのコードをより宣言的かつ再利用可能な方法で記述できる一連のツールを提供します。

前の戦略は「キャッシュ ファースト」と呼ばれます。Workbox を使用して同じことを記述するには、次のようにします。

registerRoute(
  ({ request }) => request.destination === 'video',
  new CacheFirst()
);

Workbox には、他のキャッシュ保存戦略や、WebpackRollup などのビルドツールとの統合を含む、一般的な Service Worker タスクに関する同様のレシピも用意されています。

Workbox を設定したら、動画をキャッシュに保存するタイミングを選択する必要があります。この場合、ページ読み込み時に積極的に行う方法と、動画がリクエストされたときに遅延して行う方法の 2 つがあります。

積極的なアプローチ

プリキャッシュは、サービス ワーカーのインストール中にファイルをキャッシュに保存し、サービス ワーカーが起動するとすぐに利用できるようにする手法です。Workbox は、ビルドプロセス中にアクセスできるファイルのプリキャッシュを自動的に設定できます。

次の Workbox コードをサービス ワーカーで使用して、ファイルをプリキャッシュできます。

import { addPlugins, precacheAndRoute } from 'workbox-precaching';
import { RangeRequestsPlugin } from 'workbox-range-requests';

addPlugins([new RangeRequestsPlugin()]);
precacheAndRoute(self.__WB_MANIFEST);
  • import(s) - 対応する Workbox モジュールから必要なバインディングを読み込みます。Service Worker はまだ ESModules を普遍的にサポートしていないため、Workbox を利用した Service Worker を本番環境で動作させるには、バンドラーを介して渡す必要があります。
  • RangeRequestsPlugin - Range ヘッダーを含むリクエストをキャッシュに保存されたレスポンスで満たすことができるようにします。ブラウザは通常、メディア コンテンツに Range ヘッダーを使用するため、これは必要です。
  • addPlugins - すべての Workbox リクエストに Workbox プラグインを追加できます。
  • precacheAndRoute - プリキャッシュ リストにエントリを追加し、対応する fetch リクエストを処理するルートを作成します。
  • __WB_MANIFEST - Workbox CLI(またはビルドツール プラグイン)がプリキャッシュ マニフェストに置き換えるプレースホルダ。

Workbox CLI または任意のビルドツールにサービス ワーカーを渡し、プリキャッシュの生成方法を構成します。次の例のような workbox-config.js ファイルは、サービス ワーカーのレンダリング方法を CLI に指示します。

module.exports = {
  globDirectory: '.',
  globPatterns: ['**/*.{html,mp4}'],
  maximumFileSizeToCacheInBytes: 5000000,
  swSrc: 'sw.js',
  swDest: 'sw.js',
};
  • globDirectory - プリキャッシュ ファイルの検索を開始するルートフォルダ
  • globPatterns - プリキャッシュするファイル パターン(glob)。
  • maximumFileSizeToCacheInBytes - プリキャッシュできるファイルのサイズの上限(バイト単位)。
  • swSrc - サービス ワーカーの生成に使用されるファイルの場所。
  • swDest - 生成されたサービス ワーカーの宛先(ソースファイルと同じにできますが、実行ごとに self.__WB_MANIFEST が存在することを確認してください)。

ビルドプロセスが実行されると、新しいバージョンのサービス ワーカーが生成され、self.__WB_MANIFEST がファイルのリストに置き換えられます。各ファイルには、リビジョンを示すハッシュが含まれます。

precacheAndRoute([
  {
    revision: '524ac4b453c83f76eb9caeec11854ca5',
    url: 'ny.mp4',
  },
]);

ビルドプロセスが実行されるたびに、このリストは、一致するファイルの現在のセットとその現在のリビジョン ハッシュで書き換えられます。これにより、ファイルが追加、削除、変更されるたびに、サービス ワーカーが次のインストール時にキャッシュを更新します。

遅延アプローチ

ビルド時にすべての動画を利用できない場合や、必要なときにのみ動画をキャッシュに保存したい場合は、遅延アプローチを採用する必要があります。このアプローチでは、キャッシュ保存と配信を分離する必要があります。動画の再生中にネットワークから取得されるのはコンテンツの一部のみであるため、ストリーミング中にファイルをキャッシュ保存することはできません。

ファイルのキャッシュ保存

キャッシュは Cache.open() を使用して作成し、Cache.add() または Cache.addAll() を使用してキャッシュにファイルを追加できます。アプリがキャッシュに保存する動画の JSON リストを受け取った場合、次のように動画キャッシュに追加できます。

// Open video cache
const cache = await caches.open('video-cache');
// Fetch list of videos
const videos = await (await fetch('/video-list.json')).json();
// Add videos to cache
await cache.addAll(videos);

このアプローチの利点は、他のウェブ ワーカーからでも、サービス ワーカーのライフサイクルとは独立してキャッシュ保存ステップを制御できることです。欠点は、ストレージ管理部分がデベロッパーに委ねられることです。ファイルの変更を追跡し、ブラウザで現在キャッシュに保存されているファイルを追跡し、変更されたファイルのみが更新されるようにファイル更新を管理する独自のアルゴリズムを作成する必要があります。

キャッシュに保存された動画ファイルを配信する

サービス ワーカーのランタイム キャッシュ保存戦略キャッシュ優先など)を使用して、以前にキャッシュ保存された動画ファイルを提供できます。

import { registerRoute } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
import { CacheableResponsePlugin } from 'workbox-cacheable-response';
import { RangeRequestsPlugin } from 'workbox-range-requests';

registerRoute(
  ({ request }) => request.destination === 'video',
  new CacheFirst({
    cacheName: 'video-cache',
    plugins: [
      new CacheableResponsePlugin({
        statuses: [200],
      }),
      new RangeRequestsPlugin(),
    ],
  }),
);
  • import(s) - 対応するワークボックス モジュールから必要なバインディングを読み込みます。
  • registerRoute - レスポンスを提供する関数(キャッシュ保存戦略とプラグイン)にリクエストを転送します。
  • CacheFirst - キャッシュからリクエストを満たすキャッシュ戦略。キャッシュが利用できない場合は、ネットワークから取得してキャッシュを更新します。
  • CacheableResponsePlugin - レスポンスをキャッシュに保存するために必要なヘッダーを示すために使用されます。動画をキャッシュに保存するルートには 200 ステータスのみを含めてください。そうすることで、動画のストリーミング中に部分的なコンテンツ レスポンス(206)がキャッシュに保存されるのを防ぐことができます。
  • RangeRequestsPlugin - Range ヘッダーを含むリクエストをキャッシュに保存されたレスポンスで満たすことができるようにするプラグイン。ブラウザは通常、メディア コンテンツに Range ヘッダーを使用するため、これは必要です。

動画の読み込みを最適化することは、ストリーミングを多用するアプリにとって重要なタスクです。ブラウザの Cache Storage API と Workbox を活用することで、この難しいタスクを管理しやすくなり、ユーザーの帯域幅の節約、サーバー負荷の軽減、動画再生の高速化、オフラインでの動画再生が可能になります。