在一切皆可视频化的今天,短视频内容作为移动端产品新的促活点,受到了越来越多的重视与投入。盒马在秒播、卡顿率、播放成功率等基础优化之外,在用户使用体验上引入了无痕续播能力,提升用户观看视频内容的延续性。本篇将分享盒马在 iOS 短视频方面的实践干货。

作者|神捕

审校|泰一

跨页面续播是除秒播外另一个可以从体感上增加用户体验的能力。由于一些业务场景需要在不同页面上播放同一个视频内容的场景,而这些场景页面切换往往是连续的,这就要求短视频的播放也是连续。这样才能使得体验上会有连贯性,让用户在进入沉浸式页面时,能流畅的过度,且无感知的继续播放,从而产生连续不间断的感受。下面我们开始介绍盒马短视频的跨页面续播能力和流畅的动画切换效果的流畅性。

https://v.youku.com/v_show/id_XNTgwNTk4NzQ4MA==.html

如上视频所示,视频在列表页预览观看后,用户很可能继续点击跳到下一个全屏页面,进入沉浸式体验。在这过程中,视频窗口平滑变大至全屏,视频进度是延续的,中间没有感觉到视频或音频的停顿感。在页面返回后,视频窗口也有相应的还原效果。

目标

接入简单,只需要关心并加一个参数,其它逻辑内聚。

适配性好,支持裁剪模式的切换。

视频、音频无缝衔接,不能有任何停顿感。

页面间播放状态隔离,互不干扰。

实现方案

在方案选择上,主要考虑了以下三种:

目前盒马采用的是第 3 种 ——playerView 的复用方式,具体来说,无痕续播的实现,至少需要以下几个步骤:

  1. 用户点击,从 A 页面跳转到 B 页面,如:domain/path?reusedPlayerView=0xyyyyyy, 在原有业务参数的基础上,添加一个 reusedPlayerView 参数,把 playerView 传给下个页面 。
  2. B 页面 HMTBPlayerView 的实例化:内部实例化一个或复用 A 页面的 reusedPlayerView。
  3. playerView 的大小位置换算,实现切换动画。
  4. 从 B 页面返回 A 时,实现退出动画并返还 playerView。

以上步骤不多,但具体实现起来是比较复杂的,下面我们将围绕 4 个主要问题的解决过程,来说明具体实现方式。

尺寸变化的动画

正常来说,只要计算好 playerView 的原始 Rect,以及最终 Rect,基于 UIView 做 frame 动画就可以简单实现窗口变大效果。但实现时发现,手淘播放器内部重写了 setFrame 方法,只要修改了 frame,playerView 将直接显示为终态,动画没有效果。

于是,这里采用了 CGAffineTransform 的 scale 实现:先把 playerView 的 frame 设置为终态,计算好变化前后的尺寸比例 ratio,设置 playerView.transform = CGAffineTransformMakeScale (ratio, ratio),将其尺寸等比缩小为初始位置大小,而后就可以执行 transform 的动画实现从起点到终点的变换。

需要注意的是,此处 ratio 的计算方式,是以 playerView 内真实渲染的视频尺寸计算,而不是 playerView 本身大小。

渲染 mode 的切换

视频渲染本身可以设置为 ScaleAspectFit 或 ScaleAspectFill,目前在盒马的场景中,存在一种 A 页面的播放器为 fill mode,且 playerView 固定正方形,但跳转到 B 页面时,变成 fit mode,这样就出现了一个在尺寸变化动画进行时的 mode 切换的问题。

上述通过 setFrame 并修改 transform 的方式,可以实现把 playerView 大小变换成与动画前的初始大小一致,但是,如果此时存在 mode 切换需求就有可能出现计算后的大小不一致,比如从一个 9:16 长方形的 playerview 变成一个 1 : 1 且 mode 为 fill 的正方形 playerView,此时宽度一致,但高度明显多出了,直接做动画会导致初始状态闪动。

这里的解决方式,我们使用了 maskView 进行 mode 切换过渡:首先,计算 maskView 分别在宽高上的 scale,然后设置 playerView.maskView.transform。计算方式如下:

CGAffineTransformMakeScale(originalRect.size.width/(destRect.size.width*ratio), originalRect.size.height/(destRect.size.height*ratio))

这样就实现利用 maskView,把 9:16 的长方形显示成 1:1 的可见区域,实现动画的起始位置重合。最后,结合上述 playerView.transform 动画,再添加一个 maskView.transform 动画,二者配合,模拟出带 mode 切换场景下的动画过渡效果。

主动回收与主动归还策略

在实现了进场动画之后,最重要的是需要考虑 playerView 复用逻辑,其中比较重要的一点就是 playerView 什么时候归还给 A 页面。

目前我们采用的是租借思路:

  1. 有可借 playerView 时,进行借用;
  2. 复用的 playerview 不再使用时,及时主动归还;
  3. 当出租方自己要使用时,发现租方还未返还,此时进行主动回收。

具体场景来说:进场时,判断有 reusePlayerView,则进行复用;当沉浸式视频(B 页面,类似抖音)翻到下一个视频时,上一个视频进行主动归还操作,如果用户又划回到第 1 个视频,此时是 new 的 playerView 了;另外,当用户点击页面关闭时,主动归还(如果还未还的话);特别要注意的是,这里还增加了一个主动回收机制,场景比如用户通过一些我们未知的方式,回到了页面 A,此时 reusePlayerView 是没有主动归还的,但页面 A 自己又需要 play,此时就触发了主动回收机制,保证当前页面可用。

有一点需要提一下的是,在页面返回时也有动画,实现方面与上述类似,唯一区别是,返回时页面可能 dealloc 了,动画会有问题,所以我们做法是先把 playerView 从 B 页面,添加到 window, 做好缩放动画,结束后,再主动归还给页面 A。

状态隔离

在使用播放器复用时,需要考虑一个重要的问题,就是复用后,播放器状态、设置的隔离。比如,在页面 A 进入页面 B 后,播放器无痕续播,但播放器的状态对 A 来说是暂停,对于 B 来说必须是播放状态,虽然二者使用的是同一个 playerView。

这种隔离是很有必要的,比如业务想要引导用户进入页面 A 的业务,在这里观看视频可以得积分,那么在他进入页面 B 时,就不应该继续结算积分(业务依赖了播放状态通知)。还有,A 与 B 页面的播放设置可能不同,A 可能是静音,B 是有声音,设置不同,也需要隔离。我们是这样做的,如下图(视图层级):

图中,最外层的 view 是盒马自己封装的播放器 HMTBPlayerView,内部有一个手淘的 TBMPBPlayerView,大小一样。我们拿来做复用的其实是 TBMPBPlayerView 这一层,而把业务层的所有设置放在 HMTBPlayerView,这样的话,在 TBMPBPlayerView 被移走时,重新根据新的 HMTBPlayerView 设置它,做好关联,而旧的 HMTBPlayerView 设置不受影响,包括播放器回调。

总结

综上,我们实现了一种播放器复用方式,在播放器内部实现了窗口切换、状态隔离等逻辑,对 App 使用方来说是几乎无感的。该方案不仅可用在无痕续播场景上,今后也可以用在 App 内全局播放器实例复用优化方向。

「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。公众号后台回复【技术】可加入阿里云视频云产品技术交流群,和业内大咖一起探讨音视频技术,获取更多行业最新信息。

如何实现 iOS 短视频跨页面的无痕续播?的更多相关文章

  1. 如何实现 Android 短视频跨页面的流畅续播?

    在一切皆可视频化的今天,短视频内容作为移动端产品新的促活点,受到了越来越多的重视与投入,同时短视频也是增加用户粘性.增加用户停留时长的一把利器.那么如何快速实现移动端短视频功能呢?前两篇我们介绍了盒马 ...

  2. 从350ms到80ms,揭秘阿里工程师 iOS 短视频优化方案

    内容作为 App 产品新的促活点,受到了越来越多的重视与投入,短视频则是增加用户粘性.增加用户停留时长的一把利器.短视频的内容与体验直接关系到用户是否愿意长时停留,盒马也提出全链路内容视频化的规划,以 ...

  3. iOS 短视频开发总结二 AVCaptureSession

    开始录制短视频 录制完成 根据项目需求,对视频URL进行处理 基本原理和方法已经完成,具体细节根据项目实际需求再处理

  4. 揭秘盒马鲜生 Android 短视频秒播优化方案

    短视频作为内容重要的承载方式,是吸引用户的重点,短视频的内容与体验直接关系到用户是否愿意长时停留.因此,体验的优化就显得尤为重要.上一篇我们分享了 iOS 短视频秒播优化,这篇我们来聊聊 Androi ...

  5. 如何快速打造一款高清又极速的短视频APP?

    整个短视频的市场规模一直在增长,网络数据显示2018年已经突破100亿大关,在2019年预测将超过200亿.纵观行业,在生活资讯.美食.搞笑.游戏.美妆等领域,短视频流量巨大但竞争激烈,但是在教育.财 ...

  6. EasyVideoRecorder短视频拍摄、短视频录制SDK支持IOS版本

    在前面的博客<EasyDarwin开发出类似于美拍.秒拍的短视频拍摄SDK:EasyVideoRecorder>和<美拍.秒拍中安卓.IOS短视频拍摄的一些关键技术>中我们简单 ...

  7. 干货|浅谈iOS端短视频SDK技术实现

    短视频SDK主要包含"视频录制"和"视频编辑"这两个核心功能. 视频录制包括:视频采集.美颜.滤镜.摄像头切换.视音频采集参数设置等功能: 视频编辑包括:视频导 ...

  8. 快手、抖音、微视类短视频SDK接入教程,7步就能搞定

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由视频咖 发表于云+社区专栏 终端部分 按照如下三步操作,可以用 XCode 或者 Android Studio 编译和调试小视频 Ap ...

  9. 浅谈短视频APP的发展趋势

    2014年6月20日,在AppAnnie最新发布5月应用指数中,美拍荣登“非游戏类iOS榜单”全球下载量第一位置,成为全球iOS应用商店最热门APP.能在<AppAnnie应用指数>这份A ...

随机推荐

  1. linux signal信号(SIGHUP、SIGINT、SIGQUIT、SIGILL、SIGTRAP、SIGABRT...........................)

    SIGHUP /* hangup */       ~~~~~~      SIGHUP,hong up ,挂断.本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知 ...

  2. Java面向对象03——类与对象的创建

    类的创建与初始化对象 age: 以类的方式组织代码,以对象的组织(封装)数据  package oop.demon01.demon02; ​ // 学生类(抽象模板) public class Stu ...

  3. onethink-i春秋

    记一道onethink漏洞拿flag的题. 因为用户名长度被限制了,注册两个账号分别为 %0a$a=$_GET[a];// %0aecho `$a`;// #(%0a是换行符的urlencode) 点 ...

  4. Salesforce Integration 概览(七) Data Virtualization数据可视化

    本篇参考:https://resources.docs.salesforce.com/sfdc/pdf/integration_patterns_and_practices.pdf Salesforc ...

  5. Python3中dict字典的相关操作函数

    字典对象的内建函数 1. clear() 清空字典. 例: >>> a = {1:3, 2:4} >>> a.clear() >>> a {} 2 ...

  6. GO语言安装以及国内镜像

    首先,下载GO语言,国内的话用 Go下载 - Go语言中文网 - Golang中文社区 (studygolang.com) 可能会快一点 然后根据自己的系统选择下载的包,我是win10,就选go1.1 ...

  7. HttpClient调用doGet、doPost、JSON传参及获得返回值

    调用 doPost:map传参 Map<String,Object> map = new HashMap<>(); map.put("test"," ...

  8. noip19

    sb\(O(n^{2})\)传参 T1 暴力一会儿就码好,结果.. 祭奠一下死去的代码 died #include<cstdio> #define MAX 1010 #define re ...

  9. redis如何实现分布式锁?

    1.使用redis中的自增来实现 2.使用setnx + del # 如果不存在set(返回1),如果存在则失败(返回0) 为了避免死锁会加上一个过期时间 自增方式 boolean isSelf = ...

  10. Docker创建Gitea(git服务)

    背景 Gitea是流行的自托管Git服务Gogs的社区分支.gogs作者想一个人维护gogs,但是大家想一起维护.就把gogs项目fork了. 下面是gitea的介绍: https://blog.gi ...