暂存和分发:LNDanmakuDispatcher

这个文章的前置文章:LNDanmakuMaster

Dispatcher工作方式

Dispatcher的工作方式非常像配货站,通常有闲置卡车的司机会将自己的卡车信息登记在配货站,需要运送屋子的雇主把货物、目的地等信息登记在配货站,然后由专人将一些顺路的货物分配到一辆卡车上,这辆卡车装满了就发车;这种配货方式可以使运输资源得到最大限度利用。

Dispatcher的工作方式与配货站十分类似,一个Dispatcher通常会管理多条轨道,内置一个队列存储弹幕,弹幕在这里就代表了货物,轨道代表了卡车;Dispatcher会在时钟的驱动下不断check那些空闲的轨道,当某个轨道同时符合空闲度要求和Dispatcher策略时,队首的弹幕会被添加到这条轨道上。

Dispatcher同时兼顾了自身的分配策略和轨道的空闲度,优先级:空闲度 > 分配策略,例如:在宽松策略下,同时有多个轨道都可以播放队首的弹幕时,Dispatcher会选择当前最空闲的那个轨道来放置这条弹幕,这个空闲度数值是由TrackController根据当前自身的状态决定,如:条形轨道的最后一条弹幕的剩余存活时间就代表了这条轨道的空闲度。

Queue

LNDanmakuQueue是Dispatcher使用到的存储结构,为了保证按照一定顺序添加的弹幕也会按照一定顺序播放,同时也提供一定的容错能力,让那些因为轨道拥塞而不能播放的弹幕也不会被立即丢弃掉。
以下是LNDanmakuQueue对外提供的方法列表,包含了一个Queue结构常规的push、pop、top、空判断、最大容量等方外,也包括了额外的清空、包含判断和移除判断,提供一个代理来向外界传达自己的操作时机。

@interface LNDanmakuDispatchQueue : NSObject
@property (nonatomic, weak) id <LNDanmakuDispatchQueueDelegate> delegate;
@property (nonatomic, assign) NSInteger maxCapacity;
- (void)push:(LNDanmakuAbstractAttributes *)attributes;
- (void)push:(LNDanmakuAbstractAttributes *)attributes priority:(LNDanmakuDispatchQueuePriority)priority;
- (LNDanmakuAbstractAttributes *)top;
- (LNDanmakuAbstractAttributes *)pop;
- (NSArray<LNDanmakuAbstractAttributes *> *)clearQueue;
- (BOOL)contains:(LNDanmakuAbstractAttributes *)attributes;
- (void)remove:(LNDanmakuAbstractAttributes *)attributes;
- (BOOL)isEmpty;
@end

与常规队列提供的push方法稍有不同,这个队列提供了额外的一种按优先级的push,使弹幕可以以不同的优先级push进这个队列,高优的弹幕会先于默认优先级弹幕播放。内部依靠两个子队列实现了这种优先级:

@interface LNDanmakuDispatchQueue ()
@property (nonatomic, strong) LNDanmakuDispatchSubQueue *highQueue;
@property (nonatomic, strong) LNDanmakuDispatchSubQueue *defaultQueue;
@end

在pop的时候,我们会优先检查高优子队列的空状态,如果高优队列不为空则pop高优队列,否则pop默认优先级队列,这里截取了pop方法:

- (LNDanmakuAbstractAttributes *)pop
{
    if ([self.highQueue top]) {
        return [self.highQueue pop];
    }
    return [self.defaultQueue pop];
}

这种优先级队列的用途:我们考虑展示用户自己发送的弹幕在本地展示的情景,用户希望自己发出的弹幕在第一时间得到展示,如果将这条弹幕和普通弹幕一样插入到队列中,它可能会在当前队列中已存在的弹幕都被播完之后才会显示出来;反之插在队首,如果有两条或以上的高优弹幕被插入队首,那么对这些高优弹幕而言,这个结构就变成了栈。因此,我们提供了额外的高优队列来解决这个问题。
每个子队列内部实现在此不过多赘述,本质上就是封装了NSMutableOrderedSet,然后对外暴露方法。

Dispatcher

LNDanmakuAbstrackDispatcher抽象类中规定了一个Dispatcher需要支持的所有能力,以下是抽象类的定义:

@interface LNDanmakuAbstractDispatcher (Override)
//private use
- (void)dispatchNewAttributesToFreeTracks:(NSArray<LNDanmakuAbstractTrackController *> *)trackControllerArray;
//public use
- (void)insertNewAttributes:(NSArray <LNDanmakuAbstractAttributes *> *)newAttributesArray;
- (void)insertHighPriorityAttributes:(NSArray <LNDanmakuAbstractAttributes *> *)highPriorityAttributesArray;
- (void)clear;
- (BOOL)containsAttributes:(LNDanmakuAbstractAttributes *)attributes;
- (void)removeAttributes:(LNDanmakuAbstractAttributes *)attributes;
@end
  • -(void)dispatchNewAttributesToFreeTracks 方法被标记了private,意思是,这个方法只能被LNDanmakuMaster框架内部的类调用,这个方法翻译成中文:“我这里有一些轨道,如果你有需要播放的弹幕,请从这些轨道中挑选出一个放置你的弹幕”。因此,这个方法只会被Player和TrackGroup调用,Player和TrackGroup是轨道列表的持有者。
  • 其他的方法和队列的方法是大体对应的,它们都被标记为public,也就是使用者可调用的方法,当然这个界限并不是绝对的,因此总是有我们预料不到的场景需要特殊处理。

QA:为什么为dispatcher提供抽象类,虽然大部分场景下的弹幕都是按照顺序分发的,但产品经理的意识总会超出常人预料,我们假设他们提出一种从给定的弹幕池子里随机抽取弹幕进行分发的需求,我们重新实现一种新的Dispatcher接入后仍然可以让这个框架的其他部分正常工作。

梳理一下Dispatcher的工作原理:

  • 使用者通过insertNewAttributes方法将弹幕插入Dispatcher的队列。
  • Player受Clock驱动,调用dispatchNewAttributesToFreeTracks方法让Dispatcher做出选择并放置弹幕。
  • Dispatcher从给定的TrackController列表中挑选出空闲且符合分发策略的轨道,把队首的弹幕放上去。
  • 弹幕被添加到TrackController后,走TrackController的播放流程。

后续会单独有一个文章介绍三种分发策略:

typedef NS_ENUM(NSInteger, LNDanmakuDispatchStrategy)
{
    LNDanmakuDispatchStrategyDefault = 0, //Find the first track to insert.
    LNDanmakuDispatchStrategyLowDensity, //Find the most free track to insert.
    LNDanmakuDispatchStrategyMostFastDisplay //Find the track with shortest waiting time.
};

typedef NS_ENUM(NSInteger, LNDanmakuRecoverDispatchStrategy)
{
    LNDanmakuRecoverDispatchStrategyDefault = 0,
    LNDanmakuRecoverDispatchStrategyLowDensity,
    LNDanmakuRecoverDispatchStrategyMostFastDisplay,
};
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容