HJDanmaku 1.0版本發(fā)布已經(jīng)過(guò)去兩年之久,直播行業(yè)的快速崛起催生了直播彈幕的迫切需求,高并發(fā)、大流量卓舵、實(shí)時(shí)性的特性和以往視頻彈幕的場(chǎng)景都大有不同,為了滿足新的直播業(yè)務(wù)場(chǎng)景膀钠,HJDanmaku2.0正式發(fā)布!
流暢度
相較于1.0版本裹虫, HJDanmaku2.0采用全新的異步渲染引擎肿嘲,98%的計(jì)算工作轉(zhuǎn)移到子線程執(zhí)行,避免了主線程的卡頓延時(shí)筑公。同時(shí)雳窟,參考離屏渲染技術(shù),將組裝彈幕和渲染彈幕分布在兩個(gè)獨(dú)立線程異步執(zhí)行,確保了彈幕渲染的流暢性
NSBlockOperation?*operation?=?[NSBlockOperation?blockOperationWithBlock:^{
NSArray??*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];
將組裝彈幕的過(guò)程拆分為獨(dú)立的子線程任務(wù)封救,統(tǒng)一由NSOperationQueue單執(zhí)行隊(duì)列管理拇涤,有效的降低CPU的使用率,提升系統(tǒng)運(yùn)行穩(wěn)定性誉结。此外鹅士,在2.0版本中,使用CADisplayLink替換定時(shí)器NSTimer惩坑,與屏幕刷新頻率保持一致掉盅,可以避免NSTimer由于線程阻塞導(dǎo)致的刷新延時(shí)
高并發(fā)
直播與傳統(tǒng)視頻最大區(qū)別在于其實(shí)時(shí)性,短時(shí)間大量的彈幕發(fā)送對(duì)底層渲染引擎是個(gè)不小的挑戰(zhàn)以舒。為了解決這個(gè)問(wèn)題趾痘,HJDanmaku2.0引入數(shù)據(jù)源Source的思想,將彈幕接收與組裝的過(guò)程分開(kāi)蔓钟,可以針對(duì)直播永票、視頻場(chǎng)景實(shí)現(xiàn)差異化的處理方案。視頻場(chǎng)景對(duì)時(shí)間精確度要求較高滥沫,涉及到彈幕的時(shí)間排序侣集,同時(shí),播放進(jìn)度回放也需要數(shù)據(jù)源保存所有的彈幕數(shù)據(jù)佣谐。直播場(chǎng)景則比較單一肚吏,播放完可以立刻釋放,避免內(nèi)存的過(guò)度消耗
13u_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];
}
}];
通過(guò)拆分入庫(kù)數(shù)據(jù)分布添加可以避免線程鎖的長(zhǎng)時(shí)間占有狭魂,提升系統(tǒng)的穩(wěn)定性和流暢度
精確度
與1.0版本不同罚攀,新版本通過(guò)toleranceCount維度判斷彈幕是否過(guò)期,默認(rèn)允許最大2秒誤差雌澄。彈幕刷新頻率為0.5秒斋泄,即每個(gè)彈幕有效等待次數(shù)為2/0.5 = 4次,超過(guò)4次沒(méi)有渲染將自動(dòng)丟棄
-?(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];
}
}];
}
彈幕冗余度的設(shè)計(jì)使得彈幕顯示更加平均镐牺,優(yōu)化了彈幕顯示效果炫掐,但是會(huì)降低彈幕顯示的精確度,特別對(duì)于視頻場(chǎng)景睬涧,相對(duì)于1.0版本有所下降募胃,如果你對(duì)精確度要求較高,可以降低tolerance冗余值
碰撞檢測(cè)
與1.0相同畦浓,HJDanmaku2.0仍然使用系統(tǒng)動(dòng)畫(huà)的方式提供彈幕動(dòng)畫(huà)支持痹束,但是碰撞檢測(cè)方式略有不同
-?(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)?{
returnYES;
}
CGFloat?curDanmakuSpeed?=?(width?+?danmakuAgent.size.width)?/?self.configuration.duration;
if(curDanmakuSpeed?*?preDanmakuAgent.remainingTime?>?width)?{
returnYES;
}
returnNO;
}
在HJDanmaku2.0中,碰撞檢測(cè)不再以彈幕時(shí)間點(diǎn)為參考維度讶请,渲染的彈幕擁有剩余時(shí)間屬性祷嘶,通過(guò)剩余時(shí)間與速度的關(guān)系即可判斷兩者之間是否碰撞。同時(shí),2.0版本只在添加彈幕和恢復(fù)動(dòng)畫(huà)時(shí)為彈幕視圖添加動(dòng)畫(huà)论巍,其它時(shí)間不再校驗(yàn)
手勢(shì)
運(yùn)動(dòng)視圖系統(tǒng)默認(rèn)無(wú)法響應(yīng)手勢(shì)交互事件烛谊,整個(gè)點(diǎn)擊事件交由全局統(tǒng)一處理。HJDanmakuCell定義屬性selectionStyle控制彈幕能否點(diǎn)擊嘉汰,默認(rèn)HJDanmakuCellSelectionStyleNone丹禀,即不能點(diǎn)擊
-?(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;
returnself;
}
CGPoint?cellPoint?=?[self?convertPoint:point?toView:danmakuAgent.danmakuCell];
return[danmakuAgent.danmakuCell?hitTest:cellPoint?withEvent:event];
}
return[superhitTest:point?withEvent:event];
}
視圖整體響應(yīng)鏈參考以上代碼,當(dāng)收到點(diǎn)擊事情時(shí)郑现,優(yōu)先判斷彈幕cell是否響應(yīng)湃崩,如果響應(yīng)則交由彈幕cell處理,否則交由全局統(tǒng)一處理
總結(jié)
時(shí)隔兩年接箫,HJDanmaku2.0在性能攒读、并發(fā)以及定制型方面都有較大的提升,以iphone6設(shè)備測(cè)試為例辛友,CPU整體使用率穩(wěn)定在5%左右薄扁,大并發(fā)100條/秒彈幕的持續(xù)輸入,F(xiàn)PS可以維持在55幀以上废累。