- 视频作为淘宝教育业务的基础服务,本文根据自身在手淘中视频播放的实践,谈谈在手淘中视频播放遇到的问题及其解决方案。
播放器
-
在手淘过去一年多的历史长河中存在五种类型的播放器。
- 原生 HTML5 video 标签
- Android 5.3.2 版本之后的 UC 内核增强 SAC 播放器
- Android 5.4.9 版本之后的 UC HAC 播放器
- Android 5.3.2 版本之前的 Glue native 播放器
- Android 5.3.2 版本及其之后的 PlayBuddy 播放器
-
下面从支持平台,loading 动画,全屏,模拟全屏和兼容性五个方面对各个播放器做个横向对比。
上面介绍了手淘中可供 WebView 选择的播放器,对于业务方而言迫切需要一个解决方,无需关心底层差异。为此,我们屏蔽移动端不同系统平台、宿主环境、播放器的实现细节和兼容性问题,提供统一的接口和事件,具体如下:
- 方法
- play 播放
- pause 暂停
- stop 停止
- show 显示
- hide 隐藏
- requestFullscreen 全屏
- exitFullScreen 退出全屏
- getCurrentTime 获取当前播放时间
- setCurrentTime 设置播放时间
- getDuration 获取视频时长
- setPoster 设置背景图
- destory 销毁
- reset 重置视频
-
事件
- timeupdate 进度更新
- ended 停止
- error 错误
- play (专指video)
- pause 停止(专指video)
- firstpaint 视频真正开始播放(专指video)
-
controls 播放控件(专指video)
- 播放
- 暂停
- 进度更新
- 全屏
- loading
兼容性处理
接下来谈谈在开发过程中遇到的各种小问题及其解决办法。
video
-
内联播放。iPhone 在视频播放时默认全屏播放,参考。
-
WebView 中,可以对 UIWebView 做如下配置,并且在 video 中配置 webkit-playsinline 属性即可:
webview.allowsInlineMediaPlayback = YES;
-
iPhone Safari 在 IOS >= 8 的系统中,有人也提出了一个方案
-
-
自定义播放控件
- 部分 Android 机型不支持内置控件,或者说内置控件无法正常使用;各个产品都有特定的视觉规范,默认控件的交互和视觉无法满足需求。因此,我们推荐默认不启用默认控件,采用自定义控件。
- IOS 下播放时还可能还展示系统自带播放按钮,可以配置如下 CSS。
video::-webkit-media-controls-start-playback-button { |
-
poster 视频底图
- 在 iPhone 中视频加载完第一帧数据后会覆盖 Poster 底图展示第一帧画面,这时可以使用 DIV 覆盖在视频上方模拟,监听 timeupdate 事件做隐藏操作。
- 在 UC WebView 中动态设置 poster 可能会导致手淘 crash,方案跟上方一样。
- 在使用 Native 播放器时,在播放器未初始化时使用 DIV 替换 video 标签,并设置底图为背景图。
-
播放首屏:IOS 通过监听 playing 事件可以准确获取视频播放的时间点;Android 中在该事件触发时,还没真正开始播放。我们通过监听 timeupdate 的事件做模拟处理。
_timeUpdate(e) { |
- 视频切换:在android 4.4 以下版本,在视频切换时存在第一次切换不能正常播放,第二次才能正常播放情况。通过调试人肉分析,发现切换视频的 video 存在以下两个特征:readyState 值为 0,videoWidth 为 0。因此我们判断当两个属性为0时,则切换失败,再次调用播放逻辑。存在误判的可能,但是能保证正常工作。
isWork() { |
-
全屏:手淘 IOS 支持竖全屏效果,Android 虽然具有全屏方法,但是被手淘限制,调用全屏方法无效。
-
方案一
:为了支持横全屏,我们使用 css3 的 rotate 对视频区域进行90度旋转,并且调用 bridge 接口隐藏 native 顶部的 navibar,并对自定义控件进行响应优化调整。基本到达 native全屏效果。当然顶部状态栏不能隐藏还是有些小瑕疵。同时旋转之后元素的 z-index失效,导致视频覆盖控件问题,可以通过设置 -webkit-transform: translate3d(0,0,0) 来修复requestFullscreen() {
var element = this.el[0];
var method = FullscreenApi.requestFullscreen;
if (method) {
element[method]();
} else if (element.webkitEnterFullscreen || element.enterFullScreen) {
element.webkitEnterFullscreen && element.webkitEnterFullscreen();
element.enterFullScreen && element.enterFullScreen();
} else {
// 模拟全屏
// enterFullWindow();
}
}
// 模拟全屏js核心代码
_mockFullscreen() {
if (curEl.hasClass('normal')) {
this.fullscreen = false;
playerEl.css({
width: this.originWidth,
height: this.originHeight,
left: 0
}).removeClass('fullscreen');
wrapperEl.css({
width: this.wrapperOriginWidth,
height: this.wrapperOriginHeight
});
videoEl.css('height', '100%');
curEl.removeClass('normal');
contentEl.removeClass('fullscreen');
} else {
this.fullscreen = true;
this.originWidth = playerEl.width();
this.originHeight = playerEl.height();
this.wrapperOriginWidth = wrapperEl.width();
this.wrapperOriginHeight = wrapperEl.height();
playerEl.css({
width: $(window).height(),
height: $(window).width(),
left: $(window).width()
}).addClass('fullscreen');
wrapperEl.css({
width: $(window).height(),
height: $(window).width()
});
videoEl.css('height', videoEl.height() - controlsHeight);
curEl.addClass('normal');
contentEl.addClass('fullscreen');
}
} -
效果图:
- 预览地址(请用手淘扫码):
-
方案二
。方案一只是模拟了横全屏效果,对于追求完美的处女座不能忍。还有其他方案吗?有时候只需要转换下思维,问题即可迎刃而解。既然是横屏播放,只需要让 WebView 横屏即可,同时在横屏之后重新调整控件即可,关键手淘提供了打开应用横全屏的接口。注意点:横屏之后需要禁止页面滚动,要不然全屏就露馅了,因为本质还是个 WebView。
-
if (this.transverseFullScreen) { |
- Demo:
- 手淘 IOS 扫码:
-
方案3
。在 UC HAC 方案视频提供全屏接口 UCSettings.setVideoViewFullscreenByDefault(true),开启后,视频全屏默认为横屏
-
- 自动播放
- 出于用户节省用户流量考虑,iPhone 下播放视频需要用户手动触发,即使配置了 autoplay 属性也是无效的。在业务中,特定场景还是需要视频能够自动播放,对此我们可以监听页面的 touchstart 事件,做如下处理:
if (this.auoplay && env.app.TB && env.network.wifi) { |
- 其他
- 部分机型手淘低版本使用 video 播放时,会出现有声音没画面的问题,升级手淘后即恢复。例如,小米4 手淘 4.2.0
- IOS 5.1 和部分 android 手机暂停和开始按钮不触发点击事件(元素的 :after 为iconfont)。通过父元素添加background即可
- Android UC 内核的播放器,在未设置 source 资源时,设置 poster 无效
- Android UC 内核的播放器无法自定义控件和样式操作,但是可以正常的监听事件。
- 直接替换 source 不会改变当前正在播放的视频,需要调用 load 方法。
- UC 浏览器中 video 标签会被 UC 的播放器插件替换
- 使用 m3u8 和 mp4 基本可以兼容所有机型
- 在 IOS 视频初始化后设置 currrentTime 无效,在 loadedmetadata 事件触发后,设置 currentTime 即可。
native播放器
-
destroy:
- Glue:Glue native 播放器在页面跳转,WebView 后退等操作时,不会自动析构,好的情况是视频依然在背后播放,有时候会直接导致手淘 crash。
-
PlayBuddy:在页面跳转时依然会继续播放
处理方式:页面跳转时需要手动的销毁native播放器。
document.addEventListener('WV.Event.Page.Refresh', $.proxy(this.destory, this), false); |
- 定位
- Glue 播放器使用 dip 作为播放器的定位单位,rem 布局会对页面进行缩放,导致定位位置和视频大小错误。同时定位时参数有小数点会导致播放器错误。
/__ |
- 视频源地址:Glue 播放器不支持以 // 开始的视频资源,例如 //video.xxx
- PlayBuddy 播放器不会随着页面滚动而滚动。
总结
- 如果业务中需要在手淘中播放视频,IOS 直接使用原生 video 即可。在 Android 中较为复杂,没有完全兼容的方案。建议使用 video,对于 Android 低版本建议使用native 播放器。随着uc内核接入,未来完全抛弃 native 方案也是可行的。
- 本文基于过去一年在手淘视频播放过程中遇到问题的小结,后续会整理视频监控和视频娱乐化相关内容。
附:手淘同学播放器兼容性表
品牌 | 机型 | 手淘版本 | 操作系统版本 | 播放器控件 | 视频列表切换 | 试看控制 | 观看进度同步 | 切换模式 | 问题 |
---|---|---|---|---|---|---|---|---|---|
苹果 | 6 plus | 5.2.7 | 8.11 | √ | √ | √ | √ | √ | * |
苹果 | 6 | 5.2.7 | 8.11 | √ | √ | √ | √ | √ | * |
苹果 | 5s | 5.2.7 | 8.11 | √ | √ | √ | √ | √ | * |
苹果 | 5 | 5.2.7 | 8.11 | √ | √ | √ | √ | √ | * |
苹果 | 4s | 5.2.7 | 8.11 | √ | √ | √ | √ | √ | * |
苹果 | 4 | * | * | √ | √ | √ | √ | √ | * |
nexus 5 | * | yun os 3 | √ | √ | √ | √ | × | * | |
nexus 5 | * | 安卓 5 | √ | √ | √ | √ | × | * | |
三星 | N7100 | 4.9 | * | √ | √ | √ | √ | × | * |
三星 | NOTE4 | 4.9 | * | √ | √ | √ | √ | √ | 模式二点最大化crash |
三星 | NOTE3 | 4.9 | * | √ | √ | √ | √ | × | * |
三星 | S4 | 5.2.7.3 | * | √ | √ | √ | √ | √ | 模式二,播放有问题 |
三星 | I9300 | 4.3 | * | √ | √ | √ | √ | √ | * |
三星 | S3 | 5.2.8.2 | * | √ | √ | √ | √ | √ | * |
三星 | S5 | 5.2.7.3 | * | √ | √ | √ | √ | √ | * |
魅族 | MX2 | * | * | √ | * | √ | √ | √ | * |
魅族 | MX4 PRO | * | * | √ | √ | √ | √ | √ | * |
魅族 | 魅蓝Note | * | * | √ | √ | √ | √ | √ | * |
魅族 | MX3(安装不上) | * | * | √ | √ | √ | √ | √ | * |
华为 | 荣耀6 | * | * | √ | √ | √ | √ | √ | 模式二可能播放不了 |
华为 | mate7 | * | * | √ | √ | √ | √ | √ | 模式二可能播放不了 |
华为 | c8816 | * | * | √ | √ | √ | √ | √ | 进度条拖动会跳 |
华为 | 荣耀3c | * | 4.4.2 | √ | √ | √ | √ | ×一直展示loading的图片 | 模式二播放不了 |
华为 | C8813 | * | 4.1.1 | √ | √ | √ | √ | √ | 高清视频不能播放 |
HTC | MAX | * | * | √ | √ | √ | √ | * | * |
HTC | 816w | * | * | √ | √ | √ | √ | √ | * |
VIVO | Find5 | * | 4.1.1 | √ | √ | √ | √ | √ | * |
VIVO | X3 | * | 4.2.2 | √ | √ | √ | √ | √ | * |
小米 | 2S | * | 4.3 | √ | √ | √ | √ | * | 高清的播放不了 |
小米 | 3 | * | * | * | √ | √ | √ | * | * |
小米 | 4 | * | 4.4.4 | √ | √ | √ | √ | * | * |
索尼 | M512 | * | 4.4.2 | √ | √ | √ | √ | √ | * |
索尼 | xperia 36l | * | 4.1.2 | √ | √ | √ | √ | * | * |
nubia | nx403 | * | 4.2.2 | √ | × | √ | √ | * | 模式一,模式二播放均有问题 |
锤子 | * | * | * | √ | √ | √ | √ | * | |
oppo | x907 | * | 4.0.3 | 很难点到 | √ | √ | √ | √ | * |
nexus5 | * | * | * | √ | √ | √ | √ | √ | 第二种模式crash |