一、需求
- 1、视频播放列表中,对于曝光的视频,需要播放视频前面N秒;
- 2、如果同时曝光多个视频,则视频循环播放视频。如A、B、C三个视频同时都在屏幕内,那么依照曝光的先后顺序播放A-B-C;
- 3、视频的显示格式有两种:一个Cell显示一个视频;一个Cell显示两个视频。
- 4、只有当上一个cell的视频全部播放完成后,才会播放下一个Cell的视频。
二、设计
2.1、设计要求
- 使用系统AVPlayer、AVPlayerLayer、AVPlayerItem来播放视频。
- 多个视频,同时只有一个Player存在。
- Cell的曝光控制逻辑。
- 使用数据驱动渲染Cell(DataItem唯一,Cell复用)。
- 通用组件设计标准,多个页面可以简单集成使用。
2.2、类图
短视频预览控制~.png2.3、设计说明
- 采用数据驱动框架
RETableViewManager
,具体的搜索GitHub。 -
RETBL_ViewController
信息流列表,持有DataItemList
,将其交给RETableViewManager
渲染出cell;每个DataItem
对应列表每一条数据,且唯一,但Cell复用,根据DataItem渲染页面。RETBL_ViewController
持有VideoPlayerController
,功能如下。
@interface RETBL_ViewController : NSObject
@property (nonatomic, strong) VideoPlayerController *videoPlayerManager;
@property (nonatomic, strong) RETableViewManager *tblViewManager;
// 曝光的时候
[self.videoPlayerManager addVideoIntoList:item];
// cell 消失
[self.videoPlayerManager removeVideoFromList:item];
@end
-
ShortVideoPlayerViewDelegate
协议接口,VideoPlayerController
可以通过id< ShortVideoPlayerViewDelegate> obj
(本例是DataItem
) 向外部获取VideoUrl
和VideoContainerView
(AVPlayerLayer的播放容器View)。
@protocol VideoPlayerPreviewDelegate <NSObject>
@optional
- (NSArray<NSString *> *)videoUrlList;
- (NSArray<UIView *> *)videoContainerViewList;
@end
- 代理模式,
Cell
和DataItem
实现ShortVideoPlayerViewDelegate
接口,并且DataItem
有代理属性cDelegate
指向Cell
。这样设计之后,针对信息流每一个Cell中,DataItem
提供VideoUrl
;又通过DataItem
的代理向Cell
获取VideoContainerView
。
@interface VideosPreviewTableCell : RETableCell < ShortVideoPlayerViewDelegate >
@end
@implementation VideosPreviewTableCell
#pragma mark - ZTHVideoPlayerPreviewDelegate
- (NSArray<UIView *> *)videoContainerViewList {
return viewList; // 一个cell可能有多个视频view
}
@end
@interface VideosPreviewTableCellItem : RETableCellItem < ShortVideoPlayerViewDelegate >
@property (nonatomic, weak) id< ShortVideoPlayerViewDelegate > cDelegate;
@end
@implementation VideosPreviewTableCellItem
#pragma mark - ZTHVideoPlayerPreviewDelegate
- (NSArray<NSString *> *)videoUrlList {
return urlList;
}
- (NSArray<UIView *> *)videoContainerViewList {
if (self.cDelegate && [self.cDelegate respondsToSelector:@selector(videoContainerViewList)]) {
return [self.cDelegate videoContainerViewList];
}
return nil;
}
@end
-
VideoPlayerController
用来管理哪些DataItem
需要播放,当cell
曝光%50以上,则加入播放队列;当Cell
不可见从播放队列中移除。
@interface VideoPlayerController ()
/// 由于Player的ContentView,可能被复用的,如在Cell中使用。但是每个Id
@property (nonatomic, strong) NSMutableArray<id< ShortVideoPlayerViewDelegate >> *videoItemList;
@property (nonatomic, strong) ShortVideoPlayerCenter *currentPlayerCenter;
@property (nonatomic, strong) GCDTimer *timer;
@end
@implementation VideoPlayerController
// timer回调函数,播放videoItemList的控制
- (void)videoPlayControlLoopHandler {
...
}
@end
同时,内部设计一个定时器1s
,定时器函数videoPlayControlLoopHandler
主要代码是,检查VideoPlayer
播放器状态是否可以加载下一个DataItem
,可以则加载下一个,播放其对应视频。
采用定时器的原因在于,将列表DataITem
播放与否 和 视频播放逻辑剥离,彼此互不影响。类似RunLoop循环机制,将需要播放的DataItem
放入队列中,内部定时循环是否启动播放。而不是来一个就马上播放一个,消失一个立即暂停,当列表快速滚动的时候,会频繁让播放器在播放和暂停不断切换,会影响性能。
当然,后续的优化中,可以加入列表的ScrollRate,当滚动太快的时候,暂停播放逻辑提高性能。也可以考虑加入displaylink,当有空余能力的时候,才启动播放逻辑。
-
ShortVideoPlayerCenter
专门负责每个DataItem
对应视频的播放。
播放某个DataItem
的时候,通过id< ShortVideoPlayerViewDelegate > obj
(本例即DataItem
自身),获取videoUrlList 和 viewList,保存在ShortVideoPlayerUnit
实例中。
VideoPlayer
播放时候,取出videoUrl
和 对应的containerView
(将PlayerLayer
添加上去);播放N秒后,播放当前DataItem
的下一个视频。如果全部播放结束,改变播放器状态允许加载下一个。
@interface ShortVideoPlayerCenter ()
/// 由于Player的ContentView,可能被复用的,如在Cell中使用。但是每个Id
@property (nonatomic, strong) EasyVideoPlayer *videoPlayer;
@property (nonatomic, strong) ShortVideoPlayerUnit *playerUnit;
@end
@implementation ShortVideoPlayerCenter
// timer回调函数,播放videoItemList的控制
- (void)videoPlayControlLoopHandler {
...
}
@end
-
ShortVideoPlayerUnit
存储DataItem
对应的videoUrlList
和viewList
;原则上一个url对应一个view。
ShortVideoPlayerUnit
的设计主要考虑的是,播放逻辑中的可变和不可变剥离。VideoPlayer
、PlayerLayer
和 播放逻辑控制,是不变的部分;但播放数据是经常异动,且url
和view
都不同,因此封装起来,好管理这部分数据的创建和销毁。
@interface ShortVideoPlayerUnit : NSObject
@property (nonatomic, strong) NSArray<NSString *> *videoUrlList;
@property (nonatomic, strong) NSArray<UIView *> *videoViewList;
@end
三、总结
- 通用性能力,需要“视频预览”能力的列表,只需要让数据源和Cell实现
ShortVideoPlayerViewDelegate
,提供对应url和view,然后交给VideoPlayerController
进行管理即可,然后在Cell曝光和消失的时候添加和移除数据源即可。 - 由于考虑到
NSTimer
的强引用
性,需要手动是否timer否则会导致循环引用。因此这边使用GCDTimer
。 - 使用Timer左右loop动力驱动,可以根据具体的需求切换为
DisplayLink
,然后加入scrollRate
滚动速率等因此,进一步提高性能。
其他
- 上面类的命名已经非项目命名,叫法不甚合适,本篇旨在说明设计意图。
网友评论