问题描述
我知道有一个 simple solution 可以在 HTML
中一个接一个地播放视频。但是,我还没有找到一种可以顺利从视频过渡的。我的问题是我的服务器上有很长的视频流,由多个 MP4
文件组成。我通过 HTTP 将这些文件提供给我的网络客户端并使用 video.js
播放它们。为了一个接一个地播放文件,我简单地实现了 this solution,它只是更改了 ended
事件的源。问题是当视频结束并开始新的视频时会出现轻微的口吃。对于用户来说,这些小文件必须以单个连续视频的形式出现,所以我不能接受文件变化时的小卡顿。
有没有办法在 videojs
或仅在 video
的 HTML5
元素中执行此操作?我目前在我的客户端上将我的 MP4 文件保存为 blob
,所以我想也许我可以尝试“合并”blob 以避免更改源。但是,我认为我不能简单地将 MP4 文件连接在一个 blob
中并将它们作为单个源播放。是否有另一种格式可以支持动态“合并”视频文件以避免更改视频源?
请注意,任何 HTTP
流媒体协议(例如 HLS
或 DASH
)对我都不起作用。我的服务器必须从可能的长期存档存储中获取 MP4 文件,我不知道视频的总持续时间。因此,我无法真正为块构建清单。我的文件也是普通的 MP4
,而不是碎片化的 MP4
。
解决方法
如果我们总是有一个视频设置并准备好播放,即两个视频元素,一个在准备好 src 设置的情况下播放一个,似乎没有卡顿。这需要在其他视频上进行测试,但此片段似乎不会在视频之间断断续续。
function createObjectURL(object) {
return (window.URL) ? window.URL.createObjectURL(object) : window.webkitURL.createObjectURL(object);
}
async function makeBlob(i){
let blob = await fetch(srcs[i]).then(r => r.blob());
blobs.push(createObjectURL(blob));
if ( i == (srcs.length -1) ) {
videoEls[0].src = blobs[0];
videoEls[1].src = blobs[1];
videoEls[0].style.visibility = 'visible';
videoEls[0].play();
}
}
const srcs = ['https://vjs.zencdn.net/v/oceans.mp4','https://vjs.zencdn.net/v/oceans.mp4','https://vjs.zencdn.net/v/oceans.mp4'];
let blobs= [];
let curBlob = 0;
let curVideo = 0;
for (let i=0; i < srcs.length; i++) {
makeBlob(i);
}
let videoEls = document.querySelectorAll('video');
for (let j = 0; j < videoEls.length; j++) {
videoEls[j].addEventListener("ended",function() {
videoEls[curVideo].style.visibility = 'hidden';
curVideo = (curVideo+1)%(videoEls.length);
videoEls[curVideo].play();
videoEls[curVideo].style.visibility = 'visible';
curBlob = (curBlob+1)%(blobs.length);
videoEls[(curVideo+1)%(videoEls.length)].src = blobs[curBlob];
});
}
video {
position: absolute;
visibility: hidden;
}
<video></video>
<video></video>
我尝试了很多方法,但在转换过程中总是有一点点卡顿,在音轨上尤其明显。我最终将服务器上的 MP4 文件转换为碎片化的 MP4。我只是将新的 MP4 片段附加到同一个缓冲区中,然后将其作为单个视频播放。