HTML5视频缓冲属性功能

时间:2013-08-24 19:49:56

标签: javascript html5 html5-video buffering

我正在设计一个自定义HTML5视频播放器。因此,它将拥有自己的自定义滑块来模仿视频进度,因此我需要了解HTML5视频的整个缓冲框。

我看到了这篇文章:Video Buffering。它表示缓冲对象由几个时间范围组成,它们以开始时间的线性顺序排列。但我找不到以下内容:

  1. 说视频开始了。它自己一直持续到1:45(偶尔也会停滞,等待进一步的数据),之后我突然跳到32:45。现在过了一段时间,如果我跳回到1:27(在最初加载和播放的时间范围内,在我跳跃之前),它会立即开始播放,因为之前已经加载了吗?或者是因为我跳了一下,那部分会丢失并且必须再次取出?无论哪种方式,所有这些情况的行为是否一致?

  2. 假设我做了5次或6次这样的跳转,每次等待几秒钟以便在跳转后加载一些数据。这是否意味着buffered对象将存储所有这些时间范围?或者有些人会迷路?它是一种堆栈类型的东西,当更多的范围由于进一步的跳跃而加载时,较早的范围会弹出吗?

  3. 检查buffered对象是否有一个时间范围从0开始(忘记直播)并以视频持续时间长度结束,确保整个视频资源已完全加载?如果没有,是否有某种方式可以知道整个视频已被下载,并且任何部分都是可搜索的,视频可以连续播放而不会有片刻的延迟?

  4. W3C规范对此并不十分清楚,我也找不到适当大的(比如一个多小时)远程视频资源来测试。

5 个答案:

答案 0 :(得分:31)

视频的缓冲方式取决于浏览器的实现,因此浏览器可能会有所不同。

各种浏览器可以使用不同的因素来确定保留或丢弃缓冲区的一部分。旧段,磁盘空间,内存和性能是典型因素。

真正了解的唯一方法是“查看”浏览器的内容或正在加载的内容。

为此,我制作了一个缓冲区查看器,显示缓冲区中的哪个部分。查看器将显示整个缓冲区的当前和所有部分:

<强> ONLINE BUFFER VIEWER

例如 - 在Chrome中我播放了几秒钟然后我跳过了大约30秒,您可以看到它开始从该位置开始加载另一部分。

(缓冲区似乎也与关键帧有关,因此可以对该缓冲区中的n帧进行解码。这意味着缓冲区可以在实际位置之前开始加载数据。)

Example

我提供了大约1分钟的演示视频 - 但这不足以进行正确的测试。免费提供包含更长视频的视频链接(如果您希望我用此更新演示,请分享)。

主函数将遍历视频元素上的buffered对象。它将呈现画布正下方的所有部分以红色显示。

您可以在此查看器上单击(不拖动)以将视频移动到不同的位置。

/// buffer viewer loop (updates about every 2nd frame)
function loop() {

    var b = vid.buffered,  /// get buffer object
        i = b.length,      /// counter for loop
        w = canvas.width,  /// cache canvas width and height
        h = canvas.height,
        vl = vid.duration, /// total video duration in seconds
        x1, x2;            /// buffer segment mark positions

    /// clear canvas with black
    ctx.fillStyle = '#000';
    ctx.fillRect(0, 0, w, h);

    /// red color for loaded buffer(s)
    ctx.fillStyle = '#d00';

    /// iterate through buffers
    while (i--) {
        x1 = b.start(i) / vl * w;
        x2 = b.end(i) / vl * w;
        ctx.fillRect(x1, 0, x2 - x1, h);
    }

    /// draw info
    ctx.fillStyle = '#fff';

    ctx.textBaseline = 'top';
    ctx.textAlign = 'left';
    ctx.fillText(vid.currentTime.toFixed(1), 4, 4);

    ctx.textAlign = 'right';
    ctx.fillText(vl.toFixed(1), w - 4, 4);

    /// draw cursor for position
    x1 = vid.currentTime / vl * w;

    ctx.beginPath();
    ctx.arc(x1, h * 0.5, 7, 0, 2 * Math.PI);
    ctx.fill();

    setTimeout(loop, 29);
}

答案 1 :(得分:7)

根据

buffered属性包含有关所有当前缓冲时间范围的信息。根据我的理解,如果缓冲部分丢失,它将从对象中删除(万一发生)。

特别是最后一个链接对于理解这个问题似乎非常有用(因为它提供了代码示例),但请记住这些是mozilla文档,并且在其他浏览器中支持可能会有所不同。

回答你的问题

  

说视频开始了。它自己一直持续到1:45(偶尔也会停滞,等待进一步的数据),之后我突然跳到32:45。现在过了一段时间,如果我跳回到1:27(在最初加载和播放的时间范围内,在我跳转之前),它会立即开始播放,因为之前已经加载了吗?

应该在跳回时立即播放,除非该部分的缓冲区被卸载。我认为假设缓冲区或缓冲区范围在某些时候卸载是非常合理的,如果整个缓冲区大小超过一定的体积。

  

假设我做了5或6次这样的跳转,每次等待几秒钟以便在跳转后加载一些数据。这是否意味着缓冲对象将存储所有这些时间范围?

是的,所有缓冲的范围都应该通过属性可读。

  

检查缓冲对象是否有一个从0开始的时间范围(忘记直播)并以视频持续时间长度结束,确保整个视频资源已经完全加载?

是的,这是最后一个链接中的代码示例。显然,这是一种确定整个视频是否已加载的适用方法。

if (buf.start(0) == 0 && buf.end(0) == v.duration)

答案 2 :(得分:3)

  1. 几乎每个浏览器都将缓冲的数据保存在该会话的缓存中。用户离开该页面后,缓存将过期。我不认为用户每次从加载视频的点加载视频时都必须加载页面。仅当服务器清除所有缓存数据时,用户才会遇到此问题。 HTML5视频代码将支持此功能,并将视频保存到点,直到加载完毕。

  2. 这并不意味着会话已经丢失,这意味着对象(如果您使用的是Flash播放器)正在寻找该特定点的某些数据,或者html5视频标签是否存在某些问题,因为INTERNET连接失败或其他一些服务器错误。

  3. 自动加载元数据,直到你使用它 <audio preload="none"...这将使浏览器不从服务器下载任何内容,您可以将其用作:
    <audio preload="auto|metadata|none"...如果您使用none,则除非用户单击播放按钮,否则不下载任何内容,元数据将从服务器下载名称,时间和其他元数据,但不会以某种方式下载文件,auto会在页面加载后立即开始下载

  4. 我将一直推荐您阅读jQuery的一些文档。由于jQuery允许您使用ajax API更改和更新内容,因此也会有所帮助。希望你成功!欢呼声。

答案 3 :(得分:0)

虽然接受的答案描述非常好,但我决定更新其代码示例,原因如下:

  • 只应在progress事件上触发进度渲染任务。
  • 进度渲染任务与其他一些任务混在一起,例如绘制时间戳和播放头位置。
  • 代码通过ID来引用多个DOM元素,而不使用document.getElementById()
  • 变量名称都被遮盖了。
  • 我认为前向for()循环比后向while()循环更优雅。

请注意,我已删除播放头和时间戳以保持代码清洁,因为此答案主要关注视频缓冲区的可视化。

LINK TO ONLINE VIDEO BUFFER VISUALISER

重写已接受答案的loop()功能:

function drawProgress(canvas, buffered, duration){
    // I've turned off anti-aliasing since we're just drawing rectangles.
    var context = canvas.getContext('2d', { antialias: false });
    context.fillStyle = 'blue';

    var width = canvas.width;
    var height = canvas.height;
    if(!width || !height) throw "Canvas's width or height weren't set!";
    context.clearRect(0, 0, width, height); // clear canvas

    for(var i = 0; i < buffered.length; i++){
        var leadingEdge = buffered.start(i) / duration * width;
        var trailingEdge = buffered.end(i) / duration * width;
        context.fillRect(leadingEdge, 0, trailingEdge - leadingEdge, height);
    }
}

答案 4 :(得分:0)

这只是这个出色答案https://stackoverflow.com/a/18624833/985454

的变体

我只是使它工作而无需任何工作,并且增加了一些额外的好处。一切都是自动的。

  • 当前用于全屏视频播放,例如netflix或hbogo
  • 自动创建画布
  • 将宽度自动更新为100%视口宽度
  • 用作书签
  • 不会阻塞视图(透明,高度2px)

enter image description here

function prepare() {
    console.log('prepare');

    _v = $('video')[0];
    _v.insertAdjacentHTML('afterend',
    `<canvas
        id="WowSuchName"
        height="1"
        style="
            position: absolute;
            bottom: 0;
            left: 0;
            opacity: 0.4;
        "></canvas>`);

    _c = WowSuchName
    _cx = _c.getContext('2d');

    window.addEventListener('resize', resizeCanvas, false);

    function resizeCanvas() {
        console.log('resize');
        _c.width = window.innerWidth;
    }
    resizeCanvas();
}

/// buffer viewer loop (updates about every 2nd frame)
function loop() {
    if (!window.WowSuchName) { prepare(); }

    var b = _v.buffered,  /// get buffer object
        i = b.length,     /// counter for loop
        w = _c.width,     /// cache canvas width and height
        h = _c.height,
        vl = _v.duration, /// total video duration in seconds
        x1, x2;           /// buffer segment mark positions

    /// clear canvas
    _cx.clearRect(0, 0, w, h);

    /// color for loaded buffer(s)
    _cx.fillStyle = '#888';

    /// iterate through buffers
    while (i--) {
        x1 = b.start(i) / vl * w;
        x2 = b.end(i) / vl * w;
        _cx.fillRect(x1, 0, x2 - x1, h);
    }

    /// draw cursor for position
    _cx.fillStyle = '#fff';
    x1 = _v.currentTime / vl * w;
    _cx.fillRect(x1-1, 0, 2, h);

    setTimeout(loop, 29);
}

loop();

书签的代码在这里

javascript:eval(atob("CmZ1bmN0aW9uIHByZXBhcmUoKSB7CiAgICBjb25zb2xlLmxvZygncHJlcGFyZScpOwoKICAgIF92ID0gJCgndmlkZW8nKVswXTsKICAgIF92Lmluc2VydEFkamFjZW50SFRNTCgnYWZ0ZXJlbmQnLAogICAgYDxjYW52YXMKICAgICAgICBpZD0iV293U3VjaE5hbWUiCiAgICAgICAgaGVpZ2h0PSIxIgogICAgICAgIHN0eWxlPSIKICAgICAgICAgICAgcG9zaXRpb246IGFic29sdXRlOwogICAgICAgICAgICBib3R0b206IDA7CiAgICAgICAgICAgIGxlZnQ6IDA7CiAgICAgICAgICAgIG9wYWNpdHk6IDAuNDsKICAgICAgICAiPjwvY2FudmFzPmApOwogICAgCiAgICBfYyA9IFdvd1N1Y2hOYW1lCiAgICBfY3ggPSBfYy5nZXRDb250ZXh0KCcyZCcpOwoKICAgIHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdyZXNpemUnLCByZXNpemVDYW52YXMsIGZhbHNlKTsKCiAgICBmdW5jdGlvbiByZXNpemVDYW52YXMoKSB7CiAgICAgICAgY29uc29sZS5sb2coJ3Jlc2l6ZScpOwogICAgICAgIF9jLndpZHRoID0gd2luZG93LmlubmVyV2lkdGg7CiAgICB9CiAgICByZXNpemVDYW52YXMoKTsKfQoKLy8vIGJ1ZmZlciB2aWV3ZXIgbG9vcCAodXBkYXRlcyBhYm91dCBldmVyeSAybmQgZnJhbWUpCmZ1bmN0aW9uIGxvb3AoKSB7CiAgICBpZiAoIXdpbmRvdy5Xb3dTdWNoTmFtZSkgeyBwcmVwYXJlKCk7IH0KCiAgICB2YXIgYiA9IF92LmJ1ZmZlcmVkLCAgLy8vIGdldCBidWZmZXIgb2JqZWN0CiAgICAgICAgaSA9IGIubGVuZ3RoLCAgICAgLy8vIGNvdW50ZXIgZm9yIGxvb3AKICAgICAgICB3ID0gX2Mud2lkdGgsICAgICAvLy8gY2FjaGUgY2FudmFzIHdpZHRoIGFuZCBoZWlnaHQKICAgICAgICBoID0gX2MuaGVpZ2h0LAogICAgICAgIHZsID0gX3YuZHVyYXRpb24sIC8vLyB0b3RhbCB2aWRlbyBkdXJhdGlvbiBpbiBzZWNvbmRzCiAgICAgICAgeDEsIHgyOyAgICAgICAgICAgLy8vIGJ1ZmZlciBzZWdtZW50IG1hcmsgcG9zaXRpb25zCgogICAgLy8vIGNsZWFyIGNhbnZhcwovLyAgICAgX2N4LmZpbGxTdHlsZSA9ICcjMDAwJzsKLy8gICAgIF9jeC5maWxsUmVjdCgwLCAwLCB3LCBoKTsKICAgIF9jeC5jbGVhclJlY3QoMCwgMCwgdywgaCk7CgogICAgLy8vIGNvbG9yIGZvciBsb2FkZWQgYnVmZmVyKHMpCiAgICBfY3guZmlsbFN0eWxlID0gJyM4ODgnOwoKICAgIC8vLyBpdGVyYXRlIHRocm91Z2ggYnVmZmVycwogICAgd2hpbGUgKGktLSkgewogICAgICAgIHgxID0gYi5zdGFydChpKSAvIHZsICogdzsKICAgICAgICB4MiA9IGIuZW5kKGkpIC8gdmwgKiB3OwogICAgICAgIF9jeC5maWxsUmVjdCh4MSwgMCwgeDIgLSB4MSwgaCk7CiAgICB9CgogICAgLy8vIGRyYXcgY3Vyc29yIGZvciBwb3NpdGlvbgogICAgX2N4LmZpbGxTdHlsZSA9ICcjZmZmJzsKICAgIHgxID0gX3YuY3VycmVudFRpbWUgLyB2bCAqIHc7CiAgICBfY3guZmlsbFJlY3QoeDEtMSwgMCwgMiwgaCk7CgogICAgc2V0VGltZW91dChsb29wLCAyOSk7Cn0KCmxvb3AoKTsK"))