iOS audio dispenser -- AVAudioSession

As a hardware resource, audio output is unique to the iOS system, so how to coordinate the relationship with each app for this scarce hardware?

The solution given by iOS is "AVAudioSession", through which you can control the current context audio resources of the App, such as
plugging and unplugging headphones, answering calls, mixing with other audio data, etc. When you encounter:

  • Is it recording or playing?
  • How does it behave when the system mute button is pressed?
  • Does the sound come from the speaker or the earpiece?
  • How does it behave after plugging and unplugging the headset?
  • How do you behave when the phone/alarm goes off?
  • How do other audio apps behave when launched?
  • ...

In these scenarios, you can consider "AVAudioSession".
������ There was an AudioSession
a long time ago (actually not too long ago - before iOS7) , its function is similar to AVAudioSession, but it has been marked as "Not Applicable" after iOS7, so if Google When it comes to the content of AudioSession instead of the AVAudioSession used, then you can PASS directly. Of course, if you want to be compatible with iOS6 , it is another matter, but now QQ/WeChat requires iOS7. Whether you need to be compatible with iOS6 depends on the bosses. mean.

Session default behavior

  • Playback is possible, but recording is not possible.
  • When the user dials the mute paddle on the mobile phone to the "mute" state, if the audio is playing at this time, the playback content will be muted.
  • When the user presses the lock screen key of the phone or the phone automatically locks the screen, if the audio is playing at this time, the playback will be muted and paused.
  • If other apps such as QQ Music are playing when your app starts playing, other players will be muted and paused.

The default behavior is equivalent to setting Category to "AVAudioSessionCategorySoloAmbient"

Check out the Demo .

 
demo_player

The default Session behavior above can be verified through this player demo.

AVAudioSession

AVAudioSession exists as a singleton entity, via class methods:

+ (AVAudioSession *)sharedInstance;

Get a singleton.

Although the system will activate the only AVAudioSession when the App starts, it is best viewDidLoadto activate it again in your ViewController:

- (BOOL)setActive:(BOOL)active 
        error:(NSError * _Nullable *)outError;

Activate the session by setting activeit to "YES", and deactivate the session by setting it to "NO". error.localizedDescriptionThe return value of BOOL indicates whether it was successful or not. If it fails, you can check the cause of the error through NSError .

Because AVAudioSession will affect the performance of other apps, when the session of your own app is activated, other apps will be deactivated. How to restore the activation state of other app sessions after deactivating your own session?

At this point you can use:

  • (BOOL)setActive:(BOOL)active
    withOptions:(AVAudioSessionSetActiveOptions)options
    error:(NSError * _Nullable *)outError;

The options here can be passed AVAudioSessionSetActiveOptionNotifyOthersOnDeactivationin.

Of course, you can also use otherAudioPlayingvariables to determine in advance whether other apps are currently playing audio.

able to pass:

@property(readonly) NSString *category;

Attribute, get the current Category, such as the above playback, the default is

 NSLog(@"Current Category:%@", [AVAudioSession sharedInstance].category);

output:

Current Category:AVAudioSessionCategorySoloAmbien

Seven Categories

What performance of the App can be controlled by AVAudioSession and how? First of all, AVAudioSession divides the scenes using audio into seven categories. By setting the Session to different categories, you can control:

  • When the app activates the session, will it interrupt the sound of other apps that do not support mixing?
  • When the user triggers the "mute" key on the phone or locks the screen, whether to mute the phone accordingly
  • Whether the current state supports recording
  • Whether the current state supports playback
    ? Each app will be set to the default state mentioned above when it starts, that is, the playback mode in which other apps will be interrupted and the corresponding "mute" button will be played. The following table provides a breakdown of support for each category:
category Whether to mute when pressing "Mute" or lock screen Whether to cause app interruptions that do not support mixing Whether to support recording and playback
AVAudioSessionCategoryAmbient Yes no Only supports playback
AVAudioSessionCategoryAudioProcessing - neither support
AVAudioSessionCategoryMultiRoute no Yes Both recording and playback
AVAudioSessionCategoryPlayAndRecord no Default does not cause Both recording and playback
AVAudioSessionCategoryPlayback no Caused by default for playback only
AVAudioSessionCategoryRecord no Yes for recording only
AVAudioSessionCategorySoloAmbient Yes Yes for playback only

As you can see, the default is the "AVAudioSessionCategorySoloAmbient" category. From the table we can summarize as follows:

  • AVAudioSessionCategoryAmbient: It is only used for playing music, and can be played at the same time as QQ music. For example, if you want to listen to QQ music songs when playing games, then set the background sound of the game to this category. At the same time, when the user locks the screen or mutes the sound, it will also be muted. This category basically uses the background scene of all apps.
  • AVAudioSessionCategorySoloAmbient: It is also only used for playback, but unlike "AVAudioSessionCategoryAmbient", you don't want to listen to QQ music when you use it, such as apps that don't want QQ music to interfere, similar to Rhythm Master. Similarly, when the user locks the screen or mutes the sound, it will also be muted, and if the screen is locked, you will not be able to play Rhythm Master.
  • AVAudioSessionCategoryPlayback: What if I still want to hear the sound when the screen is locked? Using this category, for example, the App itself is a player, and when the App is playing, other similar QQ music cannot be played. So this category is generally used for player apps
  • AVAudioSessionCategoryRecord: With a player, you must have a recorder. For example, for WeChat voice recording, this category is used. Since you want to record quietly, you definitely don't want QQ music, so other playback sounds will be interrupted. Think of the WeChat voice scene, and you will know when to use it.
  • AVAudioSessionCategoryPlayAndRecord: What mode should I use if I want to play and record at the same time? For example, VoIP, making phone calls, PlayAndRecord is specially designed for such scenarios.
  • AVAudioSessionCategoryMultiRoute: Imagine an App for DJs, the mobile phone is connected to HDMI to the speaker to play the current music, and then the next song is played in the headphones. This kind of scene is not understood by ordinary people. This category can support the input and output of multiple devices.
  • AVAudioSessionCategoryAudioProcessing: Mainly used for audio format processing, generally can be used with AudioUnit

Knowing these seven categories, we can set the corresponding categories according to our own needs:

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

Pass in the corresponding list enumeration. If it returns "NO", you can error.localizedDescriptioncheck the reason through NSError.

able to pass:

@property(readonly) NSArray<NSString *> *availableCategories;

properties, check which categories are supported by the current device, and then set them to ensure the legality of the incoming parameters and reduce the possibility of errors.

For example, modify the Demo example above:

    NSLog(@"Current Category:%@", [AVAudioSession sharedInstance].category);
    NSError *error = nil; [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error]; if (nil != error) { NSLog(@"set Option error %@", error.localizedDescription); } NSLog(@"Current Category:%@", [AVAudioSession sharedInstance].category); 

At this time, when playing music, press the mute button again, and you will find that the music continues to play and will not be muted.

category options

The seven categories described above can be considered to have seven main scenarios, and these seven categories certainly cannot meet all the needs of developers. The approach provided by CoreAudio is to first set the tone of one of the seven, and then fine-tune it. CoreAudio provides a few options for fine-tuning each category.
After setting the category, you can pass

@property(readonly) AVAudioSessionCategoryOptions categoryOptions;

properties, to see which options are set in the current category, note that the return value here is AVAudioSessionCategoryOptions, which is actually the "|" operation of multiple options. By default it is 0.

Options Applicable category effect
AVAudioSessionCategoryOptionMixWithOthers AVAudioSessionCategoryPlayAndRecord, AVAudioSessionCategoryPlayback, and AVAudioSessionCategoryMultiRoute Is it possible to mix with other background apps
AVAudioSessionCategoryOptionDuckOthers AVAudioSessionCategoryAmbient, AVAudioSessionCategoryPlayAndRecord, AVAudioSessionCategoryPlayback, and AVAudioSessionCategoryMultiRoute Whether to suppress the sound of other apps
AVAudioSessionCategoryOptionAllowBluetooth AVAudioSessionCategoryRecord and AVAudioSessionCategoryPlayAndRecord Whether to support bluetooth headset
AVAudioSessionCategoryOptionDefaultToSpeaker AVAudioSessionCategoryPlayAndRecord Whether to use hands-free voice by default

At present, there are these main options, all of which have corresponding usage scenarios. In addition, iOS9 also provides the latest iOS10 and two new ones AVAudioSessionCategoryOptionInterruptSpokenAudioAndMixWithOthersare added to support Bluetooth A2DP headset and AirPlay.AVAudioSessionCategoryOptionAllowBluetoothA2DPAVAudioSessionCategoryOptionAllowAirPlay

Let's take a look at the basic effect of each option:

  • AVAudioSessionCategoryOptionMixWithOthers: If you really use a background sound implemented by AVAudioSessionCategoryPlayback, but you want to coexist with QQ music, you can set this option under the AVAudioSessionCategoryPlayback category to achieve coexistence.
  • AVAudioSessionCategoryOptionDuckOthers: In real-time call scenarios, such as QQ Music, when making a video call, you will find that the automatic sound of QQ Music is reduced. At this time, other music apps are suppressed by setting this option.
  • AVAudioSessionCategoryOptionAllowBluetooth: If you want to support Bluetooth headset phone, you need to set this option
  • AVAudioSessionCategoryOptionDefaultToSpeaker: If you want to turn on the hands-free function by default in VoIP mode, you need to set this option

Through the interface:
- (BOOL)setCategory:(NSString *)category withOptions:(AVAudioSessionCategoryOptions)options error:(NSError **)outError
to set options for the current category.

For example in Demo:

    [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];
    if (nil != error) { NSLog(@"set Option error %@", error.localizedDescription); } options = [[AVAudioSession sharedInstance] categoryOptions]; NSLog(@"Category[%@] has %lu options", [AVAudioSession sharedInstance].category, options); 

At this point, first open the QQ music player, and then start playing, you will find that both QQ and our player are playing, and the audio is automatically mixed.

However, in this process, I feel that CoreAudio lacks an setOptioninterface. Since it is already the current Category, why should I specify Category when setting options? ? Confuse. . .

seven modes

Just finished talking about the seven major categories, and now come to the seven major modes. Through the above seven categories, we basically cover the common main scenes, in each main scene can be fine-tuned by Option. To this end, CoreAudio provides seven more common fine-tuned sub-scenarios. are called the modes of each category.

model Applicable categories Scenes
AVAudioSessionModeDefault All categories default mode
AVAudioSessionModeVoiceChat AVAudioSessionCategoryPlayAndRecord VoIP
AVAudioSessionModeGameChat AVAudioSessionCategoryPlayAndRecord Game recording, set automatically by GKVoiceChat, no need to call manually
AVAudioSessionModeVideoRecording AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord While recording video
AVAudioSessionModeMoviePlayback AVAudioSessionCategoryPlayback video playback
AVAudioSessionModeMeasurement AVAudioSessionCategoryPlayAndRecord AVAudioSessionCategoryRecord AVAudioSessionCategoryPlayback smallest system
AVAudioSessionModeVideoChat AVAudioSessionCategoryPlayAndRecord video call

Each mode has its applicable category, so there are not "seven, seven, forty-nine" combinations. If the current category does not have this mode, the setting is unsuccessful. After setting up Category, you can pass:

@property(readonly) NSArray<NSString *> *availableModes;

properties, check which properties it supports, and check the validity.

Let's take a look at the specific application:

  • AVAudioSessionModeDefault: This mode is the default for each category. If you want to restore it, set it to this mode.
  • AVAudioSessionModeVoiceChat: It is mainly used in VoIP scenarios. At this time, the system will select the best input device. For example, plug in the headset and use the microphone on the headset to capture. There is a side effect at this time, he will set the category option to "AVAudioSessionCategoryOptionAllowBluetooth" to support Bluetooth headsets.
  • AVAudioSessionModeVideoChat : 主要用于视频通话,比如QQ视频、FaceTime。时系统也会选择最佳的输入设备,比如插上耳机就使用耳机上的麦克风进行采集并且会设置类别的选项为"AVAudioSessionCategoryOptionAllowBluetooth" 和 "AVAudioSessionCategoryOptionDefaultToSpeaker"。
  • AVAudioSessionModeGameChat : 适用于游戏App的采集和播放,比如“GKVoiceChat”对象,一般不需要手动设置

另外几种和音频APP关系不大,一般我们只需要关注VoIP或者视频通话即可。

通过调用:

- (BOOL)setMode:(NSString *)mode error:(NSError **)outError

可以在设置Category之后再设置模式。

当然,这些模式只是CoreAduio总结的,不一定完全满足要求,对于具体的模式,在iOS10中还是可以微调的。通过接口:

  • (BOOL)setCategory:(NSString *)category mode:(NSString *)mode options:(AVAudioSessionCategoryOptions)options error:(NSError **)outError

但是在iOS9及以下就只能在Category上调了,其实本质是一样的,可以认为是个API糖,接口封装。

系统中断响应

上面说的这些Category啊、Option啊以及Mode都是对自己作为播放主体时的表现,但是假设,现在正在播放着,突然来电话了、闹钟响了或者你在后台放歌但是用户启动其他App用上面的方法影响的时候,我们的App该如何表现呢?最常用的场景当然是先暂停,待恢复的时候再继续。那我们的App要如何感知到这个终端以及何时恢复呢?

AVAudioSession提供了多种Notifications来进行此类状况的通知。其中将来电话、闹铃响等都归结为一般性的中断,用
AVAudioSessionInterruptionNotification来通知。其回调回来的userInfo主要包含两个键:

  • AVAudioSessionInterruptionTypeKey: 取值为AVAudioSessionInterruptionTypeBegan表示中断开始,我们应该暂停播放和采集,取值为AVAudioSessionInterruptionTypeEnded表示中断结束,我们可以继续播放和采集。
  • AVAudioSessionInterruptionOptionKey: 当前只有一种值AVAudioSessionInterruptionOptionShouldResume表示此时也应该恢复继续播放和采集。

而将其他App占据AudioSession的时候用AVAudioSessionSilenceSecondaryAudioHintNotification来进行通知。其回调回来的userInfo键为:

AVAudioSessionSilenceSecondaryAudioHintTypeKey

可能包含的值:

  • AVAudioSessionSilenceSecondaryAudioHintTypeBegin: 表示其他App开始占据Session
  • AVAudioSessionSilenceSecondaryAudioHintTypeEnd: 表示其他App开始释放Session

外设改变

除了其他App和系统服务,会对我们的App产生影响以外,用户的手也会对我们产生影响。默认情况下,AudioSession会在App启动时选择一个最优的输出方案,比如插入耳机的时候,就用耳机。但是这个过程中,用户可能拔出耳机,我们App要如何感知这样的情况呢?

同样AVAudioSession也是通过Notifications来进行此类状况的通知。

假设有这样的App:

route_change
route_change

最开始在录音时,用户插入和拔出耳机我们都停止录音,这里通过Notification来通知有新设备了,或者设备被退出了,然后我们控制停止录音。或者在播放时,当耳机被拔出出时,Notification给了通知,我们先暂停音乐播放,待耳机插回时,在继续播放。

在NSNotificationCenter中对AVAudioSessionRouteChangeNotification进行注册。在其userInfo中有键:

  • AVAudioSessionRouteChangeReasonKey : 表示改变的原因
枚举值 意义
AVAudioSessionRouteChangeReasonUnknown 未知原因
AVAudioSessionRouteChangeReasonNewDeviceAvailable 有新设备可用
AVAudioSessionRouteChangeReasonOldDeviceUnavailable 老设备不可用
AVAudioSessionRouteChangeReasonCategoryChange 类别改变了
AVAudioSessionRouteChangeReasonOverride App重置了输出设置
AVAudioSessionRouteChangeReasonWakeFromSleep 从睡眠状态呼醒
AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory 当前Category下没有合适的设备
AVAudioSessionRouteChangeReasonRouteConfigurationChange Rotuer的配置改变了
  • AVAudioSessionSilenceSecondaryAudioHintTypeKey: 和上面的中断意义意义。

总结:

AVAudioSession构建了一个音频使用生命周期的上下文。当前状态是否可以录音、对其他App有怎样的影响、是否响应系统的静音键、如何感知来电话了等都可以通过它来实现。尤为重要的是AVAudioSession不仅可以和AVFoundation中的AVAudioPlyaer/AVAudioRecorder配合,其他录音/播放工具比如AudioUnit、AudioQueueService也都需要他进行录音、静音等上下文配合。

文中Demo参见GitHub

参考文档

  1. Audio Session Programming Guide
  2. AVAudioSession Class Reference
  3. Audio Session Services Reference



作者:CZ_iOS
链接:https://www.jianshu.com/p/3e0a399380df
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324896182&siteId=291194637