Tiện ích nguồn phương tiện cho âm thanh

Dale Curtis
Dale Curtis

Giới thiệu

Tiện ích nguồn nội dung đa phương tiện (MSE) cung cấp tính năng lưu vào bộ đệm và điều khiển chế độ phát mở rộng cho các phần tử <audio><video> của HTML5. Mặc dù ban đầu được phát triển để hỗ trợ trình phát video dựa trên công nghệ Truyền phát thích ứng động qua HTTP (DASH), nhưng ở phần dưới đây, chúng ta sẽ xem cách sử dụng các trình phát này cho âm thanh; cụ thể là để phát không gián đoạn.

Có thể bạn đã nghe một đĩa nhạc mà các bài hát chuyển đổi liền mạch giữa các bản nhạc; thậm chí có thể bạn đang nghe một đĩa nhạc như vậy ngay lúc này. Nghệ sĩ tạo ra trải nghiệm phát không gián đoạn này vừa là một lựa chọn nghệ thuật vừa là một cấu phần phần mềm của đĩa thanđĩa CD, trong đó âm thanh được ghi dưới dạng một luồng liên tục. Tuy nhiên, do cách hoạt động của các bộ mã hoá và giải mã âm thanh hiện đại như MP3AAC, trải nghiệm âm thanh liền mạch này thường bị mất đi ngày nay.

Chúng ta sẽ tìm hiểu chi tiết về lý do ở bên dưới, nhưng hiện tại, hãy bắt đầu bằng một minh hoạ. Dưới đây là 30 giây đầu tiên của bộ phim Sintel tuyệt vời, được cắt thành 5 tệp MP3 riêng biệt và được kết hợp lại bằng MSE. Các đường màu đỏ cho biết khoảng trống xuất hiện trong quá trình tạo (mã hoá) từng tệp MP3; bạn sẽ nghe thấy các sự cố ở những điểm này.

Bản minh hoạ

Ôi trời! Đó không phải là trải nghiệm tốt và chúng tôi có thể làm tốt hơn. Với một chút công sức, sử dụng chính xác các tệp MP3 trong bản minh hoạ trên, chúng ta có thể sử dụng MSE để xoá những khoảng trống gây phiền toái đó. Các đường màu xanh lục trong bản minh hoạ tiếp theo cho biết vị trí các tệp đã được nối và khoảng trống đã bị xoá. Trên Chrome 38 trở lên, video sẽ phát liền mạch!

Bản minh hoạ

nhiều cách để tạo nội dung liền mạch. Đối với mục đích của bản minh hoạ này, chúng ta sẽ tập trung vào loại tệp mà người dùng thông thường có thể có. Trong đó, mỗi tệp được mã hoá riêng biệt mà không quan tâm đến các đoạn âm thanh trước hoặc sau tệp đó.

Thiết lập cơ bản

Trước tiên, hãy quay lại và tìm hiểu cách thiết lập cơ bản của một thực thể MediaSource. Như tên gọi, Tiện ích nguồn nội dung nghe nhìn chỉ là tiện ích mở rộng cho các phần tử nội dung nghe nhìn hiện có. Dưới đây, chúng ta sẽ chỉ định Object URL, đại diện cho thực thể MediaSource, cho thuộc tính nguồn của một phần tử âm thanh; giống như cách bạn đặt URL chuẩn.

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

Sau khi đối tượng MediaSource được kết nối, đối tượng này sẽ thực hiện một số thao tác khởi chạy và cuối cùng sẽ kích hoạt một sự kiện sourceopen; tại thời điểm đó, chúng ta có thể tạo một SourceBuffer. Trong ví dụ trên, chúng ta đang tạo một audio/mpeg có thể phân tích cú pháp và giải mã các phân đoạn MP3; có một số loại khác.

Dạng sóng bất thường

Chúng ta sẽ quay lại đoạn mã này sau, nhưng bây giờ, hãy xem xét kỹ hơn tệp mà chúng ta vừa thêm vào, cụ thể là phần cuối của tệp. Dưới đây là biểu đồ của 3000 mẫu gần đây nhất được tính trung bình trên cả hai kênh của bản nhạc sintel_0.mp3. Mỗi pixel trên đường màu đỏ là một mẫu dấu phẩy động trong phạm vi [-1.0, 1.0].

Kết thúc sintel_0.mp3

Những mẫu âm thanh bằng 0 (âm thanh im lặng) đó có ý nghĩa gì vậy!? Thực ra, chúng là do các hiện tượng nén xuất hiện trong quá trình mã hoá. Hầu hết mọi bộ mã hoá đều đưa ra một số loại khoảng đệm. Trong trường hợp này, LAME đã thêm đúng 576 mẫu khoảng đệm vào cuối tệp.

Ngoài khoảng đệm ở cuối, mỗi tệp cũng có khoảng đệm được thêm vào đầu. Nếu xem trước kênh sintel_1.mp3, chúng ta sẽ thấy 576 mẫu khoảng đệm khác ở phía trước. Lượng dữ liệu đệm sẽ khác nhau tuỳ theo bộ mã hoá và nội dung, nhưng chúng ta biết được giá trị chính xác dựa trên metadata có trong mỗi tệp.

Đầu tệp sintel_1.mp3

Đầu tệp sintel_1.mp3

Các phần im lặng ở đầu và cuối mỗi tệp là nguyên nhân gây ra lỗi giữa các phân đoạn trong bản minh hoạ trước. Để đạt được chế độ phát không gián đoạn, chúng ta cần xoá những đoạn im lặng này. May mắn là bạn có thể dễ dàng thực hiện việc này bằng MediaSource. Dưới đây, chúng ta sẽ sửa đổi phương thức onAudioLoaded() để sử dụng cửa sổ nốichênh lệch dấu thời gian để loại bỏ khoảng lặng này.

Mã ví dụ

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

Dạng sóng liền mạch

Hãy xem mã mới của chúng ta đã làm được gì bằng cách xem lại dạng sóng sau khi áp dụng cửa sổ nối. Dưới đây, bạn có thể thấy phần không có âm thanh ở cuối sintel_0.mp3 (màu đỏ) và phần không có âm thanh ở đầu sintel_1.mp3 (màu xanh dương) đã bị xoá; giúp chúng ta có được quá trình chuyển đổi liền mạch giữa các phân đoạn.

Kết hợp sintel_0.mp3 và sintel_1.mp3

Kết luận

Như vậy, chúng ta đã ghép liền mạch cả 5 phân đoạn thành một và kết thúc phần minh hoạ. Trước khi kết thúc, bạn có thể nhận thấy rằng phương thức onAudioLoaded() của chúng ta không xem xét các vùng chứa hoặc bộ mã hoá và giải mã. Điều đó có nghĩa là tất cả các kỹ thuật này sẽ hoạt động bất kể loại vùng chứa hoặc bộ mã hoá và giải mã. Dưới đây, bạn có thể phát lại bản minh hoạ gốc ở định dạng MP4 phân đoạn, sẵn sàng cho DASH thay vì MP3.

Bản minh hoạ

Nếu bạn muốn tìm hiểu thêm, hãy xem các phụ lục bên dưới để hiểu rõ hơn về việc tạo nội dung liền mạch và phân tích cú pháp siêu dữ liệu. Bạn cũng có thể khám phá gapless.js để xem xét kỹ hơn mã nguồn hỗ trợ bản minh hoạ này.

Cảm ơn bạn đã đọc!

Phụ lục A: Tạo nội dung liền mạch

Việc tạo nội dung liền mạch có thể rất khó. Dưới đây, chúng ta sẽ hướng dẫn cách tạo nội dung nghe nhìn Sintel dùng trong bản minh hoạ này. Để bắt đầu, bạn cần có một bản sao của nhạc nền FLAC không tổn hao cho Sintel; để lưu truyền cho hậu thế, SHA1 được đưa vào bên dưới. Đối với các công cụ, bạn sẽ cần FFmpeg, MP4Box, LAME và một bản cài đặt OSX có afconvert.

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

Trước tiên, chúng ta sẽ tách 31,5 giây đầu tiên của bản nhạc 1-Snow_Fight.flac. Chúng ta cũng muốn thêm hiệu ứng mờ dần trong 2,5 giây bắt đầu từ giây thứ 28 để tránh mọi lượt nhấp sau khi phát xong. Bằng cách sử dụng dòng lệnh FFmpeg bên dưới, chúng ta có thể hoàn thành tất cả các thao tác này và đưa kết quả vào sintel.flac.

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

Tiếp theo, chúng ta sẽ chia tệp thành 5 tệp wave, mỗi tệp có thời lượng 6,5 giây; bạn nên sử dụng wave vì hầu hết bộ mã hoá đều hỗ trợ việc truyền dẫn tệp này. Xin nhắc lại, chúng ta có thể thực hiện việc này một cách chính xác bằng FFmpeg, sau đó chúng ta sẽ có: sintel_0.wav, sintel_1.wav, sintel_2.wav, sintel_3.wavsintel_4.wav.

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

Tiếp theo, hãy tạo các tệp MP3. LAME có một số tuỳ chọn để tạo nội dung liền mạch. Nếu có quyền kiểm soát nội dung, bạn có thể cân nhắc sử dụng --nogap với tính năng mã hoá hàng loạt tất cả tệp để tránh khoảng đệm giữa các phân đoạn. Tuy nhiên, cho mục đích của bản minh hoạ này, chúng ta muốn có khoảng đệm đó nên sẽ sử dụng phương thức mã hoá VBR chất lượng cao tiêu chuẩn cho các tệp 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

Đó là tất cả những gì cần thiết để tạo tệp MP3. Bây giờ, hãy cùng tìm hiểu cách tạo tệp MP4 bị phân mảnh. Chúng ta sẽ làm theo hướng dẫn của Apple để tạo nội dung nghe nhìn được xử lý cho iTunes. Dưới đây, chúng ta sẽ chuyển đổi các tệp wave thành tệp CAF trung gian theo hướng dẫn, trước khi mã hoá các tệp đó thành AAC trong vùng chứa MP4 bằng các tham số được đề xuất.

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

Bây giờ, chúng ta có một số tệp M4A cần phân mảnh một cách thích hợp trước khi có thể sử dụng với MediaSource. Đối với mục đích của mình, chúng ta sẽ sử dụng kích thước mảnh là một giây. MP4Box sẽ ghi từng tệp MP4 bị phân mảnh dưới dạng sintel_#_dashinit.mp4 cùng với tệp kê khai MPEG-DASH (sintel_#_dash.mpd) có thể bị loại bỏ.

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

Vậy là xong! Bây giờ, chúng ta có các tệp MP4 và MP3 phân mảnh với siêu dữ liệu chính xác cần thiết để phát không gián đoạn. Hãy xem Phụ lục B để biết thêm thông tin chi tiết về siêu dữ liệu đó.

Phụ lục B: Phân tích cú pháp siêu dữ liệu không có khoảng trống

Giống như việc tạo nội dung liền mạch, việc phân tích cú pháp siêu dữ liệu liền mạch có thể gặp khó khăn vì không có phương thức chuẩn để lưu trữ. Dưới đây, chúng ta sẽ tìm hiểu cách hai bộ mã hoá phổ biến nhất, LAME và iTunes, lưu trữ siêu dữ liệu không có khoảng trống. Hãy bắt đầu bằng cách thiết lập một số phương thức trợ giúp và một bản phác thảo cho ParseGaplessData() được sử dụng ở trên.

// 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.

Trước tiên, chúng ta sẽ đề cập đến định dạng siêu dữ liệu iTunes của Apple vì đây là định dạng dễ phân tích cú pháp và giải thích nhất. Trong tệp MP3 và M4A, iTunes (và afconvert) sẽ viết một phần ngắn bằng ASCII như sau:

iTunSMPB[ 26 bytes ]0000000 00000840 000001C0 0000000000046E00

Thông tin này được viết bên trong thẻ ID3 trong vùng chứa MP3 và bên trong một nguyên tử siêu dữ liệu trong vùng chứa MP4. Đối với mục đích của mình, chúng ta có thể bỏ qua mã thông báo 0000000 đầu tiên. Ba mã thông báo tiếp theo là khoảng đệm trước, khoảng đệm cuối và tổng số mẫu không có khoảng đệm. Việc chia mỗi giá trị này cho tốc độ lấy mẫu của âm thanh sẽ cho chúng ta biết thời lượng của mỗi giá trị.

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

Mặt khác, hầu hết các bộ mã hoá MP3 nguồn mở sẽ lưu trữ siêu dữ liệu không có khoảng trống trong một tiêu đề Xing đặc biệt được đặt bên trong một khung MPEG im lặng (khung này im lặng để các bộ giải mã không hiểu tiêu đề Xing sẽ chỉ phát âm thanh im lặng). Đáng tiếc là thẻ này không phải lúc nào cũng xuất hiện và có một số trường không bắt buộc. Đối với mục đích của bản minh hoạ này, chúng ta có quyền kiểm soát nội dung nghe nhìn, nhưng trong thực tế, bạn sẽ phải kiểm tra thêm để biết thời điểm siêu dữ liệu không có khoảng trống thực sự có sẵn.

Trước tiên, chúng ta sẽ phân tích cú pháp tổng số mẫu. Để đơn giản, chúng ta sẽ đọc thông tin này từ tiêu đề Xing, nhưng bạn có thể tạo thông tin này từ tiêu đề âm thanh MPEG thông thường. Bạn có thể đánh dấu tiêu đề Xing bằng thẻ Xing hoặc Info. Chính xác 4 byte sau thẻ này là 32 bit đại diện cho tổng số khung hình trong tệp; việc nhân giá trị này với số lượng mẫu trên mỗi khung hình sẽ cho chúng ta tổng số mẫu trong tệp.

// 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.

Bây giờ, chúng ta đã có tổng số mẫu, chúng ta có thể chuyển sang đọc số lượng mẫu đệm. Tuỳ thuộc vào bộ mã hoá, thông tin này có thể được viết trong thẻ LAME hoặc Lavf lồng trong tiêu đề Xing. Chính xác 17 byte sau tiêu đề này là 3 byte đại diện cho khoảng đệm đầu và cuối theo thứ tự tương ứng là 12 bit.

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

Với hàm này, chúng ta có một hàm hoàn chỉnh để phân tích cú pháp phần lớn nội dung liền mạch. Tuy nhiên, chắc chắn sẽ có rất nhiều trường hợp ngoại lệ, vì vậy, bạn nên thận trọng trước khi sử dụng mã tương tự trong phiên bản chính thức.

Phụ lục C: Về việc thu gom rác

Bộ nhớ thuộc về các thực thể SourceBuffer được thu thập rác một cách chủ động theo loại nội dung, các giới hạn cụ thể của nền tảng và vị trí phát hiện hiện tại. Trong Chrome, trước tiên, bộ nhớ sẽ được thu hồi từ các vùng đệm đã phát. Tuy nhiên, nếu mức sử dụng bộ nhớ vượt quá giới hạn dành riêng cho nền tảng, thì bộ nhớ sẽ bị xoá khỏi bộ đệm chưa phát.

Khi phát đến một khoảng trống trong tiến trình do bộ nhớ được lấy lại, quá trình phát có thể gặp sự cố nếu khoảng trống đủ nhỏ hoặc bị tạm dừng hoàn toàn nếu khoảng trống quá lớn. Cả hai đều không mang lại trải nghiệm tốt cho người dùng. Vì vậy, bạn cần tránh thêm quá nhiều dữ liệu cùng một lúc và xoá các phạm vi không cần thiết khỏi tiến trình phát nội dung nghe nhìn theo cách thủ công.

Bạn có thể xoá các dải ô thông qua phương thức remove() trên mỗi SourceBuffer; phương thức này sẽ lấy một dải ô [start, end] theo giây. Tương tự như appendBuffer(), mỗi remove() sẽ kích hoạt một sự kiện updateend sau khi hoàn tất. Bạn không nên xoá hoặc thêm các phần tử khác cho đến khi sự kiện kích hoạt.

Trên Chrome dành cho máy tính, bạn có thể lưu trữ khoảng 12 megabyte nội dung âm thanh và 150 megabyte nội dung video trong bộ nhớ cùng một lúc. Bạn không nên dựa vào các giá trị này trên các trình duyệt hoặc nền tảng; ví dụ: chắc chắn rằng các giá trị này không đại diện cho thiết bị di động.

Việc thu gom rác chỉ ảnh hưởng đến dữ liệu được thêm vào SourceBuffers; không có giới hạn về lượng dữ liệu bạn có thể lưu vào bộ đệm trong các biến JavaScript. Bạn cũng có thể nối lại cùng một dữ liệu ở cùng một vị trí nếu cần.