【问题标题】:How to detect whether HTML5 video has paused for buffering?如何检测 HTML5 视频是否已暂停缓冲?
【发布时间】:2014-02-19 09:55:55
【问题描述】:

我正在尝试测试视频是否断断续续。我注意到当视频暂停缓冲时不会触发pause 事件。检测视频是否已暂停缓冲的最佳方法是什么?

【问题讨论】:

    标签: javascript html html5-video


    【解决方案1】:

    我通过每 x 毫秒检查玩家进度来做到这一点,例如50. 如果玩家没有达到预期的进步,那么我们正在缓冲。这是相当可靠的,因为我发现在视频缓冲的所有情况下都不会触发其他事件,例如 waitingstalled

    请注意,间隔必须大于预期的帧间差异,但我相信您无论如何都不会想要那么精确。考虑到人类很可能无法感知该区域的差异,估计在 ±300 毫秒内的缓冲时间仍然可以。

    检查用户是否没有主动暂停播放很重要。

    var checkInterval  = 50.0 // check every 50 ms (do not use lower values)
    var lastPlayPos    = 0
    var currentPlayPos = 0
    var bufferingDetected = false
    var player = document.getElementById('videoPlayer')
    
    setInterval(checkBuffering, checkInterval)
    function checkBuffering() {
        currentPlayPos = player.currentTime
    
        // checking offset should be at most the check interval
        // but allow for some margin
        var offset = (checkInterval - 20) / 1000
    
        // if no buffering is currently detected,
        // and the position does not seem to increase
        // and the player isn't manually paused...
        if (
                !bufferingDetected 
                && currentPlayPos < (lastPlayPos + offset)
                && !player.paused
            ) {
            console.log("buffering")
            bufferingDetected = true
        }
    
        // if we were buffering but the player has advanced,
        // then there is no buffering
        if (
            bufferingDetected 
            && currentPlayPos > (lastPlayPos + offset)
            && !player.paused
            ) {
            console.log("not buffering anymore")
            bufferingDetected = false
        }
        lastPlayPos = currentPlayPos
    }
    

    【讨论】:

    • 应该注意checkInterval需要大于currentTime的更新间隔。我尝试使用30.0 并没有得到“不再缓冲”状态,因为offset 太小了。
    • @Elmo 我很确定。它不适合你吗?至少 HTML 规范没有改变——对我来说,这似乎仍然是最可靠的方法。您可以检查 waiting 事件(如 brinchirls 的回答中所建议的)现在在不同的浏览器中是否更可靠。
    • @slhck 我只是没有尝试它,因为现在似乎有更好的东西可用。我希望有类似谷歌分析或一些开源库来跟踪视频性能。
    • 计算偏移量时是我弄错了还是代码中有错误?当将checkInterval 更改为例如500var offset = 1 / checkInterval 变为 1/500。每半秒检查一次,这将是一个非常小的偏移量。如果checkInterval 应该是您想要的任何间隔,那么偏移量是否应该以不同的方式计算?我会建议类似:(checkInterval - x) / 1000;。所以间隔的大小以秒为单位,减去一些余量x &lt; checkInterval 以解释不准确
    • @DavidNathan 是的,你完全正确。感谢您发现这一点。我从未发现过这个错误,因为我从未更改过偏移量的值。不确定x 的值应该是多少……甚至可能(checkInterval / 2) / 1000 也可以。
    【解决方案2】:

    您要查找的事件是waiting

    来自spec

    一个等待的 DOM 事件可以作为一个元素的结果被触发 由于其 readyState 属性,可能正在播放停止播放 更改为低于 HAVE_FUTURE_DATA 的值。

    paused 状态不会改变,因为视频仍在“可能播放”(即“正在尝试”播放)。所以waiting 事件触发。当加载足够的数据时,playing 会触发。

    您还可以通过查看networkStatereadyState 两个属性随时检查状态

    if (video.networkState === video.NETWORK_LOADING) {
        // The user agent is actively trying to download data.
    }
    
    if (video.readyState < video.HAVE_FUTURE_DATA) {
        // There is not enough data to keep playing from this point
    }
    

    【讨论】:

    • 据我观察,waiting 事件不会在所有情况下都被触发。我模拟了一个慢速连接,视频每隔几秒钟缓冲一次,没有看到waiting 事件被触发(stalled 也没有)。检查这一点的最佳方法是每隔几毫秒监控一次播放进度。
    • Chrome 稳定在 Linix 中,带有 HTML5 兼容的 MP4 文件。我能够搜索,但是当带宽有限且缓冲区无法填充时,播放器会停止,但不会触发任何事件。这也可能是 API 的错误,但我没有进行任何跨浏览器检查。
    • @slhck 是正确的。我遇到了完全相同的问题,我通过每秒检查播放进度来解决它(我发现较低的值是多余的)。我已经在 Windows 7 上的最新版本的 FF、Chrome 和 IE 上对此进行了测试。在 Chrome 和 IE 上,readyState 属性是无用的,因为它报告HAVE_FUTURE_DATA,而由于缺少数据,播放实际上被冻结。到目前为止,在我测试的三个浏览器中,FF似乎是唯一一个触发waiting事件的浏览器。
    • Chrome 对readyState 的支持似乎很粗略。看到这个错误code.google.com/p/chromium/issues/detail?id=144683code.google.com/p/chromium/issues/detail?id=73609 也许值得为waiting 提交另一个错误
    • "waiting" 事件确实在 2019 年可靠地触发。我注意到很多这些问题是从 2014 年开始的,所以从那时起事情似乎已经发生了变化。
    【解决方案3】:

    您可以只检查缓冲的视频内容长度,如果它小于当前播放部分,则只需触发暂停事件。使用以下代码,您可以检查缓冲的视频长度。

    $vid = $("#video_id");
    
    $vid.on('progress', function(e) {
    
        percentVidLoaded = null;
        // FF4+, Chrome
        if ($vid[0] && $vid[0].buffered && $vid[0].buffered.length > 0 && $vid[0].buffered.end && $vid[0].duration) {
            percentVidLoaded = $vid[0].buffered.end(0) / $vid[0].duration;
        }
        /* Some browsers (e.g., FF3.6 and Safari 5) cannot calculate target.bufferered.end()
         *  to be anything other than 0. If the byte count is available we use this instead.
         *  Browsers that support the else if do not seem to have the bufferedBytes value and
         *  should skip to there.
         */
        else if ($vid[0] && $vid[0].bytesTotal != undefined && $vid[0].bytesTotal > 0 && $vid[0].bufferedBytes != undefined) {
            percentVidLoaded = $vid[0].bufferedBytes / $vid[0].bytesTotal;
        }
        if (percentVidLoaded !== null) {
            percentVidLoaded = 100 * Math.min(1, Math.max(0, percentVidLoaded));
        }
    });
    

    【讨论】:

    • 使用typeof !== "undefined"
    • 这只会读取缓冲区并将其转换为百分比。这不会根据 video.currentTime 检查缓冲区。
    • 反正video.currentTime的值不是视频应该在的currentTime,不幸的是真正的currentTime,所以没有办法检查缓冲的百分比。
    【解决方案4】:

    根据 MDN 文档的“等待”事件是 -

    当请求的操作(例如播放)延迟等待另一个操作(例如搜索)完成时发送。

    所以搜索或网络请求将触发“等待”。 cmets 中的迈克尔确实指出,截至 2019 年,“等待”是可靠的,所以我试了一下,它奏效了!

    let slowInternetTimeout = null;
    
    let threshold = 3000; //ms after which user perceives buffering
    
    video.addEventListener('waiting', () => {
        slowInternetTimeout = setTimeout(() => {
            //show buffering
        }, threshold);
    });
    video.addEventListener('playing', () => {
        if(slowInternetTimeout != null){
            clearTimeout(slowInternetTimeout);
            slowInternetTimeout = null;
        }
    });
    

    【讨论】:

      【解决方案5】:

      您需要检查缓冲区是否小于当前视频时间。如果是这样,则视频正在缓冲。但是,您应该以较小的容差进行检查,以确保在确实需要缓冲之前检测到它。

      例子:

      var video = document.getElementById("myVideo");
      var prevBuffer = {
          "buffer": null,
          "time": null
      };
      var isBuffering = function(){
      
          if(video && video.buffered && video.buffered.end && video.buffered.length > 0){
              var buffer = video.buffered.end(0);
              var time   = video.currentTime;
      
              // Check if the video hangs because of issues with e.g. performance
              if(prevBuffer.buffer === buffer && prevBuffer.time === time && !video.paused){
                  return true;
              }
              prevBuffer = {
                  "buffer": buffer,
                  "time": time
              };
              // Check if video buffer is less
              // than current time (tolerance 3 sec)
              if((buffer - 3) < time){
                  return true;
              }
          }
          return false;
      
      };
      video.addEventListener("play", function(e){
          // Make sure this handler is only called once
          e.target.removeEventListener(e.type, arguments.callee);
          // Give browsers 3secs time to buffer
          setTimeout(function(){
              // As "progress", "stalled" or "waiting" aren't fired
              // reliable, we need to use an interval
              var interval = setInterval(function(){
                  if(isBuffering()){
                      clearInterval(interval);
                      console.log("Buffering");
                  }
              }, 500);
          }, 3000);
      });
      

      【讨论】:

      • 即使缓冲区结束时间大于当前时间,流也可能会停止。我观察到在某些浏览器上,无论我们是否由于下溢以及 readyState HAVE_ENOUGH_DATA 以及 buffered.end() > currentTime + 容差而暂停,我们都会有网络状态 NETWORK_LOADING。容差值是导致问题的原因。我们能否从视频元素中读取容差值,以便我们始终知道它是否正在缓冲?
      • 据我所知,没有观察缓冲区状态的具体方法。没有什么比你刚才提到的(“networkState”)和“readyState”属性更多的了。但是,它们是为不同的方法而构建的。这是在哪个浏览器中发生的?
      • @angie 我刚刚编辑了我的答案。这也可以解决您的用例。
      • 感谢编辑。 3 秒的值是基于观察吗? 3 秒的容差可以解决问题(尽管如果播放器很聪明,它可能会因带宽而异,我不确定)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-03
      • 1970-01-01
      • 2019-01-09
      • 1970-01-01
      • 2010-12-24
      相关资源
      最近更新 更多