iOS弹幕——HJDanmaku 2.0发布

Hi,好久不见,HJDanmaku 1.0版本发布已经过去两年之久,直播行业的快速崛起催生了直播弹幕的迫切需求,高并发、大流量、实时性的特性和以往视频弹幕的场景都大有不同,为了满足新的直播业务场景,HJDanmaku2.0正式发布!

流畅度

相较于1.0版本, HJDanmaku2.0采用全新的异步渲染引擎,98%的计算工作转移到子线程执行,避免了主线程的卡顿延时。同时,参考离屏渲染技术,将组装弹幕和渲染弹幕分布在两个独立线程异步执行,确保了弹幕渲染的流畅性

1
2
3
4
5
6
7
8
9
10
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
     NSArray <hjdanmakuagent *> *danmakuAgents = [self.danmakuSource fetchDanmakuAgentsForTime:(HJDanmakuTime){HJMaxTime(time), time.interval}];
     dispatch_async(_renderQueue, ^{
         if  (danmakuAgents.count > 0) {
             [self.danmakuQueuePool insertObjects:danmakuAgents atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, danmakuAgents.count)]];
         }
     });
}];
[self.sourceQueue cancelAllOperations];
[self.sourceQueue addOperation:operation];</hjdanmakuagent *>

将组装弹幕的过程拆分为独立的子线程任务,统一由NSOperationQueue单执行队列管理,有效的降低CPU的使用率,提升系统运行稳定性。此外,在2.0版本中,使用CADisplayLink替换定时器NSTimer,与屏幕刷新频率保持一致,可以避免NSTimer由于线程阻塞导致的刷新延时

高并发

直播与传统视频最大区别在于其实时性,短时间大量的弹幕发送对底层渲染引擎是个不小的挑战。为了解决这个问题,HJDanmaku2.0引入数据源Source的思想,将弹幕接收与组装的过程分开,可以针对直播、视频场景实现差异化的处理方案。视频场景对时间精确度要求较高,涉及到弹幕的时间排序,同时,播放进度回放也需要数据源保存所有的弹幕数据。直播场景则比较单一,播放完可以立刻释放,避免内存的过度消耗

1
2
3
4
5
6
7
8
9
10
11
12
13
u_int interval = 100;
NSMutableArray *danmakuAgents = [NSMutableArray arrayWithCapacity:interval];
NSUInteger lastIndex = danmakus.count - 1;
[danmakus enumerateObjectsUsingBlock:^(HJDanmakuModel *danmaku, NSUInteger idx, BOOL *stop) {
     HJDanmakuAgent *agent = [[HJDanmakuAgent alloc] initWithDanmakuModel:danmaku];
     [danmakuAgents addObject:agent];
     if  (idx == lastIndex || danmakuAgents.count % interval == 0) {
         OSSpinLockLock(&_spinLock);
         [self.danmakuAgents addObjectsFromArray:danmakuAgents];
         OSSpinLockUnlock(&_spinLock);
         [danmakuAgents removeAllObjects];
      }
}];

通过拆分入库数据分布添加可以避免线程锁的长时间占有,提升系统的稳定性和流畅度

精确度

与1.0版本不同,新版本通过toleranceCount维度判断弹幕是否过期,默认允许最大2秒误差。弹幕刷新频率为0.5秒,即每个弹幕有效等待次数为2/0.5 = 4次,超过4次没有渲染将自动丢弃

1
2
3
4
5
6
7
8
- (void)removeExpiredDanmakusForTime:(HJDanmakuTime)time {
     [self.danmakuQueuePool enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(HJDanmakuAgent *danmakuAgent, NSUInteger idx, BOOL *stop) {
         danmakuAgent.toleranceCount --;
         if  (danmakuAgent.toleranceCount <= 0) {
             [self.danmakuQueuePool removeObjectAtIndex:idx];
         }
     }];
}

弹幕冗余度的设计使得弹幕显示更加平均,优化了弹幕显示效果,但是会降低弹幕显示的精确度,特别对于视频场景,相对于1.0版本有所下降,如果你对精确度要求较高,可以降低tolerance冗余值

碰撞检测

与1.0相同,HJDanmaku2.0仍然使用系统动画的方式提供弹幕动画支持,但是碰撞检测方式略有不同

1
2
3
4
5
6
7
8
9
10
11
12
- (BOOL)checkLRIsWillHitWithPreDanmaku:(HJDanmakuAgent *)preDanmakuAgent danmaku:(HJDanmakuAgent *)danmakuAgent {
     CGFloat width = CGRectGetWidth(self.bounds);
     CGFloat preDanmakuSpeed = (width + preDanmakuAgent.size.width) / self.configuration.duration;
     if  (preDanmakuSpeed * (self.configuration.duration - preDanmakuAgent.remainingTime) < preDanmakuAgent.size.width) {
         return  YES;
     }
     CGFloat curDanmakuSpeed = (width + danmakuAgent.size.width) / self.configuration.duration;
     if  (curDanmakuSpeed * preDanmakuAgent.remainingTime > width) {
         return  YES;
     }
     return  NO;
}

在HJDanmaku2.0中,碰撞检测不再以弹幕时间点为参考维度,渲染的弹幕拥有剩余时间属性,通过剩余时间与速度的关系即可判断两者之间是否碰撞。同时,2.0版本只在添加弹幕和恢复动画时为弹幕视图添加动画,其它时间不再校验

手势

运动视图系统默认无法响应手势交互事件,整个点击事件交由全局统一处理。HJDanmakuCell定义属性selectionStyle控制弹幕能否点击,默认HJDanmakuCellSelectionStyleNone,即不能点击

1
2
3
4
5
6
7
8
9
10
11
12
13
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
     self.selectDanmakuAgent = nil;
     HJDanmakuAgent *danmakuAgent = [self danmakuAgentAtPoint:point];
     if  (danmakuAgent) {
         if  (danmakuAgent.danmakuCell.selectionStyle == HJDanmakuCellSelectionStyleDefault) {
             self.selectDanmakuAgent = danmakuAgent;
             return  self;
         }
         CGPoint cellPoint = [self convertPoint:point toView:danmakuAgent.danmakuCell];
         return  [danmakuAgent.danmakuCell hitTest:cellPoint withEvent:event];
     }
     return  [ super  hitTest:point withEvent:event];
}

视图整体响应链参考以上代码,当收到点击事情时,优先判断弹幕cell是否响应,如果响应则交由弹幕cell处理,否则交由全局统一处理

总结

时隔两年,HJDanmaku2.0在性能、并发以及定制型方面都有较大的提升,以iphone6设备测试为例,CPU整体使用率稳定在5%左右,大并发100条/秒弹幕的持续输入,FPS可以维持在55帧以上。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值