Skip to content

前端视频的请求、处理与播放

首先这个问题是在一个科研项目中遇到的,当时要实现的是视频数据的标注功能,用户每次看到一个视频,基于视频内容来进行描述。当时大概有两百条左右的视频,最初的时候由于没有经验,我把所有视频都像图片一样放在了前端的 public 中,这就造成当时打开网页的速度变得很慢;在这个情况的基础上,我就想,那把视频放在后端应该就可以了,切换视频的时候进行请求(懒加载的思路),尝试之后发现确实可以减少首屏加载的时间,但依然存在一个问题就是用户每次点击下一个都需要等待两三秒的时间,这种用户体验依旧不好;最后采取了预加载的方式,也就是首次加载的时候加载两条视频,用户每次点击下一个的时候请求的其实是下下条视频,解决了上面的问题。

后面可以优化的方面:

  1. 及时释放不需要的 blob。URL.revokeObjectURL(blobUrl);
  2. 引入流式加载技术。
  3. 利用 HTTP 缓存机制,避免重复加载相同的视频文件。
js
class VideoManager {
  constructor(videos) {
    this.videos = videos; // 视频 URL 列表
    this.currentIndex = 0; // 当前正在播放的视频索引
    this.cache = {}; // 缓存 blob URL
  }

  async loadVideo(index) {
    if (this.cache[index]) return this.cache[index];

    const response = await fetch(this.videos[index]);
    const blob = await response.blob();
    const url = URL.createObjectURL(blob);
    this.cache[index] = url;

    // 预加载下一条
    if (index + 1 < this.videos.length) {
      this.loadVideo(index + 1);
    }

    // 释放上一条
    if (index - 1 >= 0 && this.cache[index - 1]) {
      URL.revokeObjectURL(this.cache[index - 1]);
      delete this.cache[index - 1];
    }

    return url;
  }

  async playVideo(videoElement, index) {
    const url = await this.loadVideo(index);
    videoElement.src = url;
    videoElement.play();
    this.currentIndex = index;
  }
}

// 使用示例
const videoUrls = ['video1.mp4', 'video2.mp4', 'video3.mp4'];
const manager = new VideoManager(videoUrls);

const videoElement = document.querySelector('video');
document.querySelector('#next').addEventListener('click', () => {
  manager.playVideo(videoElement, manager.currentIndex + 1);
});