这是我参与更文挑战的第3天,活动详情查看:更文挑战
开发一个视频上传组件,其中有一步的需求,是在上传前获取到视频的分辨率和时长,校验是否符合要求,然后再进行上传。
<video> 元素本身提供了 duration 属性用来获取时长, videoWidth 和 videoHeight 也可以获取视频的原始分辨率( width 和 height 属性也可以获取宽高,但是当前元素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 ,通常还可以用 FileReader 的 readAsDataURL 方法来实现这个功能。
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);
}
});
};
两种方法的区别是:
createObjectURL是同步返回,而readAsDataURL需要监听load事件异步获取;createObjectURL返回的是blob url,readAsDataURL返回的是base64,相对来说blob url更为简洁。
所以果断选择createObjectURL。
实现完成,测试,手上有1M.mp4和128M.mp4两个视频,小的那个返回正确值,大的那个 duration 正确, width 和 height 却始终返回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格式,不过这次仅用来获取视频元信息没有涉及播放,所以忽略了格式问题,导致浪费了半天时间。
特写篇文章敲黑板记重点。