2013-08-24 192 views
18

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

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

  1. 说视频开始。它自行持续到1:45(偶尔也许会拖延,等待更多数据),之后我突然跳到32:45。现在过了一段时间后,如果我跳回1:27(在最初加载和播放的时间范围内,在我跳转之前),它会立即开始播放,因为它之前已经加载过了吗?或者是因为我跳了一跳,那部分输了,将不得不再次提取?无论哪种方式,所有这些情景的行为是否一致?

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

  3. 将检查buffered对象是否具有从0开始的一个时间范围(忘记实时流)并结束于视频持续时间长度,以确保整个视频资源已完全加载?如果没有,有什么方法可以知道整个视频已经被下载,并且任何部分都是可搜索的,从哪个视频可以持续播放直至结束而没有一点延迟?

的W3C规范都没有对这个很清楚,我也无法找到一个合适的大(比如一个多小时),远程视频资源进行测试。

回答

27

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

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

真正知道的唯一方法是“查看”浏览器具有或正在加载的内容。

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

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

当你暂停视频并运行buffered.end(索引)时,它返回1并停止工作。这是一种常见的行为吗?找不到相关问题。 –

7

根据

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

Esepcially的最后一个环节,似乎是理解这个问题(因为它提供了一个代码示例)非常有用,但要记住,这些是Mozilla的文件和支持可能会在其他浏览器不同。

回答您的问题

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

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

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

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

将检查缓冲对象是否具有从0开始的一个时间范围(忽略实时流)并结束于视频持续时间长度,以确保整个视频资源已完全加载?

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

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

的问题是,我试图与缓冲试验。我让视频流持续2-3分钟,然后跳到10多分钟,然后让它流2分钟,然后到20和相同。然后我预计''缓冲区'会有3个范围,而我找到一个,目前正在缓冲。 – SexyBeast

+1

@Cupidvogel嗯,无论你在每个位置上花了足够的时间,它有足够的时间下载视频的大部分连续部分,或者我正在谈论的卸载实际上是在那里发生的。无论哪种方式,“缓冲”属性都应该反映发生的情况。 –

+0

是的,我也这么认为。但是我只需要3个部分,看起来数量太小,无法开始清除缓存。不,结果是单个缓冲区没有合并前一个和当前的一个(它们以10分钟的时间间隔分开,间歇部分仍然没有加载,所以它们不能组合),它只是指到我寻找的起点,随着更多的数据被加载,终点也随之更新。 – SexyBeast

3
  1. 几乎所有的浏览器缓存中为该会话保存缓冲的数据。用户离开该页面后缓存过期。我认为用户每次从视频加载点加载视频时都不得不加载页面。仅当服务器清除所有缓存数据时,用户才会面临此问题。 HTML5视频标签将支持这一点,并将视频保存至点的位置,直到它被加载。

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

  3. 元数据被自动加载,直到你使用这个 <audio preload="none"...这将使浏览器不能从服务器下载任何东西,你可以使用它作为:
    <audio preload="auto|metadata|none"...如果使用没有,没有被下载,除非用户点击播放按钮,元数据将从服务器下载名称,时间和其他元数据,但不知道该文件,只要页面加载,自动开始下载。

我总是会引用你阅读jQuery的一些文档。由于jQuery将允许您使用ajax API更改和更新内容,并且也会有帮助。希望你成功!干杯。

0

虽然接受答案的描述非常出色,我决定更新其代码示例,有以下几个原因:

  • 进度渲染任务真的应该只在progress触发的事件。
  • 进度渲染任务与其他一些任务混合在一起,如绘制时间戳和播放头位置。
  • 该代码通过其ID使用document.getElementById()来引用多个DOM元素。
  • 变量名称全部被遮蔽。
  • 我以为前进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); 
    } 
}