【转】iOS弹幕库OCBarrage-如何hold住每秒5000条巨量弹幕
最近公司做新需求, 原来用的老弹幕库, 已经无法满足需要. 迫不得已自己写了一套弹幕库OCBarrage. 这套弹幕库轻量, 可拓展, 高度自定义, 超高性能, 简单易上手.
无论哪家公司软件的性能绝对是衡量APP好坏的重要指标. 之前有一次开会, 我们领导说:”我们写的东西, 有哪些是可以拿的出手,让我们引以为豪的?”. 之前还真就得想一会儿, 现在可以毫不犹豫的说我们的弹幕库绝对是一个好家伙.
做直播类软件核心功能一个是播放器另一个就是弹幕了. 现在iOS开源的弹幕库性能好的不多, 弹幕量稍微大一点, 或者弹幕稍微复杂一点, 就会出现卡顿,这与它的底层实现, 设计策略以及你的使用方法都有关系. 关键是动画单一,无法定制,满足不了动画的多样化需求!OCBarrage正是为解决这些问题而生的!
OCBarrage底层使用Core Animation驱动, Core Graphics绘图, GPU渲染, 性能极高, 哪怕是同时渲染5000条弹幕也不会感觉到卡顿. 开源地址:https://github.com/w1531724247/OCBarrage
(以下测试基于iPhone7真机)




对于全民直播这样的平台来说,在大主播高峰时期的弹幕量是很大的,特别是当主播说一句:“我们现在开始弹幕抽奖”。弹幕量瞬间就会涨的很高!所以对弹幕这一块的要求还是蛮高的.
性能优化原理
弹幕渲染时比较耗性能的点:
1. ######弹幕阴影
主播在户外直播时偶尔会有白色的背景, 而弹幕文字的颜色也是白色的, 这个时候弹幕飘到直播画面的白色区域会导致看不到文字内容. 为了解决这个问题我们通常会给弹幕文字添加一个隐影.以防止这种情况的发生. 然而别小看这几个像素阴影, 它可是性能消耗的大户. 哪怕是用GPU渲染因为是动态的实时的所以也相当吃性能. 在实验的过程中发现如果有文字阴影几十条弹幕就会出现弹幕卡顿, 结果就是弹幕抖动一跳一跳的.
解决办法就是用NSAttributeString的NSStrokeColorAttributeName属性设置文字的轮廓颜色替换文字阴影.效果对比如下: 

都能解决我们的问题, 但是性能差的可不是一丁半点.
2. ######用CALayer替代UIView展示
与UIView相比CALayer更轻量. 性能更好.系统提供的组件为了保证其通用性, 难免有些冗余.这就是我们优化的空间.
3. ######弹幕文字下面的渐变色背景
彩色弹幕下面的渐变色背景如果用CAGradientLayer实现也是比较耗性能的, 但是如果是用图片呈现的话效果就会好的多, 但是不够灵活, 没关系, 我们都一并解决了.
4. ######将内容合成一张图片展现
将所有的内容呈现在layer上并布局好位置以后将所有的内容合成一张图片展现在barrageCell的layer上, 并删除所有的子subview及sublayer, 以提高性能.
基础类
OCBarrageManager
提供了弹幕渲染视图并负责弹幕引擎的启动, 暂停, 继续及停止.
- renderView:弹幕呈现的视图.
- renderStatus:renderView的状态, 开始, 暂停, 结束.
- - (void)resgisterBarrageCellClass:(Class)barrageCellClass withBarrageIndentifier:(NSString *)barrageIndentifier;:
注册cell类型, 在调用- (void)renderBarrageDescriptor:(OCBarrageDescriptor *)barrageDescriptor;方法的时候会根据barrageDescriptor的barrageIndentifier去自动去缓存池中取一个注册的与barrageDescriptor的barrageIndentifier相同的cell.并将传入的barrageDescriptor赋值给cell的barrageDescriptor属性.
- - (void)start;:允许接收并渲染弹幕
- - (void)puase;:暂停弹幕动画. 如果弹幕正在运动则会暂停在当前位置, 并且不会渲染并丢弃新收到的弹幕.
- - (void)resume; :继续弹幕动画, 并渲染新收到的弹幕.
- - (void)stop;: 清空当前正在动画的弹幕, 并停止接收渲染新的弹幕.
OCBarrageDescriptor
OCBarrageDescriptor是负责传递弹幕数据的类, 通过barrageIndentifier找到对应的OCBarrageCell并将数据展示在OCBarrageCell上.
- positionPriority:属性决定了弹幕渲染之后在视图层次上所处的位置, 因为经常会有一些比较重要的弹幕不希望被普通弹幕覆盖, 这个时候可以将重要弹幕的positionPriority设置的高一点, 这样重要弹幕就可以渲染在普通弹幕的上方而不会被普通弹幕覆盖.
- animationDuration:动画时间, 这条弹幕从开始到结束的总时间.
- touchAction:弹幕被点击时候执行的动作.
- bindingOriginY:本条弹幕在渲染的时候将frame.origin.y固定在bindingOriginY的位置.>=0时生效. 可以固定弹幕Y坐标的位置.
OCBarrageCell
呈现弹幕数据的视图.
- idle:是否是空闲状态, 如果正在运动则为NO, 如果正在缓存池中等待被复用则为YES.
- idleTime:弹幕动画执行完毕后的时间点. 弹幕动画执行完毕后当前cell会被放进缓存池等待下次复用减少cell创建释放的性能开销. 如果超过5秒没有被复用则会被释放回收内存, 减少内存占用.
- barrageIndentifier:标识符. 通过这个属性与OCBarrageDescriptor类绑定. 与OCBarrageDescriptor的barrageIndentifier对应.
- barrageDescriptor:当前展示的OCBarrageDescriptor.
- barrageAnimation:当前cell所执行的动画.
- trackIndex当前cell所在的弹幕轨道的索引.
- - (instancetype)initWithBarrageIndentifier:(NSString *)barrageIndentifier;:根据传入的barrageIndentifier创建一个实例.
- - (void)addBarrageAnimationWithDelegate:(id<CAAnimationDelegate>)animationDelegate;: 子类可以通过重写这个方法为当前cell添加自定义的动画类型.animationDelegate默认为OCBarrageRenderView, 可以监听动画执行完毕的事件以便将cell放入缓存池等待下次复用.
- - (void)updateSubviewsData;:在这个方法里给cell的各个子视图赋值. 也可以在- (void)setBarrageDescriptor:(OCBarrageDescriptor *)barrageDescriptor方法里给子视图复制. 在这个方法调用以后可以计算一下子视图的的大小及位置.. 也可以在 - (void)layoutSubviews设置子视图的大小及位置.
- - (void)clearContents;设置数据前清空一下上次展示的遗留内容.
- - (void)sizeToFit;:根据子视图的边界自适应大小.
- - (void)convertContentToImageWithSize:(CGSize)contentSize;: 将当前cell上的子视图的内容绘制成一张图片, 并将生成的图片赋值给当前cell的layer的contents属性展现出来. 这个方法要在设置完数据和布局完子视图的位置之后调用. 这个方法会自动调用- (void)layoutSubviews因此不可以在layoutSubviews里调用这个方法不然后造成死循环. 图片生成之后这个方法会执行removeAllSubViewsAndSublayers

删除当前cell上所有的子视图以及子layer以优化展示性能.所以不希望被删除的视图或者layer请在这个方法调用之后添加, 在这个方法执行之后建议将子layer或者子view设置为nil以回收内存. 
- (void)prepareForReuse;: 在复用之前要进行的操作可以放在这个方法里执行, 在重写这个方法的时候记得调用一下[super prepareForReuse];.
OCBarrageRenderView
- animatingCells: 正在运动的弹幕cell的数组.
- idleCells: 弹幕动画执行完毕后等待复用的弹幕cell的数组.
- renderPositionStyle: 新出生的弹幕的位置OCBarrageRenderPositionRandomTracks, 有弹幕轨道, 新出生的弹幕随机展示在其中一条轨道上, 并且尽量不覆盖在还没呈现完整的正在出来的弹幕上-默认类型.OCBarrageRenderPositionRandom没有弹幕轨道, y坐标完全随机.OCBarrageRenderPositionIncrease有弹幕轨道, y坐标循环递增.
- renderStatus: 引擎状态正在OCBarrageRenderStoped, 不接收不渲染新弹幕. OCBarrageRenderStarted接受并渲染新弹幕.OCBarrageRenderPaused暂停中, 不接收不渲染新弹幕, 已经渲染的弹幕停留在当前所处的位置.
- - (void)resgisterBarrageCellClass:(Class)barrageCellClass withBarrageIndentifier:(NSString *)barrageIndentifier;: 同OCBarrageManager的- (void)resgisterBarrageCellClass:(Class)barrageCellClass withBarrageIndentifier:(NSString *)barrageIndentifier;.
- - (nullable OCBarrageCell *)cellWithBarrageIndentifier:(NSString *)barrageIndentifier;: 根据传入的barrageIndentifier从缓存池或者注册的cell列表中返回一个cell.
- - (void)fireBarrageCell:(OCBarrageCell *)barrageCell;: 发射弹幕.
- - (void)start;: 同OCBarrageManager.
- - (void)puase;: 同OCBarrageManager.
- - (void)resume;: 同OCBarrageManager.
- - (void)stop;: 同OCBarrageManager.
OCBarrageTrackInfo
弹幕轨道信息类.
- trackIndex: 弹幕轨道所在的位置.
- trackIdentifier: 弹幕轨道标识符, 不同类型的弹幕, 不重用同一条轨道.
- nextAvailableTime:下次可用时间, 主要是用来防止某条轨道上刚出现了一条弹幕还没有完全展现在屏幕上, 后面这条轨道上就又来了一条弹幕盖在之前的那个弹幕上, 影响用户体验.
- barrageCount: 当前轨道的弹幕数量.
当然写到这里依然还有优化的空间, 后续会继续优化, 欢迎各位仁人志士共同探讨指点.
开源地址:https://github.com/w1531724247/OCBarrage
from:https://blog.csdn.net/w1531724247/article/details/77619111
【转】iOS弹幕库OCBarrage-如何hold住每秒5000条巨量弹幕的更多相关文章
- 鸿蒙开源第三方组件 ——B站开源弹幕库引擎的迁移(上)
鸿蒙入门指南,小白速来!0基础学习路线分享,高效学习方法,重点答疑解惑--->[课程入口] 目录: 一.弹幕库的基础知识 二.弹幕库的使用方法 三.sample解析 四.作者系列文章合集 前言 ...
- iOS 第三方库、插件、知名博客总结
iOS 第三方库.插件.知名博客总结 用到的组件 1.通过CocoaPods安装 项目名称 项目信息 AFNetworking 网络请求组件 FMDB 本地数据库组件 SDWebImage 多个缩略图 ...
- 一个线上运营着3000+人的游戏,因为我不小心一个DROP DATABASE,全没了。 怎么办??跟我HOLD住!!!
前言 今天下午3点,我按照惯例,打开游戏服务器,开新服部署嘛,游戏在腾讯开放平台,简单.闭着眼睛都OK.于是一轮子的复制黏贴拷贝,把服务器加起来,然后启动查看日志. ....突然发现不断的有Excep ...
- iOS - 静态库的创建与使用
在日常项目开发中,不论是为了两个公司项目上的业务交流还是为了减少项目的编译时间,有的时候我们会把项目中的私密内容打包成静态库,或者是把项目中变动较少一部分打包成静态库以便提高编译效率,那么下面我们就来 ...
- IOS静态库
如何在Xcode中创建C++静态库 http://jingyan.baidu.com/article/03b2f78c111fca5ea237ae26.html iOS 如何创建和使用静态库 http ...
- 快快快!27个提升效率的iOS开源库推荐
文章来源:http://www.csdn.net/article/2015-07-21/2825264-27-ios-open-source-libraries/1 我热爱开源,更喜爱那些花费宝贵的业 ...
- 封装ios静态库碰到的一些问题(一)
封装IOS动态库,碰到的第一个问题,就是资源文件的问题,如果将你的程序封装成为静态库,那么静态库中不会包含资源文件和xib文件,这个时候就需要自己封装bundle文件了,而笔者开发环境默认是xcode ...
- fir.im Weekly - 热门 iOS 第三方库大盘点
本期 fir.im Weekly 收集的热度资源,大部分关于Android.iOS 开发工具.源码和脑洞大开的 UI 动画,希望给你带来更多的工作创意与灵感. 盘点国内程序员不常用的热门iOS第三方库 ...
- iOS静态库.a文件制作和导入使用
iOS静态库.a文件制作: 1.新建Cocoa Touch Static Library工程 新建工程 - 选择iOS-FrameWork&Libary,选择 Cocoa Touch Stat ...
随机推荐
- 汇编语言--微机CPU的指令系统(五)(字符串操作指令)
(11)字符串操作指令 字符串操作指令的实质是对一片连续存储单元进行处理,这片存储单元是由隐含指针DS:SI或ES:DI来指定的.字符串操作指令可对内存单元按字节.字或双字进行处理,并能根据操作对象的 ...
- js对HTML字符转义与反转义
注意: 在编写html时,经常需要转义,才能正常显示在页面上. 并且,还可以防止xss. 解决方案: 一, 使用正则: 使用正则转码: var value = document.getElementB ...
- javascript 动态修改css样式
方法一:改变外联css文件,这里不讲这个. 方法二:通过改变claaName来改变样式,语法: obj.className = "style2"; //或者 obj.setAttr ...
- Ext.isEmpty()的使用
说明如下: isEmpty( Object value, Boolean allowEmptyString ) : Boolean 如果传递的值为空,则返回 true,否则返回 false.该值被认为 ...
- jQuery中$.ajax()方法参数解析
本文实例为大家讲解了jQuery $.ajax()方法参数,供大家参考,具体内容如下 $.ajax({ url:'test.do', data:{id:123,name:'xiaoming'}, ty ...
- JAVA 多线程(2)
一.首先 Thread 是实现了Runable 接口的类 理论上Thread 实例与直接实现runable接口的实例运行起来没有什么不同,但是由于JAVA 是单继承,所以如果想再一个类中实现2个不同的 ...
- 小tips:JS数值之间的转换,JS中最大的Number是多少?,JS == 与 === 的区别
JS数值之间的转换 Number(), parseInt(),parseFloat() Number()函数的转换规则如下: 1.如果boolean值,true和false将分别被转换为1和02.如果 ...
- 洛谷P3245 [HNOI2016]大数(莫队)
题意 题目链接 Sol 莫队板子题.. 维护出每个位置开始的字符串\(mod P\)的结果,记为\(S_i\) 两个位置\(l, r\)满足条件当且仅当\(S_l - S_r = 0\),也就是\(S ...
- GeoServer中使用样式化图层描述符(sld)给WMS加注记
现有一个shp格式的道路注记文件,用点来表示注记的位置,属性表中保存每个点对应的字,在QGIS中打开如下图所示 它的属性表如下 现需要把道路数据和它的注记发布为WMS服务,通过GeoServer直接发 ...
- JMeter 配置元件之HTTP Cookie Manager 介绍
配置元件之HTTP Cookie Manager 介绍 by:授客 QQ:1033553122 测试环境 apache-jmeter-2.13 1. Cookie管理器介绍 Cookie Ma ...