媒体缓冲、查找和时间范围

有时了解有多少 <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

这可能有助于确定媒体中收听或观看次数最多的部分。