Nếu đang dùng Tiện ích nguồn phương tiện (MSE), thì có một việc bạn cần phải giải quyết là bộ đệm quá đầy. Khi điều này xảy ra, bạn sẽ nhận được một QuotaExceededError
. Trong bài viết này, tôi sẽ trình bày một số cách để xử lý vấn đề này.
Lỗi vượt quá hạn mức là gì?
Về cơ bản, QuotaExceededError
là giá trị bạn nhận được nếu cố gắng thêm quá nhiều dữ liệu vào đối tượng SourceBuffer
. (Việc thêm các đối tượng SourceBuffer
khác vào phần tử MediaSource
mẹ cũng có thể gây ra lỗi này. Điều này nằm ngoài phạm vi của bài viết này.) Nếu SourceBuffer
có quá nhiều dữ liệu, lệnh gọi SourceBuffer.appendBuffer()
sẽ kích hoạt thông báo sau trong cửa sổ bảng điều khiển Chrome.
Có một vài điều cần lưu ý về vấn đề này. Trước tiên, hãy lưu ý rằng tên QuotaExceededError
không xuất hiện ở đâu trong thông báo. Để xem lỗi đó, hãy đặt điểm ngắt tại vị trí mà bạn có thể phát hiện lỗi và kiểm tra lỗi đó trong cửa sổ theo dõi hoặc phạm vi. Tôi đã cho bạn thấy lựa chọn này bên dưới.
Thứ hai, không có cách nào chắc chắn để tìm hiểu lượng dữ liệu mà SourceBuffer
có thể xử lý.
Hành vi trong các trình duyệt khác
Tại thời điểm viết bài, Safari không gửi QuotaExceededError
trong nhiều bản dựng. Thay vào đó, hệ thống sẽ xoá khung hình bằng thuật toán 2 bước, sẽ dừng nếu có đủ chỗ để xử lý appendBuffer()
. Trước tiên, phương thức này giải phóng các khung hình từ 0 đến 30 giây trước thời gian hiện tại theo các đoạn 30 giây. Tiếp theo, phương thức này sẽ giải phóng khung hình trong các phân đoạn 30 giây từ thời lượng ngược trở lại cho đến gần 30 giây sau currentTime
. Bạn có thể đọc thêm về vấn đề này trong thay đổi Webkit từ năm 2014.
Rất may, cùng với Chrome, Edge và Firefox cũng gặp phải lỗi này. Nếu đang sử dụng trình duyệt khác, bạn cần tự kiểm thử. Mặc dù có thể không phải là điều bạn sẽ xây dựng cho trình phát nội dung đa phương tiện thực tế, nhưng kiểm thử giới hạn vùng đệm nguồn của François Beaufort ít nhất cũng cho phép bạn quan sát hành vi.
Tôi có thể thêm bao nhiêu dữ liệu?
Số liệu chính xác sẽ khác nhau tuỳ theo trình duyệt. Vì không thể truy vấn lượng dữ liệu hiện được thêm vào, nên bạn sẽ phải theo dõi lượng dữ liệu mà bạn thêm vào. Về nội dung cần xem, đây là dữ liệu tốt nhất mà tôi có thể thu thập tại thời điểm viết bài. Đối với Chrome, những con số này là giới hạn trên, nghĩa là chúng có thể nhỏ hơn khi hệ thống gặp phải áp lực về bộ nhớ.
Chrome | Chromecast* | Firefox | Safari | Edge | |
---|---|---|---|---|---|
Video | 150MB | 30MB | 100MB | 290MB | Không xác định |
Âm thanh | 12MB | 2MB | 15MB | 14MB | Không xác định |
- Hoặc thiết bị Chrome có bộ nhớ bị giới hạn khác.
Vậy tôi phải làm gì?
Vì lượng dữ liệu được hỗ trợ rất khác nhau và bạn không thể tìm thấy lượng dữ liệu trong SourceBuffer
, nên bạn phải lấy dữ liệu đó gián tiếp bằng cách xử lý QuotaExceededError
. Bây giờ, hãy cùng xem một số cách để thực hiện việc đó.
Có một số phương pháp để xử lý QuotaExceededError
. Trong thực tế, tốt nhất là bạn nên kết hợp một hoặc nhiều phương pháp. Phương pháp của bạn nên dựa trên lượng thông tin bạn đang tìm nạp và cố gắng bổ sung ngoài HTMLMediaElement.currentTime
, đồng thời điều chỉnh kích thước đó dựa trên QuotaExceededError
. Ngoài ra, việc sử dụng một tệp kê khai như tệp mpd (MPEG-DASH) hoặc tệp m3u8 (HLS) có thể giúp bạn theo dõi dữ liệu mà bạn đang thêm vào vùng đệm.
Bây giờ, hãy cùng xem xét một số phương pháp để xử lý QuotaExceededError
.
- Xoá dữ liệu không cần thiết rồi nối lại.
- Nối các mảnh nhỏ hơn.
- Giảm độ phân giải phát.
Mặc dù bạn có thể sử dụng kết hợp các phương thức này, nhưng tôi sẽ trình bày từng phương thức một.
Xoá dữ liệu không cần thiết và nối lại
Thực sự thì phương thức này nên được gọi là "Xoá dữ liệu ít có khả năng sẽ được sử dụng sớm nhất, sau đó thử lại việc nối dữ liệu có khả năng sẽ được sử dụng sớm nhất". Tiêu đề đó quá dài. Bạn chỉ cần nhớ ý tôi thực sự muốn nói.
Việc xoá dữ liệu gần đây không đơn giản như việc gọi SourceBuffer.remove()
. Để xoá dữ liệu khỏi SourceBuffer
, cờ cập nhật của dữ liệu đó phải là false. Nếu không, hãy gọi SourceBuffer.abort()
trước khi xoá bất kỳ dữ liệu nào.
Có một vài điều cần lưu ý khi gọi SourceBuffer.remove()
.
- Điều này có thể ảnh hưởng tiêu cực đến quá trình phát. Ví dụ: nếu muốn video phát lại hoặc lặp lại ngay, bạn không nên xoá phần đầu video. Tương tự, nếu bạn hoặc người dùng tua đến một phần của video mà bạn đã xoá dữ liệu, bạn sẽ phải thêm lại dữ liệu đó để đáp ứng yêu cầu tua đó.
- Xoá càng ít nội dung càng tốt. Hãy cẩn thận khi xoá nhóm khung hình đang phát bắt đầu từ khung hình chính tại hoặc trước
currentTime
vì việc này có thể gây ra tình trạng tạm dừng phát. Ứng dụng web có thể cần phân tích cú pháp thông tin đó từ luồng byte nếu thông tin đó không có trong tệp kê khai. Tệp kê khai nội dung đa phương tiện hoặc kiến thức ứng dụng về các khoảng thời gian của khung hình chính trong nội dung nghe nhìn có thể giúp ứng dụng của bạn lựa chọn phạm vi xoá để ngăn việc xoá nội dung nghe nhìn đang phát. Dù bạn xoá gì, đừng xoá nhóm ảnh đang phát hoặc thậm chí là một vài ảnh đầu tiên sau đó. Nhìn chung, đừng xoá sau thời điểm hiện tại trừ phi bạn chắc chắn rằng nội dung nghe nhìn đó không còn cần thiết nữa. Nếu xoá gần đầu phát, bạn có thể gây ra sự cố. - Safari 9 và Safari 10 không triển khai
SourceBuffer.abort()
đúng cách. Trên thực tế, các lỗi này sẽ khiến quá trình phát bị tạm dừng. May mắn là có các trình theo dõi lỗi mở tại đây và tại đây. Trong thời gian chờ đợi, bạn sẽ phải giải quyết vấn đề này theo cách nào đó. Shaka Player thực hiện việc này bằng cách tạo một hàmabort()
trống trên các phiên bản Safari đó.
Nối các mảnh nhỏ hơn
Tôi đã trình bày quy trình bên dưới. Cách này có thể không hiệu quả trong mọi trường hợp, nhưng có ưu điểm là bạn có thể điều chỉnh kích thước của các phần nhỏ hơn cho phù hợp với nhu cầu của mình. Phương thức này cũng không yêu cầu quay lại mạng, điều này có thể gây ra thêm chi phí dữ liệu cho một số người dùng.
const pieces = new Uint8Array([data]);
(function appendFragments(pieces) {
if (sourceBuffer.updating) {
return;
}
pieces.forEach(piece => {
try {
sourceBuffer.appendBuffer(piece);
}
catch e {
if (e.name !== 'QuotaExceededError') {
throw e;
}
// Reduction schedule: 80%, 60%, 40%, 20%, 16%, 12%, 8%, 4%, fail.
const reduction = pieces[0].byteLength * 0.8;
if (reduction / data.byteLength < 0.04) {
throw new Error('MediaSource threw QuotaExceededError too many times');
}
const newPieces = [
pieces[0].slice(0, reduction),
pieces[0].slice(reduction, pieces[0].byteLength)
];
pieces.splice(0, 1, newPieces[0], newPieces[1]);
appendBuffer(pieces);
}
});
})(pieces);
Giảm độ phân giải phát
Điều này tương tự như việc xoá dữ liệu gần đây và thêm lại. Trên thực tế, bạn có thể thực hiện cả hai thao tác này cùng lúc, mặc dù ví dụ bên dưới chỉ cho thấy việc giảm độ phân giải.
Sau đây là một vài điều cần lưu ý khi sử dụng kỹ thuật này:
- Bạn phải thêm một phân đoạn khởi chạy mới. Bạn phải làm việc này bất cứ khi nào bạn thay đổi nội dung đại diện. Phân đoạn khởi chạy mới phải dành cho các phân đoạn nội dung nghe nhìn theo sau.
- Dấu thời gian trình bày của nội dung nghe nhìn được thêm vào phải khớp với dấu thời gian của dữ liệu trong vùng đệm càng gần càng tốt, nhưng không được vượt quá. Việc chồng chéo dữ liệu đã lưu vào bộ đệm có thể gây ra hiện tượng giật hoặc tạm dừng ngắn, tuỳ thuộc vào trình duyệt. Bất kể bạn thêm nội dung gì, đừng chồng chéo với đầu phát vì điều này sẽ gây ra lỗi.
- Thao tác tua có thể làm gián đoạn quá trình phát. Bạn có thể muốn tua đến một vị trí cụ thể và tiếp tục phát từ vị trí đó. Xin lưu ý rằng việc này sẽ gây gián đoạn quá trình phát cho đến khi quá trình tìm kiếm hoàn tất.