إضافات مصدر الوسائط للصوت

Dale Curtis
Dale Curtis

مقدمة

توفّر إضافات مصادر الوسائط (MSE) إمكانية التخزين المؤقت الممتد والتحكم في التشغيل للعناصر <audio> و<video> في HTML5. وقد تم تطويره في الأصل لتسهيل استخدام مشغّلات الفيديو المستندة إلى البث الديناميكي التكيُّفي عبر HTTP (DASH)، ولكن سنوضّح أدناه كيفية استخدامها للصوت، لا سيّما للتشغيل بدون فجوات.

من المحتمل أنّك استمعت إلى ألبوم موسيقي استمِع فيه إلى الأغاني بسلاسة على مختلف المقاطع. ومن المحتمل أنّك تستمع إلى ألبوم موسيقي الآن. يقدّم الفنانون تجربة تشغيل سلسة كخيار فني وجزء من أسطوانات الفينيل والأقراص المدمجة حيث تمت كتابة الصوت في بث متواصل. بسبب الطريقة التي تعمل بها برامج ترميز الصوت الحديثة مثل MP3 وAAC، غالبًا ما يتم فقدان هذه التجربة السمعية السلسة في الوقت الحالي.

سندخل في تفاصيل السبب أدناه، ولكن في الوقت الحالي، سنبدأ بشرح توضيحي. ستجد أدناه أول ثلاثين ثانية من ملف Sintelالممتاز ومقسم إلى خمسة ملفات MP3 منفصلة ثم يُعاد تجميعه باستخدام الخطأ التربيعي المتوسط. تشير الخطوط الحمراء إلى الفجوات التي حدثت أثناء إنشاء (الترميز) لكل ملف MP3؛ وستسمع مواطن خلل في هذه النقاط.

العرض التوضيحي

يا للهول! لم تكن هذه تجربة رائعة، بل يمكننا تقديم ما هو أفضل منها. بعد بذل مجهود أكبر، وباستخدام ملفات MP3 نفسها المتوفّرة في الإصدار التجريبي أعلاه، يمكننا استخدام الخطأ التربيعي المتوسط لإزالة هذه الثغرات المزعجة. تشير الخطوط الخضراء في العرض التوضيحي التالي إلى مكان ضم الملفات وإزالة الفجوات. سيتم تشغيل هذا التحديث بسلاسة على الإصدار 38 من Chrome والإصدارات الأحدث.

العرض التوضيحي

تتوفر مجموعة متنوعة من الطرق لإنشاء محتوى بلا حدود. لأغراض هذا العرض التوضيحي، سنركّز على نوع الملفات التي قد يكون المستخدم العادي محفوظًا فيها. يشير إلى ترميز كل ملف بشكل منفصل بدون مراعاة المقاطع الصوتية قبله أو بعده.

الإعداد الأساسي

لنبدأ أولاً بالعودة إلى موضع سابق ونتناول خطوات الإعداد الأساسية لمثيل MediaSource. إضافات مصدر الوسائط، كما هو واضح من الاسم، هي مجرد إضافات لعناصر الوسائط الحالية. نحدّد أدناه Object URL، الذي يمثّل مثيل MediaSource، لسمة المصدر لعنصر صوتي، تمامًا كما تضبط عنوان URL عاديًا.

var audio = document.createElement('audio');
var mediaSource = new MediaSource();
var SEGMENTS = 5;

mediaSource.addEventListener('sourceopen', function() {
    var sourceBuffer = mediaSource.addSourceBuffer('audio/mpeg');

    function onAudioLoaded(data, index) {
    // Append the ArrayBuffer data into our new SourceBuffer.
    sourceBuffer.appendBuffer(data);
    }

    // Retrieve an audio segment via XHR.  For simplicity, we're retrieving the
    // entire segment at once, but we could also retrieve it in chunks and append
    // each chunk separately.  MSE will take care of assembling the pieces.
    GET('sintel/sintel_0.mp3', function(data) { onAudioLoaded(data, 0); } );
});

audio.src = URL.createObjectURL(mediaSource);

بعد ربط كائن MediaSource، سيتم إجراء عملية الإعداد وتنشيط حدث sourceopen في النهاية. وعندها يمكننا إنشاء SourceBuffer. في المثال أعلاه، ننشئ شريحة audio/mpeg يمكنها تحليل وفك ترميز مقاطع MP3، وتتوفّر عدّة أنواع أخرى.

الأشكال الموجية الشاذة

سنعود إلى التعليمة البرمجية بعد لحظات، لكن لنلقِ نظرة الآن عن كثب على الملف الذي ألحقناه للتو، وعلى وجه التحديد في نهايته. وفي ما يلي رسم بياني لآخر 3, 000 عيّنة تم حساب متوسطها على مستوى القناتين من مسار sintel_0.mp3. كل وحدة بكسل على الخط الأحمر هي عينة من نقطة عائمة في نطاق [-1.0, 1.0].

نهاية sintel_0.mp3

ما مغزى تلك العينات الصفرية (الصامتة)؟ أنها ترجع في الواقع إلى عناصر الضغط التي تم تقديمها أثناء عملية الترميز. يقدّم كل برنامج ترميز تقريبًا نوعًا من المساحة المتروكة. في هذه الحالة، أضاف LAME 576 عينة مساحة متروكة بالضبط إلى نهاية الملف.

بالإضافة إلى المساحة المتروكة في النهاية، تمت أيضًا إضافة مساحة متروكة في البداية في كل ملف. إذا ألقينا نظرة خاطفة على المسار sintel_1.mp3، سنرى 576 عينة أخرى من المساحة المتروكة في المقدّمة. يختلف مقدار المساحة المتروكة حسب برنامج الترميز والمحتوى، ولكننا نعرف القيم الدقيقة بناءً على metadata المضمّنة في كل ملف.

بداية sintel_1.mp3

بداية sintel_1.mp3

أقسام الصمت في بداية ونهاية كل ملف هي ما تسبب في حدوث أعطال بين المقاطع في العرض التوضيحي السابق. لتشغيل المحتوى بلا حدود، يجب إزالة أجزاء محدّدة من كتم الصوت. لحسن الحظ، يمكن تنفيذ هذا الإجراء بسهولة باستخدام "MediaSource". سنُعدّل أدناه طريقة onAudioLoaded() لاستخدام نافذة إضافة ومعادلة طابع زمني لإزالة هذا الصمت.

مثال التعليمة البرمجية

function onAudioLoaded(data, index) {
    // Parsing gapless metadata is unfortunately non trivial and a bit messy, so
    // we'll glaze over it here; see the appendix for details.
    // ParseGaplessData() will return a dictionary with two elements:
    //
    //    audioDuration: Duration in seconds of all non-padding audio.
    //    frontPaddingDuration: Duration in seconds of the front padding.
    //
    var gaplessMetadata = ParseGaplessData(data);

    // Each appended segment must be appended relative to the next.  To avoid any
    // overlaps, we'll use the end timestamp of the last append as the starting
    // point for our next append or zero if we haven't appended anything yet.
    var appendTime = index > 0 ? sourceBuffer.buffered.end(0) : 0;

    // Simply put, an append window allows you to trim off audio (or video) frames
    // which fall outside of a specified time range.  Here, we'll use the end of
    // our last append as the start of our append window and the end of the real
    // audio data for this segment as the end of our append window.
    sourceBuffer.appendWindowStart = appendTime;
    sourceBuffer.appendWindowEnd = appendTime + gaplessMetadata.audioDuration;

    // The timestampOffset field essentially tells MediaSource where in the media
    // timeline the data given to appendBuffer() should be placed.  I.e., if the
    // timestampOffset is 1 second, the appended data will start 1 second into
    // playback.
    //
    // MediaSource requires that the media timeline starts from time zero, so we
    // need to ensure that the data left after filtering by the append window
    // starts at time zero.  We'll do this by shifting all of the padding we want
    // to discard before our append time (and thus, before our append window).
    sourceBuffer.timestampOffset =
        appendTime - gaplessMetadata.frontPaddingDuration;

    // When appendBuffer() completes, it will fire an updateend event signaling
    // that it's okay to append another segment of media.  Here, we'll chain the
    // append for the next segment to the completion of our current append.
    if (index == 0) {
    sourceBuffer.addEventListener('updateend', function() {
        if (++index < SEGMENTS) {
        GET('sintel/sintel_' + index + '.mp3',
            function(data) { onAudioLoaded(data, index); });
        } else {
        // We've loaded all available segments, so tell MediaSource there are no
        // more buffers which will be appended.
        mediaSource.endOfStream();
        URL.revokeObjectURL(audio.src);
        }
    });
    }

    // appendBuffer() will now use the timestamp offset and append window settings
    // to filter and timestamp the data we're appending.
    //
    // Note: While this demo uses very little memory, more complex use cases need
    // to be careful about memory usage or garbage collection may remove ranges of
    // media in unexpected places.
    sourceBuffer.appendBuffer(data);
}

شكل موجي سلس

لنرى ما حققه الرمز البرمجي الجديد من خلال إلقاء نظرة أخرى على الشكل الموجي بعد تطبيق نوافذ الإضافة. يظهر أدناه القسم الصامت في نهاية sintel_0.mp3 (باللون الأحمر) والقسم الصامت في بداية sintel_1.mp3 (باللون الأزرق) قد تمت إزالته، ما يوفّر لنا انتقالاً سلسًا بين المقاطع.

ضم sintel_0.mp3 وsintel_1.mp3

الخلاصة

من هذا المنطلق، انتهينا من ربط جميع الأقسام الخمسة بسلاسة مترابطة في شريحة واحدة، وبالتالي وصلنا إلى نهاية العرض التوضيحي. قبل بدء هذا التعديل، ربما لاحظت أنّ طريقة onAudioLoaded() لا تأخذ في الاعتبار الحاويات أو برامج الترميز. بتعبير آخر، ستعمل كل هذه الأساليب بغض النظر عن نوع الحاوية أو برنامج الترميز. يمكنك أدناه إعادة تشغيل تنسيق MP4 الأصلي المجزأ والجاهز لاستخدام DASH بدلاً من MP3.

العرض التوضيحي

للاطّلاع على المزيد من المعلومات، يُرجى مراجعة الملحقات أدناه للاطّلاع على معلومات مفصّلة حول إنشاء المحتوى وتحليل البيانات الوصفية بسهولة. يمكنك أيضًا استكشاف gapless.js لإلقاء نظرة فاحصة على الرمز الذي يوفّر هذا العرض التوضيحي.

شكرًا على القراءة.

الملحق أ: إنشاء محتوى بلا حدود

قد يكون من الصعب إنشاء محتوى بلا حدود. سنشرح أدناه كيفية إنشاء وسائط Sintel المستخدمة في هذا العرض التوضيحي. للبدء، يجب الحصول على نسخة من مقاطع صوتية بتنسيق FLAC بدون فقدان بيانات لتقنية Sintel، لقد تكون خوارزمية SHA1 مضمّنة أدناه بالنسبة إلى الأجيال القادمة. بالنسبة إلى الأدوات، ستحتاج إلى تثبيت FFmpeg وMP4Box وLAME، وتثبيت نظام التشغيل OSX مع afconversion.

unzip Jan_Morgenstern-Sintel-FLAC.zip
sha1sum 1-Snow_Fight.flac
# 0535ca207ccba70d538f7324916a3f1a3d550194  1-Snow_Fight.flac

أولاً، سنقسّم أول 31.5 ثانية من المقطع الصوتي 1-Snow_Fight.flac. نريد أيضًا إضافة تأثير التلاشي للخارج لمدة 2.5 ثانية بدءًا من 28 ثانية لتجنب حدوث أي نقرات بعد انتهاء التشغيل. باستخدام سطر الأوامر FFmpeg أدناه، يمكننا تنفيذ كل ذلك ووضع النتائج في sintel.flac.

ffmpeg -i 1-Snow_Fight.flac -t 31.5 -af "afade=t=out:st=28:d=2.5" sintel.flac

بعد ذلك، سنقسّم الملف إلى 5 ملفات موجة تبلغ مدة كل ملف منها 6.5 ثانية، ومن الأسهل استخدام الموجات لأنّ كل برنامج ترميز تقريبًا يتيح عرض هذا الملف. مرة أخرى، يمكننا تنفيذ هذا الإجراء تحديدًا باستخدام FFmpeg، وبعد ذلك سيكون لدينا: sintel_0.wav وsintel_1.wav وsintel_2.wav وsintel_3.wav وsintel_4.wav.

ffmpeg -i sintel.flac -acodec pcm_f32le -map 0 -f segment \
        -segment_list out.list -segment_time 6.5 sintel_%d.wav

بعد ذلك، لنقم بإنشاء ملفات MP3. يوفّر LAME خيارات متعدّدة لإنشاء محتوى بلا فجوات. إذا كنت تتحكّم في المحتوى، يمكنك استخدام --nogap مع ترميز مُجمَّع لجميع الملفات لتجنُّب إضافة مساحة متروكة بين المقاطع تمامًا. ولأغراض هذا العرض التوضيحي، نريد هذه المساحة المتروكة، لذا سنستخدم ترميز VBR عادي عالي الجودة لملفات الموجات.

lame -V=2 sintel_0.wav sintel_0.mp3
lame -V=2 sintel_1.wav sintel_1.mp3
lame -V=2 sintel_2.wav sintel_2.mp3
lame -V=2 sintel_3.wav sintel_3.mp3
lame -V=2 sintel_4.wav sintel_4.mp3

هذا كل ما هو ضروري لإنشاء ملفات MP3. والآن، في ما يتعلق بإنشاء ملفات MP4 المجزأة. وسنتّبع توجيهات Apple لإنشاء وسائط متقنة الاستخدام على iTunes. سنحوّل أدناه ملفات الموجات إلى ملفات CAF متوسطة، وفقًا للتعليمات، قبل ترميزها كملفات AAC في حاوية MP4 باستخدام المَعلمات المقترَحة.

afconvert sintel_0.wav sintel_0_intermediate.caf -d 0 -f caff \
            --soundcheck-generate
afconvert sintel_1.wav sintel_1_intermediate.caf -d 0 -f caff \
            --soundcheck-generate
afconvert sintel_2.wav sintel_2_intermediate.caf -d 0 -f caff \
            --soundcheck-generate
afconvert sintel_3.wav sintel_3_intermediate.caf -d 0 -f caff \
            --soundcheck-generate
afconvert sintel_4.wav sintel_4_intermediate.caf -d 0 -f caff \
            --soundcheck-generate
afconvert sintel_0_intermediate.caf -d aac -f m4af -u pgcm 2 --soundcheck-read \
            -b 256000 -q 127 -s 2 sintel_0.m4a
afconvert sintel_1_intermediate.caf -d aac -f m4af -u pgcm 2 --soundcheck-read \
            -b 256000 -q 127 -s 2 sintel_1.m4a
afconvert sintel_2_intermediate.caf -d aac -f m4af -u pgcm 2 --soundcheck-read \
            -b 256000 -q 127 -s 2 sintel_2.m4a
afconvert sintel_3_intermediate.caf -d aac -f m4af -u pgcm 2 --soundcheck-read \
            -b 256000 -q 127 -s 2 sintel_3.m4a
afconvert sintel_4_intermediate.caf -d aac -f m4af -u pgcm 2 --soundcheck-read \
            -b 256000 -q 127 -s 2 sintel_4.m4a

لدينا الآن العديد من ملفات M4A التي نحتاج إلى تقسيمها بشكل مناسب قبل استخدامها مع MediaSource. لأغراضنا، سنستخدم حجم تجزئة يبلغ ثانية واحدة. سيكتب MP4Box كل ملفات MP4 مجزأة على النحو sintel_#_dashinit.mp4، بالإضافة إلى بيان MPEG-DASH (sintel_#_dash.mpd) الذي يمكن تجاهله.

MP4Box -dash 1000 sintel_0.m4a && mv sintel_0_dashinit.mp4 sintel_0.mp4
MP4Box -dash 1000 sintel_1.m4a && mv sintel_1_dashinit.mp4 sintel_1.mp4
MP4Box -dash 1000 sintel_2.m4a && mv sintel_2_dashinit.mp4 sintel_2.mp4
MP4Box -dash 1000 sintel_3.m4a && mv sintel_3_dashinit.mp4 sintel_3.mp4
MP4Box -dash 1000 sintel_4.m4a && mv sintel_4_dashinit.mp4 sintel_4.mp4
rm sintel_{0,1,2,3,4}_dash.mpd

أكملت هذه الخطوة. أصبح لدينا الآن ملفات MP4 وMP3 مجزّأة مع تضمين البيانات الوصفية الصحيحة اللازمة لتشغيل المحتوى بلا فجوات. راجِع الملحق (ب) للحصول على مزيد من التفاصيل حول شكل تلك البيانات الوصفية.

الملحق ب: تحليل البيانات الوصفية غير المتضمّنة ثغرات

تمامًا مثل إنشاء محتوى خالٍ من الأخطاء، قد يكون تحليل البيانات الوصفية الخالية من الأخطاء أمرًا صعبًا نظرًا لعدم وجود طريقة قياسية للتخزين. في ما يلي طريقة تخزين البيانات الوصفية السهلة الاستخدام من قِبل برنامجَي الترميز LAME وiTunes. لنبدأ بإعداد بعض طرق المساعدة ومخطط ParseGaplessData() المستخدَم أعلاه.

// Since most MP3 encoders store the gapless metadata in binary, we'll need a
// method for turning bytes into integers.  Note: This doesn't work for values
// larger than 2^30 since we'll overflow the signed integer type when shifting.
function ReadInt(buffer) {
    var result = buffer.charCodeAt(0);
    for (var i = 1; i < buffer.length; ++i) {
    result <<../= 8;
    result += buffer.charCodeAt(i);
    }
    return result;
}

function ParseGaplessData(arrayBuffer) {
    // Gapless data is generally within the first 512 bytes, so limit parsing.
    var byteStr = new TextDecoder().decode(arrayBuffer.slice(0, 512));

    var frontPadding = 0, endPadding = 0, realSamples = 0;

    // ... we'll fill this in as we go below.

سنتناول أولاً تنسيق البيانات الوصفية على iTunes من Apple، إذ يسهل تحليلها وشرحها. في ملفات MP3 وM4A، يمكنك كتابة مقطع قصير بتنسيق ASCII على النحو التالي:

iTunSMPB[ 26 bytes ]0000000 00000840 000001C0 0000000000046E00

هذا الرمز مكتوب داخل علامة ID3 داخل حاوية MP3 وفي جزء البيانات الوصفية في حاوية MP4. لأغراضنا، يمكننا تجاهل أول رمز 0000000 مميز. الرموز المميزة الثلاثة التالية هي المساحة المتروكة الأمامية والمساحة المتروكة في النهاية وإجمالي عدد العينات غير المتروكة. من خلال قسمة كلّ من هذه المصادر على معدّل العينة الصوتي، يمكننا الحصول على المدة الخاصة بكل منها.

// iTunes encodes the gapless data as hex strings like so:
//
//    'iTunSMPB[ 26 bytes ]0000000 00000840 000001C0 0000000000046E00'
//    'iTunSMPB[ 26 bytes ]####### frontpad  endpad    real samples'
//
// The approach here elides the complexity of actually parsing MP4 atoms. It
// may not work for all files without some tweaks.
var iTunesDataIndex = byteStr.indexOf('iTunSMPB');
if (iTunesDataIndex != -1) {
    var frontPaddingIndex = iTunesDataIndex + 34;
    frontPadding = parseInt(byteStr.substr(frontPaddingIndex, 8), 16);

    var endPaddingIndex = frontPaddingIndex + 9;
    endPadding = parseInt(byteStr.substr(endPaddingIndex, 8), 16);

    var sampleCountIndex = endPaddingIndex + 9;
    realSamples = parseInt(byteStr.substr(sampleCountIndex, 16), 16);
}

على الجانب الآخر، ستخزن معظم برامج ترميز MP3 مفتوحة المصدر البيانات الوصفية الخالية من الأخطاء داخل عنوان Xing خاص وموضوع داخل إطار MPEG صامت (صامت بحيث تعمل برامج فك الترميز التي لا تفهم رأس Xing على كتم الصوت). للأسف، لا تتوفر هذه العلامة دائمًا وتحتوي على عدد من الحقول الاختيارية. لأغراض هذا العرض التوضيحي، يمكننا التحكّم في الوسائط، إلا أنّه من الناحية العملية، يجب إجراء بعض عمليات التحقّق الإضافية لمعرفة متى تكون البيانات الوصفية خالية من أي فجوات.

أولاً سنقوم بتحليل إجمالي عدد العينات. ولتبسيط الأمر، سنقرأ هذا من رأس Xing، ولكن يمكن إنشاؤه من رأس MPEG audio العادي. يمكن وضع علامة Xing أو Info على عناوين Xing. بعد هذه العلامة بالضبط، يوجد 32 بت تمثل العدد الإجمالي للإطارات في الملف؛ وضرب هذه القيمة في عدد العينات لكل إطار سوف يعطينا إجمالي العينات في الملف.

// Xing padding is encoded as 24bits within the header.  Note: This code will
// only work for Layer3 Version 1 and Layer2 MP3 files with XING frame counts
// and gapless information.  See the following document for more details:
// http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
var xingDataIndex = byteStr.indexOf('Xing');
if (xingDataIndex == -1) xingDataIndex = byteStr.indexOf('Info');
if (xingDataIndex != -1) {
    // See section 2.3.1 in the link above for the specifics on parsing the Xing
    // frame count.
    var frameCountIndex = xingDataIndex + 8;
    var frameCount = ReadInt(byteStr.substr(frameCountIndex, 4));

    // For Layer3 Version 1 and Layer2 there are 1152 samples per frame.  See
    // section 2.1.5 in the link above for more details.
    var paddedSamples = frameCount * 1152;

    // ... we'll cover this below.

الآن بعد أن أصبح لدينا العدد الإجمالي للعينات، يمكننا الانتقال إلى قراءة عدد العينات المتروكة. وحسب برنامج الترميز الذي تستخدمه، يمكن كتابة هذه العملية ضمن علامة LAME أو Lavf المضمّنة في عنوان Xing. بعد هذا العنوان بالضبط، هناك 3 بايت تمثل المساحة المتروكة الأمامية والنهاية في 12 بت لكل منهما على التوالي.

xingDataIndex = byteStr.indexOf('LAME');
if (xingDataIndex == -1) xingDataIndex = byteStr.indexOf('Lavf');
if (xingDataIndex != -1) {
    // See http://gabriel.mp3-tech.org/mp3infotag.html#delays for details of
    // how this information is encoded and parsed.
    var gaplessDataIndex = xingDataIndex + 21;
    var gaplessBits = ReadInt(byteStr.substr(gaplessDataIndex, 3));

    // Upper 12 bits are the front padding, lower are the end padding.
    frontPadding = gaplessBits >> 12;
    endPadding = gaplessBits & 0xFFF;
}

realSamples = paddedSamples - (frontPadding + endPadding);
}

return {
audioDuration: realSamples * SECONDS_PER_SAMPLE,
frontPaddingDuration: frontPadding * SECONDS_PER_SAMPLE
};
}

وبالتالي، لدينا وظيفة كاملة لتحليل الغالبية العظمى من المحتوى الخالي من الأخطاء. تجدر الإشارة إلى أنّ الحالات الحدّية كثيرة بالتأكيد، لذا ننصح بتوخي الحذر قبل استخدام رمز مشابه في مرحلة الإنتاج.

الملحق (ج): حول جمع القمامة

إنّ الذاكرة الخاصة بمثيلات SourceBuffer يتم جمع بيانات غير مهمة بشكل نشط وفقًا لنوع المحتوى والحدود الخاصة بالنظام الأساسي وموضع التشغيل الحالي. في Chrome، سيتم أولاً استعادة الذاكرة من المخازن المؤقتة التي سبق تشغيلها. ومع ذلك، إذا تجاوز استخدام الذاكرة الحدود الخاصة بالنظام الأساسي، ستتم إزالة الذاكرة من المخازن الاحتياطية التي لم يتم تشغيلها.

عند وصول التشغيل إلى فجوة في المخطط الزمني بسبب استعادة الذاكرة، قد يحدث خلل في حال كانت الفجوة صغيرة بما يكفي أو قد تتوقف تمامًا إذا كانت الفجوة كبيرة جدًا. ولا تقدِّم تجربة مستخدم رائعة أيضًا، لذا من المهم تجنُّب إضافة الكثير من البيانات في آنٍ واحد وإزالة النطاقات التي لم تعُد ضرورية من المخطط الزمني للوسائط يدويًا.

يمكن إزالة النطاقات باستخدام طريقة remove() في كل SourceBuffer، ما يستغرق نطاق [start, end] بالثواني. على غرار appendBuffer()، سينشط كل remove() حدث updateend بعد اكتماله. يجب عدم إصدار عمليات الإزالة أو الملحقات الأخرى إلى أن يبدأ الحدث.

في متصفح Chrome على جهاز سطح المكتب، يمكنك الاحتفاظ بحوالي 12 ميغابايت من المحتوى الصوتي و150 ميغابايت من محتوى الفيديو في الذاكرة في آنٍ واحد. لا ينبغي لك الاعتماد على هذه القيم عبر المتصفحات أو الأنظمة الأساسية؛ على سبيل المثال، إنها بالتأكيد لا تمثل الأجهزة الجوّالة.

لا تؤثّر عملية جمع البيانات المهملة إلا في البيانات التي تتم إضافتها إلى SourceBuffers، وما مِن حدود لمقدار البيانات التي يمكنك الاحتفاظ بها في متغيّرات JavaScript. يمكنك أيضًا إعادة إلحاق نفس البيانات في نفس الموضع إذا لزم الأمر.