带有循环功能的<video>元素无法在Chrome或Firefox </video>中无缝循环播放视频

时间:2013-07-29 17:58:56

标签: javascript html google-chrome firefox video

<video width="640" height="360" src="http://jakelauer.com/fireplace.mp4" autoplay loop muted/>

在这里小提琴:http://jsfiddle.net/bWqVf/

IE9做得不错。是否有任何建议可以克服这个问题?在像这样的视频中,非常明显地应该无缝循环,但是有一个恼人的跳过/暂停。

编辑: 如您所见,如果我使用javascript来模拟循环,则存在可测量的延迟:http://jsfiddle.net/bWqVf/13/

9 个答案:

答案 0 :(得分:15)

问题似乎与Chrome和FF如何填充预加载缓冲区有关。在这两种情况下,它们似乎都忽略了循环标志并从开始“重置”缓冲区意味着在这种情况下缓冲区被清空并在视频开始时再次预加载导致轻微的延迟/跳转。

IE似乎考虑了循环标志并继续填充到最后。

这意味着要让这种外观变得无缝是非常困难的。我在几个小时内尝试了几种技术,包括将第一帧预先缓存到15帧的屏幕外画布。我最接近无缝的是修改视频以在其中有两个片段(我不(不再)有能力的硬件所以我需要减少尺寸以测试 - 请参阅小提琴)。

但是,这里也有缺点:

  • 视频是双倍长度
  • 您需要同时播放两个实例
  • 发生同一视频的两次下载
  • 滞后补偿因计算机而异
  • 将来的浏览器更新会影响结果的好坏。

换句话说 - 没有稳定的解决方案来解决这些浏览器的问题。

我建议对上面提到的内容进行扩展,以预先循环一些段。这样可以减少故障。

但是,分享我在这里所做的事情。

首先,我使用额外的片段扩展视频(并缩小尺寸以在我的计算机上运行它):

premiere

然后我使用以下代码进行重叠循环。那就是:

  • 我同时开始播放视频,但中间有一段视频。
  • 目前的视频=&gt;中间显示
  • 我使用canvas元素将视频绘制到
  • 当结束时,当前视频被切换,以便新视频仍然是从中间播放的视频

这里的理论是,这将掩盖你在开始时得到的故障,因为视频播放总是在中间(从第二段开始)。

代码如下所示:

由于视频是异步加载的,我们需要计算负载,因为此技术使用两个视频实例,浏览器似乎无法共享下载。

我们还为视频1设置了一个新位置。当视频移动并准备就绪时,会引发一个事件,因此我们从那一点开始所有事情:

v1.addEventListener('canplay', init, false);
v2.addEventListener('canplay', init, false);
v1.addEventListener('timeupdate', go, false);

处理程序:

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();
}

lag值是为了弥补两个视频之间的差异,因为它们并非完全同时开始。

主代码draw只是根据主视频的位置(v1)在视频之间切换 - 帧速率也降低到30 fps,以减少drawImage的开销因为requestAnimationFrame最佳运行速度为60 fps(此处的视频为30 fps,因此我们只需要每隔一段时间绘制一帧):

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);
}

现在,使用画布还可以打开其他可能性,例如在两个视频之间进行交叉淡入淡出以进一步平滑过渡。我没有实现这个,因为它超出了范围(大小/宽度),但值得一提,因为这本身就是一个解决方案。

在任何情况下 - 如上所述,这是一个有许多缺点的解决方案,但它是我可以减少故障的最接近的(使用Chrome)。

唯一可以正常工作的解决方案是内部浏览器驱动,因为您需要访问缓冲区才能完全无缝地执行此操作。

我的“解决方案”实质上是在说:忘了!它不适用于这些浏览器,而是使用重复的循环视频。 : - )

答案 1 :(得分:3)

我认为问题与浏览器特定的视频处理有关。

作为一个怪癖,您可以减少将视频转换为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>

答案 2 :(得分:1)

<强> Heureka!

我们在工作的地方找到了解决这个问题的实际,真实,无解决方案。它解释了多个开发人员的不一致行为。

tl; dr版本是:比特率。谁会猜到谁?我想的是,如果使用Adobe Media Encoder,许多人使用标准值,对于高清视频通常约为10 Mbit / s。这还不够。正确的值将是 18 Mbit / s 或甚至更高。 16仍然有点janky。我无法表达这种效果如何。到目前为止,我已经尝试了最混乱的解决方法大约五个小时,直到我和我们的视频编辑器一起找到它。

我希望这可以帮助每个人并为您节省大量时间!

我也希望我也可以在另一个帖子中发布这个,但是有很多相同类型的问题,我希望能够吸引很多人。

答案 3 :(得分:0)

我不认为您的问题与“代码相关”。它更多地与实际视频本身有关。如果您编辑视频以实现无缝循环,那会好得多。

看看 HERE ,因为它会为您提供有关如何操作的指导。

希望这会对你有所帮助。

编辑:您可以尝试将视频分为两部分:简介和循环部分。为每个元素创建一个<video>元素并将它们放在同一个位置,隐藏第二个视频。在介绍中设置“已结束”事件以交换显示并启动第二个视频。然后,您可以在第二个视频元素上设置循环属性。

只要您至少在循环视频上拥有preload属性,就不会有问题让两个视频无缝地一起播放。

如果这不起作用,请尝试使用相同的循环视频制作两个视频元素。当一个人正在玩时,你可以隐藏另一个并将其当前时间设置回零,所以任何寻求延迟都会在没人看的时候发生。

如果以上都不适合您,那么您可以尝试使用其他方式使用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
    }
}

答案 4 :(得分:0)

对我有用的解决方案(并且不需要大量的JavaScript)类似于:

var video = document.getElementById('background-video');
var loopPoint = 15; // s

function resetVideo() {
  if (video.currentTime >= loopPoint) {
    video.currentTime = 0;
  }
}
video.addEventListener('timeupdate', resetVideo);

不幸的是,我认为这非常昂贵,因为每次视频/音频更新时都会使用回调。

答案 5 :(得分:0)

这个问题发生在我身上使用带有Electron的Chromium包装器。无论如何,我更接近解决问题(不够接近)。这里列出了一些改进循环到从cuepoint A到B附近无缝跳回的东西:

  1. 只有关键帧的mp4视频才是关键(稍微增加视频大小)
  2. 获取帧率敏感的循环。使用关键帧和时间码时,这个小工具可以提供很多帮助:http://x3technologygroup.github.io/VideoFrameDocs/#!/documentation/FrameRates
  3. (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);

    我遇到这个代码的唯一问题是搜索会停止视频,而play()需要再次触发。这导致了一个小故障,我通过在我想要跳转到的实际提示点之前返回3帧来解决。

    如果在具有不同视频的不同硬件上使用,这仍然是不准确的,但也许它可以让您更接近解决方案 - 我也是! :)

答案 6 :(得分:-2)

问题无关紧要。

起始幻灯片和结束幻灯片不同。如果两个幻灯片都相同,则循环看起来很好。由于这些幻灯片不匹配,它看起来像在几秒钟暂停。避免这些事情并尝试。

答案 7 :(得分:-2)

检查以下jsFiddle网址我小心地添加了console.log和跟踪视频标签事件,如播放,暂停,结束等,我检查窗口chrome版本28(我的工作循环没有火灾暂停事件)

http://jsfiddle.net/bWqVf/6/

答案 8 :(得分:-2)

好的......经过多次试验和错误,这才是最终适合我的。在我看来,视频在结束后没有更新,所以我只是在完成播放时再次提醒它所有的属性。

myVid.setAttribute('src', "videos/clip1.mp4");
myVid.autoplay = true;  
myVid.addEventListener('ended', vidEnded);  

function vidEnded()
{
    myVid.setAttribute('src', "videos/clip1.mp4");
    myVid.autoplay = true;          
}