فراتر از سهمیه بافر

جو مدلی
Joe Medley

اگر با برنامه‌های افزودنی منبع رسانه (MSE) کار می‌کنید، یکی از مواردی که در نهایت باید با آن مقابله کنید یک بافر بیش از حد پر است. هنگامی که این اتفاق می افتد، چیزی را دریافت خواهید کرد که QuotaExceededError نامیده می شود. در این مقاله به برخی از راه های مقابله با آن می پردازم.

QuotaExceededError چیست؟

اساساً، QuotaExceededError چیزی است که اگر بخواهید داده های زیادی را به شی SourceBuffer خود اضافه کنید، دریافت می کنید. (افزودن اشیاء SourceBuffer بیشتر به یک عنصر MediaSource مادر نیز می تواند این خطا را ایجاد کند. این خطا از حوصله این مقاله خارج است.) اگر SourceBuffer داده های زیادی در خود داشته باشد، فراخوانی SourceBuffer.appendBuffer() پیام زیر را در پنجره کنسول کروم راه اندازی می کند. .

خطای کنسول سهمیه.

در این مورد چند نکته قابل ذکر است. ابتدا توجه داشته باشید که نام QuotaExceededError در هیچ کجای پیام ظاهر نمی شود. برای مشاهده آن، نقطه انفصال را در مکانی تنظیم کنید که بتوانید خطا را دریافت کنید و آن را در ساعت یا پنجره محدوده خود بررسی کنید. من این را در زیر نشان داده ام.

پنجره ساعت سهمیه ای.

دوم، هیچ راه قطعی برای یافتن میزان داده ای که SourceBuffer می تواند مدیریت کند وجود ندارد.

رفتار در مرورگرهای دیگر

در زمان نگارش، سافاری در بسیاری از بیلدهای خود QuotaExceededError وارد نمی کند. در عوض، فریم‌ها را با استفاده از یک الگوریتم دو مرحله‌ای حذف می‌کند و در صورت وجود فضای کافی برای رسیدگی به appendBuffer() متوقف می‌شود. اول، فریم‌ها را بین 0 تا 30 ثانیه قبل از زمان فعلی در تکه‌های 30 ثانیه آزاد می‌کند. در مرحله بعد، فریم‌ها را در تکه‌های 30 ثانیه‌ای از مدت زمان به عقب تا نزدیک به 30 ثانیه پس از currentTime آزاد می‌کند. می‌توانید اطلاعات بیشتری در مورد این موضوع در مجموعه تغییرات Webkit مربوط به سال 2014 بخوانید.

خوشبختانه، همراه با کروم، اج و فایرفاکس این خطا را انجام می دهند. اگر از مرورگر دیگری استفاده می کنید، باید خودتان تست کنید. اگرچه احتمالاً آن چیزی نیست که برای یک پخش کننده رسانه واقعی می‌سازید، آزمون حد بافر منبع فرانسوا بوفور حداقل به شما امکان می‌دهد رفتار را مشاهده کنید.

چه مقدار داده می توانم اضافه کنم؟

تعداد دقیق از مرورگری به مرورگر دیگر متفاوت است. از آنجایی که نمی‌توانید مقدار داده‌های اضافه‌شده فعلی را پرس و جو کنید، باید مقداری را که خودتان اضافه می‌کنید پیگیری کنید. در مورد آنچه که باید تماشا کرد، در اینجا بهترین داده هایی است که می توانم در زمان نوشتن جمع آوری کنم. برای کروم، این اعداد حد بالایی هستند، به این معنی که وقتی سیستم با فشار حافظه مواجه می‌شود، می‌توانند کوچک‌تر باشند.

کروم Chromecast* فایرفاکس سافاری حاشیه، غیرمتمرکز
ویدئو 150 مگابایت 30 مگابایت 100 مگابایت 290 مگابایت ناشناخته
سمعی 12 مگابایت 2 مگابایت 15 مگابایت 14 مگابایت ناشناخته
  • یا دستگاه Chrome با حافظه محدود دیگر.

بنابراین چه کار کنم؟

از آنجایی که مقدار داده های پشتیبانی شده بسیار متفاوت است و شما نمی توانید مقدار داده را در یک SourceBuffer پیدا کنید، باید با مدیریت QuotaExceededError به طور غیرمستقیم آن را دریافت کنید. حال بیایید به چند روش برای انجام این کار نگاه کنیم.

چندین روش برای مقابله با QuotaExceededError وجود دارد. در واقع ترکیبی از یک یا چند رویکرد بهترین است. رویکرد شما باید این باشد که کار را بر اساس مقداری که دارید واکشی می‌کنید و تلاش می‌کنید فراتر از HTMLMediaElement.currentTime اضافه کنید و آن اندازه را بر اساس QuotaExceededError تنظیم کنید. همچنین استفاده از مانیفست‌هایی مانند فایل mpd (MPEG-DASH) یا فایل m3u8 (HLS) می‌تواند به شما در پیگیری داده‌هایی که به بافر اضافه می‌کنید کمک کند.

اکنون، بیایید به چندین روش برای مقابله با QuotaExceededError نگاه کنیم.

  • داده های غیر ضروری را حذف کرده و دوباره اضافه کنید.
  • قطعات کوچکتر را اضافه کنید.
  • وضوح پخش را کاهش دهید.

اگرچه آنها را می توان در ترکیب استفاده کرد، من آنها را یکی یکی پوشش می دهم.

داده های غیر ضروری را حذف کرده و دوباره اضافه کنید

واقعاً این یکی باید نامیده شود، "داده هایی که کمترین احتمال استفاده را دارند حذف کنید، و سپس دوباره سعی کنید داده هایی را که احتمالاً به زودی مورد استفاده قرار می گیرند اضافه کنید." این عنوان خیلی طولانی بود. شما فقط باید به یاد بیاورید که واقعاً منظور من چیست.

حذف داده های اخیر به سادگی فراخوانی SourceBuffer.remove() نیست. برای حذف داده‌ها از SourceBuffer ، پرچم به‌روزرسانی آن باید نادرست باشد. اگر اینطور نیست، قبل از حذف هر گونه داده SourceBuffer.abort() را فراخوانی کنید.

هنگام فراخوانی SourceBuffer.remove() چند نکته را باید در نظر داشت.

  • این می تواند تأثیر منفی بر پخش داشته باشد. برای مثال، اگر می‌خواهید ویدیو به‌زودی دوباره پخش شود یا حلقه شود، ممکن است نخواهید ابتدای ویدیو را حذف کنید. به همین ترتیب، اگر شما یا کاربر به دنبال بخشی از ویدیو هستید که در آن داده‌ها را حذف کرده‌اید، باید آن داده‌ها را دوباره اضافه کنید تا آن جستجو را برآورده کنید.
  • تا جایی که می توانید محافظه کارانه حذف کنید. از حذف گروه فریم های در حال پخش که در فریم کلیدی در زمان فعلی یا قبل از currentTime شروع می شوند، مراقب باشید زیرا انجام این کار می تواند باعث توقف پخش شود. اگر چنین اطلاعاتی در مانیفست موجود نباشد، ممکن است نیاز به تجزیه بایت توسط برنامه وب داشته باشد. مانیفست رسانه یا دانش برنامه از فواصل فریم‌های کلیدی در رسانه می‌تواند به راهنمایی برنامه شما در انتخاب محدوده حذف برای جلوگیری از حذف رسانه در حال پخش کمک کند. هر چیزی را که حذف می‌کنید، گروه عکس‌های در حال پخش یا حتی چند عکس اول فراتر از آن را حذف نکنید. به طور کلی، بیش از زمان فعلی حذف نکنید، مگر اینکه مطمئن باشید که دیگر به رسانه نیازی نیست. اگر نزدیک هد پخش را بردارید ممکن است باعث توقف شود.
  • Safari 9 و Safari 10 به درستی SourceBuffer.abort() را پیاده سازی نمی کنند . در واقع، آنها خطاهایی ایجاد می کنند که پخش را متوقف می کند. خوشبختانه ردیاب‌های باگ باز در اینجا و اینجا وجود دارد. در ضمن، شما باید به نحوی در این مورد کار کنید. Shaka Player این کار را با حذف یک تابع خالی 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);

وضوح پخش را کاهش دهید

این شبیه به حذف داده های اخیر و اضافه کردن مجدد است. در واقع، این دو ممکن است با هم انجام شوند، اگرچه مثال زیر فقط کاهش وضوح را نشان می دهد.

هنگام استفاده از این تکنیک باید به چند نکته توجه داشت:

  • شما باید یک بخش اولیه جدید اضافه کنید. هر زمان که نمایش ها را تغییر دادید باید این کار را انجام دهید. بخش اولیه سازی جدید باید برای بخش های رسانه ای باشد که به دنبال آن هستند.
  • مهر زمانی ارائه رسانه ضمیمه شده باید تا حد امکان با مهر زمانی داده های موجود در بافر مطابقت داشته باشد، اما نباید جلوتر بپرد. همپوشانی داده های بافر بسته به مرورگر ممکن است باعث ایجاد لکنت یا توقف کوتاه شود. صرف نظر از اینکه چه چیزی را اضافه می کنید، سر پخش را با هم تداخل ندهید زیرا این کار باعث ایجاد خطا می شود.
  • جستجو ممکن است پخش را قطع کند. ممکن است وسوسه شوید که به یک مکان خاص بگردید و پخش را از آنجا از سر بگیرید. توجه داشته باشید که این امر باعث وقفه در پخش می شود تا زمانی که جستجو کامل شود.