编程技术记录

世界你好!

内容来自互联网,使用了google翻译。

简介

​ 在iOS中,音频焦点是操作系统为了管理音频硬件而引入的概念,是指App对音频硬件在时间纬度上的使用控制(不是使用权限,使用权限需要App单独申请);App可以独占使用,也可以共享使用—-独占/共享音频焦点。

​ 独占音频焦点,则只能有一个App使用音频硬件;共享音频焦点,则多个App可以同时使用音频硬件,通常共享音频焦点会导致多个App播放的音频被混音。

​ iOS中还有个音频会话的概念,在一个App中只有一个音频会话。这里需要注意下,iOS系统只有一个音频焦点,所以App要想使用音频硬件,需要使用音频会话来抢占音频焦点。

​ 抢占音频焦点示例:

// 一个App内所有场景/任务都共享同一个音频会话
AVAudioSession *session = [AVAudioSession sharedInstance]; //获取共享单例

//设置音频硬件使用模式
NSError *error = nil;
if ([session setCategory:AVAudioSessionCategoryAmbient error:&error] == NO)
{
    NSLog(@"%@",error); //发生错误
    return;
}
//抢占音频焦点
error = nil;
if ([session setActive:YES error:&error] == NO)
{
    NSLog(@"%@",error); //发生错误
    return;
}

​ 上述代码中,先设置AVAudioSessionCategoryAmbient模式,表示应用会随着静音键和屏幕关闭而静音,并且不会中止其它应用播放声音(可以和其它自带应用如iPod,safari等同时播放声音),然后再使用setActive抢占音频焦点。

释放音频焦点

​ 我们的App抢占音频焦点后,可能会导致其他App失去音频焦点,所以当我们使用完音频硬件时,应将音频焦点释放出来。

// 一个App内所有场景/任务都共享同一个音频会话
AVAudioSession *session = [AVAudioSession sharedInstance]; //获取共享单例

//释放音频焦点
NSError *error = nil;
if ([session setActive:NO error:&error] == NO)
{
    NSLog(@"%@",error); //发生错误
    return;
}

释放音频焦点还可以这样:

//也可以这样释放音频焦点, 为了确保之前被打断的正在播放音频的App能收到resume消息。
if ([session setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error] == NO )
{
    NSLog(@"%@",error); //发生错误
    return;
}

使用哪种方式,取决于App的音频硬件使用模式。

音频硬件使用模式

设置接口有两个,都可以设置音频使用模式。相关参数为AVAudioSessionCategory和AVAudioSessionCategoryOptions。需要注意的是,AVAudioSessionCategoryOptions是位枚举,可以组合使用。

- (BOOL)setCategory:(AVAudioSessionCategory)category error:(NSError **)outError;

- (BOOL)setCategory:(AVAudioSessionCategory)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError;

AVAudioSessionCategory

  • AVAudioSessionCategoryAmbient

    使用这个category的应用会随着静音键和屏幕关闭而静音。并且不会中止其它应用播放声音,可以和其它自带应用如iPod,safari等同时播放声音。

    该Category无法在后台播放声音。

  • AVAudioSessionCategorySoloAmbient

    类似于AVAudioSessionCategoryAmbient ,不同之处在于它会中止其它应用播放声音。 这个category为默认category。

    该Category无法在后台播放声音。

  • AVAudioSessionCategoryPlayback

    使用这个category的应用不会随着静音键和屏幕关闭而静音。默认情况下,其他不支持混音(nonmixable)的应用将被中止播放声音;也可以设置AVAudioSessionCategoryOptionMixWithOthers,让自己的应用支持混音,这时不会中止其他不支持混音的应用播放声音;

    该Category可在后台播放声音。

  • AVAudioSessionCategoryRecord

    用于需要录音的场景,设置该category后,除了来电铃声,闹钟或日历提醒之外的其它系统声音都不会被播放。

    该Category只提供单纯录音功能,可以在后台录音。

  • AVAudioSessionCategoryPlayAndRecord

    在播放和录音同时存在的场景,这个category是唯一选择,应用不会随着静音键和屏幕关闭而静音。默认情况下,其他不支持混音(nonmixable)的应用将被中止播放声音;也可以设置AVAudioSessionCategoryOptionMixWithOthers,让自己的应用支持混音,这时不会中止其他不支持混音的应用播放声音
    该Category可以在后台录音、播放声音。

  • AVAudioSessionCategoryMultiRoute

    非专业场景基本上不会使用。支持多个音频路由同时工作。例如,手机扬声器播放歌曲A,同时连接该手机的耳机中播放歌曲B。

AVAudioSessionCategoryOptions

  • AVAudioSessionCategoryOptionMixWithOthers

    播放声音时,支持和其他App混音。

    仅在AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryPlayback 或 AVAudioSessionCategoryMultiRoute 时,才可以设置这个选项。

    如果音频使用模式设置为 AVAudioSessionCategoryAmbient,则会自动设置此选项。 同样,设置 AVAudioSessionCategoryOptionDuckOthers 或 AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers 选项也会启用此选项。

  • AVAudioSessionCategoryOptionDuckOthers

    播放声音时,支持和其他App混音,但是会降低其他App播放的声音。可以用在偶尔播放音频的场景,如导航播报。

    仅在AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryPlayback 或 AVAudioSessionCategoryMultiRoute 时,才可以设置此选项。

  • AVAudioSessionCategoryOptionAllowBluetooth

    支持蓝牙设备作为输入(这应指录音)。

    仅 AVAudioSessionCategoryPlayAndRecord 或 AVAudioSessionCategoryRecord 时,才能设置此选项。

  • AVAudioSessionCategoryOptionDefaultToSpeaker

    在没有其他音频设备时,使用内置扬声器播。

    其他音频设备如耳机,会影响该选项,当耳机插入时,音频路由会转向耳机麦克风/耳机;拔出耳机时,音频路由则恢复到内置麦克风/内置扬声器。

    只有在使用 AVAudioSessionCategoryPlayAndRecord 时才能设置此选项

  • AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthers

    iOS9及以上可用,播放音频时暂停来自其他App音频播放(注意,暂停而不是中止),播放完成释放音频焦点后,系统会恢复其他App的音频播放。

    可以用在偶尔播放音频的场景,如导航播报。

    仅当 AVAudioSessionCategoryPlayAndRecord、AVAudioSessionCategoryPlayback 或 AVAudioSessionCategoryMultiRoute 时,才能设置此选项。

  • AVAudioSessionCategoryOptionAllowBluetoothA2DP

    iOS10及以上可用,支持A2DP的蓝牙设备。

    如果将应用程序配置为 AVAudioSessionCategoryAmbient、AVAudioSessionCategorySoloAmbient 或 AVAudioSessionCategoryPlayback 类别,系统会自动路由到 A2DP 端口。

    从 iOS10开始,使用 AVAudioSessionCategoryPlayAndRecord 的应用程序允许将输出路由到配对的蓝牙 A2DP 设备。

    要启用此行为,请在设置音频会话的类别时传递此类别选项。

    使用 AVAudioSessionCategoryMultiRoute 或 AVAudioSessionCategoryRecord 时,会隐式清除此选项(将不可使用A2DP设备)

  • AVAudioSessionCategoryOptionAllowAirPlay

    iOS10及以上可用,支持AirPlay设备。

    如果设置了 AVAudioSessionCategoryPlayAndRecord,那么需要显式设置此选项才支持AirPlay。

    对于其他大多数音频使用模式,系统会隐式设置此选项;例外是AVAudioSessionCategoryMultiRoute 和 AVAudioSessionCategoryRecord ,这两个会隐式清除此选项。

  • AVAudioSessionCategoryOptionOverrideMutedMicrophoneInterruption

    iOS14.5及以上可用,出于隐私考虑,系统在静音时使内置麦克风会中断录制。

    当使用AVAudioSessionCategoryPlayAndRecord且开启了该选项,那么在静音时,播放将照常继续,但是麦克风中断录制(硬件会生成样本缓冲区,但值为 0)。

音频焦点使用原则

App使用音频焦点的原则:

  • 谁使用谁抢占,使用完毕后要释放;
  • 最小限度的音频模式。不要申请不必要的硬件资源,例如在听音乐场景申请麦克风是不必要的。
// 一个App内所有场景/任务都共享同一个音频会话
AVAudioSession *session = [AVAudioSession sharedInstance]; //获取共享单例

//当前音频模式
AVAudioSessionCategory lastCategory =  session.category;
AVAudioSessionCategoryOptions lastCategoryOptions = session.categoryOptions;

if(音频模式是否满足场景需要)
{
    //需要修改音频模式
    NSError *error = nil;
    if ([session setCategory:按需要设置 withOptions:按需要设置 error:&error] == NO)
    {
        //发生错误,异常处理
        NSLog(@"%@",error); 
        return;
    }

    //抢占音频焦点
    error = nil;
    if ([session setActive:YES error:&error] == NO)
    {
        //发生错误,异常处理
        NSLog(@"%@",error); 
        return;
    }
}

/// 使用音频资源

///使用完毕后

if(是否需要恢复音频模式)
{
    //需要恢复
      NSError *error = nil;
    if ([session setCategory:lastCategory withOptions:lastCategoryOptions error:&error] == NO)
    {
        //发生错误,异常处理
        NSLog(@"%@",error); 
        return;
    }
}

/// 没有其他场景需要使用音频焦点了
//释放音频焦点
NSError *error = nil;
if ([session setActive:YES error:&error] == NO)
{
    //发生错误,异常处理
    NSLog(@"%@",error); 
    return;
}
//也可以这样释放音频焦点,这时系统会通知其他App中断结束,可以恢复音频焦点了
//if ([session setActive:NO withOptions:AVAudioSessionSetActiveOptionNotifyOthersOnDeactivation error:&error] == NO )
//{
//    NSLog(@"%@",error); //发生错误
//    return;
//}

需要注意的是,对于SDK开发来说,因为自身不能完全掌控宿主App,所以使用音频资源时应每次都要抢占音频焦点;而对音频焦点的释放,应提供释放音频焦点的默认实现,同时建议增加接口让供宿主App实现释放动作,以增加宿主App对音频焦点的掌控能力。

© Beli. All Rights Reserved.