2013-08-24 398 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); 
    } 
}