Media Source Extensions(MSE)を使用している場合、最終的にはバッファの過剰な状態に対処する必要があります。この場合、QuotaExceededError
と呼ばれるものが返されます。この記事では、この問題に対処する方法について説明します。
QuotaExceededError とは何ですか?
基本的に、SourceBuffer
オブジェクトにデータが多すぎると、QuotaExceededError
が返されます。(親 MediaSource
要素に SourceBuffer
オブジェクトを追加すると、このエラーがスローされる可能性があります。これはこの記事の対象外です)。SourceBuffer
にデータが多すぎる場合、SourceBuffer.appendBuffer()
を呼び出すと、Chrome コンソール ウィンドウに次のメッセージが表示されます。
ただし、注意すべき点がいくつかあります。まず、メッセージのどこにも QuotaExceededError
という名前がないことを確認します。エラーを検出できる場所にブレークポイントを設定し、ウォッチウィンドウまたはスコープ ウィンドウでエラーを確認します。その手順を以下に示します。
2 つ目は、SourceBuffer
が処理できるデータ量を特定する確実な方法がないことです。
他のブラウザでの動作
執筆時点では、Safari の多くのビルドで QuotaExceededError
がスローされません。代わりに、2 段階のアルゴリズムを使用してフレームを削除し、appendBuffer()
を処理するのに十分なスペースがある場合は停止します。まず、現在の時刻の 0 ~ 30 秒前から 30 秒のチャンクでフレームを解放します。次に、currentTime
の 30 秒後に近い時間から、時間の逆方向に 30 秒単位でフレームを解放します。詳しくは、2014 年の WebKit の変更セットをご覧ください。
幸い、Chrome だけでなく、Edge と Firefox でもこのエラーがスローされます。別のブラウザを使用している場合は、ご自身でテストする必要があります。実際のメディア プレーヤー用にビルドするものではないかもしれませんが、François Beaufort のソース バッファの上限テストでは、少なくとも動作を確認できます。
追加できるデータの量はどれくらいですか?
正確な数はブラウザによって異なります。現在アペンドされているデータの量をクエリすることはできないため、アペンドするデータの量を自分で追跡する必要があります。注意すべき点としては、この記事の執筆時点で収集できる最良のデータは次のとおりです。Chrome では、これらの数値は上限です。つまり、システムでメモリ不足が発生した場合は、この数値より小さくなる可能性があります。
Chrome | Chromecast* | Firefox | Safari | Edge | |
---|---|---|---|---|---|
動画 | 150MB | 30MB | 100 MB | 290MB | 不明 |
音声 | 12MB | 2MB | 15 MB | 14MB | 不明 |
- または、メモリ容量が限られている他の Chrome デバイス。
どうすればよいですか?
サポートされるデータの量は非常に大きく、SourceBuffer
でデータの量を確認できないため、QuotaExceededError
を処理して間接的に取得する必要があります。その方法をいくつか見てみましょう。
QuotaExceededError
に対処する方法はいくつかあります。実際には、1 つ以上のアプローチを組み合わせるのが最善です。フェッチして HTMLMediaElement.currentTime
を超えるデータを追加しようとする量に基づいて処理を行い、そのサイズを QuotaExceededError
に基づいて調整することをおすすめします。また、mpd ファイル(MPEG-DASH)や m3u8 ファイル(HLS)などのマニフェストを使用すると、バッファに追加するデータを追跡できます。
次に、QuotaExceededError
に対処するためのいくつかのアプローチを見てみましょう。
- 不要なデータを削除して、再度追加します。
- 小さいフラグメントを追加します。
- 再生解像度を下げる。
これらは組み合わせて使用することもできますが、ここでは 1 つずつ説明します。
不要なデータを削除して再度追加する
正確には、「近い将来使用される可能性の低いデータを削除してから、近い将来使用される可能性の高いデータの追加を再試行する」という名前にすべきです。タイトルが長すぎます。 私が本当に言いたいことを覚えておいてください。
最近のデータの削除は、SourceBuffer.remove()
を呼び出すだけでは簡単ではありません。SourceBuffer
からデータを削除するには、更新フラグを false にする必要があります。有効でない場合は、データを削除する前に SourceBuffer.abort()
を呼び出します。
SourceBuffer.remove()
を呼び出す際には、いくつか注意すべき点があります。
- これにより、再生に悪影響が生じる可能性があります。たとえば、動画をすぐに再生またはループさせたい場合は、動画の冒頭を削除しないことをおすすめします。同様に、ユーザーまたはクリエイターが、データを削除した動画の部分にシークした場合は、そのシークを満たすために、そのデータを再度追加する必要があります。
- 削除はできるだけ控えめに行ってください。
currentTime
以前のキーフレームで現在再生中のフレーム グループを削除すると、再生が停止する可能性があるため、注意してください。このような情報は、マニフェストで利用できない場合は、ウェブアプリによってバイトストリームから解析される必要があります。メディア マニフェストまたはメディア内のキーフレーム間隔に関するアプリの知識は、現在再生中のメディアが削除されないように、アプリが削除範囲を選択する際に役立ちます。削除する写真は、現在再生中のグループや、その最初の数枚は削除しないでください。一般的に、メディアが不要になったことが確実な場合を除き、現在時刻以降は削除しないでください。プレイヘッドの近くで取り外すと、ストールが発生することがあります。 - Safari 9 と Safari 10 では
SourceBuffer.abort()
が正しく実装されていません。実際には、エラーをスローして再生を停止します。幸い、こちらとこちらにオープンなバグトラッカーがあります。それまでは、何らかの回避策を講じる必要があります。Shaka Player では、これらのバージョンの Safari で空のabort()
関数をスタブ化することでこれを実現しています。
小さいフラグメントを追加する
手順は次のとおりです。これはすべてのケースで機能するとは限りません。ただし、小さなチャンクのサイズをニーズに合わせて調整できるという利点があります。また、ネットワークに戻る必要がないため、一部のユーザーで追加のデータ通信料が発生することはありません。
const pieces = new Uint8Array([data]);
(function appendFragments(pieces) {
if (sourceBuffer.updating) {
return;
}
pieces.forEach(piece => {
try {
sourceBuffer.appendBuffer(piece);
}
catch e {
if (e.name !== 'QuotaExceededError') {
throw e;
}
// Reduction schedule: 80%, 60%, 40%, 20%, 16%, 12%, 8%, 4%, fail.
const reduction = pieces[0].byteLength * 0.8;
if (reduction / data.byteLength < 0.04) {
throw new Error('MediaSource threw QuotaExceededError too many times');
}
const newPieces = [
pieces[0].slice(0, reduction),
pieces[0].slice(reduction, pieces[0].byteLength)
];
pieces.splice(0, 1, newPieces[0], newPieces[1]);
appendBuffer(pieces);
}
});
})(pieces);
再生解像度を下げる
これは、最近のデータの削除と再追加に似ています。実際には、この 2 つを一緒に行うこともできますが、以下の例は解像度を下げるだけを示しています。
この手法を使用する場合は、次の点に注意してください。
- 新しい初期化セグメントを追加する必要があります。これは、表現を変更するたびに行う必要があります。新しい初期化セグメントは、続くメディア セグメント用にする必要があります。
- 追加されたメディアのプレゼンテーション タイムスタンプは、バッファ内のデータのタイムスタンプとできるだけ一致させる必要がありますが、先に進まないようにする必要があります。バッファに格納されたデータを重ねると、ブラウザによっては、途切れや短時間の停止が発生することがあります。追加する内容にかかわらず、再生ヘッドを重ねないようにしてください。重ねるとエラーが発生します。
- シークすると再生が中断される場合があります。特定の場所まで移動して、そこから再生を再開したくなるかもしれませんが、シークが完了するまで、再生が中断されます。