iOS AVPlayer 的使用实践
前两天在网上看到一篇博客,介绍AVPlayer的使用,但是只简单介绍了一下单个的本地文件如何播放,心血来潮,就想着做一个类似于播放器的东西,能够实现播放网络歌曲,循环播放多首音乐,下面我们来实现一下
首先明确一下,在本文中需要讲到的几点:
实现网络歌曲的播放 实现在后台也能播放歌曲 实现多首歌曲的循环播放 需要有播放/暂停和下一首的功能 需要在播放期间能够得知该首歌曲的总时长和当前播放时长
本文中就暂时将这名多,后面还会丰富,例如实现缓存下载,实现歌曲缓存的进度查看,实现能够使用耳机按钮控制播放等等。
播放网络歌曲
因为需要播放网络歌曲,我就往七牛云上传了几首歌,就不用再自己到处去找歌曲了
首先,明确我们播放歌曲使用的是AVPlayer,至于为什么使用它不使用其他的,因为它好用啊,苹果封装了强大的功能,让我们使用,干嘛不用!其实还有其他原因,这个就等着你自己去搜索了。
AVQueuePlayer
AVQueuePlayer是AVPlayer的一个子类,他可以实现多首歌曲播放,所以我们直接使用它了
//传入多个AVPlayerItem来初始化AVQueuePlayer
+ (instancetype)queuePlayerWithItems:(NSArray<AVPlayerItem *> *)items;
复制代码
AVPlayerItem
AVPlayerItem是一个资源对象,我们加载歌曲的时候都是使用它,它提供了两种初始化方法
//初始化网络资源
+ (instancetype)playerItemWithURL:(NSURL *)URL;
//初始化本地资源,本地的音乐或者影片资源都是通过AVAsset拿出来
+ (instancetype)playerItemWithAsset:(AVAsset *)asset;
复制代码
先来试一下:
//初始化AVPlayerItem
NSMutableArray *items = [NSMutableArray array];
NSArray *urls = @[MUSIC_URL1,MUSIC_URL2,MUSIC_URL3,MUSIC_URL4,MUSIC_URL5];
for (NSString *url in urls) {
AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:url]];
[items addObject:item];
}
//初始化AVQueuePlayer
AVQueuePlayer *player = [AVQueuePlayer queuePlayerWithItems: items];
//测试播放
if(player.status == AVPlayerStatusReadyToPlay){
[player play];
}
复制代码
上面的代码看起来没有错,但是我在做的时候,却遇到一个问题,第一次点击的时候,并不会播放,第二次第三次就会开始播放了。
其实这里是有一个缓冲的原因,因为是网络资源,涉及到一个缓冲,后面我们会对这个做处理,歌曲确实是能够播放的
就这样,简单实现了多首歌曲的播放,但是我们还需要实现循环播放,这个就相对麻烦一点了。
要实现循环播放,我们就需要知道AVQueuePlayer的播放机制,对于AVQueuePlayer播放,是有一个队列,每次播放完成一首歌曲过后,这首歌曲就会从队列中删除,即这个item会从队列中删除,并且如果我们想直接再将这个item再次加入队列,是不能够加入的,我们必须要在new 一个item,再次加载到这个队列当中,才能够实现再次播放。这个也是挺蛋疼的。
知道了这个,我们就有想法了,我们能够在player最后一首歌曲即将播放完成后,再来新建一个队列啊。思路是正确的,但是我们不能够直接得到player正在播放最后一首歌曲,这时候我想到的是一个timer检测,通过timer去检测player的播放队列是否还剩下一首歌曲,如果是的话,我们就新建队列,加入到player的播放序列中
首先,我们在开始播放歌曲的时候,就需要将timer启动,监测player
self.checkMusicTimer = [NSTimer scheduledTimerWithTimeInterval:3 target:self selector:@selector(checkMusic) userInfo:nil repeats:YES];
复制代码
在checkMusic我们判断当前是否队列中只有一首歌曲
- (void)checkMusic
{
if (self.player.items.count == 1){
[self prepareItems];//这个方法即是再次创建队列,加入到player播放序列中
[self play];
}
}
复制代码
// 准备歌曲
// 因为需要歌曲循环播放,每次AVQueuePlayer播放完成一首歌曲,就会将其从队列中移除
// 所以我们需要在歌曲最后一首播放完之前重新为AVQueuePlayer创建一个播放队列,这样就能够实现循环播放
//
//
- (void)prepareItems{
NSMutableArray *items = [NSMutableArray array];
NSArray *urls = @[MUSIC_URL1,MUSIC_URL2,MUSIC_URL3,MUSIC_URL4,MUSIC_URL5];
for (NSString *url in urls) {
AVPlayerItem *item = [[AVPlayerItem alloc] initWithURL:[NSURL URLWithString:url]];
[items addObject:item];
//这里是添加每首歌曲的监测,我们后面会讲到
[item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:item];
}
self.playerItems = items;
for (AVPlayerItem *item in items) {
if ([self.player canInsertItem:item afterItem:self.player.items.lastObject]) {
[self.player insertItem:item afterItem:self.player.items.lastObject];
}
}
}
复制代码
这样一来,我们就能够实现循环播放了,这里的代码和后面要讲到的有关联,所以这里看不清晰也没关系,接着往后看
上面我们讲了,有个缓冲的原因,导致首次点击播放的时候,不能够成功播放,在AVPlayerItem中有一个属性loadedTimeRanges,表示的是缓存状态,我们可以对他进行观察,来进行播放
//上面的代码已经写出了对缓冲的检测
[item addObserver:self forKeyPath:@"loadedTimeRanges" options:NSKeyValueObservingOptionNew context:nil];
复制代码
然后我们在观察者中
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
if ([keyPath isEqualToString:@"loadedTimeRanges"]) {
NSLog(@"缓冲");
[self play];
}
}
复制代码
我们在每个item中加入了观察者,在什么时候移除呢,当然是在每首歌曲播放完成后移除,如果不移除将会崩溃
再次对每个item进行观测,播放结束时
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(playbackFinished:) name:AVPlayerItemDidPlayToEndTimeNotification object:item];
复制代码
在播放结束,移除观察者
- (void)playbackFinished:(NSNotification *)notice {
NSLog(@"播放完成");
[self.currentItem removeObserver:self forKeyPath:@"loadedTimeRanges"];
}
复制代码实现后台播放
要实现后台播放,很简单只需要加入几行代码
//设置可后台播放
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
复制代码
然后我们还需要在项目里设置
播放暂停
这个就很简单了
直接调方法就行
上一首下一首也是直接调用方法就行
/*!
@method play
@abstract Signals the desire to begin playback at the current item's natural rate.
@discussion Equivalent to setting the value of rate to 1.0.
*/
- (void)play;
- /*!
@method pause
@abstract Pauses playback.
@discussion Equivalent to setting the value of rate to 0.0.
*/
- (void)pause;
/*!
@method advanceToNextItem
@abstract Ends playback of the current item and initiates playback of the next item in the player's queue.
@discussion Removes the current item from the play queue.
*/
- (void)advanceToNextItem;
复制代码时长计算
为player加一个观察者就行
-(void)playerDidPlay{
// //添加播放进度观察者
__weak typeof(self) weakSelf = self;
self.timeObserver = [self.manager.player addPeriodicTimeObserverForInterval:CMTimeMake(1.0,1.0) queue:dispatch_get_main_queue() usingBlock:^(CMTime time) {
float current = CMTimeGetSeconds(time);
float total = CMTimeGetSeconds(weakSelf.manager.currentItem.duration);
weakSelf.total = [NSString stringWithFormat:@"%.2f",total];
weakSelf.current = [NSString stringWithFormat:@"%.f",current];
weakSelf.label.text = [NSString stringWithFormat:@"%@/%@",weakSelf.current,weakSelf.total];
}];
_isPlaying = YES;
[self.play setTitle:@"暂停" forState:UIControlStateNormal];
}
复制代码
其中的CMTime指的是帧数
iOS AVPlayer 的使用实践的更多相关文章
- iOS应用开发最佳实践
<iOS应用开发最佳实践> 基本信息 作者: 王浩 出版社:电子工业出版社 ISBN:9787121207679 上架时间:2013-7-22 出版日期:2013 年8月 开本:16 ...
- iOS AVPlayer视频播放器
代码地址如下:http://www.demodashi.com/demo/11168.html 一.运行效果 二.实现过程 ①.创建播放器avPlayer //创建播放器 url = [url str ...
- iOS代码瘦身实践
1 分析当前ipa的组成 一般一个ipa会包含: 1) 资源文件 本地文件:数据.配置.数据库等等 字体文件 图片资源 2) 源代码 通过生成linkmap文件,分析源代码生成的编译文件的大小.在B ...
- 奇艺iOS移动端网络优化实践 | 请求成功率优化篇 原创 Charles 爱奇艺技术
奇艺iOS移动端网络优化实践 | 请求成功率优化篇 原创 Charles 爱奇艺技术
- iOS - AVPlayer 音视频播放
前言 NS_CLASS_AVAILABLE(10_7, 4_0) @interface AVPlayer : NSObject @available(iOS 4.0, *) public class ...
- iOS AvPlayer AvAudioPlayer音频的后台播放问题
iOS 4开始引入的multitask,我们可以实现像ipod程序那样在后台播放音频了.如果音频操作是用苹果官方的AVFoundation.framework实现,像用AvAudioPlayer,Av ...
- 视频云SDK iOS持续集成项目实践
1. 前言 2016年, 我们维护的 iOS推流播放融合SDK KSYLive_iOS 在github上发布了40多个版本, 平均两周发布一个新版本, 经历了最初痛苦的全手动版本构建和维护, 到后来慢 ...
- 学习笔记TF066:TensorFlow移动端应用,iOS、Android系统实践
TensorFlow对Android.iOS.树莓派都提供移动端支持. 移动端应用原理.移动端.嵌入式设备应用深度学习方式,一模型运行在云端服务器,向服务器发送请求,接收服务器响应:二在本地运行模型, ...
- 【转】iOS:AvPlayer设置播放速度不生效的解决办法
现象: 项目有一个需求是实现视频的慢速播放,使用的是封装的AvPlayer,但是设置时发现比如设置rate为0.5,0.1,0.01都是一样的速度,非常疑惑.后来经过查找资料,发现iOS10对这个AP ...
随机推荐
- 【论文笔记】DeepOrigin: End-to-End Deep Learning for Detection of New Malware Families
DeepOrigin: End-to-End Deep Learning for Detection of New Malware Families 标签(空格分隔): 论文 论文基本信息 会议: I ...
- 假如React没了JSX
如题,想必React大家早已不陌生,而React里面的JSX都是玩的得心应手了,但是假如说React里面没有了React那会是一种什么样的情形呢,我们来简单的看一下. 首先我们来实现一个简单的list ...
- Java并发编程系列-(7) Java线程安全
7. 线程安全 7.1 线程安全的定义 如果多线程下使用这个类,不过多线程如何使用和调度这个类,这个类总是表示出正确的行为,这个类就是线程安全的. 类的线程安全表现为: 操作的原子性 内存的可见性 不 ...
- 硬核讲解 Jetpack 之 LifeCycle 源码篇
前一篇 硬核讲解 Jetpack 之 LifeCycle 使用篇 主要介绍了 LifeCycle 存在的意义,基本和进阶的使用方法.今天话不多说,直接开始撸源码. 本文基于我手里的 android_9 ...
- 电力项目十一--js添加浮动框
1.添加浮动窗口样式 <!-- 浮动窗口样式css begin --> <style type="text/css"> #msg_win{border:1p ...
- 【网站部署】flask
记录下常用的命令: nohup gunicorn --workers=2 --timeout=400 app:app &ps -ax scp -p ./***.h5 user@**.**.** ...
- 每个Xcode开发者应该知道的几个使用技巧
1.快速打开 快速打开(Open Quickly)命令在Xcode的File菜单中,当然,用快捷键Command+Shift+O会更方便一些.这个命令可以开启一个小窗格用来快速搜索浏览文件.类.算法以 ...
- 用Python搞出自己的云词图 | 【带你装起来】
作者:AI算法与图像处理 参考:http://www.sohu.com/a/149657007_236714 云词图简介 什么词云 由词汇组成类似云的彩色图形.“词云”就是对网络文本中出现频率较高的“ ...
- Awvs–网络漏洞扫描工具
Awvs漏洞扫描自动化 在WEB安全方面,安全侧使用Acunetix_Web_Vulnerability_Scanner_11.xWEB漏洞扫描工具定期手工对m站和www站进行全站漏洞扫描. 为了防止 ...
- Java 中 try、catch、finally 语句块的执行顺序
假设代码顺序书写如下:try → catch → finally → 其他代码 则: 1.正常执行顺序:try → catch → finally → 其他代码 2.try,catch和finally ...