媒體緩衝、查詢和時間範圍

有時瞭解 <audio><video> 已經下載了多少或可以立即播放多少內容會很有用——一個很好的例子就是音訊或影片播放器的緩衝進度條。本文將討論如何使用 TimeRanges 和媒體 API 的其他功能來構建一個緩衝/跳轉條。

緩衝

buffered 屬性將告訴我們媒體的哪些部分已被下載。它返回一個 TimeRanges 物件,該物件將告訴我們媒體的哪些塊已被下載。這通常是連續的,但如果使用者在媒體緩衝時跳轉,它可能會包含空隙。

這適用於 <audio><video>;現在,讓我們以音訊為例

html
<audio id="my-audio" controls src="music.mp3"></audio>

我們可以像這樣訪問這些屬性

js
const audio = document.getElementById("my-audio");
const bufferedTimeRanges = audio.buffered;

TimeRanges 物件

TimeRanges 是由起始時間和停止時間組成的一系列不重疊的時間範圍。(瞭解更多關於 TimeRanges 的資訊)。

一個 TimeRanges 物件包含以下屬性:

  • length:物件中時間範圍的數量。
  • start(index):時間範圍的起始時間(以秒為單位)。
  • end(index):時間範圍的結束時間(以秒為單位)。

在沒有任何使用者互動的情況下,通常只有一個時間範圍,但如果你在媒體中跳轉,可能會出現多個時間範圍,如下面的視覺化所示。這表示兩個緩衝的時間範圍——一個跨度為 0 到 5 秒,第二個跨度為 15 到 19 秒。

------------------------------------------------------
|=============|                    |===========|     |
------------------------------------------------------
0             5                    15          19    21

對於這個音訊例項,相關的 TimeRanges 物件將具有以下可用屬性:

js
audio.buffered.length; // returns 2
audio.buffered.start(0); // returns 0
audio.buffered.end(0); // returns 5
audio.buffered.start(1); // returns 15
audio.buffered.end(1); // returns 19

為了嘗試和視覺化緩衝時間範圍,我們可以編寫一些 HTML

html
<p>
  <audio id="my-audio" controls>
    <source
      src="https://cdn.freesound.org/previews/155/155386_326032-lq.mp3"
      type="audio/mpeg" />
  </audio>
</p>
<p>
  <canvas id="my-canvas" width="300" height="20"> </canvas>
</p>

以及一些 JavaScript

js
const audio = document.getElementById("my-audio");
const canvas = document.getElementById("my-canvas");
const context = canvas.getContext("2d");

context.fillStyle = "lightgray";
context.fillRect(0, 0, canvas.width, canvas.height);
context.fillStyle = "red";
context.strokeStyle = "white";

// Display TimeRanges
audio.addEventListener("seeked", () => {
  const inc = canvas.width / audio.duration;
  for (let i = 0; i < audio.buffered.length; i++) {
    const startX = audio.buffered.start(i) * inc;
    const endX = audio.buffered.end(i) * inc;
    const width = endX - startX;

    context.fillRect(startX, 0, width, canvas.height);
    context.rect(startX, 0, width, canvas.height);
    context.stroke();
  }
});

這在較長的音訊或影片片段上效果更好,但按下播放並點選播放器進度條,你應該會看到紅色的片段。每個紅色填充的白色矩形代表一個時間範圍。

可跳轉

seekable 屬性返回一個 TimeRanges 物件,並告訴我們媒體的哪些部分可以立即播放;這與該部分是否已下載無關。媒體的某些部分可能是可跳轉但未緩衝的,如果伺服器啟用了位元組範圍請求。位元組範圍請求允許從伺服器交付媒體檔案的部分,因此幾乎可以立即播放——因此它們是可跳轉的。有關位元組範圍請求的更多資訊,請參閱 HTTP 範圍請求

js
const seekableTimeRanges = audio.seekable;

建立我們自己的緩衝反饋

如果我們想建立自己的自定義播放器,我們可能需要提供關於媒體已準備好播放多少的反饋。實際上,一種很好的方法是使用 seekable 屬性,儘管如上所述,媒體的可跳轉部分不一定是連續的——但它們通常是連續的,我們可以安全地近似這些資訊來向用戶指示哪些部分可以直接播放。我們可以使用以下程式碼行找到媒體中的這個點:

js
const seekableEnd = audio.seekable.end(audio.seekable.length - 1);

注意: audio.seekable.end(audio.seekable.length - 1) 實際上告訴我們最後一個可跳轉時間範圍的結束點(不是所有可跳轉的媒體)。在實踐中,這已經足夠了,因為瀏覽器要麼啟用範圍請求,要麼不啟用。如果不啟用,audio.seekable 將等同於 audio.buffered,這將給出可跳轉媒體結束的有效指示。如果啟用了範圍請求,這個值通常會幾乎立即變成媒體的時長。

也許最好能指示媒體實際下載了多少——瀏覽器原生播放器似乎就是這樣顯示的。

所以,讓我們來實現它。我們播放器的 HTML 如下:

html
<audio id="my-audio" preload controls>
  <source
    src="https://cdn.freesound.org/previews/155/155386_326032-lq.mp3"
    type="audio/mpeg" />
</audio>
<div class="buffered">
  <span id="buffered-amount"></span>
</div>
<div class="progress">
  <span id="progress-amount"></span>
</div>

我們將使用以下 CSS 來樣式化緩衝顯示:

css
.buffered {
  height: 20px;
  position: relative;
  background: #555555;
  width: 300px;
}

#buffered-amount {
  display: block;
  height: 100%;
  background-color: #777777;
  width: 0;
}

.progress {
  margin-top: -20px;
  height: 20px;
  position: relative;
  width: 300px;
}

#progress-amount {
  display: block;
  height: 100%;
  background-color: #559955;
  width: 0;
}

以下 JavaScript 提供了我們的功能:

js
const audio = document.getElementById("my-audio");

audio.addEventListener("progress", () => {
  const duration = audio.duration;
  if (duration > 0) {
    for (let i = 0; i < audio.buffered.length; i++) {
      if (
        audio.buffered.start(audio.buffered.length - 1 - i) < audio.currentTime
      ) {
        document.getElementById("buffered-amount").style.width = `${
          (audio.buffered.end(audio.buffered.length - 1 - i) * 100) / duration
        }%`;
        break;
      }
    }
  }
});

audio.addEventListener("timeupdate", () => {
  const duration = audio.duration;
  if (duration > 0) {
    document.getElementById("progress-amount").style.width = `${
      (audio.currentTime / duration) * 100
    }%`;
  }
});

當資料下載時,會觸發 progress 事件,如果我們要顯示下載或緩衝進度,這是一個很好的事件來響應。

當媒體播放時,timeupdate 事件每秒觸發 4 次,我們可以在那裡增加播放進度條。

這次,你應該會看到兩種型別的段。淺灰色條表示緩衝進度,綠色條表示已播放進度。

關於已播放的快速說明

值得一提的是 played 屬性——它告訴我們媒體中已播放的時間範圍。例如:

js
const played = audio.played; // returns a TimeRanges object

這可能有助於確定你的媒體中最常收聽或觀看的部分。