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]; // 添加觀察者,通知
}