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 nghe nhìn (MSE) cung cấp chức năng lưu vào bộ đệm và kiểm soát phát mở rộng cho các phần tử HTML5 <audio><video>. Mặc dù ban đầu được phát triển để hỗ trợ các trình phát video dựa trên Truyền phát thích ứng động qua HTTP (DASH), nhưng ở phần dưới đây, chúng ta sẽ tìm hiểu cách sử dụng các trình phát này cho âm thanh; đặc biệt là cho tính năng phát không gián đoạn.

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

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

Bản minh hoạ

Thật tuyệt! Đó không phải là một trải nghiệm tuyệt vời; chúng tôi có thể làm tốt hơn. Việc sử dụng các tệp MP3 giống hệt như trong bản minh hoạ ở trên, chúng ta có thể dùng MSE để loại bỏ những khoảng trống khó chịu đó. 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 kết hợp và các khoảng trống đã được xoá. Trên Chrome 38 trở lên, nội dung này sẽ phát lại liền mạch!

Bản minh hoạ

nhiều cách để sáng tạo nội dung không có khoảng cách. Trong 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ể đang sử dụng. Khi mỗi tệp được mã hoá riêng biệt bất kể các đoạn âm thanh trước hay sau tệp đó.

Thiết lập cơ bản

Trước tiên, hãy cùng tìm hiểu kỹ hơn và tìm hiểu cách thiết lập cơ bản cho thực thể MediaSource. Tiện ích nguồn nội dung nghe nhìn, đúng như tên gọi, chỉ là phần mở rộng cho các phần tử nội dung nghe nhìn hiện có. Ở bên dưới, chúng ta sẽ gán một 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 mộ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ố hoạt động khởi chạy và cuối cùng kích hoạt sự kiện sourceopen; chúng tôi có thể tạo SourceBuffer tại thời điểm đó. Trong ví dụ trên, chúng ta đang tạo một tệp audio/mpeg có thể phân tích cú pháp và giải mã các phân đoạn MP3; thì bạn có thể sử dụng một số loại khác.

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

Chúng ta sẽ quay lại sau giây lát, 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à ở cuối tệp. Dưới đây là biểu đồ về 3.000 mẫu gần đây nhất được tính trung bình trên cả hai kênh từ kênh 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].

Hết sintel_0.mp3

Tất cả những mẫu không (nghĩa là im lặng) đó có nghĩa là gì!? Đó thực ra là do các cấu phần phần mềm nén được đưa vào trong quá trình mã hoá. Hầu hết bộ mã hoá nào cũng sử dụng một số loại khoảng đệm. Trong trường hợp này, LAME đã thêm chính xác 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 ở đầ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 tồn tại ở phía trước. Số lượng khoảng đệm thay đổi theo bộ mã hoá và nội dung, nhưng chúng tôi biết giá trị chính xác dựa trên metadata có trong mỗi tệp.

Đầu năm sintel_1.mp3

Đầu năm sintel_1.mp3

Các phần khoảng lặng ở đầu và cuối mỗi tệp là nguyên nhân gây ra sự cố giữa các phân đoạn trong bản minh hoạ trước. Để phát video không gián đoạn, chúng ta cần loại bỏ những khoảng im lặng này. Thật may 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ốidấu thời gian bù trừ nhằm xoá 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 những gì bằng cách xem lại dạng sóng sau khi áp dụng các cửa sổ bổ sung. Dưới đây, bạn có thể thấy rằng phần im lặng ở cuối sintel_0.mp3 (màu đỏ) và phần im lặng ở đầu sintel_1.mp3 (màu xanh dương) đã bị xoá; giúp chúng tôi chuyển đổi liền mạch giữa các phân khúc.

Tham gia của sintel_0.mp3 và sintel_1.mp3

Kết luận

Theo đó, chúng tôi đã ghép cả năm phân đoạn một cách liền mạch thành một và đến phần cuối bản minh hoạ của mình. Trước khi tiếp tục, có thể bạn đã nhận thấy rằng phương thức onAudioLoaded() không xem xét đến 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 hay bộ mã hoá và giải mã. Dưới đây, bạn có thể phát lại bản MP4 phân mảnh sẵn sàng cho DASH bản minh hoạ ban đầu thay vì MP3.

Bản minh hoạ

Nếu bạn muốn biết thêm, hãy xem các phụ lục dưới đây để biết thêm chi tiết về việc tạo nội dung không có dấu cá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 để biết rõ hơn về mã hỗ trợ bản minh hoạ này.

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

Phụ lục A: Tạo nội dung không có khoảng trống

Thật khó để tạo được nội dung liền mạch. Dưới đây, chúng ta sẽ tìm hiểu cách tạo phương tiện Sintel dùng trong bản minh hoạ này. Để bắt đầu, bạn cần một bản sao bản nhạc FLAC không mất dữ liệu cho Sintel; để sử dụng sau này, SHA1 được trình bày bên dưới. Đối với các công cụ, bạn cần FFmpeg, MP4Box, LAME và cài đặt OSX với 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 cho bản nhạc 1-Snow_Fight.flac. Chúng ta cũng muốn thêm hiệu ứng làm mờ dần (2,5 giây) bắt đầu từ giây thứ 28 để tránh mọi cú nhấp chuột 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ể thực hiện tất cả các thao tác này và đặt 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; cách dễ nhất là sử dụng sóng vì hầu hết mọi bộ mã hoá đều hỗ trợ truyền dẫn tín hiệu này. Xin nhắc lại, chúng ta có thể thực hiện chính xác với 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 tệp MP3. Nhóm LAME có nhiều phương án để sáng tạo nội dung liền mạch. Nếu nắm quyền kiểm soát nội dung, bạn có thể cân nhắc sử dụng --nogap với phương thức mã hoá hàng loạt cho tất cả các tệp để tránh hoàn toàn khoảng đệm giữa các phân đoạn. Trong bản minh hoạ này, chúng ta muốn có khoảng đệm đó, vì vậy, chúng ta sẽ sử dụng chế độ mã hoá VBR chất lượng cao tiêu chuẩn cho các tệp sóng.

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. Giờ chúng ta sẽ tìm hiểu về cách tạo tệp MP4 bị phân mảnh. Chúng tôi sẽ làm theo hướng dẫn của Apple để tạo nội dung đa phương tiện dành cho iTunes. Dưới đây, chúng ta sẽ chuyển đổi các tệp wave thành các tệp CAF trung gian theo hướng dẫn, trước khi mã hoá các tệp này dưới dạng 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

Hiện tại, chúng ta có một số tệp M4A mà chúng ta cần phân mảnh một cách thích hợp trước khi dùng được với MediaSource. Để phục vụ mục đích này, chúng ta sẽ sử dụng kích thước mảnh là 1 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 một tệp kê khai MPEG-DASH (sintel_#_dash.mpd) mà bạn có thể 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! Nay chúng tôi có các tệp MP4 và MP3 bị phân mảnh với siêu dữ liệu chính xác cần thiết để phát không bị gián đoạn. Xem Phụ lục B để biết thêm chi tiết về hình thức của 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

Cũng giống như việc tạo nội dung không có khoảng trống, việc phân tích cú pháp siêu dữ liệu không có khoảng trống có thể là việc phức tạp vì không có phương pháp chuẩn để lưu trữ. Dưới đây, chúng tôi sẽ đề cập đến cách hai bộ mã hoá phổ biến nhất là LAME và iTunes, lưu trữ siêu dữ liệu không có khe cắm. Hãy bắt đầu bằng cách thiết lập một số phương thức trợ giúp và đường viền cho ParseGaplessData() được 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.

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

iTunSMPB[ 26 bytes ]0000000 00000840 000001C0 0000000000046E00

Mã này được ghi bên trong thẻ ID3 trong vùng chứa MP3 và trong một nguyên tử siêu dữ liệu bên trong vùng chứa MP4. Vì mục đích này, 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. Khi chia từng phần này cho tốc độ lấy mẫu âm thanh, chúng ta sẽ có được thời lượng cho mỗi phần tử.

// 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 khung MPEG im lặng (nên bộ giải mã không hiểu được tiêu đề Xing sẽ chỉ phát âm thanh im lặng). Rất tiếc, thẻ này không phải lúc nào cũng hiện diện và có một số trường không bắt buộc. Trong bản minh hoạ này, chúng tôi nắm quyền kiểm soát nội dung nghe nhìn. Tuy nhiên, trên thực tế, chúng tôi sẽ thực hiện một số bước kiểm tra bổ sung để biết khi nào siêu dữ liệu không có khoảng cách thực sự có sẵn.

Trước tiên, chúng ta sẽ phân tích cú pháp của tổng số mẫu. Để cho đơn giản, chúng tôi sẽ đọc tiêu đề này từ tiêu đề Xing, nhưng tiêu đề này có thể được tạo từ tiêu đề âm thanh MPEG thông thường. Tiêu đề hình xing có thể được đánh dấu bằng thẻ Xing hoặc Info. Chính xác 4 byte sau thẻ này có 32 bit đại diện cho tổng số khung trong tệp; nhân giá trị này với số mẫu trên mỗi khung sẽ cho ra 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 khoảng đệm. Tuỳ thuộc vào bộ mã hoá của bạn, thông tin này có thể được viết dưới thẻ LAME hoặc Lavf được lồng trong tiêu đề Xing. Chính xác 17 byte sau tiêu đề này, có 3 byte đại diện cho khoảng đệm phía trước và cuối trong mỗi khoảng 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
};
}

Do đó, chúng tôi có chức năng hoàn chỉnh để phân tích cú pháp phần lớn nội dung không có dấu cách. Tuy nhiên, chắc chắn có rất nhiều trường hợp phức tạp, 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 phát hành chính thức.

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

Bộ nhớ của các thực thể SourceBuffer đang được chủ động thu thập dữ liệu theo loại nội dung, giới hạn tuỳ theo nền tảng và vị trí phát hiện tại. Trong Chrome, trước tiên, bộ nhớ sẽ được lấy lạ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 cụ thể của nền tảng, thì việc này sẽ xoá bộ nhớ khỏi vùng đệm chưa phát.

Khi phát lại đế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 dừng hoàn toàn nếu khoảng trống quá lớn. Trải nghiệm người dùng cũng không tốt. 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 khoảng thời gian không còn cần thiết khỏi dòng thời gian của 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; Thao tác này mất khoảng [start, end] tính bằng 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 phát hành các lệnh xoá hoặc bổ sung khác cho đến khi sự kiện kích hoạt.

Trên Chrome dành cho máy tính để bàn, bạn có thể lưu 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úng chắc chắn 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 chính dữ liệu đó vào cùng một vị trí, nếu cần.