当我向 MSE 缓冲区添加小块时,Chrome 停止播放视频
Posted
技术标签:
【中文标题】当我向 MSE 缓冲区添加小块时,Chrome 停止播放视频【英文标题】:Chrome stops playing video when I add small pieces to the MSE buffer 【发布时间】:2020-11-15 16:10:24 【问题描述】:我正在尝试基于 https://developers.google.com/web/updates/2016/03/mse-sourcebuffer 的媒体源扩展
-
通过 XMLHttpRequest 下载视频 (link)
我通过 Blob 和 FileReader 将它分成 20 块(1 块大约 3 秒)
创建 MediaSource 并向其添加缓冲区
我将这些片段添加到缓冲区中
我不是一次全部添加,而是一个接一个地添加,即视频中缓冲区为空时,即发生等待事件。
-
下载文件并拆分后,我立即添加第一块进行初始化并设置2.duration
视频开始播放,一切正常
然后,当播放器中没有新数据可以播放时(等待事件之后),我添加一个新片段
视频又开始播放了,来了canplay、playing、canplaythrough、timeupdate、timeupdate……
循环良好地重复 3 次
当播放器时间线达到 10.61 秒时,视频卡住
视频在添加另一片10.61秒后卡住,虽然缓冲区中有新数据(2.5秒),但由于某种原因,添加数据后canplay事件没有发生。 Chrome 期待一些东西,尽管它似乎有新数据 - 继续播放它们。没有错误、没有视频、没有缓冲区、没有 MSE
是什么阻止 chrome 播放下一首曲子尚不清楚,它只是停止,标准***转动并等待数据(?)
有三种方法可以让视频重新开始播放:
video.currentTime = video.currentTime //愚蠢地将当前位置设置为相同 手动拉动时间线,无论在哪里稍微后退或前进 添加新作品然后视频将继续播放,但它已经停止了 31.9 秒(已经更长)
当视频没有停止时:
如果你分成更少的部分,例如 5 个 如果您在 onupdateend 事件之后立即添加新块我无法理解视频停止的神奇时间戳:10.61 和 31.9 秒。
在 Firefox 中,一切都播放到最后,这样的 bug 只在类似 chrome 的浏览器中出现(在 chrome 和 opera 中检查)
这是一个示例代码,你可以在那里看到 10 秒。秤上有加载数据,但没有播放 https://jsfiddle.net/1w4hyrke/1/
function playVideo(video)
var NUM_CHUNKS = 20;
var sourceBuffer;
var segments = [];
var mimeCodec = 'video/mp4; codecs="avc1.4d401f,mp4a.40.2"';
var mediaSource = new MediaSource();
video.src = URL.createObjectURL(mediaSource);
video.play();
function addBuffer()
if (!segments.length) return console.error("segments empty");
if (sourceBuffer.updating) return console.error("buffer updating");
var segment = segments.shift();
console.info("appendBuffer", segments.length, mediaSource.duration);
sourceBuffer.appendBuffer(segment);
mediaSource.addEventListener('sourceopen', () =>
sourceBuffer = mediaSource.addSourceBuffer(mimeCodec);
//sourceBuffer.mode = 'segments';
sourceBuffer.addEventListener('error', (e) =>
console.error("sourceBuffer error", segments);
);
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
sourceBuffer.onupdateend = () =>
console.info("onupdateend");
//addBuffer();
;
$(video).on("waiting abort canplay canplaythrough durationchange emptied encrypted ended error interruptbegin interruptend loadeddata loadedmetadata loadstart mozaudioavailable pause play playing progress ratechange seeked seeking stalled suspend timeupdate volumechange", (e) =>
var vbuf = video.buffered;
var vstart = vbuf && vbuf.length > 0 ? vbuf.start(0) : "";
var vend = vbuf && vbuf.length > 0 ? vbuf.end(0) : "";
var videoDelta = vend ? (vend - video.currentTime).toFixed(2) : "-";
var videoLen = vend ? (vend - vstart).toFixed(2) : "-";
var quality = video.getVideoPlaybackQuality();
let warn = ["timeupdate", "progress"].indexOf(e.type) === -1;
console[!warn ? "log" : "error"].call(console, "VIDEO EVENT", e.type,
`currentTime: $video.currentTime, videoDelta: $videoDelta, videoBuffLen: $videoLen, mediaSource: $mediaSource.duration`,
);
if (e.type === "waiting")
addBuffer();
);
xhr.onload = () =>
let buf = xhr.response;
var uInt8Array = new Uint8Array(buf);
var file = new Blob([uInt8Array],
type: 'video/mp4'
);
var i = 0;
function readChunk()
var reader = new FileReader();
reader.onload = function(e)
console.log('Appending chunk: ' + i);
segments.push(new Uint8Array(e.target.result));
if (i === 0) addBuffer(); //init first chunk
if (i === NUM_CHUNKS - 1)
sourceBuffer.addEventListener('updateend', function()
if (!sourceBuffer.updating && mediaSource.readyState === 'open')
//mediaSource.endOfStream();
);
else
readChunk(++i);
;
var chunkSize = Math.ceil(file.size / NUM_CHUNKS);
var startByte = chunkSize * i;
var chunk = file.slice(startByte, startByte + chunkSize);
reader.readAsArrayBuffer(chunk);
readChunk();
;
xhr.open("GET", "https://nickdesaulniers.github.io/netfix/demo/frag_bunny.mp4");
xhr.send();
);
mediaSource.addEventListener('sourceended', () =>
console.log("mediaSource ended");
);
mediaSource.addEventListener('sourceclose', () =>
console.log("mediaSource closed");
);
mediaSource.addEventListener('error', () =>
console.error("mediaSource error");
);
mediaSource.addEventListener('abort', () =>
console.error("mediaSource abort");
);
$(function()
var video = $('#video')[0];
playVideo(video);
)
【问题讨论】:
【参考方案1】:您的错误是监听来自video
的“等待”事件。事件的定义是:
由于暂时缺少数据而停止播放时触发。
使用它是个坏主意,因为这意味着播放器可以随时暂停(在 Chrome 中,它似乎可能在“等待”事件之后发生),这也会导致口吃。
解决方案是在onupdateend
事件中执行addBuffer()
,我看到您评论了这一行,也许您确实发现了问题?
如果您不想在缓冲区准备就绪时将数据包推送到 SourceBuffer 中,您还可以使用 setTimeout
或 setInterval
进行异步调用。
以下是在 Firefox 和 Chrome 上运行的代码示例: https://jsfiddle.net/thomasjammet/bvm8ueoj/
我删除了不必要的 FileReader 部分(您可以直接将 UInt8Array 数据包推送到 SourceBuffer 实例)。
【讨论】:
【参考方案2】:根据我的经验,视频停止播放的大部分错误都是由视频片段乱序引起的。我认为 MediaSource 期望这些段是单调递增的。因此,我将代码从视频缓冲区的异步发送/接收更改为同步。
【讨论】:
您能否将您更改的代码包含在内,以供其他遇到相同问题的人使用?以上是关于当我向 MSE 缓冲区添加小块时,Chrome 停止播放视频的主要内容,如果未能解决你的问题,请参考以下文章