Przekroczono limit buforowania

Jan Kowalski
Joe Medley

Jeśli używasz rozszerzeń źródeł multimediów (MSE), musisz się najpierw zająć zbyt dużym buforem. Otrzymasz wtedy tzw. QuotaExceededError. W tym artykule omówię, co można sobie z tym radzić.

Co to jest błąd QuotaExceededError?

Zasadniczo QuotaExceededError otrzymasz, jeśli spróbujesz dodać zbyt dużo danych do obiektu SourceBuffer. Ten błąd może skutkować także dodaniem większej liczby obiektów SourceBuffer do elementu nadrzędnego MediaSource. Nie leży to w zakresie tego artykułu). Jeśli SourceBuffer zawiera za dużo danych, wywołanie metody SourceBuffer.appendBuffer() spowoduje wyświetlenie w oknie konsoli Chrome tego komunikatu.

Błąd konsoli limitów.

Należy pamiętać o kilku kwestiach. Po pierwsze, nazwa QuotaExceededError nie pojawia się nigdzie w wiadomości. Aby to zobaczyć, ustaw punkt przerwania w miejscu, w którym możesz wykryć błąd i sprawdzić go w oknie zegarka lub zakresu. Zostało to pokazane poniżej.

Okno monitorowania limitu.

Po drugie: nie da się dokładnie sprawdzić, ile danych może obsłużyć SourceBuffer.

Działanie w innych przeglądarkach

W momencie tworzenia tego tekstu Safari nie zgłasza QuotaExceededError w wielu swoich kompilacjach. Zamiast tego usuwa klatki za pomocą algorytmu dwuetapowego, zatrzymując je, jeśli wystarczy miejsca na obsługę elementu appendBuffer(). Po pierwsze zwolniono klatki pomiędzy 0 a 30 sekund przed bieżącym czasem w 30-sekundowych fragmentach. Następnie usuwa klatki w 30-sekundowych fragmentach od początku do maksymalnie 30 sekund po currentTime. Więcej informacji na ten temat znajdziesz w zbiorze zmian Webkit z 2014 roku.

Na szczęście ten błąd powoduje też takie same błędy jak Chrome, Edge i Firefox. Jeśli używasz innej przeglądarki, zrób to samodzielnie. Choć nie wszystko, co można utworzyć na potrzeby rzeczywistego odtwarzacza, François Beaufort test limitu bufora źródła umożliwia obserwowanie przynajmniej takiego działania.

Ile danych mogę dołączyć?

Dokładna liczba różni się w zależności od przeglądarki. Nie możesz wysyłać zapytań o ilość dołączonych danych, więc musisz wiedzieć, ile wydajesz. Jeśli chodzi o treści do obejrzenia, oto najlepsze dane, które udało mi się zebrać w momencie pisania pisania. W przypadku Chrome te wartości to górne granice, co oznacza, że mogą być mniejsze, gdy system wykryje większe wykorzystanie pamięci.

Chrome Chromecast* Firefox Safari Edge
Wideo 150MB 30MB 100 MB 290MB Brak informacji
Audio 12MB 2MB 15 MB 14MB Brak informacji
  • lub inne urządzenie z Chrome o ograniczonej pamięci.

Co mam zrobić?

Ilość obsługiwanych danych jest bardzo zróżnicowana i nie można znaleźć ilości danych w polu SourceBuffer, dlatego musisz uzyskać je pośrednio, korzystając z metody QuotaExceededError. Przyjrzyjmy się teraz kilku sposobom, aby to zrobić.

QuotaExceededError można radzić sobie na kilka sposobów. W rzeczywistości najlepiej jest kombinować co najmniej jedną metodę. Twoje podejście powinno opierać się na tym, ile pobierasz i próbujesz przyłączyć poza HTMLMediaElement.currentTime, oraz dostosowując ten rozmiar na podstawie QuotaExceededError. Użycie pliku manifestu, takiego jak plik mpd (MPEG-DASH) lub plik m3u8 (HLS), może ułatwić śledzenie danych dodawanych do bufora.

Przyjrzyjmy się teraz kilku działaniom związanym z QuotaExceededError.

  • Usuń niepotrzebne dane i dołącz ponownie.
  • Dołącz mniejsze fragmenty.
  • Zmniejszyć rozdzielczość odtwarzania.

Chociaż można ich używać razem, będę je omawiać pojedynczo.

Usuń niepotrzebne dane i dołącz ponownie

Tak naprawdę powinno ono nosić nazwę „Usuwaj dane, których w najbliższym czasie prawdopodobnie nie zostaną użyte, a potem ponownie spróbuj dołączyć dane, które prawdopodobnie zostaną wkrótce wykorzystane”. Tytuł był za długi. Musisz pamiętać, co tak naprawdę mam na myśli.

Usuwanie najnowszych danych nie jest tak proste jak wywołanie SourceBuffer.remove(). Aby można było usunąć dane z SourceBuffer, flaga aktualizacji musi mieć wartość Fałsz. Jeśli nie, przed usunięciem danych wywołaj SourceBuffer.abort().

Gdy dzwonisz pod numer SourceBuffer.remove(), musisz pamiętać o kilku kwestiach.

  • Może to mieć negatywny wpływ na odtwarzanie. Jeśli na przykład chcesz, aby film wkrótce został ponownie odtworzony lub zapętlony, możesz nie usuwać początku filmu. Podobnie jeśli Ty lub użytkownik wyszukacie fragment filmu, z którego dane zostały usunięte, musicie dołączyć te dane ponownie, aby spełnić kryteria wyszukiwania.
  • Usuwaj materiały maksymalnie zachowawczo. Uważaj, aby nie usuwać aktualnie odtwarzanej grupy klatek, zaczynając od klatki kluczowej currentTime lub przed tą datą, ponieważ może to spowodować zatrzymanie odtwarzania. Takie informacje mogą zostać wyodrębnione ze strumienia bajtów przez aplikację internetową, jeśli nie są one dostępne w pliku manifestu. Plik manifestu lub wiedza o aplikacji na temat interwałów klatek kluczowych w multimediach może pomóc w wyborze zakresów przeznaczonych do usunięcia, aby zapobiec usunięciu aktualnie odtwarzanych multimediów. Cokolwiek usuniesz, nie usuwaj aktualnie odtwarzanej grupy zdjęć ani nawet kilku pierwszych poza nią. Nie usuwaj treści po czasie, gdy nie masz pewności, że nie są już potrzebne. Jeśli usuniesz go w pobliżu suwaka odtwarzania, może to spowodować wstrzymanie odtwarzania.
  • Safari 9 i Safari 10 nieprawidłowo implementują SourceBuffer.abort(). W rzeczywistości generują błędy, które wstrzymują odtwarzanie. Na szczęście są dostępne narzędzia do śledzenia błędów tutaj i tutaj. Tymczasem musisz jakoś temu zaradzić. Zrobi to w Shaka Player, eliminując pustą funkcję abort() w tych wersjach przeglądarki Safari.

Dołącz mniejsze fragmenty

Przedstawiliśmy tę procedurę poniżej. Nie zawsze się to sprawdza, ale jego zaletą jest możliwość dostosowania rozmiaru mniejszych fragmentów do własnych potrzeb. Nie trzeba też wracać do sieci, co w przypadku niektórych użytkowników może wiązać się z dodatkowymi kosztami za transmisję danych.

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);

Zmniejsz rozdzielczość odtwarzania

Działa to podobnie do usuwania najnowszych danych i ich ponownego dołączania. Obie te czynności można wykonać razem, ale poniższy przykład pokazuje tylko zmniejszenie rozdzielczości.

Stosując tę metodę, pamiętaj o kilku kwestiach:

  • Musisz dołączyć nowy segment inicjowania. Musisz to robić za każdym razem, gdy zmieniasz reprezentację. Nowy segment inicjowania musi być przeznaczony dla następujących po nim segmentów multimediów.
  • Sygnatura czasowa prezentacji załączonych multimediów powinna być jak najbardziej zbliżona do sygnatury czasowej danych w buforze, ale nie przeskakuj do przodu. Nakładanie się danych zbuforowanych może spowodować zacinanie się lub krótkie zatrzymywanie się, w zależności od przeglądarki. Niezależnie od tego, co dodasz, nie zasłaniaj suwaka odtwarzania, ponieważ spowoduje to wystąpienie błędów.
  • Przewijanie może zakłócić odtwarzanie. Może Cię kusić, by wyszukać konkretne miejsce i wznowić w nim odtwarzanie. Pamiętaj, że spowoduje to przerwanie odtwarzania do czasu zakończenia wyszukiwania.