揭秘盒马鲜生 Android 短视频秒播优化方案
短视频作为内容重要的承载方式,是吸引用户的重点,短视频的内容与体验直接关系到用户是否愿意长时停留。因此,体验的优化就显得尤为重要。上一篇我们分享了 iOS 短视频秒播优化,这篇我们来聊聊 Android 端的优化。
作者|少阳
审校|泰一

优化前的盒马沉浸式短视频播放页面,体感和流畅度上与主流短视频 App 有明显差距。主要问题有播放封面闪屏、出流速度慢两个问题。所以优化的目标是解决盒马沉浸式短视频现有短板,与主流 App 的沉浸式短视频体验对齐,如抖音、手淘等。具体指标有:
- 满足硬性指标:播放成功率、首帧时长、秒开率。
- 满足用户体感流畅度。(为反应用户观看短视频过程中的真实体验,盒马新增秒播体感指标:从用户划到视频,到视频首帧播放的时间。)
优化效果对比
首先我们来看一下优化前后与其他 App 的效果对比:
https://v.youku.com/v_show/id_XNTgwMzAwNDQ0OA==.html
环境
- 手机:Pixel 4
- OS:Android 10
- 播放器:淘宝播放器
问题分析
首页闪屏
盒马最初为了保证进入画面时不是空白页面而增加了封面图显示,在播放时隐藏。从体感指标可以看出,即便是优化前,体感播放时间很短,只有 200ms 左右(不包含滑动过程)。由于滑动过程中,到视频正式播放有约 600ms 左右时间显示封面,随后又迅速显示播放画面,此时用户仍有强烈的屏幕闪烁和顿挫感,体验极差。
解决思路:在滑动过程中就显示视频首帧画面,不再显示封面,则播放时不再产生顿挫感。这里的优化需要结合出流慢问题一起优化。

出流速度慢(播放体感慢)
服务端:服务端造成的出流速度慢,一般是文件大,网络链路差造成。可用 H.265 和 CDN 加速优化
客户端:客户端播放需要经历下载 -> 加载 + 解码 -> 渲染三个步骤,并且三个步骤为线性执行。所以在窗口播放画面前必然需要经过 1s 左右的准备工作。这里可以考虑提前执行下载 -> 加载 + 解码。

优化方案选型
在优化前期,我们考虑了三种优化方案。
方案一:双播放器 + 预下载
优点:占内存小,思路简单。
缺点:优化力度有限,无法同时兼顾上滑和下滑。
方案二:自定义三播放器管理 + 预下载
优点:同时兼顾上下翻页,体验接近抖音。
缺点:播放器管理与回收实现复杂,容易错乱;占用内存高。
方案三:三播放器(基于 RecyclerView 的缓存机制实现)+ 预下载
优点:同时兼顾上下翻页,体验接近抖音,缓存机制由 RecyclerView 托管。
缺点:占用内存高,频繁创建和销毁播放器。
最终因为性价比因素,选择了第三个方案。
方案三原理:翻页前
- current 播放器开始播放视频 1。
- pre,next 播放器分别加载视频数据 0 和 2。
- 同时视频数据 3-7 加入预下载队列。

方案三原理:翻页后
- 被 RecyclerView 回收的 holder 销毁播放器。
- RecyclerView onBind 中的 holder 创建新的 next 播放器。
- current 播放器开始播放视频 2。
- pre 播放器 seek 0 并暂停, next 播放器创建并加载视频 3 文件。
- 同时预下载清除未消费的队列,视频数据 4-8 加入预下载队列开始下载(此处已有缓存的视频不会被重复下载)。

具体优化方案
多播放器改造
为了解决体感上的顿挫和出流慢的问题,采用多播放器结合 RecyclerView 方案进行改造,步骤如下:
- 设置缓存数量:利用 RecyclerView 特性,配置 setItemViewCacheSize,确保内存中存在 3 个 holder(缓存的 1 个 holder,预创建 1 个 holder, 当前 holder)。
mRecyclerView.setItemViewCacheSize(1); // 设置缓存数量 - 重写 Adapter 的 onBindViewHolder, 用于创建播放器,并预加载解码视频内容,播放器控制解析到首帧时暂停。此时 onSurfaceCreated 尚未回调,画面未渲染至屏幕。
- 监听 onPageRelase 控制即将移除屏幕的播放器暂停,并 seekTo (0),方便滑回屏幕时立即播放。
- 监听 onPageSelected 控制即将进入屏幕的播放器开始播放。注意:由于在 onBindViewHolder 期间已解码完成,这里只需要进入屏幕 1px,就会立即触发 Surface 的绘制(只会执行一次),即进入窗口的内容会显示视频的首帧画面。
- 重写 Adapter 的 onViewRecycled, 由于当前 holder 即将移出屏幕,移出方向上屏幕外的 holder 将被回收。此时回收并销毁播放器。

多播放器 + RecyclerView 原理图
三播放器让沉浸式短视频的体验大幅提高,主要解决了以下问题:
- 上下滑动过程中,进入屏幕的画面为视频的第一帧画面,并且不会有视觉上的顿挫。
- 正式播放前预创建播放器,并加载和解码,节省了播放视频之前的准备工作。(ps:这里还包括了下载的过程)。
- 由于提前加载和解码,进入屏幕时,触发 Surface 瞬间渲染,视觉上无感知,因此播放视频前不再需要封面图,避免了封面图和首帧不一致导致的闪屏问题。
预下载优化
前面讲到了多播放器实现翻页秒播能力,在体验上有了非常大的改善,但由于预创建的播放器在加载时,同时需要下载视频文件,导致这里的下一个播放器准备好视频的时间会增加到 1s 左右。如果用户在播放器加载解码完成前滑至该视频,则会出现明显的黑屏,带来非常差的体验。
由于预加载的时间过长,且无法预知用户是否会快速滑动。这里需要提前进行下载和快速滑动检测。
关于预下载,我们首先要知道播放器内部播放过程。这里的本地代理是视频缓存机制实现的,具体参照下一章节。

播放器内部流程
预下载策略
这里,我们为了节约请求网络数据的过程,在播放之前提前下载视频的首帧片段,采用如下策略:
- 文件大小:下载 1MB 视频文件的方式进行提前首帧下载。(ps:经测试 1MB 已包含了首帧,且文件相对较小)。
- 提前量:提前 5 个下载量(pageSize 为 10 的情况)。
- 并发情况:下载采用同步队列下载(避免异步下载导致带宽占用,正常播放的视频卡顿)。
- 快滑优化:快滑清除下载队列,避免快滑过程中频繁触发下载。
- 下载时机:loadMore 时将前 5 个推入队列;onPageSelected 时,跳过下一个开始起算 5 个视频推入队列(下一个视频由预加载的播放器自动下载,这里重复下载会导致视频花屏)。
快滑定义
当用户快速翻页时(onPageSelected 调用之前又滑了一下),onPageSelected 不会触发,onPageRelease 会触发多次。在 onPageRelease 中判断 release position 与 current postion 的差值如果 > 1 则表示用户至少快速翻页 1 次,此时定义为快滑状态,应当停止预下载和播放器预加载。
当 onPageSelected 回调时,说明用户没有继续翻页,此时取消快滑状态。开始执行预下载和恢复播放器预加载。

预下载流程图
缓存优化
目前盒马使用的播放器为淘宝内部播放器。 播放器本身不存在文件缓存和预下载功能。在播放器重新创建后,即便是同一个视频也不会有文件缓存,需要重新下载。这里引入一个开源缓存框架 “com.danikula:videocache”。
方案原理
播放器播放的地址代理给本地的缓存服务,缓存服务负责转发数据流的同时进行文件保存如:
视频地址为:https://****.mp4。
本地代理地址为:http://127.0.0.1:8888 (假设端口号分配为 8888)。
代理后的地址为: 本地代理地址 + 视频地址(URL 编码)。
即:http://127.0.0.1:8888/https%3A%2F%2F****.mp4

后续优化展望
关于多媒体的优化工作还有很多可以做。除了沉浸式秒播的场景外,我们还可以:
- 对播放器的一般性场景进行秒播优化,如首页列表的卡片视频。目前首页平均首帧画面需要 550ms,较长的有 1000s 以上,有明显的顿挫感。在沉浸式的方案基础上,我们可以提供一般性的预下载能力,从而减少播放器的下载渲染时长。
- 续播和内存优化。续播是另一个提升体验的方面,用户能够非常直观的感受连贯与否。
- 页面单播放器托管。大多场景下,一个页面只有一个播放器在播放,这就可以通过管理唯一的播放器实现页面播放器复用,从而优化内存和体验。
下一期我们将继续分享盒马 iOS / Android 端短视频续播的体验优化实践。
「视频云技术」你最值得关注的音视频技术公众号,每周推送来自阿里云一线的实践技术文章,在这里与音视频领域一流工程师交流切磋。公众号后台回复【技术】可加入阿里云视频云产品技术交流群,和业内大咖一起探讨音视频技术,获取更多行业最新信息。
揭秘盒马鲜生 Android 短视频秒播优化方案的更多相关文章
- 如何实现 Android 短视频跨页面的流畅续播?
在一切皆可视频化的今天,短视频内容作为移动端产品新的促活点,受到了越来越多的重视与投入,同时短视频也是增加用户粘性.增加用户停留时长的一把利器.那么如何快速实现移动端短视频功能呢?前两篇我们介绍了盒马 ...
- Android短视频中如何实现720P磨皮美颜录制?
视频中磨皮.美颜功能已成为刚需,那么如何在Android短视频中实现720P磨皮美颜录制?本篇文章中,网易云信资深开发工程师将向大家介绍具体的操作方法. 相关阅读推荐 <短视频技术详解:Andr ...
- Android短视频滑动播放(一)
本文主要介绍采用RecyclerView配合PagerSnapHelper实现短视频滑动播放内容. 1. 主页内容构建 主页布局文件定义RecyclerView,为RecyclerView建立对应适配 ...
- Android短视频SDK转码实践
一. 前言 一些涉及的基本概念: 转码:一般指多媒体文件格式的转换,比如分辨率.码率.封装格式等: 解复用(demux):从某种封装中分离出视频track和音频track,然后交给后续模块进行处理: ...
- Android App罕见错误和优化方案
本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 1.App如果被定义一个有参数构造函数,那么需要再定义一个无参数的,如果不则会在某些情况下初始化失败 ...
- 马老师 生产环境mysql主从复制、架构优化方案
Binlog日志(主服务器) => 中继日志(从服务器 运行一遍,保持一致).从服务器是否要二进制日志取决于架构设计.如果二进制保存足够稳定,从性能上来说,从服务器不需要二进制日志.默认情况下, ...
- 从350ms到80ms,揭秘阿里工程师 iOS 短视频优化方案
内容作为 App 产品新的促活点,受到了越来越多的重视与投入,短视频则是增加用户粘性.增加用户停留时长的一把利器.短视频的内容与体验直接关系到用户是否愿意长时停留,盒马也提出全链路内容视频化的规划,以 ...
- 短视频技术详解:Android端的短视频开发技术
在 <如何快速实现移动端短视频功能?>中,我们主要介绍了当前短视频的大热趋势以及开发一个短视频应用所涉及到的功能和业务.在本篇文章中,我们主要谈一谈短视频在Android端上的具体实现技术 ...
- 短视频 SDK 架构设计实践
作者简介 孔维乐,七牛云客户端团队 Android 平台高级开发工程师,专注音视频,图形图像领域.OpenGL 专家,先后参与直播推流及连麦 SDK 的开发,主导短视频 SDK 的架构设计与实现, 对 ...
随机推荐
- element UI rules prop对应关系
- AAAI 2021 最佳论文公布
作者:Synced 翻译:仿佛若有光 第三十五届 AAAI 人工智能会议 (AAAI-21) 以虚拟会议的形式拉开帷幕.组委会在开幕式上公布了最佳论文奖和亚军.三篇论文获得了最佳论文奖,三篇被评为 ...
- 【爬虫系列】1. 无事,Python验证码识别入门
最近在导入某站数据(正经需求),看到他们的登录需要验证码, 本来并不想折腾的,然而Cookie有效期只有一天. 已经收到了几次夜间报警推送之后,实在忍不住. 得嘞,还是得研究下模拟登录. 于是,秃头了 ...
- python打包exe之pyinstaller用法
pyinstaller可以将python写好的脚本打包成exe文件,方便windows用户在没有python环境下运行.这个程序完全跨平台,包括Windows.Linux.Mac OS X等多个操作系 ...
- Mybatis学习笔记-动态SQL
概念 根据不同环境生成不同SQL语句,摆脱SQL语句拼接的烦恼[doge] 本质:SQL语句的拼接 环境搭建 搭建数据库 CREATE TABLE `blog`( `id` VARCHAR(50) N ...
- 在Linearlayout中新增ScrollView支持滚动
https://blog.csdn.net/wenzhi20102321/article/details/53491176 1.一般只需要在布局中加个ScrollView即可 2.如果布局中包含lis ...
- C++ 二进制文件 读 写文件
1 #include <iostream> 2 #include <string> 3 #include<fstream> 4 using namespace st ...
- 得到、微信、美团、爱奇艺APP组件化架构实践
一.背景 随着项目逐渐扩展,业务功能越来越多,代码量越来越多,开发人员数量也越来越多.此过程中,你是否有过以下烦恼? 项目模块多且复杂,编译一次要5分钟甚至10分钟?太慢不能忍? 改了一行代码 或只调 ...
- SaToken学习笔记-03
SaToken学习笔记-03 如果排版有问题,请点击:传送门 核心思想 所谓权限验证,验证的核心就是一个账号是否拥有一个权限码 有,就让你通过.没有?那么禁止访问! 再往底了说,就是每个账号都会拥有一 ...
- AttributeError: module 'numpy' has no attribute 'num'
AttributeError: module 'numpy' has no attribute 'num' 写在前面 总的来说,先看看自己用的计算方式是不是写对了先,多个一起使用的话记得都看看 通过想 ...