阅读 238

日常开发问题记录: 获取视频元信息

这是我参与更文挑战的第3天,活动详情查看:更文挑战


开发一个视频上传组件,其中有一步的需求,是在上传前获取到视频的分辨率和时长,校验是否符合要求,然后再进行上传。

<video> 元素本身提供了 duration 属性用来获取时长, videoWidthvideoHeight 也可以获取视频的原始分辨率( widthheight 属性也可以获取宽高,但是当前元素style的宽高)。但这几个属性都需要视频加载后才可以获取,否则返回的都是0,所以可以监听 video.onloadedmetadata 事件,此时可以获取到正确的属性。

还有一个问题是,File文件无法直接设置为video的src,需要转换为url。 URL.createObjectURL() 方法支持为File、 Blob、MediaSource类型创建一个指向他们的url,再将video的src指定为该url即可。

const getVideoMeta = file => {
  return new Promise((resolve, reject) => {
    const url = URL.createObjectURL(file);
    const video = document.createElement("video");
    video.onloadedmetadata = evt => {
      // 每次调用createObjectURL都会创建一个新的URL,因此需要调用revokeObjectURL释放掉
      URL.revokeObjectURL(url);
      const meta = {
        duration: video.duration,
        width: video.videoWidth,
        height: video.videoHeight
      }
      video.remove();
      resolve(meta);
    };
    video.src = url;
  });
};
复制代码

除了 URL.createObjectURL ,通常还可以用 FileReaderreadAsDataURL 方法来实现这个功能。

const getVideoMeta = file => {
  return new Promise((resolve, reject) => {
    const video = document.createElement("video");
    var reader  = new FileReader();
    reader.addEventListener("load", function () {
        video.src = reader.result;
      video.onloadedmetadata = evt => {
        const meta = {
          duration: video.duration,
          width: video.videoWidth,
          height: video.videoHeight
        }
        video.remove();
        resolve(meta);
      }, false);
    }, false);
    if (file) {
        reader.readAsDataURL(file);
    }
  });
};
复制代码

两种方法的区别是:

  1. createObjectURL 是同步返回,而readAsDataURL需要监听load事件异步获取;
  2. createObjectURL 返回的是blob url,readAsDataURL 返回的是base64,相对来说blob url更为简洁。

所以果断选择createObjectURL。

实现完成,测试,手上有1M.mp4和128M.mp4两个视频,小的那个返回正确值,大的那个 duration 正确, widthheight 却始终返回0。

首先想到是不是createObjectURL对大文件的转换有问题,于是试了 FileReader 的方式,没有用,一样返回0。然后在 videoWidth 文档中看到

If the element's readyState is HTMLMediaElement.HAVE_NOTHING, then the value of this property is 0, because neither video nor poster frame size information is yet available.

于是在 onloadedmetadata 事件里获取 readyState ,小视频的readyState为1,大视频则为4,查看文档两种状态都属于正常。

  • 0 = HAVE_NOTHING - 没有关于音频/视频是否就绪的信息
  • 1 = HAVE_METADATA - 关于音频/视频就绪的元数据
  • 2 = HAVE_CURRENT_DATA - 关于当前播放位置的数据是可用的,但没有足够的数据来播放下一帧/毫秒
  • 3 = HAVE_FUTURE_DATA - 当前及至少下一帧的数据是可用的
  • 4 = HAVE_ENOUGH_DATA - 可用数据足以开始播放

百思不得其解,中间又走了一些弯路,还是没找到原因。又找了新的视频来测试,一样是有的正常有的异常,但发现并不跟视频大小相关,于是想到了格式问题,之前遇到过非H.264编码的mp4文件上传后不能正常播放的情况,一看文件,出现异常的视频果然都是非H.264编码的文件。破案。

<video> 标签支持的视频格式包括 OGG 、 MPEG 4 和WebM 三种,由于safari不支持OGG和WebM,因此通常业务中的视频上传都指定带有 H.264 视频编码和 AAC 音频编码的MPEG 4格式,不过这次仅用来获取视频元信息没有涉及播放,所以忽略了格式问题,导致浪费了半天时间。

特写篇文章敲黑板记重点。

文章分类
前端
文章标签