JavaScript中HTML5视频的完全预加载

20

我有一段高质量的视频,由于它将作为很多图像分析的基础,其中每个帧都将被重新绘制到画布上并进行操作,因此我无法对其进行太多压缩。

我试图在播放之前预加载整个视频,因为不能让视频停止、缓冲和继续。是否有一种事件可以监听,表示整个视频已经预加载完毕,然后再开始播放?

以下是我在JS/jQuery中的方法:

this.canvas            = this.el.find("canvas")[0];
this.video             = this.el.find("video")[0];
this.ctx               = this.canvas.getContext("2d");
this.video.autoplay    = false;

this.video.addEventListener("play",this.draw)
this.video.addEventListener("timeupdate",this.draw)
this.video.addeventlistener("ended",this.trigger("complete",this))
7个回答

27

这将在JavaScript中加载整个视频

var r = new XMLHttpRequest();
r.onload = function() {
    myVid.src = URL.createObjectURL(r.response);
    myVid.play();
};
if (myVid.canPlayType('video/mp4;codecs="avc1.42E01E, mp4a.40.2"')) {
    r.open("GET", "slide.mp4");
}
else {
    r.open("GET", "slide.webm");
}

r.responseType = "blob";
r.send();

应该就是这样了!我还没测试,不过这正是我要找的。非常感谢。 - kroe
@kroe 只需点击此答案旁边的勾即可。 - Marco Kerwitz
2
似乎我的视频播放器仍然只在下载一定百分比后暂停。在我的情况下,它预加载约38-40%,然后等待视频开始播放,然后以滴水的方式传输文件的其余部分。是否有解决方法可以下载整个文件? - chadkouse
这种方法可以确保使用JavaScript下载整个视频。请使用Fiddler或Chrome中的网络选项卡检查您的网络流量以确保下载成功。如果以上方法均无效,可以尝试使用一些库,例如jplayer、popcorn.org或videojs。 - jacobsgriffith
这个方法真的很好,谢谢!我本来担心 data URL 会有上限限制 -- 我找了一下,有些人说 data URL 只适用于小文件。但是使用你的方法,我已经成功预加载了 ~100MB 的视频,并且在 Chrome 中可以可靠地运行。 - Marius
显示剩余2条评论

10
canplaythrough是一个事件,当足够的数据已下载,可以无需缓冲即可播放时触发。来自Opera团队出色(尽管现在可能有点过时)的资源"Everything you need to know about HTML5 video and audio"。 如果加载成功,无论使用src属性还是使用source元素,都会触发progress事件,随着数据的下载,将会触发loadedmetadata事件以确定视频的尺寸和持续时间,当足够的数据被加载以渲染一帧,将会触发loadeddata事件。当足够的数据被加载以便能够播放视频的一小部分时,将会触发canplay事件;当浏览器确定可以连续播放整个视频而无需停止下载更多数据时,将会触发canplaythrough事件;如果视频具有autoplay属性,则此时也会开始播放视频。
'canplaythrough'支持矩阵在此处可用:https://caniuse.com/mdn-api_htmlmediaelement_canplaythrough_event。您可以通过将load元素绑定到同一函数来规避支持限制,因为它将在这些元素上触发。

这个完全支持吗? - Alex
4
考虑到大部分主要厂商对HTML5视频规范实现的不够完善,可能不行。 - Simon West
谢谢这个。不过需要注意的是,Safari浏览器不会执行“canplaythrough”事件。所以你应该使用“canplay”代替它,因为它确实会触发。但是在Safari上似乎只有“loadeddata”事件总是会触发,这有点随机。 - Placeable
“areweplayingyet” 的链接已经失效了,你是否已经通过 iOS 设备上的 load 事件实现了一个可用的版本? - Volker E.
Canplaythrough是一个神话。它根本不起作用,特别是在火狐浏览器中。 - Dr.Knowitall

9
  1. 使用fetch下载视频
  2. 将响应转换为blob对象
  3. 从blob对象创建一个URL (例如:blob:http://localhost:8080/df3c4336-2d9f-4ba9-9714-2e9e6b2b8888)
async function preloadVideo(src) {
  const res = await fetch(src);
  const blob = await res.blob();
  return URL.createObjectURL(blob);
}

用法:

const video = document.createElement("video");
video.src = await preloadVideo("https://example.com/video.mp4");

这不是预加载。这只是从远程服务器获取到磁盘上。 "预加载" 是指当视频(或图像)被 加载到内存 中时。 - plafer
1
这满足作者问题的限制。虽然这并不严格算作“预加载”,但它将视频加载到 Blob 中(存储在用户机器上,直到页面生命周期结束),并防止视频缓冲或停止的问题。 - Enijar
1
这个方法太棒了!!它会下载整个视频,播放或跳转时不会有任何下载。 - alsotang

2
希望这能帮到您。
var xhrReq = new XMLHttpRequest();
xhrReq.open('GET', 'yourVideoSrc', true);
xhrReq.responseType = 'blob';

xhrReq.onload = function() {
    if (this.status === 200) {
        var vid = URL.createObjectURL(this.response);
        video.src = vid;
    }
}
xhrReq.onerror = function() {
    console.log('err' ,arguments);
}
xhrReq.onprogress = function(e){
    if(e.lengthComputable) {
        var percentComplete = ((e.loaded/e.total)*100|0) + '%';
        console.log('progress: ', percentComplete);
    }
}
xhrReq.send();

如果您的视频来源具有不同的域名,则需要处理CORS。


1
这可以工作吗?
video.onloadeddata = function(){
  video.onseeked = function(){
    if(video.seekable.end(0) >= video.duration-0.1){
      alert("Video is all loaded!");
    } else {
      video.currentTime=video.buffered.end(0); // Seek ahead to force more buffering
    }
  };
  video.currentTime=0; // first seek to trigger the event
};

1

到目前为止,我们发现最可靠的解决方案是播放视频并等待缓冲区完全加载。

这意味着如果视频很长,您将不得不等待几乎整个视频长度。

我知道这不太好。

想知道是否有人找到了其他神奇可靠的方法来做到这一点(理想情况下使用类似PreloadJS的东西,在HTML5视频不受支持时自动回退到flash)。


1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接