轉(zhuǎn)載請注明出處:http://www.olinone.com/
Hi,好久不見,HJDanmaku 1.0版本發(fā)布已經(jīng)過去兩年之久,直播行業(yè)的快速崛起催生了直播彈幕的迫切需求德撬,高并發(fā)、大流量躲胳、實時性的特性和以往視頻彈幕的場景都大有不同蜓洪,為了滿足新的直播業(yè)務(wù)場景,HJDanmaku2.0正式發(fā)布坯苹!
流暢度
相較于1.0版本隆檀,HJDanmaku2.0采用全新的異步渲染引擎,98%的計算工作轉(zhuǎn)移到子線程執(zhí)行粹湃,避免了主線程的卡頓延時恐仑。同時,參考離屏渲染技術(shù)为鳄,將組裝彈幕和渲染彈幕分布在兩個獨立線程異步執(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];
將組裝彈幕的過程拆分為獨立的子線程任務(wù),統(tǒng)一由NSOperationQueue單執(zhí)行隊列管理济赎,有效的降低CPU的使用率鉴逞,提升系統(tǒng)運行穩(wěn)定性。此外司训,在2.0版本中,使用CADisplayLink替換定時器NSTimer液南,與屏幕刷新頻率保持一致壳猜,可以避免NSTimer由于線程阻塞導(dǎo)致的刷新延時
高并發(fā)
直播與傳統(tǒng)視頻最大區(qū)別在于其實時性,短時間大量的彈幕發(fā)送對底層渲染引擎是個不小的挑戰(zhàn)滑凉。為了解決這個問題统扳,HJDanmaku2.0引入數(shù)據(jù)源Source的思想喘帚,將彈幕接收與組裝的過程分開,可以針對直播咒钟、視頻場景實現(xiàn)差異化的處理方案吹由。視頻場景對時間精確度要求較高,涉及到彈幕的時間排序朱嘴,同時倾鲫,播放進度回放也需要數(shù)據(jù)源保存所有的彈幕數(shù)據(jù)。直播場景則比較單一萍嬉,播放完可以立刻釋放乌昔,避免內(nèi)存的過度消耗
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];
? ? }
}];
通過拆分入庫數(shù)據(jù)分布添加可以避免線程鎖的長時間占有,提升系統(tǒng)的穩(wěn)定性和流暢度
精確度
與1.0版本不同壤追,新版本通過toleranceCount維度判斷彈幕是否過期磕道,默認(rèn)允許最大2秒誤差。彈幕刷新頻率為0.5秒行冰,即每個彈幕有效等待次數(shù)為2/0.5 = 4次溺蕉,超過4次沒有渲染將自動丟棄
- (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è)計使得彈幕顯示更加平均,優(yōu)化了彈幕顯示效果悼做,但是會降低彈幕顯示的精確度焙贷,特別對于視頻場景,相對于1.0版本有所下降贿堰,如果你對精確度要求較高辙芍,可以降低tolerance冗余值
碰撞檢測
與1.0相同,HJDanmaku2.0仍然使用系統(tǒng)動畫的方式提供彈幕動畫支持羹与,但是碰撞檢測方式略有不同
- (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中故硅,碰撞檢測不再以彈幕時間點為參考維度,渲染的彈幕擁有剩余時間屬性纵搁,通過剩余時間與速度的關(guān)系即可判斷兩者之間是否碰撞吃衅。同時,2.0版本只在添加彈幕和恢復(fù)動畫時為彈幕視圖添加動畫腾誉,其它時間不再校驗
手勢
運動視圖系統(tǒng)默認(rèn)無法響應(yīng)手勢交互事件徘层,整個點擊事件交由全局統(tǒng)一處理。HJDanmakuCell定義屬性selectionStyle控制彈幕能否點擊利职,默認(rèn)HJDanmakuCellSelectionStyleNone趣效,即不能點擊
- (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];
}
視圖整體響應(yīng)鏈參考以上代碼,當(dāng)收到點擊事情時猪贪,優(yōu)先判斷彈幕cell是否響應(yīng)跷敬,如果響應(yīng)則交由彈幕cell處理,否則交由全局統(tǒng)一處理
總結(jié)
時隔兩年热押,HJDanmaku2.0在性能西傀、并發(fā)以及定制型方面都有較大的提升斤寇,以iphone6設(shè)備測試為例,CPU整體使用率穩(wěn)定在5%左右拥褂,大并發(fā)100條/秒彈幕的持續(xù)輸入娘锁,F(xiàn)PS可以維持在55幀以上
目前暫時支持OC,swift版本正在開發(fā)中饺鹃,如果你有意貢獻swift代碼莫秆,可以與我聯(lián)系~
當(dāng)然,如果你喜歡尤慰,可以為本項目點點贊
寫在文后:
新建了一個iOS開發(fā)QQ交流群(首頁右上角入群)馏锡,歡迎廣大iOS開發(fā)朋友一同交流學(xué)習(xí)。當(dāng)然伟端,你也可以Follow本人GitHub杯道,或者關(guān)注我的新浪微博,感謝你的來訪责蝠,下期再見党巾!