检测iOS设备上活动的AVAudioSessions

27

我想知道这是否可能 - 我的应用程序激活了一个已初始化为以下内容的音频会话:

[[[AVAudioSession alloc] init] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:&error];

我希望能够理解当其他应用程序或操作系统发出的音频会话正在播放时。

我知道可以实现委托方法 beginInterruption:endInterruption,但由于我使用的是AVAudioSessionCategoryOptionMixWithOthers选项,这些方法不会被调用。

是否有一种方法可以在不使用私有API的情况下实现此目的?

提前致谢。

2个回答

70
自从以来,你管理应用程序的音频会话的方式发生了一些重大变化,并值得首先简要提及。在之前,您将利用AVAudioSessionAudioSessionServices类,分别包括委托和属性监听。从开始,请使用AVAudioSession类并融入通知。
以下内容适用于及以上版本。
要判断应用程序沙盒外是否播放其他音频,请使用 -
// query if other audio is playing
BOOL isPlayingWithOthers = [[AVAudioSession sharedInstance] isOtherAudioPlaying];
// test it with...
(isPlayingWithOthers) ? NSLog(@"other audio is playing") : NSLog(@"no other audio is playing");

关于中断处理,您需要观察AVAudioSessionInterruptionNotificationAVAudioSessionRouteChangeNotification。因此,在管理音频会话的类中,您可以添加以下内容-应在应用程序生命周期的开始调用一次,并不要忘记在同一类的dealloc方法中删除观察者。

// ensure we already have a singleton object
    [AVAudioSession sharedInstance];
    // register for notifications
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(interruption:)
                                                 name:AVAudioSessionInterruptionNotification
                                               object:nil];
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(routeChange:)
                                                 name:AVAudioSessionRouteChangeNotification
                                               object:nil];

最后,添加以下选择器interruption:routeChange: - 这些将接收到一个名为userInfo的类型为NSDictionary的属性NSNotification对象,您可以读取它以辅助应用程序的任何条件。

- (void)interruption:(NSNotification*)notification {
// get the user info dictionary
NSDictionary *interuptionDict = notification.userInfo;
// get the AVAudioSessionInterruptionTypeKey enum from the dictionary
NSInteger interuptionType = [[interuptionDict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue];
// decide what to do based on interruption type here...
switch (interuptionType) {
    case AVAudioSessionInterruptionTypeBegan:
        NSLog(@"Audio Session Interruption case started.");
        // fork to handling method here...
        // EG:[self handleInterruptionStarted];
        break;

    case AVAudioSessionInterruptionTypeEnded:
        NSLog(@"Audio Session Interruption case ended.");
        // fork to handling method here...
        // EG:[self handleInterruptionEnded];
        break;

    default:
        NSLog(@"Audio Session Interruption Notification case default.");
        break;
} }

同样地...

- (void)routeChange:(NSNotification*)notification {

NSDictionary *interuptionDict = notification.userInfo;

NSInteger routeChangeReason = [[interuptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue];

switch (routeChangeReason) {
    case AVAudioSessionRouteChangeReasonUnknown:
        NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonUnknown");
        break;

    case AVAudioSessionRouteChangeReasonNewDeviceAvailable:
        // a headset was added or removed
        NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonNewDeviceAvailable");
        break;

    case AVAudioSessionRouteChangeReasonOldDeviceUnavailable:
        // a headset was added or removed
        NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonOldDeviceUnavailable");
        break;

    case AVAudioSessionRouteChangeReasonCategoryChange:
        // called at start - also when other audio wants to play
        NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonCategoryChange");//AVAudioSessionRouteChangeReasonCategoryChange
        break;

    case AVAudioSessionRouteChangeReasonOverride:
        NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonOverride");
        break;

    case AVAudioSessionRouteChangeReasonWakeFromSleep:
        NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonWakeFromSleep");
        break;

    case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory:
        NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory");
        break;

    default:
        break;
} }
只要在您的应用程序生命周期的根视图控制器的viewDidLoad中检查应用程序音频会话的状态,就无需轮询任何内容。从那时起对您的应用程序音频会话的任何更改都将通过这两个主要通知进行通知。将NSLog 语句替换为基于开关中包含的情况执行的代码。
您可以在AVAudioSession类参考文档中找到有关AVAudioSessionInterruptionTypeKeyAVAudioSessionRouteChangeReasonKey的更多信息。
很抱歉我的答案有点长,但我认为iOS中的音频会话管理相当麻烦,在撰写本文时,苹果的音频会话编程指南不包括使用通知进行中断处理的代码示例。

非常感谢您提供如此深入和详细的答案。我已经尝试注册这些通知,然后通过外部应用程序播放音乐(例如设置->铃声),但它们从未被调用过。有什么想法吗? - Stavash
通知会发布在主线程上。要确认这一点,请尝试构建到设备,然后将应用程序置于后台。然后打开音乐应用并开始播放音频轨道。现在将您的应用程序带回前台,您应该会收到路由更改通知。音频会话行为不仅取决于您的音频会话,还取决于所有相关方及其当前状态。我使用音乐应用进行测试,因为我知道它的音频会话允许它与其他应用程序一起播放,而我不确定设置应用程序是否允许它表现相同。此外,请勿在模拟器中测试此功能。 - Bamsworld
谢谢,我会测试并回报结果。 - Stavash
请注意,最近更新的AurioTouch(不是AurioTouch2)现在包括了很多以上的代码。另外,请注意,从iOS 7开始,现在可以编写InterApp音频。 - TJA
3
如果用户接听电话或挂断电话,路线是否会更改或中断?我希望能够检测麦克风是否可用于录制视频,并且如果用户正在通话,则麦克风不可用。考虑到电话通话,最佳的检测麦克风可用性的方法是什么? - kevin
在中断后是否需要重新激活会话?例如,当中断结束时,通知处理程序中是否应该有代码来[[AVAudioSession sharedInstance] setActive:YES error:&theError] - IMFletcher

5

您可以通过以下方式检查是否正在播放其他音频:

UInt32 otherAudioIsPlaying;
UInt32 propertySize = sizeof (otherAudioIsPlaying);
AudioSessionGetProperty (kAudioSessionProperty_OtherAudioIsPlaying, &propertySize, &otherAudioIsPlaying );

[self handleIfAudioIsPlaying: otherAudioIsPlaying];

然后您可以添加循环,并每 X 秒检查是否有更改发生。


1
谢谢,虽然我想避免“轮询”的方法。我希望在发生这种情况时得到通知。 - Stavash
2
AudioSessionAddPropertyListener已被弃用。有任何替代方法吗? - Stavash
此外,当我在设备上播放音频时,回调函数没有被调用。 - Stavash
那我恐怕没有更多的想法了。也许没有私有API是不可能的,你只能使用我下面发布的丑陋轮询方法,并向苹果提出功能请求。 - BlackMouse
1
感谢 @BlackMouse 的帮助。 - Stavash
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接