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

Có lẽ bạn đã từng nghe một đĩa nhạc mà các bài hát chuyển tiếp 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 bây giờ. Nghệ sĩ tạo ra những trải nghiệm phát liền mạch này vừa là một lựa chọn nghệ thuật, vừa là một sản phẩm của đĩa thanđĩa CD, nơi âm thanh được ghi dưới dạng một luồng liên tục. Thật không may, 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 thính giác liền mạch này thường bị mất đi.

Chúng ta sẽ tìm hiểu chi tiết lý do bên dưới, nhưng trước tiên, hãy bắt đầu bằng một bản minh hoạ. Dưới đây là 30 giây đầu tiên của bộ phim Sintel tuyệt vời được chia thành 5 tệp MP3 riêng biệt và được lắp ráp lại bằng MSE. Các đường màu đỏ cho biết những 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 lỗi tại những điểm này.

Bản minh hoạ

Ghê quá! Đó không phải là một trải nghiệm tốt; chúng tôi có thể làm tốt hơn. Chỉ cần thêm một chút công sức, bằng cách sử dụng chính xác các tệp MP3 trong bản minh hoạ ở trên, chúng ta có thể dùng MSE để loại bỏ những khoảng trống gây 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 nối và các khoảng trống đã được 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. Trong bản minh hoạ này, chúng ta sẽ tập trung vào loại tệp mà một người dùng thông thường có thể có. Trong đó, mỗi tệp được mã hoá riêng mà không cần 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 về chế độ thiết lập cơ bản của một thực thể MediaSource. Media Source Extensions (Tiện ích nguồn nội dung nghe nhìn), như tên gọi của chúng, chỉ là các tiện ích cho các phần tử nội dung nghe nhìn hiện có. Bên dưới, 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 một URL tiêu 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ố bước khởi tạo 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 có sẵn.

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à ở cuối tệp. Dưới đây là biểu đồ của 3.000 mẫu gần nhất được tính trung bình trên cả hai kênh từ 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].

khoảng trống mp3

Tại sao lại có nhiều mẫu âm thanh bằng 0 (không có âm thanh) như vậy!? Thực ra, những hiện tượng này 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 giới thiệu 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 đệ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 phần đầu. Nếu xem trước sintel_1.mp3đoạn nhạc, chúng ta sẽ thấy có thêm 576 mẫu đệm ở phần đầu. Lượng khoảng đệm thay đổi tuỳ theo bộ mã hoá và nội dung, nhưng chúng tôi biết chính xác các giá trị dựa trên metadata có trong mỗi tệp.

mp3 gap end

Các đoạ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 có khoảng dừng, chúng ta cần loại bỏ 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. Bên dưới, chúng ta sẽ sửa đổi phương thức onAudioLoaded() để sử dụng cửa sổ nốiđộ lệch dấu thời gian nhằm 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 sáng bóng của chúng ta đã làm được gì bằng cách xem lại dạng sóng sau khi chúng ta áp dụng các cửa sổ nối. Ở bên dưới, 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 ta có một đoạn chuyển tiếp liền mạch giữa các phân đoạn.

mp3 mid

Kết luận

Như vậy, chúng ta đã ghép cả 5 đoạn video một cách liền mạch thành một video và đã xem hết video minh hoạ. Trước khi tiếp tục, có thể bạn đã nhận thấy 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 codec. Dưới đây, bạn có thể phát lại bản minh hoạ gốc về tệp MP4 phân mảnh tương thích với 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 để tìm hiểu sâu 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 kỹ hơn mã nguồn của 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ể khó thực hiện đúng cách. Dưới đây, chúng ta sẽ xem xét quy trình tạo nội dung nghe nhìn Sintel được dùng trong bản minh hoạ này. Để bắt đầu, bạn sẽ cần một bản sao của nhạc nền FLAC không tổn hao cho Sintel; để lưu giữ, 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 tôi 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 quá trình phát kết thúc. 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ả những việ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 dài 6,5 giây; cách dễ nhất là dùng wave vì hầu như mọi bộ mã hoá đều hỗ trợ việc sử dụng tệp này. Một lần nữa, 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ố lựa chọn để tạo nội dung không có khoảng trống. Nếu kiểm soát được 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ả các tệp để tránh hoàn toàn việc thêm 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 đó, vì vậy, chúng ta 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 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. Bây giờ, hãy xem cách tạo tệp MP4 phân mảnh. Chúng tôi sẽ làm theo hướng dẫn của Apple về việc tạo nội dung nghe nhìn được tinh chỉnh cho iTunes. Dưới đây, chúng ta sẽ chuyển đổi các tệp sóng thành tệp CAF trung gian theo hướng dẫn trước khi mã hoá chúng thành AAC trong vùng chứa MP4 bằng các thông 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

Giờ đây, chúng ta có một số tệp M4A cần được 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 chúng ta, chúng ta sẽ sử dụng kích thước đoạn là 1 giây. MP4Box sẽ ghi từng tệp MP4 được phân đoạn dưới dạng sintel_#_dashinit.mp4 cùng với 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! Giờ đây, 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 cho chế độ phát liền mạch. 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 không có khoảng trống, việc phân tích siêu dữ liệu không có khoảng trống có thể khó khăn vì không có phương pháp lưu trữ tiêu chuẩn. Dưới đây, chúng ta sẽ tìm hiểu cách 2 bộ mã hoá phổ biến nhất là 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 dà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.

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

    iTunSMPB[ 26 bytes ]0000000 00000840 000001C0 0000000000046E00

Thông tin này được ghi 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 chúng ta, 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à phần đệm trước, phần đệm cuối và tổng số mẫu không có phần đệm. Khi chia mỗi giá trị này cho tốc độ lấy mẫu của âm thanh, chúng ta sẽ có được thời lượng của từng 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 tiêu đề Xing đặc biệt được đặt bên trong khung MPEG im lặng (đây là khung im lặng nên các bộ giải mã không hiểu tiêu đề Xing sẽ chỉ phát ra tiếng im lặng). Rất tiếc, 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. Để minh hoạ, chúng ta sẽ kiểm soát nội dung nghe nhìn. Tuy nhiên, trên thực tế, bạn sẽ cần thực hiện thêm một số bước kiểm tra độ nhạy để biết khi nào siêu dữ liệu liền mạch 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 dữ liệu này từ tiêu đề Xing, nhưng dữ liệu này có thể được tạo từ tiêu đề âm thanh MPEG thông thường. Tiêu đề 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 hình trong tệp; nhân giá trị này với số 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ố mẫu đệm. Tuỳ thuộc vào bộ mã hoá, thông tin này có thể được ghi trong thẻ LAME hoặc Lavf lồng trong tiêu đề Xing. Chính xác 17 byte sau tiêu đề này có 3 byte đại diện cho phần đệm trước và sau, mỗi phần 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
      };
    }

Nhờ đó, 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 không có khoảng trống. Tuy nhiên, chắc chắn sẽ có nhiều trường hợp đặc biệt, vì vậy bạn nên thận trọng trước khi sử dụng mã tương tự trong môi trường phát hành chính thức.

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

Bộ nhớ thuộc các thực thể SourceBuffer được thu gom rác 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 tại. Trong Chrome, bộ nhớ sẽ được thu hồi trước tiên 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ì hệ thống sẽ xoá bộ nhớ khỏi các vùng đệm chưa phát.

Khi quá trình phát đạt đến một khoảng trống trong dòng thời gian do bộ nhớ được thu hồi, quá trình phát có thể gặp trục trặ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. 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 việc thêm quá nhiều dữ liệu cùng một lúc và xoá các dải 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 ô bằng 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] 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 một lần sau khi hoàn tất. Bạn không nên đưa ra các lệnh xoá hoặc thêm 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 nhiều trình duyệt hoặc nền tảng; ví dụ: chắc chắn các giá trị này không đại diện cho thiết bị di động.

Quá trình thu thập 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ể giữ trong các biến JavaScript. Bạn cũng có thể nối lại dữ liệu đó ở cùng một vị trí nếu cần.

Phản hồi