之前这个视频文章没开代码这个功能 我重新写了一下.
最近项目在做视频方面,于是小白就跟着ZFPlayer大神学习
因为本人项目需要多集连播,我就照着zf大神写了一下,然后加点修改.敬以此学习.
demo是传入模型数组,实现多集连播切换,是对AVPlayer的封装.
下面是对播放器的调用:
//创建控制层
MER_PlayerControllerView * controlView = [[MER_PlayerControllerView alloc]init];
//创建播放器,并将控制层添加到播放器上
self.playerView = [MER_PlayerView mer_setControlView:controlView videoURLs:self.datas];
//添加播放器
[self.view addSubview:self.playerView];
//设置约束
[self.playerView mas_makeConstraints:^(MASConstraintMaker *make) {
make.top.mas_equalTo(self.view);
make.leading.trailing.mas_equalTo(0);
// 这里宽高比16:9
make.height.mas_equalTo(self.playerView.mas_width).multipliedBy(9.0f/16.0f);
}];
//返回按钮的回调
__weak typeof(self) weakself = self;
self.playerView.backCallBack = ^{
[weakself.navigationController popViewControllerAnimated:YES];
下面是demo的效果图:
下面是我觉得比较核心的代码
- 加载资源,创建播放器
#pragma mark - 创建播放器以及设置属性
- (void)creatPlayer:(NSString *)url {
self.backgroundColor = [UIColor blackColor];
self.urlAsset = [AVURLAsset assetWithURL:[NSURL URLWithString:url]];
self.playerItem = [AVPlayerItem playerItemWithAsset:self.urlAsset];
self.player = [[AVPlayer alloc]initWithPlayerItem:self.playerItem];
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
//设置属性
//此处默认是填充模式
self.playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
//添加到view的layer上
//如果直接加上,在切换的时候,会遮挡住控制视图,这样插入,放置在最后就不会有问题
[self.layer insertSublayer:self.playerLayer atIndex:0];
//监听时间
[self obsercverTime];
//开始播放
[self play];
//监听
[self addObserver];
//获取系统音量
[self getVloume];
//开始播放
[self.controlView startplay];
}
- 监听时间进度
///监听播放的时间进度
- (void)obsercverTime {
//监听时间
CMTime time = CMTimeMake(1, 100000); //这样block每隔0,1s调用一次
//监听时间间隔,每间隔一段时间就执行一个block
__weak typeof(self) weakself = self;
self.timeObserver = [self.player addPeriodicTimeObserverForInterval:time queue:nil usingBlock:^(CMTime time) {
//如果没有自动更新进度直接返回
if (!weakself.isAutoUpdateProgress) {
return ;
}
//获取视频的持续,视频是一种资源
CMTime duration = weakself.player.currentItem.asset.duration;
//换算成秒
CGFloat currentTime = CMTimeGetSeconds(weakself.player.currentTime);
CGFloat durationTime = CMTimeGetSeconds(duration);
CGFloat value = currentTime / durationTime;
//设置滑块的播放进度
[weakself.controlView mer_updateControlCurrentTime:currentTime totalTime:durationTime sliderValue:value];
}];
}
- 监听屏幕旋转,视频播放的状态
- (void)addObserver {
/********** NSNotificationCenter ************/
///监听设备旋转
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(onDeviceOrientationChange)
name:UIDeviceOrientationDidChangeNotification
object:nil
];
///监听播放完成
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(playEndFinishi:)
name:AVPlayerItemDidPlayToEndTimeNotification
object:nil
];
/********** KVO ************/
///监听播放状态
[self.playerItem addObserver:self
forKeyPath:@"status"
options:NSKeyValueObservingOptionNew
context:nil
];
///监听缓冲
[self.playerItem addObserver:self
forKeyPath:@"loadedTimeRanges"
options:NSKeyValueObservingOptionNew
context:nil
];
///监听缓存不足了
[self.playerItem addObserver:self
forKeyPath:@"playbackBufferEmpty"
options:NSKeyValueObservingOptionNew
context:nil
];
///缓存足够了,可以播放了
[self.playerItem addObserver:self
forKeyPath:@"playbackLikelyToKeepUp"
options:NSKeyValueObservingOptionNew
context:nil
];
}
- 获取音量
#pragma mark - 获取系统的音量
///获取系统音量
- (void)getVloume {
MPVolumeView * volumeView = [[MPVolumeView alloc]init];
for (UIView *view in volumeView.subviews) {
if ([view.class.description isEqualToString:@"MPVolumeSlider"]) {
_volumeSlider = (UISlider *)view;
}
break;
}
///在界面不现实系统音量的图标
volumeView.frame = CGRectMake(-100, -100, 100, 100);
[self addSubview:volumeView];
NSError * audioCategoryError = nil;
BOOL isSuccess = [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&audioCategoryError];
if (!isSuccess) {
//错误
NSLog(@"\n\naudioCategoryError = %@",audioCategoryError);
}
//监听耳机拔出
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(audioRouteChangeNotification:) name:AVAudioSessionRouteChangeNotification object:nil];
}
- 跳到指定时间
//这个更为精确
[self.player seekToTime:dragedTime toleranceBefore:CMTimeMake(1, 1) toleranceAfter:CMTimeMake(1, 1) completionHandler:^(BOOL finished) {
///隐藏加载动画
// [self.controlView hourGlassShowAnimation:NO];
//判断回调
if (completionHander) {
completionHander(finished);
}
//开始播放视频
[weakself.player play];
//结束拖拽
[weakself.controlView endDrage];
}];
- 切换数据
因为zf大神说,调用AVPlayer的replaceCurrentItemWithPlayerItem 在切换视频时底层会调用信号量等待,然后 导致当前线程卡顿.如果在UITableviewCell上切换视频,会导致线程冻结几秒钟,所以就重新创建AVPlayer和AVPlayerItem.所以,重新创建.
但是我之前写的播放器使用没什么问题,因为没有需求在TabelView上使用
- 这个是zf大神那个思路
#pragma mark - 重置player
- (void)resetPlayer {
/*
githup上ZFPlayer 作者表示在iOS9后,AVPlayer的replaceCurrentItemWithPlayerItem方法在切换视频时底层会调用信号量等待然后导致当前线程卡顿,如果在UITableViewCell中切换视频播放使用这个方法,会导致当前线程冻结几秒钟。遇到这个坑还真不好在系统层面对它做什么,后来找到的解决方法是在每次需要切换视频时,需重新创建AVPlayer和AVPlayerItem
*/
//暂停播放
[self pause];
//移除监听
[self removeObserver];
//播放完成
self.isPlayEnd = NO;
//清除播放layer
[self.playerLayer removeFromSuperlayer];
//替换为nil
[self.player replaceCurrentItemWithPlayerItem:nil];
self.playerItem = nil;
self.player = nil;
self.playerLayer = nil;
self.controlView = nil;
//视频跳转为0
}
小弟初来乍到,不足之处请谅解
demo地址
任大神地址