1. AVPlayer播放原理整个播放视频的步骤。
1,首先,得到视频的URL
2,根据URL创建AVPlayerItem
3,把AVPlayerItem 提供给 AVPlayer
4,AVPlayerLayer 显示视频。
5,AVPlayer 控制视频, 播放, 暂停, 跳转 等等。
6,播放过程中获取缓冲进度,获取播放进度。
7,视频播放完成后做些什么,是暂停还是循环播放,还是获取最后一帧图像。
2,AVPlayer相关概念
#1,AVPlayer 管理和调控(播放, 暂停, 跳转 等等)。
#2,AVPlayerLayer 显示视频,
#3,AVPlayerItem 提供视频信息(一个 AVPlayerItem 对应着一个URL视频资源。)
#使用 AVPlayer 时需要注意,AVPlayer 本身并不能显示视频, 显示视频的是 AVPlayerLayer。
#AVPlayerLayer 继承自 CALayer,添加到 view.layer 上就可以使用了。
注意1:
在播放视频时,特别是播放网络视频往往需要知道视频加载情况、缓冲情况、播放情况,这些信息可以通过KVO监控AVPlayerItem的status、loadedTimeRanges属性来获得。
注意2:
当AVPlayerItem的status属性为AVPlayerStatusReadyToPlay是说明正在播放,只有处于这个状态时才能获得视频时长等信息;
注意3:缓存进度条UIProgress和滑块UISlider分别是干什么的?
1,UIProgress:显示当前视频缓存的进度。
2,UISlider:显示当前播放位置,以及快进、后退功能。
3,AVPlayerItem常用属性:(由2,3可以做缓存进度条功能)
1,asset;//URL视频的信息.
2,duration; // 获取视频总时间长度
#注意:使用CMTimeGetSeconds(duration)把时间转化成秒。
3,loadedTimeRanges; //已缓冲进度。(kvo监听)
// 计算当前已经缓冲的时间 = start + duration
- (NSTimeInterval)availableDurationRanges {
// 1,获取item的缓冲数组
NSArray *loadedTimeRanges = [_playerItem loadedTimeRanges];
// CMTimeRange 结构体 start、duration 表示起始位置 和 持续时间
CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue]; // 获取缓冲区域
float startSeconds = CMTimeGetSeconds(timeRange.start);
float durationSeconds = CMTimeGetSeconds(timeRange.duration);
// 计算总缓冲时间 = start + duration
NSTimeInterval result = startSeconds + durationSeconds;
return result;
}
4,AVPlayer:可以监听每秒的状态,从而获取每秒钟,AVPlayerItem正在播放第几秒。(可以完成UISlider功能,显示当前播放位置)
/*
用于监听播放时每秒的状态。
该方法在卡顿的时候不会回调,没有找到相关API。
采用的是开启定时器,然后用一个lastTime保留当前的播放进度,当下次调用的时候用lastTime跟当前的进度进行比较,如果相等说明播放卡顿了。
self.link = [CADisplayLink displayLinkWithTarget:self selector:@selector(upadte)];
[self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
//更新方法
- (void)upadte
{
NSTimeInterval current = CMTimeGetSeconds(self.avPlayer.currentTime);
NSTimeInterval total = CMTimeGetSeconds(self.avPlayer.currentItem.duration);
//如果用户在手动滑动滑块,则不对滑块的进度进行设置重绘
if (!self.slider.isSliding) {
self.slider.sliderPercent = current/total;
}
if (current!=self.lastTime) {
[self.activity stopAnimating];
self.timeLabel.text = [NSString stringWithFormat:@"%@/%@", [self formatPlayTime:current], [self formatPlayTime:total]];
}else{
[self.activity startAnimating];
}
self.lastTime = current;
}
interval:为响应的间隔时间,这里设为每秒都响应,
queue:是队列,传NULL代表在主线程执行。
- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;
这里通过addPeriodicTimeObserverForInterval获取每秒的状态,
但还是通过AVPlayerItem的属性currentTime来获取当前播放在第几秒
*/
- (void)monitoringPlayback:(AVPlayerItem *)item {
__weak typeof(self)WeakSelf = self;
_playTimeObserver = [_player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
// 当前播放在第几秒
float currentPlayTime = (double)item.currentTime.value/ item.currentTime.timescale;
[WeakSelf updateVideoSlider:currentPlayTime];
}];
}
// 更新滑动条
- (void)updateVideoSlider:(float)currentTime {
self.playProgress.value = currentTime;
//convertTime:秒转时间格式mm:ss
self.beginLabel.text = [NSString convertTime:currentTime];
}
5,两个细节:
//一定要设置AVPlayerLayer中的模式,videoGravity模式默认为AVLayerVideoGravityResizeAspect,就不会是会有空隙,不是铺满父类view。所以要设为填充:
_playerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
- (void)layoutSubviews {
[super layoutSubviews];
//_playerLayer.frame要重新设置下,不然视频不显示。
_playerLayer.frame = self.bounds;
//_playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
}
6,按照第一步中的步骤,如下操作实现播放。
#传入网络视频URL
- (void)updatePlayerWithURL:(NSURL *)url {
//TODO: 1,获取视频URL
//TODO: 2,根据视频索引取得AVPlayerItem对象
_playerItem = [AVPlayerItem playerItemWithURL:url];
//TODO: 3,把AVPlayerItem 提供给 AVPlayer,
//TODO: 4,AVPlayerLayer 显示视频。
[self initializeAVPlayer];
//TODO: 5,AVPlayer 控制视频, 播放, 暂停, 跳转 等等。
[self addObserverAndNotification]; // 添加观察者,通知
}