1.AVPlayer介绍

iOS提供了多种方法来实现视频播放,包括MPMoviePlayerController,MPMoviePlayerViewController,AVPlayer,AVPlayerViewController等。其中MPMoviePlayerController,MPMoviePlayerViewController在iOS9.0之后被弃用。四种播放方式的区别如下:

其中公司内项目使用的是AVPlayer实现视频播放,AVPlayer使用十分灵活,也更加接近底层,但是AVPlayer本身是不能直接显示视频,必须创建一个播放层AVPlayerLayer并将其添加到其他的视图Layer上才能显示

AVAsset:

  • 一个用于获取多媒体信息的抽象类,不能直接使用。

  • 为定时试听媒体定义AVFoundation模型的抽象的,不可变的类。

  • 每个asset包含想要一起呈现或处理的统一的媒体类型轨道的集合。

  • asset中的每个媒体数据片段都是统一的类型,称为轨道(track)。

  • AVAsset是一个容器对象,由一个或多个AVAssetTrack实例组成,最常用的track类型是音频和视频track,AVAssetTrack还可以模拟其他辅助轨道,如隐藏式字幕,字幕和定时元数据。

  • AVAsset通常通过具体的子类AVURLAsset通过NSURL实例化,该NSURL引用试听媒体资源,如流(包括HTTP实时流),QuickTime电影文件,MP3文件和其他类型的文件。

AVPlayerItem:

  • 一个媒体资源管理对象,用于管理视频的基本信息和状态,一个AVPlayerItem对应一个视频资源。

  • AVPlayerItem会建立媒体资源动态视角的数据模型并保存AVPlayer在播放资源时呈现状态。

  • AVPlayerItem由一个或多个媒体曲目组成,由AVPlayerItemTrack类建立模型。

  • AVPlayerItemTrack实例用于表示播放器条目中的类型统一的媒体流,比如音频或视频。

  • AVPlayerItem中的曲目直接与基础AVAsset中的AVAssetTrack实例相对应。

  • AVPlayerItem 播放完成时,会发送一个AVPlayerItemDidPlayToEndTimeNotification通知,可以监听此通知做些相应的处理.

AVPlayer:

  • 一个用来播放基于时间的视听媒体的控制器对象,支持播放从本地,分布下载或通过HTTP Live Streaming协议得到的流媒体。

  • AVPlayer是一个不可见组件,要将视频资源导出到用户界面目标位置,需要用到AVPlayerLayer类。

  • AVPlayer只管理一个单独资源的播放,AVQueuePlayer是AVPlayer的子类,可以管理一个资源队列。

  • 负责视频播放、暂停、时间控制等操作。

AVPlayerLayer:

  • 相对简单的类,负责显示视频的图层,如果不设置此属性,视频就只有声音没有图像。

  • 创建AVPlayerLayer需要一个指向AVPlayer实例的指针,将图层和播放器绑定在一起,保证了当播放器基于事件的方法出现时使二者保持同步。

2.AVPlayer使用

1.引入AVFoundation框架,添加播放器属性

#import <AVFoundation/AVFoundation.h>
@property (nonatomic, strong) AVPlayer *player;//播放器对象
@property (nonatomic, strong) AVPlayerItem *currentPlayerItem;//播放资源管理对象

2.获取需要播放的视频地址

    //本地视频路径NSString *localFilePath = [[NSBundle mainBundle] pathForResource:@"video" ofType:@"mp4"];NSURL *localVideoUrl = [NSURL fileURLWithPath:localFilePath];//网络视频路径
//    NSString *webVideoPath = @"https://vd3.bdstatic.com/mda-jivjw77a30qs7rvm/mda-jivjw77a30qs7rvm.mp4";
//    NSURL *webVideoUrl = [NSURL URLWithString:webVideoPath];

3.通过url地址创建资源管理对象AVPlayerItem,通过AVPlayerItem创建AVPlayer

    //创建视频播放器
//    AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:webVideoUrl];//加载网络视频路径//通过URL创建一个媒体资源管理对象playerItemAVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:localVideoUrl];self.currentPlayerItem = playerItem;//通过playerItem创建一个视频播放器player,进行绑定self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];

4.通过AVPlayer创建视频显示图层AVPlayerLayer,设置图层相关属性,添加到主视图中的图层上

    //创建显示视频的AVPlayerLayer,设置视频显示属性,并添加视频图层//contentView是一个普通的View,用于放置视频视图,AVPlayer不可见AVPlayerLayer *avLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];//设置播放比例,等比例播放avLayer.videoGravity = AVLayerVideoGravityResizeAspect;//设置视图区域avLayer.frame = self.view.bounds;//将视频图层加入到本视图的图层上[self.view.layer addSublayer:avLayer];

5.注册观察者,检测播放器属性,获取视频长度与缓冲进度

//注册观察者,监测播放器属性//观察Status属性,可以在加载成功之后得到视频的长度[self.player.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];//观察loadedTimeRanges,可以获取缓存进度,实现缓冲进度条[self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

6.添加属性观察,一个AVPlayerItem对象对应着一个视频,需要通过AVPlayerItem来获取视频属性,但是AVPlayerItem必须是在视频资源加载到可播放的时候才能使用。因此需要使用KVO监测AVPlayerItem的status属性,当其为AVPlayerItemStatusReadyToPlay的时候才能获取视频相关属性。

//添加属性观察
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {AVPlayerItem *playerItem = (AVPlayerItem *)object;if ([keyPath isEqualToString:@"status"]) {//获取playerItem的status属性最新的状态AVPlayerStatus status = [[change objectForKey:@"new"] intValue];switch (status) {case AVPlayerStatusReadyToPlay: {//加载完成,准备播放[self.player play];break;}case AVPlayerStatusFailed: {//加载失败break;}case AVPlayerStatusUnknown: {NSLog(@"加载遇到未知问题");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);//更新显示:视频总时长self.currentLoadTimeLabel.text = [self formatTimeWithTimeInterVal:currentLoadTotalTime];//加载完成,背景由黑变白self.view.backgroundColor = [UIColor whiteColor];}
}

7.根据AVPlayer可以获取视频播放的状态这一性质,可以创建视频进度条实时展示视频的播放进度。

//添加视频进度条
- (void)addVideoProgress {__weak __typeof(self) weakSelf = self;//使用addPeriodicTimeObserverForInterval方法必须要持有返回对象,在不需要播放器时必须移除此对象,否则占用大量内存资源//CMTimeMake(value,scale),value:帧数,scale:帧率,此处使用应该是多久调用一次,1/15秒调用一次self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 15) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {//当前播放的时间NSTimeInterval currentTime = CMTimeGetSeconds(time);//视频的总时长NSTimeInterval totalTime = CMTimeGetSeconds(weakSelf.player.currentItem.duration);//设置进度条的当前进度weakSelf.sliderView.value = currentTime / totalTime;//设置显示时间00:00weakSelf.currentLoadTimeLabel.text = [weakSelf formatTimeWithTimeInterVal:currentTime];}];
}

8.可以通过拖拽进度条,定位具体的播放位置,修改视频播放进度

- (IBAction)sliderViewChange:(id)sender {if (self.player.status == AVPlayerStatusReadyToPlay) {//获取拉拽区域对应的播放时间NSTimeInterval playTime = self.sliderView.value * CMTimeGetSeconds(self.player.currentItem.duration);//计算需要定位的时间CMTime seekTime = CMTimeMake(playTime, 1);//从定位处开始播放[self.player seekToTime:seekTime completionHandler:^(BOOL finished) {[self.player play];}];}
}

3.使用示例

#import "AVPlayerViewController.h"
#import <AVFoundation/AVFoundation.h>@interface AVPlayerViewController ()
@property (nonatomic, strong) AVPlayer *player;//播放器对象
@property (nonatomic, strong) AVPlayerItem *currentPlayerItem;//播放资源管理对象@property (nonatomic, strong) UILabel *currentLoadTimeLabel;//当前视频播放时间Label@property (nonatomic, strong) id timeObserver;//观察者
@property (nonatomic, strong) UISlider *sliderView;//视频进度条视图@end@implementation AVPlayerViewController- (void)viewDidLoad {[super viewDidLoad];self.view.backgroundColor = [UIColor blackColor];[self setupAVPlayer];[self setupControl];
}//视图销毁流程,需要移除对应的Observer,否则会一直留在内存中,占用内存控件
- (void)dealloc {[self.player removeTimeObserver:_timeObserver];
}- (void)setupControl {//显示视频播放状态的Labelself.currentLoadTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(5, 400, 35, 10)];self.currentLoadTimeLabel.font = [UIFont systemFontOfSize:12.f];//视频播放进度条,拖动响应self.sliderView = [[UISlider alloc] initWithFrame:CGRectMake(40, 400, 250, 10)];[self.sliderView addTarget:self action:@selector(sliderViewChange:) forControlEvents:UIControlEventTouchUpInside];[self.view addSubview:self.sliderView];[self.view addSubview:self.currentLoadTimeLabel];[self addVideoProgress];
}- (void)setupAVPlayer {//获取视频路径//本地视频路径
//    NSString *localFilePath = [[NSBundle mainBundle] pathForResource:@"video" ofType:@"mp4"];
//    NSURL *localVideoUrl = [NSURL fileURLWithPath:localFilePath];//网络视频路径NSString *webVideoPath = @"https://vd3.bdstatic.com/mda-jivjw77a30qs7rvm/mda-jivjw77a30qs7rvm.mp4";NSURL *webVideoUrl = [NSURL URLWithString:webVideoPath];//创建视频播放器
//    AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:webVideoUrl];//加载网络视频路径//通过URL创建一个媒体资源管理对象playerItemAVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithURL:localVideoUrl];self.currentPlayerItem = playerItem;//通过playerItem创建一个视频播放器player,进行绑定self.player = [[AVPlayer alloc] initWithPlayerItem:playerItem];//创建显示视频的AVPlayerLayer,设置视频显示属性,并添加视频图层//contentView是一个普通的View,用于放置视频视图,AVPlayer不可见AVPlayerLayer *avLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];//设置播放比例,等比例播放avLayer.videoGravity = AVLayerVideoGravityResizeAspect;//设置视图区域avLayer.frame = self.view.bounds;//将视频图层加入到本视图的图层上[self.view.layer addSublayer:avLayer];//注册观察者,监测播放器属性//观察Status属性,可以在加载成功之后得到视频的长度[self.player.currentItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];//观察loadedTimeRanges,可以获取缓存进度,实现缓冲进度条[self.player.currentItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];}//添加属性观察
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {AVPlayerItem *playerItem = (AVPlayerItem *)object;if ([keyPath isEqualToString:@"status"]) {//获取playerItem的status属性最新的状态AVPlayerStatus status = [[change objectForKey:@"new"] intValue];switch (status) {case AVPlayerStatusReadyToPlay: {//加载完成,准备播放[self.player play];break;}case AVPlayerStatusFailed: {//加载失败break;}case AVPlayerStatusUnknown: {NSLog(@"加载遇到未知问题");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);//更新显示:视频总时长self.currentLoadTimeLabel.text = [self formatTimeWithTimeInterVal:currentLoadTotalTime];//加载完成,背景由黑变白self.view.backgroundColor = [UIColor whiteColor];}
}//时间格式转换
- (NSString *)formatTimeWithTimeInterVal:(NSTimeInterval)timeInterVal {int minute = 0, hour = 0, secend = timeInterVal;minute = (secend % 3600)/60;hour = secend / 3600;secend = secend % 60;return [NSString stringWithFormat:@"%02d:%02d", minute, secend];
}//添加视频进度条
- (void)addVideoProgress {__weak __typeof(self) weakSelf = self;//使用addPeriodicTimeObserverForInterval方法必须要持有返回对象,在不需要播放器时移除此对象//CMTimeMake(value,scale),value:帧数,scale:帧率,此处使用应该是多久调用一次,1/15秒调用一次self.timeObserver = [self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 15) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {//当前播放的时间NSTimeInterval currentTime = CMTimeGetSeconds(time);//视频的总时长NSTimeInterval totalTime = CMTimeGetSeconds(weakSelf.player.currentItem.duration);//设置进度条的当前进度weakSelf.sliderView.value = currentTime / totalTime;//设置显示时间00:00weakSelf.currentLoadTimeLabel.text = [weakSelf formatTimeWithTimeInterVal:currentTime];}];
}//进度条响应,拖拽进度条
- (IBAction)sliderViewChange:(id)sender {if (self.player.status == AVPlayerStatusReadyToPlay) {//获取拉拽区域对应的播放时间NSTimeInterval playTime = self.sliderView.value * CMTimeGetSeconds(self.player.currentItem.duration);//计算需要定位的时间CMTime seekTime = CMTimeMake(playTime, 1);//从定位处开始播放[self.player seekToTime:seekTime completionHandler:^(BOOL finished) {[self.player play];}];}
}
@end

参考:

iOS视频播放的基本方法

iOS 开发 AVPlayer 深入浅出

[AVPlayer的基本使用]

AVPlayer视频播放之 - AVPlayer

【iOS开发进阶】-AVPlayer视频播放相关推荐

  1. 唐巧的《iOS开发进阶》 - 读后感

    2019独角兽企业重金招聘Python工程师标准>>> 唐巧的<iOS开发进阶> - 读后感 为什么要看书 为什么要多看书呢? 在技术类书籍上,看书的目的,不是为了记住所 ...

  2. 读iOS开发进阶有感

    花了两天时间, 零零散散看完了这本书.总的来说, 比较失望吧. 花点时间记录下. 第一次看到这本书, 是在看唐巧大神博客的时候看到的  ---------->  <iOS开发进阶>即 ...

  3. 【IOS开发进阶系列】动画专题

    1 CALayer IOS SDK详解之CALayer(一) http://doc.okbase.net/Hello_Hwc/archive/123447.html 1.1 基本概念 1.1.1 CA ...

  4. iOS开发进阶教程【第一季小试牛刀】

    2019独角兽企业重金招聘Python工程师标准>>> 一套关于iOS开发视频教程的进阶教程,主要讲解了iOS开发的基础内容:基本界面的创建.基本控件以及一个手把手的完成一个iOS小 ...

  5. 《iOS 开发进阶(唐巧)》读书笔记

    1. CocoaPods 的安装和使用 CocoaPods 是开发 iOS 应用程序的一个第三方库的依赖管理工具,起始于2011年8月,用 Ruby 写的. 1.0 CocoaPods 的原理 Coc ...

  6. 《iOS开发进阶》读书笔记

    相关代码:https://github.com/tangqiaoboy/iOS-Pro 一.iOS开发工具 1.类似java的maven,iOS中管理第三方库的工具cocoapods 2.网络封包分析 ...

  7. 【原】iOS开发进阶(唐巧)读书笔记(二)

    第三部分:iOS开发底层原理 1.Objective-C对象模型 1.1 isa指针 NSObject.h部分代码: NS_ROOT_CLASS @interface NSObject <NSO ...

  8. iOS开发-进阶:被误解的MVC和被神化的MVVM(作者:唐巧)

    文章转自: http://www.infoq.com/cn/articles/rethinking-mvc-mvvm 作者 唐巧 发布于 2015年11月1日 | 被误解的 MVC MVC 的历史 M ...

  9. 【iOS开发进阶】-RunTime

    1.基本概念 编译时与运行时 源代码转换为可执行的程序,通常需要经过三个步骤:编译.链接.运行,不同的编译语言,这三个步骤中所进行的操作又有些不同. 编译时就是正在编译的时候,即编译器将源代码翻译成机 ...

最新文章

  1. 服务器的攻与防(firewall 禁止指定Ip)
  2. 向web服务器传文件,c++实现向web服务器上传文件
  3. 专家预测第二波WannaCry勒索病毒攻击即将到来!
  4. 知识技能归档--CA-PKI体系-20210324
  5. 4-[函数]- 独立功能的代码块
  6. 品尝Android(二)jQuery Mobile初探
  7. python图像直方图、获取每一个柱的个数_python数字图像处理实现直方图与均衡化...
  8. 68.营救问题(广搜)
  9. linux时间管理代码,第二章、linux的时间管理
  10. arduino和python对接_Python:与Arduino进行交互-后续
  11. box-sizing:boder-box
  12. 为什么很多人打游戏感觉很快乐,然而学习工作中的满足感却很低
  13. 98% after emitting CopyPlugin Vue运行到98% after emitting CopyPlugin卡主不动
  14. 手机双清,三清,四清,五清,六清介绍
  15. 用Bibtex导出GB/T 7714等格式引用的方法
  16. (三)四种流行的RPC框架(Dubbo/Motan/Thrift/Grpc)
  17. nodejs websocket 实现简易聊天室功能
  18. YTU 3386 哈希查找2
  19. JS中对象用点(.)和方括号([])的区别
  20. Mac 下安装 go

热门文章

  1. 网络工程(计算机网络)毕业论文+PPT【中学实验楼综合布线和无线网络规划】
  2. vba中dir用法_利用Dir函数遍历某文件夹下的所有文件 | VBA实例教程
  3. CACHE数据库m语言表的创建
  4. ucore实验报告lab1
  5. 《数字经济全景白皮书》中国银行业场景金融生态建设分析2022 发布
  6. 如果我是DJ你会爱我吗
  7. Android 签名文件.keystore转换.jks
  8. 代价函数的系数中的二分之一做什么的
  9. 海外高级人才归国 责任是必须承担和想要承担的 新闻频道
  10. 下载: eMule 0.46b 2005-07-05