Arabelleğe alma kotasının aşılması

Joe Medley
Joe Medley

Media Source Extensions (MSE) ile çalışıyorsanız zaman içinde aşırı dolu bir arabellekle uğraşmanız gerekecektir. Bu durumda QuotaExceededError alırsınız. Bu makalede, bu sorunun üstesinden gelmenin bazı yollarını ele alacağız.

QuotaExceededError nedir?

Temel olarak, SourceBuffer nesnenize çok fazla veri eklemeye çalışırsanız QuotaExceededError değerini alırsınız. (Bir üst MediaSource öğesine daha fazla SourceBuffer nesnesi eklemek de bu hataya yol açabilir. Bunlar bu makalenin kapsamı dışındadır.) SourceBuffer çok fazla veri içeriyorsa SourceBuffer.appendBuffer() çağrısından Chrome konsol penceresinde aşağıdaki mesaj tetiklenir.

Kota konsolu hatası.

Bu konuda dikkat etmeniz gereken birkaç nokta vardır. Öncelikle, QuotaExceededError adının iletide hiçbir yerde görünmediğini fark edin. Bunu görmek için hatayı yakalayabileceğiniz bir konumda kesme noktası ayarlayın ve hatayı izleme veya kapsam pencerenizde inceleyin. Bunu aşağıda görebilirsiniz.

Kota izleme aralığı.

İkinci olarak, SourceBuffer'ün ne kadar veri işleyebileceğini kesin olarak öğrenmenin bir yolu yoktur.

Diğer tarayıcılarda davranış

Yazma sırasında Safari, derlemelerinin çoğunda QuotaExceededError vermez. Bunun yerine, iki adımlı bir algoritma kullanarak kareleri kaldırır ve appendBuffer()'yi işlemek için yeterli alan varsa durur. Öncelikle, mevcut zamandan 0 ila 30 saniye önceki kareleri 30 saniyelik parçalar halinde temizler. Ardından, sürenin gerisinden currentTime'ten sonraki 30 saniyeye kadar 30 saniyelik bloklar halinde kareleri serbest bırakır. Bu konuda daha fazla bilgiyi 2014'teki Webkit değişiklik kaydında bulabilirsiniz.

Neyse ki Chrome ile birlikte Edge ve Firefox da bu hatayı veriyor. Başka bir tarayıcı kullanıyorsanız kendi testinizi yapmanız gerekir. Gerçek bir medya oynatıcı için oluşturacağınız şey olmasa da François Beaufort'un kaynak arabellek sınırı testini kullanarak bu davranışı gözlemleyebilirsiniz.

Ne kadar veri ekleyebilirim?

Kesin sayı tarayıcıdan tarayıcıya değişir. Şu anda eklenmiş veri miktarını sorgulayamayacağınız için ne kadar veri eklediğinizi kendiniz takip etmeniz gerekir. İzlemeniz gereken içerikler konusunda, bu makaleyi yazarken toplayabildiğim en iyi verileri aşağıda bulabilirsiniz. Chrome için bu sayılar üst sınırlardır. Yani sistem bellek baskısıyla karşılaştığında bu sayılar daha küçük olabilir.

Chrome Chromecast* Firefox Safari Edge
Video 150MB 30MB 100 MB 290MB Bilinmiyor
Ses 12MB 2MB 15 MB 14MB Bilinmiyor
  • Veya sınırlı belleğe sahip başka bir Chrome cihaz.

Ne yapmalıyım?

Desteklenen veri miktarı çok büyük değişiklik gösterdiği ve veri miktarını SourceBuffer içinde bulamayacağınız için verileri QuotaExceededError işleyerek dolaylı olarak almanız gerekir. Şimdi bunu yapmanın birkaç yoluna göz atalım.

QuotaExceededError ile başa çıkmanın birkaç yolu vardır. Gerçekte, en iyi yöntem bir veya daha fazla yaklaşımın bir kombinasyonudur. Yaklaşımınız, ne kadar getirdiğinizi, HTMLMediaElement.currentTime dışında bir boyut eklemeyi ve bu boyutu QuotaExceededError'a göre ayarlamaya çalıştığınızı temel almalısınız. Ayrıca, mpd dosyası (MPEG-DASH) veya m3u8 dosyası (HLS) gibi bir tür manifest kullanmak, arabelleğe eklediğiniz verileri takip etmenize yardımcı olabilir.

Şimdi QuotaExceededError ile başa çıkmak için çeşitli yaklaşımlara göz atalım.

  • Gereksiz verileri kaldırıp yeniden ekleyin.
  • Daha küçük parçalar ekleyin.
  • Oynatma çözünürlüğünü düşürün.

Birlikte kullanılabilirler, ancak bunları teker teker ele alacağım.

Gereksiz verileri kaldırıp yeniden ekleyin

Bu işlem aslında "Yakında kullanılma olasılığı en düşük verileri kaldırın ve ardından yakında kullanılma olasılığı yüksek verileri eklemeyi yeniden deneyin" şeklinde adlandırılmalıdır. Bu çok uzun bir başlıktı. Asıl neyi kastettiğimi hatırlamanız yeterli.

Son verileri kaldırmak, SourceBuffer.remove() çağrısı yapmak kadar basit değildir. SourceBuffer kaynağından veri kaldırmak için güncelleme işaretinin yanlış olması gerekir. Aksi takdirde, veri kaldırmadan önce SourceBuffer.abort() çağrısını yapın.

SourceBuffer.remove() adlı kişiyi ararken unutulmaması gereken birkaç nokta vardır.

  • Bu durum oynatmayı olumsuz yönde etkileyebilir. Örneğin, videonun kısa süre içinde tekrar oynatılmasını veya döngüde oynatılmasını istiyorsanız videonun başlangıcını kaldırmak istemeyebilirsiniz. Benzer şekilde, siz veya kullanıcı videonun verilerini kaldırdığınız bir bölümüne giderseniz bu aramayı karşılamak için söz konusu verileri tekrar eklemeniz gerekir.
  • Mümkün olduğunca muhafazakar bir şekilde kaldırın. Oynatılan kare grubunu currentTime veya öncesindeki animasyon karesinden kaldırmamaya dikkat edin. Aksi takdirde oynatma duraklatılabilir. Bu tür bilgilerin, manifest dosyasında mevcut olmaması durumunda web uygulaması tarafından bayt akışından ayrıştırılması gerekebilir. Medya manifesti veya uygulamanın medyadaki keyframe aralıkları hakkındaki bilgisi, uygulamanızın oynatılmakta olan medyanın kaldırılmasını önlemek için kaldırma aralığı seçiminde yol gösterici olabilir. Kaldırdığınız resimler arasından, şu anda oynatılan resim grubunu veya bu grubun ilk birkaç resmini kaldırmayın. Genel olarak, medyanın artık gerekli olmadığından emin değilseniz mevcut zamandan daha eski kayıtları kaldırmayın. Oynatma çubuğunun yakınından kaldırırsanız videonun duraklamasına neden olabilirsiniz.
  • Safari 9 ve Safari 10, SourceBuffer.abort() özelliğini doğru şekilde uygulamıyor. Aslında oynatmayı durduracak hatalar gönderirler. Neyse ki burada ve burada açık hata izleyiciler bulunuyor. Bu sırada bu sorunu bir şekilde çözmeniz gerekir. Shaka Player, Safari'nin bu sürümlerinde boş bir abort() işlevi stub'layarak bunu yapar.

Daha küçük parçalar ekleme

Aşağıda bu işlemi gösterdik. Bu yöntem her durumda işe yaramayabilir ancak daha küçük parçaların boyutunun ihtiyaçlarınıza göre ayarlanabilmesi avantajına sahiptir. Ayrıca, bazı kullanıcılar için ek veri maliyetlerine neden olabilecek ağa geri dönmeyi gerektirmez.

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

Oynatma çözünürlüğünü düşürme

Bu işlem, son verileri kaldırıp yeniden eklemeye benzer. Aslında bu iki işlem birlikte yapılabilir. Ancak aşağıdaki örnekte yalnızca çözünürlüğün düşürülmesi gösterilmektedir.

Bu tekniği kullanırken göz önünde bulundurmanız gereken birkaç nokta vardır:

  • Yeni bir başlatma segmenti eklemeniz gerekir. Temsilleri her değiştirdiğinizde bunu yapmanız gerekir. Yeni başlatma segmenti, sonraki medya segmentleri için olmalıdır.
  • Eklenen medyanın sunum zaman damgası, arabellekteki verilerin zaman damgasıyla mümkün olduğunca yakın olmalı ancak ileri atlamamalıdır. Arabelleğe alınan verilerin çakışma yapması, tarayıcıya bağlı olarak takılmalara veya kısa süreli duraklamalara neden olabilir. Ne eklediğinizden bağımsız olarak oynatma başlığını örtmeyin. Aksi takdirde hata alırsınız.
  • İleri sarma, oynatmayı kesintiye uğratabilir. Belirli bir konuma gidip oynatmayı oradan devam ettirmek isteyebilirsiniz. Bu durumda, oynatma tamamlanana kadar oynatmanın kesintiye uğramasına neden olacağını unutmayın.