Przekroczono limit buforowania

Joe Medley
Joe Medley

Jeśli korzystasz z rozszerzeń źródła multimediów (MSE), musisz zająć się problemem przepełnionego bufora. W takim przypadku otrzymasz QuotaExceededError. W tym artykule omawiam kilka sposobów radzenia sobie z tym problemem.

Czym jest błąd QuotaExceededError?

QuotaExceededError to w podstawie obiekt, który powstaje, gdy do obiektu SourceBuffer próbujesz dodać zbyt dużo danych. (dodanie większej liczby obiektów SourceBuffer do elementu nadrzędnego MediaSource może też spowodować wystąpienie tego błędu). (nie jest to jednak tematem tego artykułu). Jeśli SourceBuffer zawiera zbyt dużo danych, wywołanie funkcji SourceBuffer.appendBuffer() spowoduje wyświetlenie tego komunikatu w oknie konsoli Chrome.

Błąd konsoli limitu.

Należy jednak pamiętać o kilku kwestiach. Zwróć uwagę, że nazwa QuotaExceededError nie pojawia się nigdzie w wiadomości. Aby to sprawdzić, ustaw punkt przerwania w miejscu, w którym możesz wykryć błąd i zbadać go w oknie podglądu lub w oknie zakresu. Poniżej znajdziesz zrzut ekranu.

Okno limitu

Po drugie, nie ma jednoznacznego sposobu na ustalenie, ile danych może obsłużyć SourceBuffer.

Działanie w innych przeglądarkach

W momencie pisania tego tekstu Safari nie wyrzuca błędu QuotaExceededError w wielu swoich wersjach. Zamiast tego usuwa klatki za pomocą algorytmu dwuetapowego, który zatrzymuje się, gdy jest wystarczająco dużo miejsca na appendBuffer(). Najpierw zwalnia klatki z przedziału od 0 do 30 sekund przed bieżącym czasem w odstępach 30 sekund. Następnie usuwa klatki w klockach po 30 sekund od początku do 30 sekund po currentTime. Więcej informacji na ten temat można znaleźć w zmianach w kodzie WebKit z 2014 roku.

Na szczęście Chrome, Edge i Firefox również wyświetlają ten błąd. Jeśli używasz innej przeglądarki, musisz przeprowadzić testy samodzielnie. Chociaż prawdopodobnie nie jest to coś, co tworzysz dla rzeczywistego odtwarzacza multimediów, test limitu bufora źródła Françoisa Beauforta pozwala przynajmniej obserwować zachowanie.

Ile danych mogę dołączyć?

Dokładna liczba zależy od przeglądarki. Ponieważ nie możesz wysłać zapytania o ilość danych dołączonych w danym momencie, musisz śledzić, ile danych dołączasz. Jeśli chodzi o to, co warto obejrzeć, oto najlepsze dane, które udało mi się zebrać w momencie pisania tego artykułu. W przypadku Chrome te wartości są limitami górnymi, co oznacza, że mogą być mniejsze, gdy system napotka problemy z pamięcią.

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

Co mam zrobić?

Ponieważ ilość obsługiwanych danych jest bardzo zróżnicowana i nie możesz znaleźć informacji o jej wielkości w SourceBuffer, musisz ją uzyskać pośrednio, korzystając z QuotaExceededError. Przyjrzyjmy się teraz kilku sposobom na to.

Istnieje kilka sposobów radzenia sobie z QuotaExceededError. W praktyce najlepsza jest kombinacja co najmniej 1 metody. Twoje podejście powinno polegać na określeniu, ile danych chcesz pobrać i dołączyć do danych z poziomu HTMLMediaElement.currentTime, a następnie na dostosowaniu tego rozmiaru na podstawie poziomu QuotaExceededError. Korzystanie z jakiegoś pliku manifestu, np. pliku mpd (MPEG-DASH) lub pliku m3u8 (HLS), może Ci pomóc w śledzeniu danych dodawanych do bufora.

Przyjrzyjmy się teraz kilku podejściom do rozwiązywania problemów z QuotaExceededError.

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

Chociaż można ich używać w połączeniu, omówię je po kolei.

Usuwanie niepotrzebnych danych i ponowne dołączanie

Właściwa nazwa tego zadania powinna brzmieć: „Usuń dane, które prawdopodobnie nie zostaną użyte w najbliższym czasie, a następnie spróbuj dołączyć dane, które prawdopodobnie zostaną użyte w najbliższym czasie”. Tytuł jest za długi. Musisz tylko pamiętać, co mam na myśli.

Usuwanie niedawnych danych nie jest tak proste jak wywołanie funkcji SourceBuffer.remove(). Aby usunąć dane z tabeli SourceBuffer, flaga aktualizacji musi być ustawiona na wartość false. Jeśli nie, zadzwoń pod numer SourceBuffer.abort(), zanim usuniesz jakiekolwiek dane.

Podczas rozmowy z zespołem pomocy SourceBuffer.remove() należy pamiętać o kilku kwestiach.

  • Może to negatywnie wpłynąć na odtwarzanie. Jeśli na przykład chcesz, aby film był odtwarzany w pętli, nie musisz usuwać początku filmu. Podobnie, jeśli Ty lub użytkownik przewiniesz do części filmu, w której dane zostały usunięte, musisz ponownie dołączyć te dane, aby spełnić to żądanie.
  • Usuń jak najmniej elementów. Pamiętaj, aby nie usuwać grupy klatek, która jest obecnie odtwarzana, zaczynając od klatki kluczowej lub wcześniej (currentTime), ponieważ może to spowodować zablokowanie odtwarzania. Jeśli takie informacje nie są dostępne w pliku manifestu, aplikacja internetowa może musieć wyodrębnić je z strumienia bajtów. Manifest multimediów lub wiedza aplikacji o interwałach kluczowych klatek w multimediach mogą pomóc aplikacji w określeniu zakresów usuwania, aby zapobiec usunięciu obecnie odtwarzanych multimediów. Niezależnie od tego, co usuniesz, nie usuwaj grupy zdjęć, która jest obecnie odtwarzana, ani nawet kilku pierwszych zdjęć po niej. Zazwyczaj nie usuwaj plików, które nie są już potrzebne, chyba że masz pewność, że nie są już potrzebne. Jeśli usuniesz element w pobliżu wskaźnika odtwarzania, może to spowodować zablokowanie.
  • Safari 9 i Safari 10 nieprawidłowo implementują SourceBuffer.abort(). W rzeczywistości powodują one błędy, które powodują zatrzymanie odtwarzania. Na szczęście istnieją śledzące błędy tutaj i tutaj. W międzyczasie musisz jakoś obejść ten problem. Shaka Player robi to, tworząc pustą funkcję abort() w tych wersjach Safari.

Dołącz mniejsze fragmenty

Poniżej znajdziesz opis tej procedury. Ta metoda może nie działać w każdym przypadku, ale ma tę zaletę, że rozmiar mniejszych fragmentów można dostosować do swoich potrzeb. Nie wymaga też powrotu do sieci, co może spowodować dodatkowe koszty danych dla niektórych użytkowników.

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.

Jest to podobne do usuwania ostatnich danych i ponownie dołączania. W istocie te dwie metody mogą być stosowane razem, ale w przykładzie poniżej pokazano tylko obniżenie rozdzielczości.

Podczas korzystania z tej techniki należy pamiętać o kilku kwestiach:

  • Musisz dodać nowy segment inicjalizacji. Musisz to zrobić za każdym razem, gdy zmieniasz reprezentację. Nowy segment inicjalizacji musi być przeznaczony dla następnych segmentów mediów.
  • Sygnatury czasowe multimediów dołączonych do siebie powinny być jak najbardziej zbliżone do sygnatury czasowej danych w buforze, ale nie mogą być przesunięte do przodu. Nakładanie się danych buforowanych może powodować zacinanie lub krótkie zawieszanie się filmu, w zależności od przeglądarki. Niezależnie od tego, co dołączysz, nie nakładaj elementów na pasek odtwarzania, ponieważ może to spowodować błędy.
  • Przewijanie może przerwać odtwarzanie. Możesz zechcieć przewinąć do konkretnego miejsca i wznowić odtwarzanie od tego miejsca. Pamiętaj, że do momentu zakończenia przewijania odtwarzanie zostanie przerwane.