思维导图

先来一张思维导图,作为这篇文章的目录索引:

AVPlayer.png

为什么使用AVPlayer:

首先在iOS平台使用播放视频,可用的选项一般有这四个,他们各自的作用和功能如下:

  使用环境 优点 缺点
MPMoviePlayerController MediaPlayer 简单易用 不可定制
AVPlayerViewController AVKit 简单易用 不可定制
AVPlayer AVFoundation 可定制度高,功能强大 不支持流媒体
IJKPlayer IJKMediaFramework 定制度高,支持流媒体播放 使用稍复杂

由此可以看出,如果我们不做直播功能AVPlayer就是一个最优的选择。

另外AVPlayer是一个可以播放任何格式的全功能影音播放器
支持视频格式: WMV,AVI,MKV,RMVB,RM,XVID,MP4,3GP,MPG等。
支持音频格式:MP3,WMA,RM,ACC,OGG,APE,FLAC,FLV等。
支持视频格式: MP4,MOV,M4V,3GP,AVI等。
支持音频格式:MP3,AAC,WAV,AMR,M4A等。
详见AVPlayer支持的视频格式

如何使用

AVPlayer存在于AVFoundation框架,我们使用时需要导入:
#import <AVFoundation/AVFoundation.h>

几个播放相关的参数

在创建一个播放器之前我们需要先了解一些播放器相关的类

  • AVPlayer:控制播放器的播放,暂停,播放速度
  • AVURLAsset : AVAsset 的一个子类,使用 URL 进行实例化,实例化对象包换 URL 对应视频资源的所有信息。
  • AVPlayerItem:管理资源对象,提供播放数据源
  • AVPlayerLayer:负责显示视频,如果没有添加该类,只有声音没有画面

我们这片文章就围绕这几个参数展开,光说这些你可能还有点不明白,那我们就围绕一个最简单的播放器做起,一点点扩展功能,在具体讲解这几个参数的作用。

最简单的播放器

根据上面描述,我们知道AVPlayer是播放的必要条件,所以我们可以构建的极简播放器就是:

NSURL *playUrl = [NSURL URLWithString:@"http://baobab.wdjcdn.com/14573563182394.mp4"];
self.player = [[AVPlayer alloc] initWithURL:playUrl];
[self.player play];

是不是很简单,只有三行代码!
但是它太简单了,仅可以完成音频的播放,连画面都没有。回看上面播放相关类的介绍,是因为缺少AVPlayerLayer;作为一个播放器,我不能只播放一条视频啊,我还想根据需要切换视频,那我们就得把AVPlayerItem也加上。
加上这两个属性之后的播放器是这样的:

NSURL *playUrl = [NSURL URLWithString:@"http://baobab.wdjcdn.com/14573563182394.mp4"];
self.playerItem = [AVPlayerItem playerItemWithURL:playUrl];
//如果要切换视频需要调AVPlayer的replaceCurrentItemWithPlayerItem:方法
self.player = [AVPlayer playerWithPlayerItem:_playerItem];
self.playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.player];
self.playerLayer.frame = _videoView.bounds;
//放置播放器的视图
[self.videoView.layer addSublayer:self.playerLayer];
[_player play];

现在的播放器稍微完整了一些,我们在自己创建的容器里可以看到画面了!

更多功能

但是它作为一个视频播放器,还是有很多不能让人满意的地方。例如:没有暂停、快进快退、倍速播放等,另外如果遇到url错误是不是还要有播放失败的提示,还有播放完成的相关提示。
为完成这些,我们需要对AVPlayerItemAVPlayerLayer进一步了解一下。

一、AVPlayer的控制

前面讲过该类是控制视频播放行为的,他的使用比较简单。
播放视频:

[self.player play];

暂停视频:

[self.player pause];

更改速度:

self.player.rate = 1.5;//注意更改播放速度要在视频开始播放之后才会生效

还有一下其他的控制,我们可以调转到系统API进行查看

二、AVPlayerItem的控制

AVPlayerItem作为资源管理对象,它控制着视频从创建到销毁的诸多状态。

1、播放状态 status

typedef NS_ENUM(NSInteger, AVPlayerItemStatus) {AVPlayerItemStatusUnknown,//未知AVPlayerItemStatusReadyToPlay,//准备播放AVPlayerItemStatusFailed//播放失败
};

我们使用KVO监测playItem.status,可以获取播放状态的变化

[self.playerItem addObserver:self forKeyPath:@"status" options:NSKeyValueObservingOptionNew context:nil];

在监听回调中:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{if ([object isKindOfClass:[AVPlayerItem class]]) {if ([keyPath isEqualToString:@"status"]) {switch (_playerItem.status) {case AVPlayerItemStatusReadyToPlay://推荐将视频播放放在这里[self play];break;case AVPlayerItemStatusUnknown:NSLog(@"AVPlayerItemStatusUnknown");break;case AVPlayerItemStatusFailed:NSLog(@"AVPlayerItemStatusFailed")break;default:break;}}
}

虽然设置完播放配置我们可以直接调用[self.player play];进行播放,但是更稳妥的方法是在回调收到AVPlayerItemStatusReadyToPlay时进行播放

2、视频的时间信息

在AVPlayer中时间的表示有一个专门的结构体CMTime

typedef struct{CMTimeValue    value;     // 帧数CMTimeScale    timescale;  // 帧率(影片每秒有几帧)CMTimeFlags    flags;        CMTimeEpoch    epoch;
} CMTime;

CMTime是以分数的形式表示时间,value表示分子,timescale表示分母,flags是位掩码,表示时间的指定状态。

获取当前播放时间,可以用value/timescale的方式:

float currentTime = self.playItem.currentTime.value/item.currentTime.timescale;

还有一种利用系统提供的方法,我们用它获取视频总时间:

float totalTime   = CMTimeGetSeconds(item.duration);

如果我们想要添加一个计时的标签不断更新当前的播放进度,有一个系统的方法:

- (id)addPeriodicTimeObserverForInterval:(CMTime)interval queue:(nullable dispatch_queue_t)queue usingBlock:(void (^)(CMTime time))block;

方法名如其意, “添加周期时间观察者” ,参数1 interal 为CMTime 类型的,参数2 queue为串行队列,如果传入NULL就是默认主线程,参数3 为CMTime 的block类型。
简而言之就是,每隔一段时间后执行 block。
比如:我们把interval设置成CMTimeMake(1, 10),在block里面刷新label,就是一秒钟刷新10次。

正常观察播放进度一秒钟一次就行了,所以可以这么写:

[self.player addPeriodicTimeObserverForInterval:CMTimeMake(1, 1) queue:nil usingBlock:^(CMTime time) {AVPlayerItem *item = WeakSelf.playerItem;NSInteger currentTime = item.currentTime.value/item.currentTime.timescale;NSLog(@"当前播放时间:%ld",currentTime);
}];

3、loadedTimeRange 缓存时间

获取视频的缓存情况我们需要监听playerItem的loadedTimeRanges属性

[self.playerItem addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];

在KVO的回调里:

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(@"当前缓冲时间:%f",totalBuffer);
}

4、playbackBufferEmpty

监听playbackBufferEmpty我们可以获取当缓存不够,视频加载不出来的情况:

[self.playerItem addObserver:self forKeyPath:@"playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:nil];

在KVO回调里:

if ([keyPath isEqualToString:@"playbackBufferEmpty"]) {//some code show loading
}

5、playbackLikelyToKeepUp

playbackLikelyToKeepUpplaybackBufferEmpty是一对,用于监听缓存足够播放的状态

[self.playerItem addObserver:self forKeyPath:@"playbackLikelyToKeepUp" options:NSKeyValueObservingOptionNew context:nil];
/* ... */
if([keyPath isEqualToString:@"playbackLikelyToKeepUp"]) {    //由于 AVPlayer 缓存不足就会自动暂停,所以缓存充足了需要手动播放,才能继续播放  [_player play];
}

AVURLAsset

播放视频只需一个url就能进行这样太不安全了,别人可以轻易的抓包盗链,为此我们需要为视频链接做一个请求头的认证,这个功能可以借助AVURLAsset完成。

AVPlayerItem除了可以用URL初始化,还可以用AVAsset初始化,而AVAsset不能直接使用,我们看下AVURLAsset的一个初始化方法:

/*!
@param      URLAn instance of NSURL that references a media resource.
@param      optionsAn instance of NSDictionary that contains keys for specifying options for the initialization of the AVURLAsset. See AVURLAssetPreferPreciseDurationAndTimingKey and AVURLAssetReferenceRestrictionsKey above.
*/
+ (instancetype)URLAssetWithURL:(NSURL *)URL options:(nullable NSDictionary<NSString *, id> *)options;

AVURLAssetPreferPreciseDurationAndTimingKey.这个key对应的value是一个布尔值, 用来表明资源是否需要为时长的精确展示,以及随机时间内容的读取进行提前准备。

除了这个苹果官方介绍的功能外,他还可以设置请求头,这个算是隐藏功能了,因为苹果并没有明说这个功能,我是费了很大劲找到的。

NSMutableDictionary * headers = [NSMutableDictionary dictionary];
[headers setObject:@"yourHeader"forKey:@"User-Agent"];
self.urlAsset = [AVURLAsset   URLAssetWithURL:self.videoURL options:@{@"AVURLAssetHTTPHeaderFieldsKey" : headers}];
// 初始化playerItem
self.playerItem = [AVPlayerItem playerItemWithAsset:self.urlAsset];

补充:后来得知这个参数是非公开的API,但是经多人测试项目上线不受影响。

播放相关通知

1、声音类:

//声音被打断的通知(电话打来)
AVAudioSessionInterruptionNotification
//耳机插入和拔出的通知
AVAudioSessionRouteChangeNotification

根据userInfo判断具体状态

2、播放类

//播放完成
AVPlayerItemDidPlayToEndTimeNotification
//播放失败
AVPlayerItemFailedToPlayToEndTimeNotification
//异常中断
AVPlayerItemPlaybackStalledNotification

对于播放完成的通知我们可以这么写:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playerMovieFinish:) name:AVPlayerItemDidPlayToEndTimeNotification object:[self.player currentItem]];

3、系统状态

//进入后台
UIApplicationWillResignActiveNotification
//返回前台
UIApplicationDidBecomeActiveNotification

提示:所有通知和KVO的使用我们都要记得在不用时remove掉。

小结

视频播放相关的知识比较多,细节的方面需要一点一点去扣。暂且写这么多吧,以后有需要会及时补充。
参考:
ZFPlayer
AVPlayer那些坑
如果还有什么不理解的可以简书私信问我,或者查看我写的Demo,欢迎star- ( ゜- ゜)つロ乾杯~

作者:勇闯天涯茉莉花茶
链接:https://www.jianshu.com/p/29bfc7dc401a
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

AVPlayer详解系列(一)参数设置相关推荐

  1. Java 11中的新功能和API详解系列1

    Java 11中的新功能和API详解系列1 2018.9.27 版权声明:本文为博主chszs的原创文章,未经博主允许不得转载. JDK 11在语言语法方面有一个小改动,增加了相当数量的新API,以及 ...

  2. Android高效率编码-第三方SDK详解系列(二)——Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能

    Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 我的本意是第二篇写Mob的shareSD ...

  3. Android高效率编码-第三方SDK详解系列(三)——JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送

    Android高效率编码-第三方SDK详解系列(三)--JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送 很久没有更新第三方SDK这个系列了,所以更新一下这几天工作中使用到的推送, ...

  4. linux怎么读其实很简单 微星为你详解Z77主板BIOS设置

    近期,微星科技发布了大量的7系主板,它们全部配备厂商最新的军规三代组件技术,并且同时配备了PCI-E3.0,USB3.0还有SATA3.0技术,所以我们可以称呼它们为微星3.0主板.随着微星Z77主板 ...

  5. Java并发编程最佳实例详解系列

    Java并发编程最佳实例详解系列: Java并发编程(一)线程定义.状态和属性 Java并发编程(一)线程定义.状态和属性 线程是指程序在执行过程中,能够执行程序代码的一个执行单元.在java语言中, ...

  6. SCCM 2007 R2部署、操作详解系列之部署篇

    SCCM 2007 R2部署.操作详解系列之部署篇 导语: 大家好,我是John Cai(蔡建涛),欢迎大家来到我的博客-E网情深http://johncai.blog.51cto.com/.今天,我 ...

  7. Linux详解系列2- 这100条常见命令你都用过哪些?

    Linux详解系列2- 这100条常见命令你都用过哪些? https://www.toutiao.com/i6941655011004891660/?tt_from=weixin&utm_ca ...

  8. Windows Server 2008终端服务详解系列3:结合MOSS 2007部署TS Web Access

    Windows Server 2008终端服务详解系列3:结合MOSS 2007部署TS Web Access 前言: 本系列将全面的介绍Windows Server 2008终端服务,从概念到功能, ...

  9. 会说话的狗狗本电脑版_一看就会用!Fotor 电脑版 图片后期处理详解系列教程 第一节...

    Fotor 电脑版 图片后期处理详解系列教程 第一节 这节课我们先来了解一下 Fotor 电脑版的界面功能,为了能用 Fotor 电脑版修图处理打基础. 如图示,启动 Fotor 以后出面界面,有编辑 ...

最新文章

  1. linux 安装redis4.0.6,Redis(4.0.6)在Linux(CentOS7)下的安装
  2. 百万奖金悬赏AI垃圾分类,就问你来不来?
  3. IOS开发学习笔记(1)
  4. 新手如何从0开始做东南亚电商lazada平台?
  5. 做好过程质量保证QA工作的几个关键方面
  6. Button的点击事件
  7. winform把所有dll打包成一个exe
  8. nginx根据参数转发到不同服务器_Nginx服务器之负载均衡策略
  9. Oracle创建视图的一个问题
  10. set python_Python的set集合详解
  11. 9.CSMA_CD协议
  12. DataFrame创建程序利用字典创建dataframe对象
  13. android studio | openGL es 3.0增强现实(AR)开发 (1) 建立一个openGL es 3.0开发环境
  14. 河南省学业水平测试计算机题目,2015级河南学业水平考试试题及答案
  15. 为你收罗100个学英语的网站!!!
  16. B站左程云算法笔记06
  17. 航飞原始影像外方位元素_武汉大学生历年摄影测量试题及答案
  18. Unbuntu20.04环境下一款开源翻译软件:goldendict的安装与配置(图文)
  19. 《游测攻坚日记》-- 黑盒测试1
  20. ea6500 v1 刷梅林_继续测试:Linksys EA6500 v1 的TT固件

热门文章

  1. linux系统weblogic10.3.6(jar) 下载安装
  2. 网易云音乐正式启动IPO 值不值五分之二个腾讯音乐?
  3. 数据分类与聚类的区别
  4. 一行代码实现微信多开
  5. 温度报警课程设计报告
  6. 晶闸管静态参数测试系统
  7. qt实现简单的视频播放器
  8. 算法---找出第 N 个二进制字符串中的第 K 位(Kotlin)
  9. Git ssh 下载设置
  10. PowerDesigner 生成数据字典