AVAudioSession-Category的正确使用姿势

最近,在开发一款音乐播放器类型项目中遇到的一些与AVAudioSession-Category设置的一些坑,以下是整个过程的一些经验总结。

1.常规播放

一般如果应用只有简单音乐播放功能,那么我们的AVAudioSession-Category只用像如下一样设置即可:

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];     [[AVAudioSession sharedInstance] setActive:YES error:nil];

此时如果我们只是播放音乐,而不需要独占锁屏界面时,还可以设置:

    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback
                                     withOptions:AVAudioSessionCategoryOptionMixWithOthers
                                           error:nil];

这样我们兼容其他后台播放的音乐一起进行播放,不过大部分场景下,我们是需要独占式后台播放。

2.常规录音

在录音的时候,我们一般如以下设置:

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];     
[[AVAudioSession sharedInstance] setActive:YES error:nil];

3.如果将录音和播放同时进行时,我们改选择何种Category?

同时进行播放和录音时,我们需要这样设置:

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord                                            error:nil];     
[[AVAudioSession sharedInstance] setActive:YES error:nil];

需要注意的是,设置成这样的情况下,如果,在录音未开启的情况下,直接进行播放,则会出现,播放音量特别小的情况,我们需要在播放之前,将录音打开。

4.前后台切换

上述的模式,在iOS系统下,是不允许录音和播放在后台状态下同时进行的(PS:语音视频通话是通过CallKit实现的,不用于常规的播放和录音功能)。由此,我们在应用进入后台时就需要关掉其中一个功能。

以后台支持播放为例,在应用将要失活时,先切换模式,再关掉录音功能:

// stopRecording...

// 切换模式

  [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:nil];
  [[AVAudioSession sharedInstance] setActive:YES error:nil];

应用即将进入前台时,切换模式,再开启录音功能:

    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord                                            error:nil];
    [[AVAudioSession sharedInstance] setActive:YES                                          error:nil];

// 延迟恢复,否则会导致AVAudioSession的i/o错误

    [self performSelectorOnMainThread:@selector(startRecording) withObject:nil waitUntilDone:NO];

5.电话中断

电话闹钟的中断也会对,[AVAudioSession sharedInstance] 产生影响。

我们一般场景下会用 下面这个通知进行监控并处理暂停和恢复的工作:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleInterruption:) name:AVAudioSessionInterruptionNotification object:nil]; 
- (void)handleInterruption:(NSNotification*)notification { NSLog(@"interruption info:%@",notification.userInfo); }

但是,当我们在处理第四个场景前后台的情况下,这个通知,在中断的时候会进入,但是电话结束后,不会再接收到中断结束的通知。

原因:

有的app使用了AVCaptureDevice和AVCaptureSession,以进行录音录像操作。为了调优app设置,以更好的进行录音录像,从iOS7开始,在默认情况下,AVCaptureSession会使用app的AVAudioSession,并对其进行修改。这样,设置的中断监听方法会失效。

而电话来电也会使我们的应用接收到 失活的通知,在失活的时候处理了AVAudioSession,就会导致上述通知失效。

解决方案:我这里采用了比较折中的方案,因为我们的需求,对于第四条的处理是必要的。使用的是 CoreTelephony框架下的CTCallCenter对象,来监控电话的 拨入接通、挂断等状态。代码如下:

self.center = [[CTCallCenter alloc] init];
// TODO: 检测到来电后的处理
self.center.callEventHandler = ^(CTCall * call){
    if (call.callState == CTCallStateIncoming ||
        call.callState == CTCallStateConnected ||
        call.callState == CTCallStateDialing)
    {
    }
    else if (call.callState == CTCallStateDisconnected)
    {
    }
};

通过各种打电话的场景测试后,可以实现电话中断恢复功能。

ps:至于闹钟的中断以及siri等其他中断,暂时没有调研和实现。

6.蓝牙车载

终于来到了本文的最后一个部分了,也是最为曲折的一部分。

本来以为车载的车机连接后对于iPhone的播放控制与锁屏控制类似,直接在系统媒体远程控制监控中就能够拿到相应的控制方法回调。

在APPDelegate中加上如下代码:

//监听远程交互方法
- (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
    switch (event.subtype)
    {
            //播放
        case UIEventSubtypeRemoteControlPlay:
            break;
            //停止
        case UIEventSubtypeRemoteControlPause:
            break;
            //下一首
        case UIEventSubtypeRemoteControlNextTrack:
            break;
            //上一首
        case UIEventSubtypeRemoteControlPreviousTrack:
            break;
        default:
          break;
    }
}

事实上,当我们的应用只有简单的播放功能的时候,上述代码的确可以完美的实现车机对于播放的控制功能。但是当应用出于前台的情况下,我们添加上了一直录音的功能的时候,用车机控制播放,就完全没有任何响应了。可以注意到的是,我们看到车机的屏幕上,会显示通话中。查阅了各种资料和文章,都没有找到相关的解决办法和原理解释。

最后,想到了看看有没有其他类似的语音识别及播放功能的应用(iOS)有没有类似的处理,结果调研到百度地图 中的小度 有相关的处理。在它的设置中,找到 语音设置有一个蓝牙连接设置 。两个模式设置 如下:

a.蓝牙设备播报,小度无法唤醒使用(播放体验最佳)

b.蓝牙设备播报,小度唤醒正常使用(车机显示通话中,播报音量可能变小)

由此可以看出,a场景下 录音功能关闭,只有语音播报功能,b场景下,录音功能开启,车机就是会识别到手机设备在录音和播放中,认为就是在通话中,这个是车机本身的限制,无法从应用层进行优化。而且,百度地图的给用的默认选择就是,连接蓝牙的情况下,小度不能唤醒。

综合上面我们协同产品,从交互层面上更改,保证,在连接车机的情况下,能够控制播放。具体处理交互如下:

在应用进入到前台时,检测到连接了蓝牙设备,弹出弹框,让用户选择,继续开启唤醒功能开始,关闭唤醒功能(保证播放控制功能)。继续开启的情况下,车机无法控制播放。

下面是检测是否有输出设备连接的代码(并未找到检查当前是否有连接蓝牙设备的方法):

+ (BOOL)checkIsConnectToBluetooth
{
    BOOL isBluetooth = NO;
    // 找出当前所有支持输入的设备  availableInputs 这里面会出现 iPhone麦克风, 蓝牙耳机1, 蓝牙耳机2 , 三个对象, 在一个数组里.
    NSArray* inputArray = [[AVAudioSession sharedInstance] availableInputs];
    for (AVAudioSessionPortDescription* desc in inputArray)
    {
        if ([desc.portType isEqualToString:AVAudioSessionPortBluetoothLE] ||
            [desc.portType isEqualToString:AVAudioSessionPortBluetoothHFP] ||
            [desc.portType isEqualToString:AVAudioSessionPortBluetoothA2DP])
        {
            isBluetooth = YES;
        }
    }
    return isBluetooth;
}

同时,还需要配合Category的设置:

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord                                          withOptions:AVAudioSessionCategoryOptionAllowBluetooth                                                error:&error];        
[[AVAudioSession sharedInstance] setActive:YES error:&error1];

AVAudioSessionCategoryOptionAllowBluetooth这是必须要添加的,否则上面的方法,连接蓝牙后,在应用即将活跃的监控的时候,是会返回NO,拿不到准确的值。

最后,上面的所有的经验和总结,都是通过各种查阅资料和不断调试得来的,并没有较为科学严谨的理论依据,也没有相关的官方文档的支持。总结出来,只是希望给后续如果有人遇到与我一样的难题时,少走一些弯路,有一些启发,仅此而已。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容