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

Lê Thanh Tâm
Dale ít

Giới thiệu

Tiện ích nguồn nội dung nghe nhìn (MSE) cung cấp bộ đệm mở rộng và bộ điều khiển chế độ phát 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 Truyền phát thích ứng động qua HTTP (DASH), nhưng ở bên dưới, chúng ta sẽ tìm hiểu cách sử dụng tính năng này cho âm thanh, đặc biệt là cho tính năng phát không có khoảng trống.

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

Chúng ta sẽ đi vào chi tiết lý do ở bên dưới, nhưng bây giờ, hãy bắt đầu bằng minh hoạ. Dưới đây là 30 giây đầu tiên của Sintel tuyệt vời được chia thành 5 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 xuất hiện trong quá trình tạo (mã hoá) từng tệp MP3; bạn sẽ nghe thấy sự cố ở những điểm này.

Bản minh hoạ

Thật tiếc! Đó 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 nữa. Với một chút thao tá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ể sử 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 liên kết 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 để tạo nội dung không có khoảng trống. Để minh hoạ, chúng tôi sẽ tập trung vào những loại tệp mà người dùng bình thường có thể đang sử dụng. Trong trường hợp mỗi tệp được mã hoá riêng biệt 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à đề cập đến cách thiết lập cơ bản của thực thể MediaSource. Tiện ích nguồn nội dung nghe nhìn, như tên gọi, chỉ là phần mở rộng cho các thành phần nội dung đa phương tiện hiện có. Bên dưới, chúng ta sẽ chỉ định một Object URL đại diện cho thực thể MediaSource của chúng tôi, cho thuộc tính nguồn của 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 được kết nối, đối tượng MediaSource 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 sự kiện sourceopen; tại thời điểm đó, chúng ta có thể tạo SourceBuffer. Trong ví dụ trên, chúng ta đang tạo một mã audio/mpeg. Mã này có thể phân tích cú pháp và giải mã các phân đoạn MP3; còn có một vài loại khác.

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

Chúng ta sẽ sớm quay lại mã, nhưng bây giờ, hãy xem xét kỹ hơn tệp mà chúng ta vừa thêm, cụ thể là ở phần cuối của tệp. Dưới đây là biểu đồ của 3.000 mẫu gần đây nhất, 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].

khoảng trống mp3

Có vấn đề gì với toàn bộ những mẫu không có (im lặng) đó!? Thực ra những lỗi này là do các cấu phần phần mềm nén được ra mắt trong quá trình mã hoá. Hầu hết mọi bộ mã hoá đều có 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 thêm khoảng đệ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. Số lượng khoảng đệm sẽ thay đổi tuỳ 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.

đoạn cuối ngắt quãng mp3

Các phần khoảng lặng ở đầu và cuối của 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 không gián đoạn, chúng ta cần xoá những phần 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ốibù trừ dấu thời gian để xoá khoảng lặng này.

Ví dụ về mã

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 nổi bật của chúng ta đã hoàn thành những gì bằng cách xem lại dạng sóng sau khi áp dụng các cửa sổ nối. 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á; để lại cho chúng ta quá trình chuyển đổi liền mạch giữa các phân đoạn.

mp3 giữa

Kết luận

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

Bản minh hoạ

Nếu bạn muốn biết thêm, vui lòng xem phụ lục bên dưới để tìm hiểu kỹ hơn về việc tạo nội dung không có khoảng trống 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ã 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

Việc tạo nội dung không có khoảng trống có thể là một việc khó khăn. Dưới đây, chúng tôi sẽ hướng dẫn cách tạo nội dung nghe nhìn Sintel trong bản minh hoạ này. Để bắt đầu, bạn cần một bản sao của nhạc nền FLAC không mất dữ liệu cho Sintel; để sau này, SHA1 được bao gồm bên dưới. Đối với các công cụ, bạn cần có FFmpeg, MP4Box, LAME và cài đặt OSX bằng 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 độ mờ 2,5 giây bắt đầu từ 28 giây để tránh mọi nhấp chuột sau khi phát xong. Hãy 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 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 kéo dài 6,5 giây; cách sử dụng Wave dễ nhất là hầu hết mọi bộ mã hoá đều hỗ trợ 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 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 tệp MP3. LAME có một số tuỳ chọn để tạo nội dung không có khoảng trống. 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. Tuy nhiên, để minh hoạ cho mục đích của bản minh hoạ này, chúng tôi 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 của 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 cùng tìm hiểu về việc tạo các tệp MP4 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 cải tiến cho iTunes. Dưới đây, chúng tôi 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 đó dưới dạng AAC trong vùng chứa MP4 bằng cách sử dụ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

Chúng tôi hiện có một số tệp M4A cần phân đoạn một cách phù hợp trước khi có thể dùng với MediaSource. Để phục vụ 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 ra từng MP4 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) 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! Chúng tôi hiện có các tệp MP4 và MP3 được phân tách với siêu dữ liệu chính xác cần thiết để phát mà không có gián đoạn. Hãy xem Phụ lục B để biết thêm thông tin 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ũng rất khó khăn vì không có phương thức chuẩn để lưu trữ. Dưới đây là cách 2 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à đường viền cho ParseGaplessData() đã 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 tôi 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), hãy viết một phần ngắn trong ASCII như sau:

    iTunSMPB[ 26 bytes ]0000000 00000840 000001C0 0000000000046E00

Nội dung này được viết 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 của mình, chúng ta có thể bỏ qua mã thông báo 0000000 đầu tiên. 3 mã thông báo tiếp theo là khoảng đệm trước, khoảng đệm kết thúc và tổng số lượng mẫu không có khoảng đệm. Việc chia từng giá trị này cho tốc độ lấy mẫu của âm thanh sẽ cho chúng ta 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 đặt bên trong khung MPEG im lặng (chế độ im lặng nên những bộ giải mã không hiểu tiêu đề Xing sẽ chỉ phát các khoảng lặng). Đáng tiếc là thẻ này không phải lúc nào cũng hiển thị 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 trên thực tế, chúng ta cần thực hiện một số bước kiểm tra độ nhạy bổ sung để biết khi nào siêu dữ liệu không có khoảng trống thực sự xuất hiện.

Trước tiên, chúng tôi sẽ phân tích cú pháp tổng số mẫu. Để đơn giản, chúng tôi sẽ đọc dữ liệu này từ tiêu đề Xing, nhưng bạn có thể tạo tiêu đề 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 có 32 bit biểu thị tổng số khung trong tệp; việc nhân giá trị này với số lượng mẫu trên mỗi khung 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 có thể chuyển sang đọc số mẫu khoảng đệm. Tuỳ thuộc vào bộ mã hoá của bạn, nội dung này có thể được viết dưới một thẻ LAME hoặc Lavf được lồng trong tiêu đề Xing. Chính xác là 17 byte sau tiêu đề này, có 3 byte đại diện cho khoảng đệm trước và cuối trong mỗi byte có kích thước 12 bit tương ứng.

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

Cùng với đó, 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ó khoảng trống. Tuy nhiên, các trường hợp hiếm gặp chắc chắn có rất nhiều, vì vậy, bạn nên thận trọng trước khi sử dụng mã tương tự trong sản xuất.

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

Bộ nhớ thuộc các thực thể SourceBuffer đang tích cực thu gom rác theo loại nội dung, giới hạn cụ thể 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 dành riêng cho nền tảng, thì bộ nhớ này sẽ bị xoá khỏi các vùng đệm chưa phát.

Khi hoạt động phát đạt đến một khoảng trống trong tiến trình do bộ nhớ được thu hồi, hoạt động phát có thể gặp sự cố nếu khoảng trống đủ nhỏ hoặc bị trì hoãn hoàn toàn nếu khoảng trống quá lớn. Cả hai đều không phải là trải nghiệm người dùng tuyệt vời. 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ỏi tiến trình nội dung đa phương tiện không còn cần thiết nữa.

Bạn có thể xoá các phạm vi bằng phương thức remove() trên mỗi SourceBuffer; quá trình này sẽ mất phạm vi [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 sự kiện hoàn tất. Bạn không nên đưa ra các thao tác 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 cùng lúc khoảng 12 megabyte nội dung âm thanh và 150 megabyte nội dung video trong bộ nhớ. 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ụ: các giá trị này 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 đã 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 vào cùng vị trí nếu cần.

Ý kiến phản hồi