iOS实现视频播放,原生代码播放视频更多的使用AVPlayer和AVPlayerViewController进行播放。
其中,

  • AVPlayer不能单独进行播放,仅使用AVPLayer的话,还需要将其添加到AVPlayerLayer上进行播放,不含播放控制控件,需要自定义添加;
  • AVPlayerViewController也是在AVPlayer的基础上集成的播放控件。包含完整的播放,暂停,进度拖动等控制控件。

所需库

  • 使用AVPlayer和AVPlayerViewController需要添加库文件:AVKit.framework
  • 获取时间(CMTimeGetSeconds)等等需要添加库文件:CoreMedia.framework

引用头文件

#import <AVKit/AVKit.h>
#import <AVFoundation/AVFoundation.h>

实现播放(代码)

1. AVPlayer

仅使用AVPlayer和AVPlayerLayer创建播放

//生成播放url
本地
NSString* localFilePath=[[NSBundle mainBundle]pathForResource:@"aaa" ofType:@"mp4"];
NSURL *videoUrl = [NSURL fileURLWithPath:localFilePath];
//网络
NSString *webVideoPath = @"http://122.144.137.20:81/2018/12/video/d63797a1912a4f529d8cffab862d8747.mp4";
NSURL *videoUrl = [NSURL URLWithString:webVideoPath];

//创建播放单元
AVPlayerItem* item = [AVPlayerItem playerItemWithURL:[NSURL URLWithString:videoUrl]];	//通过url生成
//创建播放器
AVPlayer* player = [AVPlayer playerWithPlayerItem:item];		//使用播放单元生成相应的播放器
或者
AVPlayer* player = [AVPlayer playerWithURL:videoUrl];			//直接使用播放URL生成
//切换播放内容时
[player replaceCurrentItemWithPlayerItem:item];
//创建播放层,将playerLayer添加到view上即可播放
AVPlayerLayer* playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.myPlayer];

2. AVPlayerViewController
AVPlayerViewController 在生成播放器player的基础上,进行下一步调用:

AVPlayerViewController* playerVC = [[AVPlayerViewController alloc] init];
//设置播放器
playerVC.player = player;	
//设置播放范围			
playerVC.view.frame = CGRectMake(0, 0, 700, 300);

/** 设置所播放视频的适配显示范围 
 *  AVLayerVideoGravityResizeAspectFill :  显示尺寸适配,保证适配内容不变形的情况下铺满显示区域,画面可能会有截断
 *  AVLayerVideoGravityResizeAspect : 保证视频不变形,画面全部显示,可能不能铺满显示区域
 *  AVLayerVideoGravityResize : 保证视频铺满显示区域,画面全部显示,但视频内容可能会有变形
 */
playerVC.videoGravity = AVLayerVideoGravityResizeAspectFill;
//显示播放开始,播放进度条等控件
playerVC.showsPlaybackControls = YES;

通过[self presentViewController:playerVC animated:YES completion:nil];进行调用

播放相关监听

  • status属性
    播放单元playerItem可以通过添加status属性的监听来获取视频加载的状态。
/** 观察status属性, 一共有三种属性
 * AVPlayerStatusReadyToPlay : 可以播放
 * AVPlayerItemStatusFailed : 加载失败
 * AVPlayerItemStatusUnknown : 未知错误
 */
[self.playerItem addObserver:self forKeyPath:@"status" options:(NSKeyValueObservingOptionNew) context:nil];
  • loadedTimeRanges属性
    播放单元playerItem还可以添加loadedTimeRanges属性。
[self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

以上两种属性均在如下代码中返回监听结果:(这里的代码直接引用了简书作者的代码)

作者:梧雨北辰
链接:https://www.jianshu.com/p/b304694af77a

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context {
    AVPlayerItem *playerItem = (AVPlayerItem *)object;
    if ([keyPath isEqualToString:@"status"]) {
        //获取playerItem的status属性最新的状态
        AVPlayerStatus status = [[change objectForKey:@"new"] intValue];
        switch (status) {
            case AVPlayerStatusReadyToPlay:{
                //获取视频长度
                CMTime duration = playerItem.duration; 
                //更新显示:视频总时长(自定义方法显示时间的格式)
                self.totalNeedPlayTimeLabel.text = [self formatTimeWithTimeInterVal:CMTimeGetSeconds(duration)];
                //开启滑块的滑动功能
                self.sliderView.enabled = YES;
                //关闭加载Loading提示
                [self showaAtivityInDicatorView:NO];
                //开始播放视频
                [self.player play];
                break;
            }
            case AVPlayerStatusFailed:{//视频加载失败,点击重新加载
                [self showaAtivityInDicatorView:NO];//关闭Loading视图
                self.playerInfoButton.hidden = NO; //显示错误提示按钮,点击后重新加载视频
                [self.playerInfoButton setTitle:@"资源加载失败,点击继续尝试加载" forState: UIControlStateNormal];
                break;
            }
            case AVPlayerStatusUnknown:{
                NSLog(@"加载遇到未知问题:AVPlayerStatusUnknown");
                break;
            }
            default:
                break;
        }
    } else if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
        //获取视频缓冲进度数组,这些缓冲的数组可能不是连续的
        NSArray *loadedTimeRanges = playerItem.loadedTimeRanges;
        //获取最新的缓冲区间
        CMTimeRange timeRange = [loadedTimeRanges.firstObject CMTimeRangeValue];
        //缓冲区间的开始的时间
        NSTimeInterval loadStartSeconds = CMTimeGetSeconds(timeRange.start);
        //缓冲区间的时长
        NSTimeInterval loadDurationSeconds = CMTimeGetSeconds(timeRange.duration);
        //当前视频缓冲时间总长度
        NSTimeInterval currentLoadTotalTime = loadStartSeconds + loadDurationSeconds;
        //NSLog(@"开始缓冲:%f,缓冲时长:%f,总时间:%f", loadStartSeconds, loadDurationSeconds, currentLoadTotalTime);
        //更新显示:当前缓冲总时长
        _currentLoadTimeLabel.text = [self formatTimeWithTimeInterVal:currentLoadTotalTime];
        //更新显示:视频的总时长
        _totalNeedLoadTimeLabel.text = [self formatTimeWithTimeInterVal:CMTimeGetSeconds(self.player.currentItem.duration)];
        //更新显示:缓冲进度条的值
        _progressView.progress = currentLoadTotalTime/CMTimeGetSeconds(self.player.currentItem.duration);
    }
}
  • 监听播放进度
    播放器player可以添加时间观察来管理播放进度
__weak __typeof(self) weakSelf = self;
[self.playerVC.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
       //当前播放的时间
       NSTimeInterval currentTime = CMTimeGetSeconds(time);
       //视频的总时间
       NSTimeInterval totalTime = CMTimeGetSeconds(weakSelf.playerVC.player.currentItem.duration);

       if (currentTime == totalTime) {
          //[weakSelf dismissViewControllerAnimated:YES completion:nil];	//播放完成退出播放界面
        }
}];

以上为目前使用iOS视频播放功能的整理。