录制完视频后,我们想在录制视频的预览层上无限循环播放我们的小视频,是不是很炫酷,这时候我们就有三中选择了:
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]; }