视频播放的那些事-阿里云开发者社区

开发者社区> 开发与运维> 正文
登录阅读全文

视频播放的那些事

简介: 视频作为淘宝教育业务的基础服务,本文根据自身在手淘中视频播放的实践,谈谈在手淘中视频播放遇到的问题及其解决方案。 播放器 在手淘过去一年多的历史长河中存在五种类型的播放器。 原生 HTML5 video 标签 Android 5.3.2 版本之后的 UC 内核增强 SAC 播放器
TB1Va8HKXXXXXckXVXXXXXXXXXX-900-500.jpg
  • 视频作为淘宝教育业务的基础服务,本文根据自身在手淘中视频播放的实践,谈谈在手淘中视频播放遇到的问题及其解决方案。

播放器

  • 在手淘过去一年多的历史长河中存在五种类型的播放器。

    • 原生 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 动画,全屏,模拟全屏和兼容性五个方面对各个播放器做个横向对比。a725c8a24592017c81992de6700301df8de1ff84

接口与事件封装

上面介绍了手淘中可供 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 {
display: none;
}

  • poster 视频底图

    • 在 iPhone 中视频加载完第一帧数据后会覆盖 Poster 底图展示第一帧画面,这时可以使用 DIV 覆盖在视频上方模拟,监听 timeupdate 事件做隐藏操作。
    • 在 UC WebView 中动态设置 poster 可能会导致手淘 crash,方案跟上方一样。
    • 在使用 Native 播放器时,在播放器未初始化时使用 DIV 替换 video 标签,并设置底图为背景图。
  • 播放首屏:IOS 通过监听 playing 事件可以准确获取视频播放的时间点;Android 中在该事件触发时,还没真正开始播放。我们通过监听 timeupdate 的事件做模拟处理。

_timeUpdate(e) {
var currentTime = this.getCurrentTime();

// 判断是否为首帧
if (currentTime !== undefined && currentTime !== 0) {
this.fire('firstpaint');
}
}

_playing() {
if (Env.os.ios) {
this.fire('firstpaint');
}
}
  • 视频切换:在android 4.4 以下版本,在视频切换时存在第一次切换不能正常播放,第二次才能正常播放情况。通过调试人肉分析,发现切换视频的 video 存在以下两个特征:readyState 值为 0,videoWidth 为 0。因此我们判断当两个属性为0时,则切换失败,再次调用播放逻辑。存在误判的可能,但是能保证正常工作。
isWork() {
if (videoEl.readyState === 0 && videoEl.videoWidth === 0) {
return false;
}

return true;
}
  • 全屏:手淘 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');
      }
      }
    • 效果图:
      IOS全屏

    • 预览地址(请用手淘扫码):
      demo
    • 方案二。方案一只是模拟了横全屏效果,对于追求完美的处女座不能忍。还有其他方案吗?有时候只需要转换下思维,问题即可迎刃而解。既然是横屏播放,只需要让 WebView 横屏即可,同时在横屏之后重新调整控件即可,关键手淘提供了打开应用横全屏的接口。注意点:横屏之后需要禁止页面滚动,要不然全屏就露馅了,因为本质还是个 WebView。
if (this.transverseFullScreen) {
if (curEl.hasClass('normal')) {
curEl.removeClass('normal');
this._transverseFullScreen(false).then(() => {
$('body').removeClass('co-fullscreen').attr({ height: 'auto' });
this.videoWrapperEl.height(this.videoOriginHeight).removeClass('fullscreen');

this.player.fire('transversefullscreen', { fullscreen: false });
this.resize();
} else {
curEl.addClass('normal');
this._transverseFullScreen(true).then(() => {
$('body').addClass('co-fullscreen').attr({ height: win.height() });
this.videoWrapperEl.height(win.height()).addClass('fullscreen');

this.player.fire('transversefullscreen', { fullscreen: true });
this.resize();
});
}
return;
}
  • Demo:
    fullscreen
  • 手淘 IOS 扫码:
    • 方案3。在 UC HAC 方案视频提供全屏接口 UCSettings.setVideoViewFullscreenByDefault(true),开启后,视频全屏默认为横屏
  • 自动播放
    • 出于用户节省用户流量考虑,iPhone 下播放视频需要用户手动触发,即使配置了 autoplay 属性也是无效的。在业务中,特定场景还是需要视频能够自动播放,对此我们可以监听页面的 touchstart 事件,做如下处理:
if (this.auoplay && env.app.TB && env.network.wifi) {
if (player.getCurrentTime() > 0 && !player.isPause()) {
return;
}
if (this.hasAutoPlay) {
return;
}
this.hasAutoPlay = true;
startEl.trigger('click');

function autoplay() {
doc.detach('touchstart', autoplay);
if (player.getCurrentTime() > 0) {
return;
}
startEl.trigger('click');
}
doc.on('touchstart', autoplay);
}
  • 其他
    • 部分机型手淘低版本使用 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);
document.addEventListener('WV.Event.Key.Back', $.proxy(this.destory, this), false);
win.on('unload', $.proxy(this.destory, this));
win.on('beforeunload', $.proxy(this.destory, this));
  • 定位
    • Glue 播放器使用 dip 作为播放器的定位单位,rem 布局会对页面进行缩放,导致定位位置和视频大小错误。同时定位时参数有小数点会导致播放器错误。
/__
* 返回值需要是整数,否则会有异常
*/
_getVedioPos(isDpr) {
var el = this.el,
offset = el.offset(),
dpr = 1;

if (isDpr) {
dpr = this._getDpr();
}

return {
x: parseInt(offset.left / dpr),
y: parseInt(offset.top / dpr),
w: parseInt(el.width() / dpr),
h: parseInt(el.height() / dpr)
};
}
  • 视频源地址: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 * * *
google nexus 5 * yun os 3 × *
google 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
转载自:http://taobaofed.org/blog/2016/05/03/promise-anti-patterns/
作者:永霸

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享: