一个在h5页面播放视频的需求。最先采用了video标签的方案,后改为使用JSMpeg。

video标签方案

使用html原生的video标签播放。

video标签文档:https://www.runoob.com/tags/tag-video.html

使用如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<template>
<video
id="video" ref="myVideo"
preload="auto"
:controls="config.controls"
:poster="config.poster"
:autoplay="config.autoplay"
:muted="config.muted"
:loop="config.loop"
x-webkit-airplay="allow"
webkit-playsinline
playsinline
x5-video-player-type="h5"
@click="handleClickVideo">
<source :src="config.video" type="video/mp4" />
</video>
</template>

只需填写配置即可,controls是控制条,poster是封面图,loop是循环播放,等等。详细见上面文档。

可以用id或ref获取video元素,调用播放、暂停等方法。

video在不同浏览器也会有不同表现,需要加上以下属性:

  • webkit-playsinline=”true” /这个属性是ios 10中设置可以让视频在小窗内播放,即不全屏播放/
  • playsinline=”true” /IOS微信浏览器支持小窗内播放/
  • x-webkit-airplay=”allow” /使此视频支持ios的AirPlay功能/
  • x5-video-player-type=”h5” /启用H5播放器,是wechat安卓版特性/
  • x5-video-player-fullscreen=”true” /全屏设置,设置为 true 是防止横屏/>
  • x5-video-orientation=”portraint” /播放器支付的方向,landscape横屏,portraint竖屏,默认值为竖屏/

遇到的问题:

  1. 自动播放:很多设备是无效的,需要手动点击播放;
  2. 静音:很多时候不设置静音也会默认静音播放,需要用户手动交互一下。
  3. ios解码问题:我们的视频是mp4格式,但是部分视频在ios不播放,调研发现是ios对h.264的支持不完善,压缩比较高的时候无法播放。
  4. 层级问题:一些浏览器会将video置于最顶层,上面遮挡的元素会失效。

左图是期望,以及在主流浏览器展示效果,右图是在一些系统浏览器、UC浏览器等展示效果。

后两个问题比较严重,因此改用JSMpeg播放。

JSMpeg方案

参考文章:https://segmentfault.com/a/1190000040622805?utm_source=sf-similar-article

使用JSMpeg,首先需要用FFMpeg将视频转换为ts格式。然后使用如下:

1
<script type="text/javascript" src="https://jsmpeg.com/jsmpeg.min.js"></script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<template>
<canvas id="canvas"></canvas>
</template>

<script>

export default {
mounted () {
this.player = new JSMpeg.Player(
config.video, {
canvas: document.getElementById('canvas'), // 容器id
throttled: false, // 这里设置为false,不然不触发onSourceCompleted事件
chunkSize: 4 * 1024 * 1024, // 使用分块加载数据时,一次加载的块大小。默认1024*1024(1mb)
progressive: true, // 是否分块加载数据
loop: this.config.loop,
autoplay: this.config.autoplay,
audio: !this.config.muted,
}
)
this.player.audioOut.unlock() // 需要用户交互以解锁音频功能
},
}
</script>

JSMpeg是先拿到视频内容进行解析视频和音频,然后用canvas去逐帧针的画出来,同时播放声音。因此上面video的不同设备表现就统一了,也没有层级问题,自动播放也没有问题。部分设备可能还是要点一下才有声音,因为声音还是调用的设备能力。

JSMpeg的缺点是,不像video标签一样有控制条盒精细的操作,不能前进后退。我们用JSMpeg的场景就是正常的播放。这里静音也是,不解析音频,中途无法再打开声音。

另外,JSMpeg是先用ajax请求资源,因此存在跨域问题,需要视频文件和页面在同一个域名,或者视频所在服务器允许跨域。

对比

原生video标签 JSMpeg
+ 可以显示控制条进行操作;
+ 无需引入额外文件
+ canvas无兼容问题,各个设备表现基本一致,自动播放和静音播放等都正常(部分设备需要点一下才有声音);
+ 可以分块加载数据,加载体验更好;
+ 自动播放部分设备不生效
+ 声音需要用户交互
+ ios不支持部分mp4压缩比
+ 不同浏览器,video展现不同
+ 部分浏览器video层级最高,无法控制
+ 不可精细控制;
+ 需要引入额外的js;
+ 视频文件需要先转换为ts格式;
+ 请求资源需要解决跨域问题

可以看到,二者各有自己的优缺点,像是做一些在线视频播放功能,video更合适一些,方便用户操作,可以前进后退、投屏等,看需求可以进一步引入一些js库操作video。而做一些活动页,插入一些广告视频,无需用户操控视频,用JSMpeg更合适一些,样式可控。

JSMpeg兼容问题

当ts文件和html文件在一起时,不存在跨域问题,播放也正常。但是当ts文件放在oss上,并设置了允许跨域,部分手机请求正常但是不播放。如小米手机用支付宝扫码时,已经iphone12、iphone13手机各种浏览器环境

开始怀疑是视频的问题,修改各种视频参数去试验,还是不行。换成相对路径或者同一个域名就可以。确认应该是服务器问题,并且我们用竞品服务器的ts链接,各个手机播放都正常。

这里我们用的阿里云oss,看了下设置都没问题。换了一个思路来解决。

写一个html文件,通过url接受视频地址参数,执行JSMpeg播放。然后把这个文件和视频文件放在同一个oss。在原先的页面,视频播放位置放一个iframe。测试iframe方案各个手机播放正常