超過緩衝配額

喬梅利
Joe Medley

如果是使用媒體來源擴充功能 (MSE),您最後需要處理的是超滿的緩衝區。發生這種情況時,您會取得所謂的 QuotaExceededError。本文將介紹幾種問題的處理方法

什麼是 QuotaExceededError?

基本上,如果您嘗試在 SourceBuffer 物件中加入過多資料,就能獲得 QuotaExceededError。(如果在父項 MediaSource 元素中加入更多 SourceBuffer 物件,也可能會擲回這個錯誤。這不在本文的討論範圍內)。如果 SourceBuffer 中的資料過多,呼叫 SourceBuffer.appendBuffer() 會在 Chrome 控制台視窗中觸發下列訊息。

配額控制台錯誤。

需要留意幾件事首先,請注意名稱 QuotaExceededError 會顯示在訊息中。如要查看錯誤,請在可以擷取錯誤的位置設定中斷點,並在手錶或範圍視窗中檢查該錯誤。方法如下。

配額監控期。

其次,沒有確切的方法可以得知 SourceBuffer 可處理多少資料。

其他瀏覽器中的行為

撰寫內容時,Safari 不會在許多建構作業中擲回 QuotaExceededError。而是使用兩步驟演算法移除影格,如果空間足以處理 appendBuffer(),請停止操作。首先,它會以 30 秒的片段,在目前時間之前的 0 到 30 秒之間釋放影格。接著,它會從 currentTime 之後的 30 秒,將影格從持續時間向後釋出到接近 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);

降低播放解析度

這與移除最近資料並重新附加的做法類似。事實上,這兩者可能會同時完成,但以下範例僅顯示較低的解析度。

使用這項技巧時,請注意下列事項:

  • 您必須附加新的初始化區隔。每次變更表示法時,都必須執行此操作。新的初始化區隔必須是後續的媒體區隔。
  • 附加媒體的顯示時間戳記應盡可能與緩衝區中的資料時間戳記相符,但不得快轉。視瀏覽器而定,重疊緩衝資料可能會導致延遲或短暫停滯。無論您附加什麼內容,都不要重疊播放頭,以免發生錯誤。
  • 搜尋影片可能會幹擾播放。您可能會想前往特定位置,從該位置繼續播放。請注意,這會導致播放中斷,直到跳轉完成。