مقدمه
افزونههای منبع رسانه (MSE) بافرینگ و کنترل پخش گستردهتری را برای عناصر <audio> و <video> در HTML5 فراهم میکنند. اگرچه در ابتدا برای تسهیل پخش تطبیقی پویا روی پخشکنندههای ویدیویی مبتنی بر HTTP (DASH) توسعه داده شده بودند، در ادامه خواهیم دید که چگونه میتوان از آنها برای صدا استفاده کرد؛ به طور خاص برای پخش بدون فاصله (gapless playback ).
احتمالاً به یک آلبوم موسیقی گوش دادهاید که آهنگها به طور یکپارچه در میان قطعات پخش میشوند؛ حتی ممکن است همین الان در حال گوش دادن به یکی از آنها باشید. هنرمندان این تجربیات پخش بدون وقفه را هم به عنوان یک انتخاب هنری و هم به عنوان یک اثر هنری از صفحات وینیل و سیدیهایی که صدا به صورت یک جریان پیوسته نوشته شده است، خلق میکنند. متأسفانه، به دلیل نحوه کار کدکهای صوتی مدرن مانند MP3 و AAC ، این تجربه شنیداری یکپارچه امروزه اغلب از بین میرود.
در ادامه به جزئیات دلیل این موضوع خواهیم پرداخت، اما فعلاً بیایید با یک نمایش شروع کنیم. در زیر سی ثانیه اول سینتل عالی را مشاهده میکنید که به پنج فایل MP3 جداگانه تقسیم شده و با استفاده از MSE دوباره سرهمبندی شده است. خطوط قرمز نشاندهنده شکافهایی هستند که در طول ایجاد (رمزگذاری) هر MP3 ایجاد شدهاند؛ در این نقاط اشکالاتی خواهید شنید.
اه! این تجربه خیلی خوبی نیست؛ میتوانیم بهتر عمل کنیم. با کمی کار بیشتر، با استفاده از دقیقاً همان فایلهای MP3 در نسخه آزمایشی بالا، میتوانیم از MSE برای حذف آن فواصل مزاحم استفاده کنیم. خطوط سبز در نسخه آزمایشی بعدی نشان میدهند که فایلها کجا به هم متصل شدهاند و فواصل حذف شدهاند. در کروم ۳۸+، این فایل به طور یکپارچه پخش میشود!
روشهای متنوعی برای ایجاد محتوای بدون شکاف وجود دارد. برای اهداف این نسخه آزمایشی، ما بر روی نوع فایلهایی که یک کاربر عادی ممکن است در دسترس داشته باشد تمرکز خواهیم کرد. در این نوع فایلها، هر فایل بدون توجه به بخشهای صوتی قبل یا بعد از آن، به طور جداگانه کدگذاری شده است.
تنظیمات اولیه
ابتدا، بیایید به عقب برگردیم و تنظیمات اولیه یک نمونه 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 ایجاد کنیم. در مثال بالا، ما یک SourceBuffer audio/mpeg ایجاد میکنیم که قادر به تجزیه و رمزگشایی بخشهای MP3 ما است؛ چندین نوع دیگر نیز موجود است.
شکل موجهای غیرعادی
کمی بعد به کد برمیگردیم، اما حالا بیایید نگاه دقیقتری به فایلی که اخیراً پیوست کردهایم، به ویژه در انتهای آن، بیندازیم. در زیر، نموداری از ۳۰۰۰ نمونه آخر که به طور میانگین در هر دو کانال از آهنگ sintel_0.mp3 گرفته شده است، آمده است. هر پیکسل روی خط قرمز یک نمونه ممیز شناور در محدوده [-1.0, 1.0] است.

ماجرای آن همه سمپلهای صفر (بیصدا) چیست!؟ آنها در واقع به دلیل مصنوعات فشردهسازی هستند که هنگام کدگذاری ایجاد میشوند. تقریباً هر کدبندی نوعی لایهگذاری را اعمال میکند. در این مورد ، LAME دقیقاً ۵۷۶ سمپل لایهگذاری به انتهای فایل اضافه کرده است.
علاوه بر لایهگذاری در انتها، هر فایل به ابتدای آن نیز لایهگذاری شده است. اگر به آهنگ sintel_1.mp3 نگاهی بیندازیم، 576 نمونه لایهگذاری دیگر در ابتدا مشاهده خواهیم کرد. میزان لایهگذاری بسته به انکودر و محتوا متفاوت است، اما ما مقادیر دقیق را بر اساس metadata موجود در هر فایل میدانیم.

بخشهای سکوت در ابتدا و انتهای هر فایل، همان چیزی هستند که باعث ایجاد وقفه بین بخشهای دموی قبلی میشوند. برای دستیابی به پخش بدون وقفه، باید این بخشهای سکوت را حذف کنیم. خوشبختانه، این کار به راحتی با MediaSource انجام میشود. در زیر، متد onAudioLoaded() خود را اصلاح میکنیم تا از یک پنجرهی الحاق و یک آفست timestamp برای حذف این سکوت استفاده کند.
کد مثال
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 (به رنگ آبی) حذف شدهاند و یک انتقال یکپارچه بین بخشها برای ما باقی مانده است.

نتیجهگیری
با این کار، ما هر پنج بخش را به طور یکپارچه در یک بخش قرار دادیم و متعاقباً به پایان نسخه آزمایشی خود رسیدیم. قبل از ادامه، ممکن است متوجه شده باشید که متد onAudioLoaded() ما هیچ توجهی به کانتینرها یا کدکها ندارد. این بدان معناست که همه این تکنیکها صرف نظر از نوع کانتینر یا کدک کار خواهند کرد. در زیر میتوانید نسخه آزمایشی اصلی MP4 تکه تکه شده آماده DASH را به جای MP3 دوباره پخش کنید.
اگر مایلید اطلاعات بیشتری کسب کنید، برای نگاهی عمیقتر به ایجاد محتوای بدون شکاف و تجزیه فراداده، پیوستهای زیر را بررسی کنید. همچنین میتوانید gapless.js برای نگاهی دقیقتر به کدی که این نسخه آزمایشی را پشتیبانی میکند، بررسی کنید.
ممنون از خواندن!
پیوست الف: ایجاد محتوای بدون شکاف
ایجاد محتوای بدون شکاف میتواند دشوار باشد. در ادامه، نحوهی ایجاد رسانهی سینتل مورد استفاده در این نسخه آزمایشی را بررسی خواهیم کرد. برای شروع، به یک کپی از موسیقی متن سینتل با فرمت FLAC نیاز دارید؛ برای استفادههای بعدی، SHA1 در زیر گنجانده شده است. برای ابزارها، به FFmpeg ، MP4Box ، LAME و نصب OSX با afconvert نیاز دارید.
unzip Jan_Morgenstern-Sintel-FLAC.zip
sha1sum 1-Snow_Fight.flac
# 0535ca207ccba70d538f7324916a3f1a3d550194 1-Snow_Fight.flac
ابتدا، ۳۱.۵ ثانیه اول آهنگ 1-Snow_Fight.flac را جدا میکنیم. همچنین میخواهیم یک محوشدگی ۲.۵ ثانیهای که از ثانیه ۲۸ شروع میشود اضافه کنیم تا از هرگونه صدای کلیک پس از پایان پخش جلوگیری شود. با استفاده از خط فرمان FFmpeg زیر میتوانیم همه این کارها را انجام دهیم و نتایج را در sintel.flac قرار دهیم.
ffmpeg -i 1-Snow_Fight.flac -t 31.5 -af "afade=t=out:st=28:d=2.5" sintel.flac
در مرحله بعد، فایل را به ۵ فایل wave هر کدام ۶.۵ ثانیه تقسیم میکنیم؛ استفاده از wave سادهترین روش است زیرا تقریباً هر رمزگذار از دریافت آن پشتیبانی میکند. باز هم، میتوانیم این کار را دقیقاً با 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 با کیفیت بالا برای فایلهای wave استفاده خواهیم کرد.
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 تکهتکه شده بپردازیم. ما دستورالعملهای اپل را برای ایجاد رسانهای که برای iTunes مستر شده است، دنبال خواهیم کرد. در زیر، فایلهای wave را طبق دستورالعملها به فایلهای 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 را با فرادادههای صحیح لازم برای پخش بدون فاصله، قطعه قطعه کردهایم. برای جزئیات بیشتر در مورد شکل ظاهری این فرادادهها، به پیوست B مراجعه کنید.
پیوست ب: تجزیه فراداده بدون شکاف
درست مانند ایجاد محتوای بدون شکاف، تجزیه فرادادههای بدون شکاف میتواند دشوار باشد زیرا هیچ روش استانداردی برای ذخیرهسازی وجود ندارد. در زیر نحوه ذخیره فرادادههای بدون شکاف توسط دو رمزگذار رایج، 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 اپل میپردازیم، زیرا تجزیه و توضیح آن آسانترین است. در فایلهای MP3 و M4A، iTunes (و afconvert) یک بخش کوتاه به صورت ASCII مانند زیر بنویسید:
iTunSMPB[ 26 bytes ]0000000 00000840 000001C0 0000000000046E00
این درون یک تگ ID3 درون محفظه MP3 و درون یک اتم فراداده درون محفظه MP4 نوشته شده است. برای اهداف ما، میتوانیم اولین توکن 0000000 را نادیده بگیریم. سه توکن بعدی عبارتند از front padding، end padding و total non-padding sample count. تقسیم هر یک از اینها بر نرخ نمونهبرداری صدا، مدت زمان هر کدام را به ما میدهد.
// 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 معمولی نیز ساخت. هدرهای Xing را میتوان با برچسب Xing یا Info علامتگذاری کرد. دقیقاً ۴ بایت بعد از این برچسب، ۳۲ بیت وجود دارد که تعداد کل فریمهای موجود در فایل را نشان میدهد. ضرب این مقدار در تعداد نمونهها در هر فریم، کل نمونههای موجود در فایل را به ما میدهد.
// 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 قرار دارد، نوشته شود. دقیقاً ۱۷ بایت بعد از این هدر، ۳ بایت وجود دارد که به ترتیب نشاندهنده لایهگذاری جلویی و انتهایی ۱۲ بیتی هستند.
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 به طور فعال بر اساس نوع محتوا، محدودیتهای خاص پلتفرم و موقعیت پخش فعلی، زبالهزدایی میشود. در کروم، ابتدا حافظه از بافرهای از قبل پخش شده بازیابی میشود. با این حال، اگر استفاده از حافظه از محدودیتهای خاص پلتفرم فراتر رود، حافظه از بافرهای پخش نشده حذف میشود.
وقتی پخش به دلیل اشغال حافظه به یک وقفه در خط زمانی میرسد، اگر این وقفه به اندازه کافی کوچک باشد، ممکن است دچار مشکل شود یا اگر این وقفه خیلی بزرگ باشد، ممکن است به طور کامل متوقف شود. هیچکدام از این دو، تجربه کاربری خوبی ایجاد نمیکنند، بنابراین مهم است که از افزودن دادههای زیاد به طور همزمان خودداری کنید و محدودههایی را که دیگر ضروری نیستند، به صورت دستی از خط زمانی رسانه حذف کنید.
محدودهها را میتوان از طریق متد remove() در هر SourceBuffer حذف کرد؛ که یک محدوده [start, end] را بر حسب ثانیه میگیرد. مشابه appendBuffer() ، هر remove() پس از اتمام، یک رویداد updateend اجرا میکند. سایر حذفها یا اضافهها نباید تا زمان وقوع رویداد اجرا شوند.
در کروم دسکتاپ، میتوانید تقریباً ۱۲ مگابایت محتوای صوتی و ۱۵۰ مگابایت محتوای ویدیویی را به طور همزمان در حافظه نگه دارید. نباید به این مقادیر در مرورگرها یا پلتفرمهای مختلف تکیه کنید؛ مثلاً، آنها مطمئناً نماینده دستگاههای تلفن همراه نیستند.
جمعآوری زباله فقط روی دادههای اضافه شده به SourceBuffers تأثیر میگذارد؛ هیچ محدودیتی در مورد میزان دادهای که میتوانید در متغیرهای جاوا اسکریپت در بافر نگه دارید وجود ندارد. همچنین میتوانید در صورت لزوم، همان دادهها را در همان موقعیت دوباره اضافه کنید.