今年在做直播業(yè)務(wù)的時(shí)候遇到一些問(wèn)題蜘渣,就是在一個(gè)套播放器UI中需要多種不同的播放器(AVPlayer圾亏、IJKPlayer哑姚、AliPlayer)支持焙格,根據(jù)ABTest開(kāi)關(guān)來(lái)切換具體使用哪種播放器,并且還要對(duì)播放器進(jìn)行日志統(tǒng)計(jì)惫皱。
首先可以想到的是需要對(duì)不同的播放器封裝一個(gè)統(tǒng)一的接口像樊,對(duì)于UI來(lái)講,不需要關(guān)系當(dāng)前操控的是哪個(gè)播放器旅敷。
其中player protocal是個(gè)關(guān)鍵
@protocol VideoPlaybackProtocal <NSObject>
@property (nonatomic, assign, setter=setDelegate:) id<LivePlaybackEventListener> delegate;
-(void)addPlaybackEventListener:(id<LivePlaybackEventListener>)listener;
-(void)removePlaybackEventListener:(id<LivePlaybackEventListener>)listener;
@property (nonatomic, strong, readonly) UIView * view;
@property (nonatomic, assign, readonly) CMTime currentPlaybackTime;
@property (nonatomic, assign, readonly) CMTime duration;
@property (nonatomic, assign, readonly) NSURL *currentPlayUrl;
@property (nonatomic, assign, readonly) BOOL supportsRTMP;
- (void)stopDueToLiveDidEnd;
- (void)resume;
- (void)pause;
- (void)replay;
- (void)reload;
- (void)startPlayingWithPlayInfo:(NSURL*)url;
- (void)shutdown;
- (void)seekToProgress:(double)progress event:(PlayerEvent)event;
- (BOOL)isPlaying;
@end
然后三個(gè)播放器對(duì)這個(gè)接口進(jìn)行各自的實(shí)現(xiàn)生棍。這樣對(duì)于ViewController來(lái)說(shuō)播放器就是一個(gè)id<VideoPlaybackProtocal>,對(duì)播放器進(jìn)行暫停播放各種操作就可以媳谁。打算用哪個(gè)播放器涂滴,就把id<VideoPlaybackProtocal>實(shí)例化成哪個(gè)播放器的實(shí)現(xiàn),輕松又簡(jiǎn)單晴音。
其中這里的Listener是一個(gè)delegate柔纵,當(dāng)播放器出現(xiàn)播放完成、播放錯(cuò)誤等事件時(shí)锤躁,Listener會(huì)對(duì)注冊(cè)了Listener的對(duì)象進(jìn)行廣播搁料,目前需要注冊(cè)的是ViewController和Logger,UI收到廣播來(lái)進(jìn)行圖形界面的變化系羞,Logger則負(fù)責(zé)將事件統(tǒng)計(jì)上傳到日志服務(wù)器當(dāng)中郭计。這個(gè)廣播器通過(guò)一個(gè)NSHashTable * _listeners;
來(lái)保證注冊(cè)了廣播的observe為弱引用,然后通過(guò)遍歷observe來(lái)進(jìn)行delegate調(diào)用椒振,例如:
- (void)videoPlayerController:(id< VideoPlaybackProtocal >)playerController didFailWithError:(NSError *)error playerLog:(NSDictionary *)playerLog
{
[self _enumerateListeners:^(id listener, NSUInteger idx) {
if ([listener respondsToSelector:_cmd]) {
[listener videoPlayerController:playerController didFailWithError:error playerLog:playerLog];
}
}];
}