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

有時瞭解有多少 <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="music.mp3" type="audio/mpeg" />
  </audio>
</p>
<p>
  <canvas id="my-canvas" width="300" height="20"> </canvas>
</p>

以及一些 JavaScript

js
window.onload = () => {
  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";

  const inc = canvas.width / audio.duration;

  // Display TimeRanges

  audio.addEventListener("seeked", () => {
    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();
    }
  });
};

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

A simple audio player with play button, seek bar and volume control, with a series of red rectangles beneath it representing time ranges.

可查詢

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="music.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: #555;
  width: 300px;
}

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

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

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

以下 JavaScript 提供了我們的功能

js
window.onload = () => {
  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
      }%`;
    }
  });
};

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

timeupdate 事件在媒體播放時每秒觸發 4 次,這就是我們在其中遞增播放進度條的地方。

這應該會為您提供類似以下的結果,其中淺灰色條表示緩衝進度,綠色條表示播放進度

A simple audio player with play button, seek bar, and volume control, and a progress bar below the controls. The progress bar has a green portion to show played video and a light grey portion to show how much has been buffered.

關於已播放的簡短說明

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

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

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