如果您使用媒體來源擴充功能 (MSE),最終需要處理的問題之一就是緩衝區過大。發生這種情況時,您會收到所謂的 QuotaExceededError
。本文將介紹一些處理方式。
什麼是 QuotaExceededError?
基本上,如果嘗試在 SourceBuffer
物件中新增過多資料,則 QuotaExceededError
會是您遇到的問題。(將更多 SourceBuffer
物件新增至父項 MediaSource
元素也可能會擲回此錯誤。這不在本文的說明範圍內)。如果 SourceBuffer
中的資料過多,呼叫 SourceBuffer.appendBuffer()
會在 Chrome 資訊主頁視窗中觸發下列訊息。
有幾點需要注意。首先,請注意訊息中沒有出現 QuotaExceededError
這個名稱。如要瞭解此功能,請在可以擷取錯誤的位置設定中斷點,並在智慧手錶或範圍視窗中檢查該錯誤。我已在下方顯示這項資訊。
其次,沒有任何方法可以確定 SourceBuffer
可以處理多少資料。
其他瀏覽器的行為
在撰寫本文時,Safari 在許多版本中並未擲回 QuotaExceededError
。而是使用兩步驟演算法移除影格,如果有足夠的空間處理 appendBuffer()
,就會停止。首先,它會以 30 秒的區塊,釋放從目前時間前 0 到 30 秒的畫格。接著,它會從時間回溯到接近 30 秒的 currentTime
片段,從持續時間回溯到接近 30 秒的片段釋放影格。詳情請參閱 2014 年起的 Webkit 異動內容。
幸運的是,除了 Chrome 之外,Edge 和 Firefox 也確實會擲回這個錯誤。如果您使用其他瀏覽器,則需要自行進行測試。雖然這可能不是您為實際媒體播放器建構的內容,但 François Beaufort 的來源緩衝區限制測試至少可讓您觀察行為。
我可以附加多少資料?
確切數量會因瀏覽器而異。您無法查詢目前附加的資料量,因此必須追蹤要自行附加的資料量。至於要觀看哪些內容,以下是目前我能收集到的最佳資料。在 Chrome 中,這些數字的上限是上限,代表如果系統遇到記憶體壓力,數值可以縮減。
Chrome | Chromecast* | Firefox | Safari | Edge | |
---|---|---|---|---|---|
影片 | 150MB | 30MB | 100MB | 290MB | 不明 |
音訊 | 12MB | 2MB | 15MB | 14MB | 不明 |
- 或其他記憶體有限的 Chrome 裝置。
那我該怎麼做?
由於支援的資料量差異極大,您無法在 SourceBuffer
中找到資料量,因此必須透過處理 QuotaExceededError
間接取得資料。接下來,我們來看看幾種方法。
處理 QuotaExceededError
的方法有很多種,實際上,最好是同時採用一或多種方法。您應該根據要擷取的資料量,以及嘗試附加至 HTMLMediaElement.currentTime
的資料量,並根據 QuotaExceededError
調整該大小,來執行這項工作。另外,使用某種資訊清單 (例如 mpd 檔案 (MPEG-DASH) 或 m3u8 檔案 (HLS)) 也可以協助您追蹤要附加到緩衝區的資料。
接下來,我們來看看如何處理 QuotaExceededError
。
- 移除不必要的資料,然後重新附加。
- 附加較小的片段。
- 降低播放解析度。
這兩項工具可以搭配使用,但逐一介紹。
移除不需要的資料並重新附加
這個方法其實應該稱為「移除最不可能很快就會使用的資料,然後重試附加可能很快就會使用的資料」。標題過長。 你只要記得我說的是什麼就好。
移除近期資料並不容易,就像呼叫 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);
降低播放解析度
這與移除最近的資料並重新附加資料類似。事實上,這兩種做法可以同時進行,但下方的範例只會顯示降低解析度。
使用這項技巧時,請留意下列事項:
- 您必須附加新的初始化區段。您必須在每次變更表示法時執行這項操作。新的初始化區段必須適用於後續的媒體區段。
- 附加媒體的呈現時間戳記應盡可能與緩衝區中資料的時間戳記相符,但不得超前。視瀏覽器而定,重疊緩衝資料可能會導致系統延遲或短暫暫停。無論您附加什麼,請勿重疊播放頭,否則會擲回錯誤。
- 尋找可能會中斷播放。您可能會想前往特定位置繼續播放。請注意,這會導致播放中斷,直到搜尋完成為止。