javascript 带有循环的 <video> 元素不会在 Chrome 或 Firefox 中无缝循环视频
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17930964/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
<video> element with looping does not loop videos seamlessly in Chrome or Firefox
提问by Jake
<video width="640" height="360" src="http://jakelauer.com/fireplace.mp4" autoplay loop muted/>
Fiddle here: http://jsfiddle.net/bWqVf/
在这里小提琴:http: //jsfiddle.net/bWqVf/
IE9 does a decent job of it. Is there any recommendation for ways to overcome this? It is very obvious in videos like this one that SHOULD seamlessly loop, but have an annoying skip/pause.
IE9 在这方面做得不错。有什么方法可以解决这个问题吗?在像这样的视频中很明显应该无缝循环,但有一个恼人的跳过/暂停。
EDIT: As you can see, if I use javascript to simulate the loop, there's a measurable lag: http://jsfiddle.net/bWqVf/13/
编辑:如您所见,如果我使用 javascript 来模拟循环,则会出现可测量的滞后:http: //jsfiddle.net/bWqVf/13/
回答by
The problems seem to be related to how both Chrome and FF fills the pre-load buffers. In both cases they seem to ignore the loop flag and "reset" the buffers from start meaning in that case that at the end the buffers are emptied and pre-loaded again when video starts causing a slight delay/jump.
问题似乎与 Chrome 和 FF 如何填充预加载缓冲区有关。在这两种情况下,他们似乎都忽略了循环标志并从开始“重置”缓冲区,这意味着在这种情况下,当视频开始导致轻微延迟/跳跃时,最后缓冲区被清空并再次预加载。
IE seem to consider the loop flag and continue to fill also towards the end.
IE 似乎考虑了循环标志并继续填充到最后。
This means it's gonna be very hard to make this look seamless. I tried several techniques over a few hours including pre-caching the first frames to 15 frames off-screen canvases. The closest I could get to seamless was modifying the video to have two segments in it (I do not (no longer) have capable hardware so I needed to reduce the dimension as well to test - see fiddle).
这意味着很难让这看起来无缝。我在几个小时内尝试了多种技术,包括将第一帧预缓存到 15 帧屏幕外画布。我最接近无缝的是修改视频以在其中包含两个片段(我没有(不再)具有功能强大的硬件,因此我还需要减小尺寸以进行测试 - 请参阅小提琴)。
However, there are drawbacks here as well:
但是,这里也有缺点:
- The video is double length
- You need to play two instances at the same time
- Two downloads of the same video happens
- Lag compensation will vary from computer to computer
- Browser updates in the future can influence good/bad how the result will end up to be.
- 视频是双倍长度
- 你需要同时玩两个实例
- 发生了同一视频的两次下载
- 延迟补偿因计算机而异
- 未来的浏览器更新会影响结果的好坏。
In other words - there is no stable solution to get around the problem with these browsers.
换句话说 - 没有稳定的解决方案来解决这些浏览器的问题。
I would recommend an extension to what I mention above, to pre-loop some segments. This way you can reduce the glitch.
我建议对我上面提到的内容进行扩展,以预循环一些片段。这样您就可以减少故障。
However, to share what I did here goes.
但是,要分享我在这里所做的事情。
First I extended the video with an extra segment (and reduced the dimension to run it on my computer):
首先,我用一个额外的片段扩展了视频(并缩小了在我的计算机上运行它的尺寸):
Then I used the following code to do an overlapping loop. That is:
然后我用下面的代码来做一个重叠循环。那是:
- I start the videos at the same time, but one video from the middle.
- The video that is currently => middle is shown
- I use a canvas element to draw the video onto
- When at end the current video is switched so that the new video is still the one being played from the middle
- 我同时开始播放视频,但从中间开始播放一个视频。
- 显示当前 => 中间的视频
- 我使用画布元素将视频绘制到
- 当最后切换当前视频时,新视频仍然是从中间播放的视频
The theory here is that this will mask the glitch you get at the start as the video playing is always in the middle (starting on the second segment).
这里的理论是,这将掩盖您在开始时遇到的故障,因为视频播放始终在中间(从第二段开始)。
The code looks like this:
代码如下所示:
As the videos are loaded async we need to count the loads as this technique uses two video instances and the browser seem to be unable to share the download.
由于视频是异步加载的,我们需要计算加载次数,因为这种技术使用两个视频实例,而浏览器似乎无法共享下载。
We also set a new position for video 1 to be at the middle. An event is raised for this when video is moved and ready, so we start everything from that point:
我们还为视频 1 设置了一个新位置,使其位于中间。当视频移动并准备好时,会为此引发一个事件,因此我们从那时开始一切:
v1.addEventListener('canplay', init, false);
v2.addEventListener('canplay', init, false);
v1.addEventListener('timeupdate', go, false);
Handlers:
处理程序:
function init() {
count--; /// = 2
/// both videos are loaded, prep:
if (count === 0) {
length = v1.duration;
mid = length * 0.5;
current = mid;
/// set first video's start to middle
v1.currentTime = mid + lag;
}
}
function go() {
/// remove listener or this will be called for each "frame"
v1.removeEventListener('timeupdate', go, false);
v1.play();
v2.play();
draw();
}
The lag
value is an attempt to compensate for the difference between the two videos starting as they don't start at the exact same time.
该lag
值是为了补偿两个视频开始的差异,因为它们不是在完全相同的时间开始。
The main code draw
simply switches between the videos depending on the position of the main video (v1
) - the frame rate is also reduce to 30 fps to reduce overhead of drawImage
as requestAnimationFrame
runs optimally at 60 fps (the video here is 30 fps so we only need to draw a frame every other time):
主要的代码draw
就可以根据主视频的位置在视频之间切换(v1
) -帧速率也降低到每秒30帧开销的减少drawImage
作为requestAnimationFrame
运行最优化以60fps(这里的视频是每秒30帧,所以我们只需要每隔一次画一个框):
function draw() {
/// reduce frame-rate from 60 to 30
if (reduce === true) {
reduce = false;
requestAnimationFrame(draw);
return;
} else {
reduce = true;
}
/// use video that is >= middle time
var v = v1.currentTime >= mid ? v1 : v2;
/// draw video frame onto canvas
ctx.drawImage(v, 0, 0);
requestAnimationFrame(draw);
}
Now, using canvas opens up other possibilities as well such as making for example a cross-fade between the two videos to smooth the transition further. I didn't implement this as it is outside the scope (in size/broadness), but worth to mention as that could be a solution in itself.
现在,使用画布还开辟了其他可能性,例如在两个视频之间制作交叉淡入淡出以进一步平滑过渡。我没有实现它,因为它超出了范围(在规模/广度上),但值得一提的是,这本身就是一个解决方案。
In any case - as mentioned, this is a solution with many drawbacks but it is the closest I could get to reduce the glitch (using Chrome).
无论如何 - 如前所述,这是一个有很多缺点的解决方案,但它是我能减少故障的最接近的解决方案(使用 Chrome)。
The only solution that can work properly is an internal browser driven one as you would need access to the buffers to be able to do this fully seamlessly.
唯一可以正常工作的解决方案是内部浏览器驱动的解决方案,因为您需要访问缓冲区才能完全无缝地执行此操作。
My "solution" is in essence saying: forget it! It won't work in these browsers, use an repeated looped video instead. :-)
我的“解决方案”本质上是说:算了!它在这些浏览器中不起作用,请改用重复的循环视频。:-)
回答by artygus
I think the problem is related to browser-specific-video-handling.
我认为问题与特定于浏览器的视频处理有关。
As a quirk, you can achieve less latency converting the video to webm
, but you should place it before mp4
source, ie:
作为一个怪癖,您可以减少将视频转换为 的延迟webm
,但您应该将其放在mp4
源之前,即:
<video width="640" height="360" autoplay loop muted>
<source src="http://jakelauer.com/fireplace.webm" type="video/webm" />
<source src="http://jakelauer.com/fireplace.mp4" type="video/mp4" />
</video>
回答by Merritt6616
Heureka!
赫里卡!
We've found the actual, real, work-around-free solution to this problem over at where I work. It explains the inconsistent behavior through multiple developers as well.
我们已经在我工作的地方找到了解决这个问题的实际、真实、无变通的解决方案。它也通过多个开发人员解释了不一致的行为。
The tl;dr version is: Bitrates. Who would've guessed? What I suppose is that many people use standard values for this that usually are around 10 Mbit/s for HD videos if you use the Adobe Media Encoder. This is not sufficient. The correct value would be 18 Mbit/sor maybe even higher. 16 is still a bit janky. I cannot express how well this works. I've, by now, tried the messiest workarounds for about five hours until I found this together with our video editor.
tl;dr 版本是:比特率。谁会猜到?我想很多人为此使用标准值,如果您使用 Adobe Media Encoder,那么 HD 视频通常约为 10 Mbit/s。这还不够。正确的值应该是18 Mbit/s或更高。16 还是有点卡。我无法表达这有多好。到目前为止,我已经尝试了大约五个小时的最混乱的解决方法,直到我与我们的视频编辑器一起找到了它。
I hope this helps everyone and saves you tons of time!
我希望这对每个人都有帮助,并为您节省大量时间!
I also hope it's okay that I posted this in another thread as well, but there are a bunch of questions of the same type about this and I wanted to reach a lot of people.
我也希望我也可以在另一个帖子中发布此内容,但是关于此问题有很多相同类型的问题,我想联系很多人。
回答by Thanos
I don't think your problem is "code-related". It has more to do with the actual video itself. It would be much better if you edit your video for a seamless looping.
我认为您的问题与“代码相关”无关。它更多地与实际视频本身有关。如果您编辑视频以实现无缝循环会更好。
Have a look HEREas it will give you some guidance on how to do so.
看看这里,因为它会给你一些关于如何这样做的指导。
Hope this helps you.
希望这对你有帮助。
EDIT: You can try breaking the video up into two sections: the intro and the looping part. Make a <video>
element for each one and position them in the same place, with the second video hidden. Set an "ended" event on the intro to swap out the display and start the second video. Then, you can set the loop attribute on the second video element.
编辑:您可以尝试将视频分成两部分:介绍部分和循环部分。<video>
为每个元素制作一个 元素并将它们放置在同一位置,隐藏第二个视频。在介绍上设置“结束”事件以换出显示并开始第二个视频。然后,您可以在第二个视频元素上设置循环属性。
You shouldn't have a problem getting the two videos to play seamlessly together as long as you have the preload
attribute on at least the looping video.
只要您preload
至少在循环视频上拥有该属性,就可以让两个视频无缝地一起播放。
If that doesn't work, try making two video elements with the same looping video. While one is playing, you can hide the other and set its currentTime back to zero, so any seeking delay will happen when nobody is looking.
如果这不起作用,请尝试使用相同的循环视频制作两个视频元素。当一个正在播放时,您可以隐藏另一个并将其 currentTime 设置为零,因此当没有人在看时,任何寻找延迟都会发生。
If none of the above works for you, then you can try an other way with javascript. Note that i haven't tested the below code. What it does is starting the video from the 2nd second and when the video reaches the 4th second it will start it again (from the 2nd second).
如果以上都不适合您,那么您可以尝试使用 javascript 的其他方法。请注意,我尚未测试以下代码。它的作用是从第 2 秒开始播放视频,当视频到达第 4 秒时,它将再次开始播放(从第 2 秒开始)。
function playVideo() {
var starttime = 2; // start at 2 seconds
var endtime = 4; // stop at 4 seconds
var video = document.getElementById('player1');
//handler should be bound first
video.addEventListener("timeupdate", function() {
if (this.currentTime >= endtime) {
this.play();
}
}, false);
//suppose that video src has been already set properly
video.load();
video.play(); //must call this otherwise can't seek on some browsers, e.g. Firefox 4
try {
video.currentTime = starttime;
} catch (ex) {
//handle exceptions here
}
}
回答by HerrHerrmann
The solution that worked for me (and doesn't require a huge amount of JavaScript) is something like:
对我有用的解决方案(并且不需要大量的 JavaScript)是这样的:
var video = document.getElementById('background-video');
var loopPoint = 15; // s
function resetVideo() {
if (video.currentTime >= loopPoint) {
video.currentTime = 0;
}
}
video.addEventListener('timeupdate', resetVideo);
Unfortunately I guess this is quite expensive because it will use a callback every time the time of the video/audio updates.
不幸的是,我想这是相当昂贵的,因为它会在每次视频/音频更新时使用回调。
回答by interactivesites
This issue happens to me using the Chromium wrapper with Electron. Regardless of that, I got closer to solving the issue ( not close enough ). Here's a list of things that improved the looping to near seamless jumping back from cuepoint A to B:
这个问题发生在我将 Chromium 包装器与 Electron 一起使用时。无论如何,我离解决问题更近了(不够接近)。以下是将循环改进为几乎无缝从提示点 A 跳回到 B 的内容列表:
- A mp4 video with keyframes only was key (increases video size a bit)
- Get a framerate-sensitive loop. This little tool helps a lot when using keyframes and timecodes: http://x3technologygroup.github.io/VideoFrameDocs/#!/documentation/FrameRates
- 仅具有关键帧的 mp4 视频是关键(稍微增加视频大小)
- 获取帧率敏感循环。这个小工具在使用关键帧和时间码时有很大帮助:http: //x3technologygroup.github.io/VideoFrameDocs/#!/documentation/FrameRates
( 3. The last thing is only needed if things in 1 & 2 do not help. I've loaded the whole video with an XmlHTTPrequest to fill the buffer completely. )
( 3. 最后一件事只有在 1 和 2 中的事情没有帮助时才需要。我已经用 XmlHTTPrequest 加载了整个视频以完全填充缓冲区。)
var xhr = new XMLHttpRequest();
xhr.open('GET', '../assets/video/Comp1.mp4', true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 0) { // I used chromium and electron, usually status == 200 !
var myBlob = this.response;
var vid = URL.createObjectURL(myBlob);
// myBlob is now the blob that the object URL pointed to.
var v = document.getElementById("video");
v.src = vid;
// not needed if autoplay is set for the video element
v.play();
// This requires the VideoFrame-tool (see Nr. 2.)
var videoFrame = new VideoFrame({
id: 'v',
frameRate: 25, // ! must match your video frame rate
callback: function(response) {
// I jump from fram 146 to 72
if (videoFrame.get() === 146) {
// now, jump! Dealbreaker is that the seek is stopping the video
// and the few ms to play it again bugger up the experience.
// Any improvements welcome!
videoFrame.seekBackward(71, function() {
v.play();
});
}
}
});
videoFrame.listen('frame', 25);
v1.play();
}
}
xhr.send(null);
The only issue I encounter with this code is that the seeking stops the video and play() needs to be triggered again. This causes a glitch which I solved by going 3 frames back before the actual cuepoint I want to jump to.
我在这段代码中遇到的唯一问题是搜索停止了视频并且 play() 需要再次触发。这会导致出现故障,我通过在我想要跳转到的实际提示点之前返回 3 帧来解决该故障。
This is still inaccurate if used on different hardware with different videos, but maybe it gets you closer to a solution -- an me too! :)
如果在具有不同视频的不同硬件上使用,这仍然不准确,但也许它可以让您更接近解决方案——我也是!:)
回答by Hank
Ok... after much trial and error, this is what finally worked for me. It seemed to me that the video is not updating after it's ended, so I just remind it all of its properties again when it finishes playing.
好的......经过多次反复试验,这最终对我有用。在我看来,视频在结束后并没有更新,所以我只是在播放结束后再次提醒它所有的属性。
myVid.setAttribute('src', "videos/clip1.mp4");
myVid.autoplay = true;
myVid.addEventListener('ended', vidEnded);
function vidEnded()
{
myVid.setAttribute('src', "videos/clip1.mp4");
myVid.autoplay = true;
}
回答by Pandiyan Cool
The problem is nothing.
问题是没什么。
The starting slide and ending slide is different. If both the slides are same, the looping will looks fine. Because of mismatch in these slides only, it looks like pausing at some seconds. Avoid those things and try out.
开始幻灯片和结束幻灯片是不同的。如果两张幻灯片相同,循环看起来会很好。由于仅在这些幻灯片中不匹配,它看起来像是在几秒钟内暂停。避免这些事情并尝试一下。
回答by Vipul Vaghasiya
check below jsFiddle
URL carefully i add console.log and trace video tag event like play, pause, ended etc, i check in window chrome version 28 (working loop for me without fire pause event )
jsFiddle
仔细检查下面的URL 我添加了 console.log 并跟踪视频标记事件,如播放、暂停、结束等,我检查了窗口 chrome 版本 28(我的工作循环没有火暂停事件)