25

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

我遇到了这篇文章:视频缓冲。它表示缓冲对象由多个按开始时间线性顺序排列的时间范围组成。但我找不到以下内容:

  1. 说视频开始。它自己一直持续到 1:45(可能偶尔会停顿,等待进一步的数据),之后我突然跳到 32:45。现在过了一段时间,如果我跳回到 1:27(在最初加载和播放的时间范围内,在我跳转之前),它会像之前加载的那样立即开始播放吗?或者是因为我做了一个跳跃,那部分丢失了,必须再次获取?无论哪种方式,所有此类场景的行为是否一致?

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

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

W3C 规范对此不是很清楚,我也找不到合适的大(比如一个多小时)远程视频资源来测试。

4

5 回答 5

37

视频的缓冲方式取决于浏览器的实现,因此可能因浏览器而异。

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

知道的唯一方法是“查看”浏览器已经或正在加载什么。

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

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

例子

我提供了一个大约 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);
}
于 2013-09-04T22:26:56.880 回答
7

根据

buffered属性包含有关所有当前缓冲时间范围的信息。据我了解,如果缓冲部分丢失,则会从对象中删除(以防万一)。

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

回答您的问题

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

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

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

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

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

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

if (buf.start(0) == 0 && buf.end(0) == v.duration)
于 2013-09-01T16:49:52.177 回答
3
  1. 几乎每个浏览器都会为该会话将缓冲数据保存在缓存中。用户离开该页面后,缓存过期。我不认为用户每次从视频加载点加载视频时都必须加载页面。仅当服务器清除所有缓存数据时,用户才会遇到此问题。HTML5 视频标签将支持这一点,并将视频保存到它被加载的位置。

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

  3. 元数据会自动加载,直到你使用它, <audio preload="none"...这将使浏览器不从服务器下载任何东西,你可以使用它:
    <audio preload="auto|metadata|none"...如果你不使用,除非用户点击播放按钮,否则不会下载任何东西,并且元数据将下载名称,时间和来自服务器的其他元数据,但不是文件,自动将在页面加载后立即开始下载。

我会一直推荐你阅读 jQuery 的一些文档。因为 jQuery 将允许您使用 ajax API 更改和更新内容,并且也会很有帮助。希望你成功!干杯。

于 2013-09-01T17:37:33.347 回答
3

Although the accepted answer's description is excellent, I decided to update its code sample, for several reasons:

  • The progress render task should really be fired only on a progress event.
  • The progress render task is mixed up with some other tasks like drawing the timestamp and the playhead position.
  • The code refers to several DOM elements by their IDs without using document.getElementById().
  • The variable names were all obscured.
  • I thought a forward for() loop was more elegant than a backward while() loop.

Note that I have removed the playhead and timestamp to keep the code clean, as this answer focusses purely on visualisation of the video buffer.

LINK TO ONLINE VIDEO BUFFER VISUALISER

Rewrite of accepted answer's loop() function:

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);
    }
}
于 2017-08-16T18:24:16.140 回答
0

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

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

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

在此处输入图像描述

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"))
于 2020-05-19T21:39:37.793 回答