作者 | 阿里文娱无线开发专家 城泉

责编 | 屠敏

概述

优酷播放内核是优酷自主开发的一个基于pipeline结构的SDK。它对上承接了优酷丰富灵活的业务逻辑,对下屏蔽了各端系统的差异,是一个高可靠、可扩展、跨平台的优秀播放SDK。

但是,跨团队协作及长时间的迭代,也使得当前播放内核显得有些“臃肿”。占用内存过高、使用线程太多等这些问题除了会影响用户的体验之外,也在一定程度上制约了一些业务的实现,例如针对短视频的多实例方案。所以,急需对内核各模块进行一次“轻量化”的改造。目标是:

1)更少的线程

2)更小的内存

3)更低的功耗

改造前的摸底

优酷播放内核实现了一套基于pipelie的框架,结构如下:

包含了接口层,处理命令和消息上报的engine,透传消息的filter层,主体干活的module层,数据下载模块以及渲染和后处理模块。

经过梳理跟测试,确认我们的播放内核使用的线程会比一些开源的播放内核(比如ijkplayer)多很多,内存使用量以及视频耗电量等数据相比竞品也处于劣势。所以我们亟需对我们的播放内核进行一轮改造。

改造的详细过程

我们改造的方向包含:线程、内存、功耗这三个方面。希望用最少的线程实现整个播放流程,用最小的内存使得播放依然流畅,占用最少的cpu资源使得播放更持久。

采用的策略是做“加法”。根据播放流程,保留必要的线程,去除冗余的线程,重用可复用的线程。然后review每一个保留下来的线程,测试使用内存及cpu占用率是否符合预期,如果异常再进行逐一排查。

▐  线程精简

优化前内核使用的线程数有近30个,相比其他开源播放器多了很多。其中有些是必不可少,有些是可被其他线程复用,还有些是逻辑冗余,可以直接去除。在梳理要留下哪些线程的时候,我们考虑了一个播放过程所需要的线程“最小集”,应该会包括如下一些线程模块:

  • engine:用于接收接口命令,以及上报内核消息;

  • source:用于数据读取并驱动pipeline数据向后流动;

  • decoder:音视频各一个,用于音视频数据解码;

  • consumer:音视频各一个,用于同步及渲染;

  • hal buffer:用于解复用及缓存状态监控;

  • ykstream:用于控制下载模块并和切片解析模块交互;

  • render:用于渲染管理。

可以看到,播放流程必须用的线程其实就9个。而其他的线程除了预加载管理、播放质量监控以及字幕相关等在需要的时候会被启用之外,其余都可以去除。

精简步骤如下:

1)去除多余的filter线程

filter只有在创建module的时候用到,后面都是消息透传,显得有些多余,所以可以直接去除。将创建module的逻辑移到engine的prepare流程,打通engine与module之间的消息通道,上面下达的命令以及下面上报的消息不再经过filter。

2)去除消息传递器和时钟管理器

优化前消息上报通道比较混乱,有些直接上报给engine,有些上报给消息传递器进行一次中转,然后再上报给engine。消息传递器这层逻辑有些多余,所以去除了这个线程,所有消息上报都通过engine。

时钟管理器作为同步时间来用,这个不需要线程,线程的存在是用作一个定时器。目前内核使用到定时器的就一两个点,通过其他线程逻辑复用,去除了对定时器的依赖,这个线程也可以去除。

3)去除接口命令线程和消息上报线程

接口层加了一个线程中转一个下发的命令,目的是为了接口超时的时候内核有forcestop的机制。在经过多轮优化后,内核触发forcestop的情况大大减少,所以这个线程显得有些多余,就算还会出现卡住的情况,也会有anr来替代原先的crash,这个线程可以去除。

消息上报线程是为了内核层多实例上报消息加上的,实际上经过代码复用,这个线程也不是必须的,可以去除。

4)去除解复用线程和二级缓存线程

内核获取数据一直是逻辑最臃肿的地方,优化前有5个线程来实现这部分功能。优化后保留3个即可,解复用线程和二级缓存线程可以去除。

5)去除预加载管理器和字幕解码模块

预加载管理器不管有没有开启预加载都会运行,需要加上开关控制,只有在预加载开启情况下才会运行。

字幕的实现主要是数据读取、解析和render,其中不同于音视频,文本信息在读取后就可以直接去解析,所以字幕解码模块可以去除。

优化后,线程有9个必须的,加上播放质量监控,总共保留12个线程。没有字幕的视频只剩下10个。

▐  内存裁剪

消耗内存地方主要有四处:缓存下载数据的buffer、pipe管线中的buffer、存msg信息的结构体、以及各class对象的内存。class对象除非不用,否则没有太多裁剪的空间,所以内存裁剪就从缓存、pipe管线及信息存储结构体三个角度去实行。

1)排查内存使用不符合预期的地方

扫描线程内存数据发现,读buffer的线程内存消耗高出设置值很多。分析每个es sample的数据,发现除了数据部分之外,还存了一个codec的context,每个packet都要存一个。各packet的codec context都应该是一样的,只需存一份即可。内核针对这部分不合理的逻辑进行了修复,内存使用降低了近1/3。

2)减少缓存buffer

缓存buffer相比竞品设置的有些大,考虑到下载模块也有一块不小的buffer,所以内核的buffer可以裁剪,平衡卡顿数据,可将buffer设置在较低的水位。

3)减少pipe管线内存使用

pipe管线内存加上内核二级缓存使用量达到3.5M,source重构后去除了二级缓存,加上对pipe buffer pool的优化,这部分内存可减小到0.5M。

4)优化部分数据结构

比如存放信息的AMessage结构,每一个AMessage会消耗4k bytes。针对hls智能档的场景,每一条记录都会创建一个AMessage,所以的记录加起来会超过6MB,这还不包括其他使用AMessage的地方。所以我们重写一个功能类似的结构体进行替换,接口上与AMessage保持一致,减少了内部不必要的内存开消。

优化后,播放内核峰值内存已经降到原来的1/3,大大减少了单个实例使用的内存数。

▐  功耗优化

功耗的主要影响因素有:cpu占用率、网络请求时长、屏幕及audio等设备的耗电。屏幕亮度音量等这些因素是固定的,所以降低功耗主要从cpu占用率和网络请求时长这两个方面去考虑。

1)减少不必要的流程,裁剪多余线程

这部分在线程裁剪中已经完成,这里不再详述。

2)控制网络请求时长,避免过长的网络连接

移动设备在请求网络的时候,网络设备wifi/4G会及时通电,这部分耗电很大。所以大块的读取一段数据然后wait要好过频繁小段的请求数据。考虑卡顿等其他因素,内核默认设置在缓存消耗到低于2/3之后才重新启动下载。

3)替换数据存储结构,去除冗余存取逻辑

排查发现,每次数据写入buffer,cpu都会异常的繁忙,这与预期不符。review代码找到异常点:我们存储数据用的是vector数据结构,每次来数据都是push到front,当vector的size达到数万的量级之后,这个push_front的操作会非常的消耗cpu。修改的办法是将vector改成list,数据写入到tail,从header读取,该问题不再复现。

4)omx同步调用改成异步,减少解码cpu耗时

android平台上,硬解omx模块默认用的是同步调用模式。android9.0以下native层只提供了这种模式,会循环的进行queue/dequeue操作,cpu消耗较大。android9.0及以上,native层提供了omx的异步调用模式,会只在queue/dequeue完毕之后callback调用解码模块干活,所以cpu消耗比同步要小。如下图所示,异步比同步要明显稀疏一些。

5)减少倍速算法冗余计算

review发现audioconsumer线程cpu消耗比audio decoder多很多,不符合预期,检查发现当没有开启倍速情况下,也会走倍速相关的运算逻辑,导致cpu异常消耗,修复前后对比如下图:

6)内核层实现弹幕逻辑

弹幕的实现原先是应用层通过view来实现,在弹幕数据多的情况下,非常影响功耗,甚至会出现弹幕模糊的情况。所以考虑将弹幕的实现移到内核层,由内核接收弹幕数据实现render。经过验证,优化后弹幕的功耗降低了2/3.

优化后,播放运行时平均cpu占用率已经低于7%(android中端机测试),1080p/90分钟的视频耗电量降到12%,相比优化前有了30%的提升。

小结

至此,播放内核相比优化前已经大大的“瘦身”了。瘦身后内核的代码逻辑变得更加的清晰,数据传递也更加简洁高效,这让参与内核开发的同学可以更多的关注到自己的业务本身。内存使用量大幅降低,只从内存的角度讲,优化前两个实例的内核,现在可以创建6个,极大的拓宽了上层业务逻辑的边界。功耗也变得更低,大大提升了用户的播放体验。

需要注意的是:我们的业务复杂多变,参与开发的团队也有很多,版本迭代一段时间之后,难免会让内核变得越来越臃肿。所以我们需要对每个正式的版本进行内存、功耗等多个纬度的监测,发现问题立即修改,这样便不会将这些问题积累下去。内核也要定期进行小规模的重构,去除不合理的代码,统一通用的逻辑处理单元,这样才能让高质量的内核持续保持下去。

推荐阅读 

☞美团十年,支撑全球最大规模外卖配送的一站式机器学习平台是如何炼成的?

☞比尔·盖茨退出微软公司董事会;苹果 WWDC、微软 Build 大会均改为线上举办;Rust 1.42.0 发布| 极客头条

☞腾讯提结合ACNet进行细粒度分类,效果达到最新SOTA | CVPR 2020

☞我最喜欢的云 IDE 推荐!

☞智能合约编写之Solidity的高级特性

☞返鄂复工人员自述:回武汉上班,要先飞合肥,再由公司包车接回去

你点的每一个在看,我认真当成了喜欢

播放内核的“瘦身”,你只需要这样做!相关推荐

  1. ttf字库瘦身,只保留自己想要的字

    在做应用或游戏的时候往往需要各种不通的字体效果,例如按钮, 用ttf字库显得有比较大一般有好几M,用图片也很占空间, 但是ttf字库可以有瘦身版,这个就要自己搞了,英文的还好处理,下载一个你需要的tt ...

  2. Linux内核有多大体积,Linux之父:Linux内核体积臃肿必须瘦身

    据媒体报道,在周一举行的LinuxCon技术大会上,Linux操作系统创始人.素有"Linux之父"之称的林纳斯·托瓦尔兹(Linus Torvalds)表示,目前Linux内核体 ...

  3. win7 瘦身 减肥 记录!

    用WIN7优化大师 里的系统盘瘦身功能 只保留壁纸(壁纸38MB也可以删)和驱动备份 保留目录为 1 Windows\Web\Wallpaper -壁纸路径2 Windows\System32\Dri ...

  4. 日志瘦身骚操作:从5G优化到1G!

    目录 背景 日志瘦身方法论 优化案例 总结 背景 在日常开发中,通常为了方便调试.方便查问题,会打印很多 INFO 级别的日志. 随着访问量越来越大,一不小心,某个日志文件一天的 size 就大于了某 ...

  5. 深入探索 Android 包瘦身(上)

    码个蛋(codeegg) 第 942 次推文 作者:jsonchao 链接:https://juejin.im/post/5e7ad1c0e51d450edc0cf053 今天分享一篇匠心制作的< ...

  6. 镜像瘦身:每一层都不能放过

    网上很多人都说镜像瘦身需要把所有命令放在一条来执行,这没有错,但只是问题表象,没有触及本质. 当我打了一个带源码编译的镜像,结果异常庞大,明明已经删了源码包,为什么还那么大呢?当我想尝试放在一条命令时 ...

  7. 电脑使用小常识(3):给C盘瘦身

    很多同学跟我讲,C盘不够用,C盘爆红了.... 望着他们一脸无辜的样子,唉~ C盘瘦身,只需要做到: 一.C盘容量大小 C盘容量,不要低于150G. win10 时代,很多人喜欢把C盘设置 100G, ...

  8. python实现英文新闻摘要自动提取_利用Python实现摘要自动提取,完美瘦身只需一行代码...

    原标题:利用Python实现摘要自动提取,完美瘦身只需一行代码 今天给大家推荐一个也可以用于关键字提取的算法TextRank,但主要实现的功能是快速从长篇新闻中抽取精准摘要. 前言介绍 TextRan ...

  9. 【剪枝算法】通过网络瘦身学习高效的卷积网络Learning Efficient Convolutional Networks through Network Slimming论文翻译

    此论文翻译仅仅用于自己方便学习.译文来自其他处. 在许多实际应用中部署深度卷积神经网络(CNN)很大程度上受到其计算成本高的限制.在本文中,我们提出了一种新的CNNs学习方案,能同时1)减小模型大小; ...

最新文章

  1. CFHD打的爽就继续下去,打的不爽就?
  2. 【机器视觉】 until算子
  3. 【牛客 - 317D】小a与黄金街道(数论,tricks)
  4. my stackoverflow
  5. c#app.config配置文件使用
  6. UPNP解读2-含netbios,wins,DNS
  7. 为什么录像总是很暗_深度:为什么看马拉多纳的踢球视频,并不觉得他很厉害?...
  8. 查询SQL的null与''
  9. 20171208校内训练
  10. Docker学习: 配置Docker阿里云的镜像加速器(图文)
  11. 重装WIN7系统 - 通用激活
  12. 【webRTC】一个基于 tornado 和 webRTC 的点对点视频语音文字聊天室
  13. Hex Editor
  14. 谈谈我的信息安全学习经历
  15. CC2530关于端口中断常用的寄存器
  16. 山东大学软件学院2022年数据库课程设计环境配置教程
  17. 线性代数(3):矩阵
  18. 6.824-lab1
  19. FairLex A Multilingual Benchmark for Evaluating Fairness in Legal Text Processing
  20. Ubuntu下PyCharm安装中文汉化包

热门文章

  1. [UI]实用案例--Shape绘制实用圆圈
  2. 网站的基本功能:RBAC
  3. 清空StringBuilder的三种方法及效率
  4. flash与js交互
  5. 东南大学计算机网络_【20考研】东南大学计算机考研分数统计
  6. pstate0 vid数值意义_老照片、新感悟:春兰摩托电喷 CB、看一口螺丝的意义
  7. leetcode python3 简单题204. Count Primes
  8. 人脸关键点标注工具_谈谈人脸关键点的江湖
  9. leetode题库5438--制作 m 束花所需的最少天数
  10. 剑指Offer之二叉树与双向链表