錄制完視頻后,我們想在錄制視頻的預覽層上無限循環播放我們的小視頻,是不是很炫酷,這時候我們就有三中選擇了:
1.MPMoviePlayerController
2.AVPlayer
3.AVAssetReader+AVAssetReaderTrackOutput
但是我們這個預覽層是自定義的喔,所以MPMoviePlayerController只能馬上給篩選掉了,所以用,那么我們就要用到 AVPlayer 了,雖然上 AVPlayer 最多只能創建16個,性能上不及用 AVAssetReader+AVAssetReaderTrackOutput 方法好,但是對於這么個視頻小播放也是足夠的了。(PS:接下來博主會寫一篇關於如何用AVAssetReader+AVAssetReaderTrackOutput來實現播放視頻)
AVPlayer
AVPlayer本身並不能顯示視頻,而且它也不像MPMoviePlayerController有一個view屬性。如果AVPlayer要顯示必須創建一個播放器層AVPlayerLayer用於展示,播放器層繼承於CALayer, 有了AVPlayerLayer之添加到控制器視圖的layer中即可。要使用AVPlayer首先了解一下幾個常用的類: AVAsset:主要用於獲取多媒體信息,是一個抽象類,不能直接使用。 AVURLAsset:AVAsset的子類,可以根據一個URL路徑創建一個包含媒體信息的AVURLAsset對象。 AVPlayerItem:一個媒體資源管理對象,管理者視頻的一些基本信息和狀態,一個AVPlayerItem對應着一個視頻資源。
我們先來熟悉一下 AVPlayer 的這些類
那么運用AVPlayer播放視頻的步驟如下:
1.創建 AVPlayerItem ,用來實例化AVPlayer並監控視頻的狀態
- (AVPlayer *)player { if (!_player ) { AVPlayerItem *playerItem = [self getPlayItem]; _player = [[AVPlayer alloc]initWithPlayerItem:playerItem]; // 可以利用 AVPlayerItem 對這個視頻的狀態進行監控 } return _player; } - (AVPlayerItem *)getPlayItem { NSString *cachePath=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]; NSString *savePath=[cachePath stringByAppendingPathComponent:MOVIEPATH]; NSURL *saveUrl=[NSURL fileURLWithPath:savePath]; // 通過文件 URL 來實例化 AVPlayerItem AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:saveUrl]; return playerItem; }
2.通過AVPlayer 創建預覽層(AVPlayerLayer)並添加到可視的圖層上播放
AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player]; playerLayer.frame = _viewContrain.bounds; playerLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//視頻填充模式 [_viewContrain.layer addSublayer:playerLayer]; [self.player play];
3.通過 KVO 來監聽視頻的屬性,查看是否播放完成,播放長度為多小,緩沖了多少等等
/** * 給播放器添加進度更新 */ -(void)addProgressObserver{ AVPlayerItem *playerItem=self.player.currentItem; UIProgressView *progress=self.progress; //這里設置每秒執行一次 [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0, 1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) { float current=CMTimeGetSeconds(time); float total=CMTimeGetSeconds([playerItem duration]); NSLog(@"當前已經播放%.2fs.",current); if (current) { [progress setProgress:(current/total) animated:YES]; } }]; } #pragma mark - KVO /** * 給AVPlayerItem添加監控 * * @param playerItem AVPlayerItem對象 */ -(void)addObserverToPlayerItem:(AVPlayerItem *)playerItem{ //監控狀態屬性,注意AVPlayer也有一個status屬性,通過監控它的status也可以獲得播放狀態 [playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil]; //監控網絡加載情況屬性 [playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil]; } -(void)removeObserverFromPlayerItem:(AVPlayerItem *)playerItem{ [playerItem removeObserver:self forKeyPath:@"status"]; [playerItem removeObserver:self forKeyPath:@"loadedTimeRanges"]; } /** * 通過KVO監控播放器狀態 * * @param keyPath 監控屬性 * @param object 監視器 * @param change 狀態改變 * @param context 上下文 */ - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context { AVPlayerItem *playerItem=object; if ([keyPath isEqualToString:@"status"]) { AVPlayerStatus status= [[change objectForKey:@"new"] intValue]; if(status==AVPlayerStatusReadyToPlay){ NSLog(@"正在播放...,視頻總長度:%.2f",CMTimeGetSeconds(playerItem.duration)); } } else if([keyPath isEqualToString:@"loadedTimeRanges"]) { NSArray *array=playerItem.loadedTimeRanges; CMTimeRange timeRange = [array.firstObject CMTimeRangeValue];//本次緩沖時間范圍 float startSeconds = CMTimeGetSeconds(timeRange.start); float durationSeconds = CMTimeGetSeconds(timeRange.duration); NSTimeInterval totalBuffer = startSeconds + durationSeconds;//緩沖總長度 NSLog(@"共緩沖:%.2f",totalBuffer); } }
到此,我們就把 AVPlayer 視頻播放的方法創建完畢了。
那么我們在視頻錄制完成的時候調用播放視頻的方法了
- (void)captureOutput:(AVCaptureFileOutput *)captureOutput didFinishRecordingToOutputFileAtURL:(NSURL *)outputFileURL fromConnections:(NSArray *)connections error:(NSError *)error { NSLog(@"---- 錄制結束 ----"); } - (void)completeHandle { // 完成后不斷播放 [_captureVideoPreviewLayer removeFromSuperlayer]; // 播放視頻 // 1.創建播放層 // 這里為什么要調用延遲1.0秒呢,我們說過用 AVCaptureMovieFileOutput 來錄制視頻,是邊錄邊寫的,即使是錄制完成了,真實的是視頻還在寫,大概時間是延遲1.2秒左右。 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 在此方法調用創建 AVPlayer 播放視頻的第二部 2.通過AVPlayer 創建預覽層(AVPlayerLayer)並添加到可視的圖層上播放 [self addNotification]; }); }
添加視頻播放完后的監控來循環播放視頻
/** * 添加播放器通知 */ -(void)addNotification{ //給AVPlayerItem添加播放完成通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:self.player.currentItem]; } -(void)removeNotification{ [[NSNotificationCenter defaultCenter] removeObserver:self]; } /** * 播放完成通知 * * @param notification 通知對象 */ -(void)playbackFinished:(NSNotification *)notification{ NSLog(@"視頻播放完成."); // 播放完成后重復播放 // 跳到最新的時間點開始播放 [_player seekToTime:CMTimeMake(0, 1)]; [_player play]; }
