iOS音频播放 (二):AudioSession 转
原文出处 :http://msching.github.io/blog/2014/07/08/audio-in-ios-2/
前言
本篇为《iOS音频播放》系列的第二篇。
在实施前一篇中所述的7个步骤之前还必须面对一个麻烦的问题,AudioSession。
AudioSession简单介绍
AudioSession这个玩意的主要功能包含下面几点(图片来自官方文档):
- 确定你的app怎样使用音频(是播放?还是录音?)
- 为你的app选择合适的输入输出设备(比方输入用的麦克风,输出是耳机、手机功放或者airplay)
- 协调你的app的音频播放和系统以及其它app行为(比如有电话时须要打断,电话结束时须要恢复,按下静音button时是否歌曲也要静音等)
AudioSession
AudioSession相关的类有两个:
AudioToolBox中的AudioSessionAVFoundation中的AVAudioSession
当中AudioSession在SDK 7中已经被标注为depracated,而AVAudioSession这个类尽管iOS 3開始就已经存在了。但当中非常多方法和变量都是在iOS 6以后甚至是iOS 7才有的。
所以各位能够按照下面标准选择:
- 假设最低版本号支持iOS 5,能够使用
AudioSession,也能够使用AVAudioSession; - 假设最低版本号支持iOS 6及以上。请使用
AVAudioSession
以下以AudioSession类为例来讲述AudioSession相关功能的使用(非常不幸我须要支持iOS 5。
。
T-T。使用AVAudioSession的同学能够在其头文件里寻找相应的方法使用就可以,须要注意的点我会加以说明).
注意:在使用AVAudioPlayer/AVPlayer时能够不用关心AudioSession的相关问题,Apple已经把AudioSession的处理过程封装了。但音乐打断后的响应还是要做的(比方打断后音乐暂停了UI状态也要变化。这个应该通过KVO就能够搞定了吧。。
我没试过瞎猜的>_<)。
初始化AudioSession
使用AudioSession类首先须要调用初始化方法:
1 |
|
前两个參数一般填NULL表示AudioSession执行在主线程上(但并不代表音频的相关处理执行在主线程上。仅仅是AudioSession)。第三个參数须要传入一个一个AudioSessionInterruptionListener类型的方法。作为AudioSession被打断时的回调,第四个參数则是代表打断回调时须要附带的对象(即回到方法中的inClientData。例如以下所看到的,能够理解为UIView
animation中的context)。
1 |
|
这才刚開始。坑就来了。
这里会有两个问题:
第一。AudioSessionInitialize能够被多次运行,但AudioSessionInterruptionListener仅仅能被设置一次,这就意味着这个打断回调方法是一个静态方法。一旦初始化成功以后全部的打断都会回调到这种方法,即便下一次再次调用AudioSessionInitialize而且把还有一个静态方法作为參数传入,当打断到来时还是会回调到第一次设置的方法上。
这样的场景并不少见,比如你的app既须要播放歌曲又须要录音,当然你不可能知道用户会先调用哪个功能,所以你必须在播放和录音的模块中都调用AudioSessionInitialize注冊打断方法。但终于打断回调仅仅会作用在先注冊的那个模块中,非常蛋疼吧。。。
所以对于AudioSession的使用最好的方法是生成一个类单独进行管理,统一接收打断回调并发送自己定义的打断通知,在须要用到AudioSession的模块中接收通知并做对应的操作。
Apple也察觉到了这一点,所以在AVAudioSession中首先取消了Initialize方法,改为了单例方法sharedInstance。在iOS 5上全部的打断都须要通过设置id<AVAudioSessionDelegate>并实现回调方法来实现,这相同会有上述的问题。所以在iOS 5使用AVAudioSession下仍然须要一个单独管理AudioSession的类存在。
delegate
在iOS 6以后Apple最终把打断改成了通知的形式。。
这下科学了。
第二,AudioSessionInitialize方法的第四个參数inClientData,也就是回调方法的第一个參数。
上面已经说了打断回调是一个静态方法,而这个參数的目的是为了能让回调时拿到context(上下文信息),所以这个inClientData须要是一个有足够长生命周期的对象(当然前提是你确实须要用到这个參数),假设这个对象被dealloc了,那么回调时拿到的inClientData会是一个野指针。
就这一点来说构造一个单独管理AudioSession的类也是有必要的。由于这个类的生命周期和AudioSession一样长,我们能够把context保存在这个类中。
监听RouteChange事件
假设想要实现类似于“拔掉耳机就把歌曲暂停”的功能就须要监听RouteChange事件:
1 |
|
调用上述方法。AudioSessionPropertyID參数传kAudioSessionProperty_AudioRouteChange,AudioSessionPropertyListener參数传相应的回调方法。inClientData參数同AudioSessionInitialize方法。
相同作为静态回调方法还是须要统一管理,接到回调时能够把第一个參数inData转换成CFDictionaryRef并从中获取kAudioSession_AudioRouteChangeKey_Reason键值相应的value(应该是一个CFNumberRef),得到这些信息后就能够发送自己定义通知给其它模块进行相应操作(比如kAudioSessionRouteChangeReason_OldDeviceUnavailable就能够用来做“拔掉耳机就把歌曲暂停”)。
1 |
|
1 |
|
注意:iOS 5下假设使用了AVAudioSession因为AVAudioSessionDelegate中并未定义相关的方法,还是须要用这种方法来实现监听。
iOS
6下直接监听AVAudioSession的通知就能够了。
这里附带两个方法的实现。都是基于AudioSession类的(使用AVAudioSession的同学帮不到你们啦)。
1、推断是否插了耳机:
1 |
|
2、推断是否开了Airplay(来自StackOverflow):
1 |
|
设置类别
下一步要设置AudioSession的Category。使用AudioSession时调用以下的接口
1 |
|
假设我须要的功能是播放,运行例如以下代码
1 |
|
使用AVAudioSession时调用以下的接口
1 |
|
至于Category的类型在官方文档中都有介绍。我这里也仅仅罗列一下详细就不赘述了,各位在使用时能够按照自己须要的功能设置Category。
1 |
|
1 |
|
启用
有了Category就能够启动AudioSession了,启动方法:
1 |
|
启动方法调用后必需要推断是否启动成功。启动不成功的情况常常存在。比如一个前台的app正在播放,你的app正在后台想要启动AudioSession那就会返回失败。
普通情况下我们在启动和停止AudioSession调用第一个方法就能够了。但假设你正在做一个即时语音通讯app的话(类似于微信、易信)就须要注意在deactive AudioSession的时候须要使用第二个方法,inFlags參数传入kAudioSessionSetActiveFlag_NotifyOthersOnDeactivation(AVAudioSession给options參数传入AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation)。当你的app
deactive自己的AudioSession时系统会通知上一个被打断播放app打断结束(就是上面说到的打断回调)。假设你的app在deactive时传入了NotifyOthersOnDeactivation參数。那么其它app在接到打断结束回调时会多得到一个參数kAudioSessionInterruptionType_ShouldResume否则就是ShouldNotResume(AVAudioSessionInterruptionOptionShouldResume),依据參数的值能够决定是否继续播放。
大概流程是这种:
- 一个音乐软件A正在播放。
- 用户打开你的软件播放对话语音,AudioSession active。
- 音乐软件A音乐被打断并收到InterruptBegin事件。
- 对话语音播放结束,AudioSession deactive而且传入NotifyOthersOnDeactivation參数;
- 音乐软件A收到InterruptEnd事件。查看Resume參数,假设是ShouldResume控制音频继续播放。假设是ShouldNotResume就维持打断状态;
官方文档中有一张非常形象的图来阐述这个现象:

然而如今某些语音通讯软件和某些音乐软件却无视了NotifyOthersOnDeactivation和ShouldResume的正确使用方法,导致我们常常接到这种用户反馈:
你们的app在使用xx语音软件听了一段话后就不会继续播放了。但xx音乐软件能够继续播放啊。
好吧,上面仅仅是吐槽一下。请无视我吧。
2014.7.14补充,7.19更新:
发现即使之前已经调用过AudioSessionInitialize方法,在某些情况下被打断之后可能出现AudioSession失效的情况。须要再次调用AudioSessionInitialize方法来又一次生成AudioSession。
否则调用AudioSessionSetActive会返回560557673(其它AudioSession方法也雷同,全部方法调用前必须首先初始化AudioSession),转换成string后为”!ini”即kAudioSessionNotInitialized。这个情况在iOS
5.1.x上比較easy发生,iOS 6.x 和 7.x也偶有发生(详细的原因还不知晓好像和打断时直接调用AudioOutputUnitStop有关,又是个坑啊)。
所以每次在调用AudioSessionSetActive时应该推断一下错误码,假设是上述的错误码须要又一次初始化一下AudioSession。
附上OSStatus转成string的方法:
1 |
|
打断处理
正常启动AudioSession之后就能够播放音频了,以下要讲的是对于打断的处理。之前我们说到打断的回调在iOS 5下须要统一管理。在收到打断開始和结束时须要发送自己定义的通知。
使用AudioSession时打断回调应该首先获取kAudioSessionProperty_InterruptionType。然后发送一个自己定义的通知并带上相应的參数。
1 |
|
收到通知后的处理方法例如以下(注意ShouldResume參数):
1 |
|
小结
关于AudioSession的话题到此结束(码字果然非常累。
。
)。小结一下:
- 假设最低版本号支持iOS 5,能够使用
AudioSession也能够考虑使用AVAudioSession。须要有一个类统一管理AudioSession的全部回调,在接到回调后发送相应的自己定义通知; - 假设最低版本号支持iOS 6及以上。请使用
AVAudioSession,不用统一管理,接AVAudioSession的通知就可以; - 依据app的应用场景合理选择
Category; - 在deactive时须要注意app的应用场景来合理的选择是否使用
NotifyOthersOnDeactivation參数。 - 在处理InterruptEnd事件时须要注意
ShouldResume的值。
演示样例代码
这里有我自己写的AudioSession的封装,假设各位须要支持iOS
5的话能够使用一下。
下篇预告
下一篇将讲述怎样使用AudioFileStreamer分离音频帧。以及怎样使用AudioQueue进行播放。
下一篇将讲述怎样使用AudioFileStreamer提取音频文件格式信息和分离音频帧。
參考资料
iOS音频播放 (二):AudioSession 转的更多相关文章
- iOS音频播放(二):AudioSession
(本文转自码农人生) 前言 在实施前一篇中所述的7个步骤步之前还必须面对一个麻烦的问题,AudioSession. AudioSession简介 AudioSession这个玩意的主要功能包括以下 ...
- IOS 音频播放
iOS音频播放 (一):概述 前言 从事音乐相关的app开发也已经有一段时日了,在这过程中app的播放器几经修改我也因此对于iOS下的音频播放实现有了一定的研究.写这个系列的博客目的一方面希望能够抛砖 ...
- iOS音频播放(一):概述
(本文转自码农人生) 前言 从事音乐相关的app开发也已经有一段时日了,在这过程中app的播放器几经修改,我也因此对于iOS下的音频播放实现有了一定的研究.写这个 系列的博客目的一方面希望能够抛砖引玉 ...
- iOS音频播放 (五):AudioQueue
码农人生 ChengYin's coding life 主页 Blog 分类 Categories 归档 Archives 关于 About Weibo GitHub RSS Where there ...
- iOS音频播放、录音、视频播放、拍照、视频录制
随着移动互联网的发展,如今的手机早已不是打电话.发短信那么简单了,播放音乐.视频.录音.拍照等都是很常用的功能.在iOS中对于多媒体的支持是非常强大的,无论是音视频播放.录制,还是对麦克风.摄像头的操 ...
- iOS音频播放概述
在iOS系统中apple对音频播放需要的操作进行了封装并提供了不同层次的接口 下面对其中的中高层接口进行功能说明: Audio File Services:读写音频数据,可以完成播放流程中的第2步: ...
- iOS音频播放 (四):AudioFile 转
原文出处 : http://msching.github.io/blog/2014/07/19/audio-in-ios-4/ 前言 接着第三篇的AudioStreamFile这一篇要来聊一下Audi ...
- iOS音频播放之AudioQueue(一):播放本地音乐
AudioQueue简单介绍 AudioStreamer说明 AudioQueue具体解释 AudioQueue工作原理 AudioQueue主要接口 AudioQueueNewOutput Audi ...
- iOS音频播放 (三):AudioFileStream 转
原文出处 :http://msching.github.io/blog/2014/07/09/audio-in-ios-3/ 前言 本来说好是要在第三篇中讲AudioFileStream和AudioQ ...
随机推荐
- Android(java)学习笔记187:多媒体之SurfaceView
1. SurfaceView: 完成单位时间内界面的快速切换(游戏界面流畅感). 我们之前知道一般的View,只能在主线程里面显示,主线程中更新UI.但是SurfaceView可以在子线程中里 ...
- nodejs的学习
nodejs 就是使用js来编写服务端的程序.它的特性是(单线程 速度快 耗内存多 异步 事件驱动) ( 一些技术的解决方案:默认情况下是 1.不支持多核,可以使用cluster 进行解 ...
- SQLSTATE=2300
在powerdesigner将表的结构运用于数据库的时候报的错. 目标: 在Hibernate中使用多表级联的插入操作. 解决办法: 将navicat中的mysql数据库表删除, 手动创建 原因: p ...
- java去左右的空格(包括全角空格,tab,回车等)
在开发中我们会遇到需要去除左右空格的需求,如果只是简单的空格,调一下trim()方法即可,但如果有中文全角.回车等看起来是空格的非空格,则需要自定义来开发实现,下面这个工具可以实现去左右那些看起来是空 ...
- Core Animation教程
http://dev.wo.com.cn/bbs/viewthread.jsp?tid=141767&page=1 http://blog.csdn.net/lvxiangan/article ...
- c++ 回调的实现
什么是回调?通常发生在需要两个角色即调用者与实现者的情形上,即我们希望当产生某个事件时,调用实现者定义的某个函数.当然这个概念很大,不是说操作系统的信号量,条件变量什么的,是在语言级别实现,如一个Fr ...
- 2018 CCPC 桂林站(upc复现赛)总结
比赛一开始盯上了A题和G题,一个小时过去了还没有出题,心里有些乱.这时我看D题很多人过了,于是宝儿去看D题,说D题简单,转化成二进制暴力,于是就去做了.写的时候好像思路有点卡,WA了一发,后来马上发现 ...
- rbac组件之权限操作(四)
对于权限表的操作有两种方式,第一种是一个个的权限进行curd,另外一种是批量操作,自动发现django程序中的路由,进行批量curd,首先介绍第一种方式. 因为在列出菜单时,已经将权限列表列出来了,所 ...
- Python面向对象之类属性类方法静态方法
类的结构 实例 使用面向对象开发时,第一步是设计类: 当使用 类名() 创建对象时,会自动执行以下操作: 1.为对象在内存中分配空间--创建对象: 2.为对象的属性 设置初始值--初始化方法(init ...
- C语言学习1
一.初识C语言 1.1 C语言的起源 1972年,贝尔实验室的丹尼斯,里奇和肯,汤普逊在开发UNIX操作系统时设计了C语言,然而,C语言不完全是里奇突发奇想出来的,他是在B语言的基础上进行设计的,至于 ...