【问题标题】:Detecting active AVAudioSessions on iOS device在 iOS 设备上检测活动的 AVAudioSessions
【发布时间】:2013-12-04 09:22:45
【问题描述】:

我正在尝试确定这是否可能 - 我的应用激活了一个音频会话,该会话初始化为:

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

我希望能够了解来自另一个应用程序或操作系统的附加音频会话何时正在播放。

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

有没有办法在不使用私有 API 的情况下实现这一点?

提前致谢。

【问题讨论】:

    标签: ios objective-c cocoa-touch avaudiosession


    【解决方案1】:

    iOS 6.0 以来,您管理应用程序音频会话的方式发生了一些重大变化,首先值得一提。在 iOS 6.0 之前,您将使用 AVAudioSessionAudioSessionServices 类,分别合并委托和属性监听。从 iOS 6.0 开始使用 AVAudioSession 类并合并通知。

    以下内容适用于iOS 6.0 及以上版本。

    判断您的应用程序沙箱之外的其他音频是否正在播放使用 -

    // 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: - 它们将接收一个 NSNotification 对象,该对象具有一个名为 userInfo 类型为 NSDictionary 的属性,您可以读取该属性以帮助应用程序具有的任何条件。

    - (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 中,就无需轮询任何内容。从那里开始对您的应用程序音频会话的任何更改都将通过这两个主要通知获知。根据 switch 中包含的情况,将 NSLog 语句替换为您的代码需要执行的操作。

    您可以在AVAudioSession 类参考文档中找到有关AVAudioSessionInterruptionTypeKeyAVAudioSessionRouteChangeReasonKey 的更多信息。

    对于冗长的回答,我深表歉意,但我认为 iOS 中的音频会话管理相当繁琐,而且 Apple 的音频会话编程指南在撰写本文时并未包含使用通知进行中断处理的代码示例。

    【讨论】:

    • 感谢您提供非常有见地和详细的回答。我尝试注册这些通知,然后通过外部应用程序播放音乐(例如设置->铃声),但它们从未被调用。有什么想法吗?
    • 通知发布在主线程上。要确认这一点,请尝试构建到设备,然后将您的应用程序设置为后台。然后打开音乐应用程序并开始播放音轨。现在将您的应用程序带回前台,您应该会收到路线更改通知。音频会话行为不仅取决于您的音频会话,还取决于所有相关人员及其当前状态。我使用音乐应用程序进行测试,因为我知道它的音频会话允许它与其他人一起播放,而我不确定设置应用程序是否允许它的行为相同。也不要在模拟器中测试这个。
    • 谢谢,我会对此进行测试并报告结果
    • 请注意,最近更新的AurioTouch(不是AurioTouch2)现在包含了很多上面的代码。另请注意,从 iOS 7 开始,现在可以编写 InterApp 音频。
    • 如果用户打入电话或挂断电话,是否会触发路由改变或中断。我希望能够检测麦克风是否可用于录制视频,以及用户是否正在通话,麦克风是否不可用。考虑到电话,检测麦克风可用性的最佳方法是什么。
    【解决方案2】:

    您可以像这样检查其他音频是否正在播放:

    UInt32 otherAudioIsPlaying;
    UInt32 propertySize = sizeof (otherAudioIsPlaying);
    AudioSessionGetProperty (kAudioSessionProperty_OtherAudioIsPlaying, &propertySize, &otherAudioIsPlaying );
    
    [self handleIfAudioIsPlaying: otherAudioIsPlaying];
    

    然后您可以添加一个循环并每隔 X 秒检查一次是否有变化。

    【讨论】:

    • 谢谢,虽然我想避免使用“轮询”方法。我希望在发生这种情况时得到通知。
    • 也许这会有所帮助:AudioSessionAddPropertyListener(kAudioSessionProperty_OtherAudioIsPlaying, callBack, (__bridge void *)(self));
    • 听起来很有趣,我试试看
    • AudioSessionAddPropertyListener 已弃用。有什么选择吗?
    • 感谢@BlackMouse 的帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-09
    • 2012-12-31
    • 1970-01-01
    相关资源
    最近更新 更多