通话后,后台音频不会恢复

时间:2021-04-10 00:08:18

My background audio works fine almost all the time. Screen locked, or mute switch on. But when the user has the application in the background, and it receives a call, even if the user doesn't answer the call, the background audio does not resumes after the interruption ends.

我的背景音频几乎一直都很好用。屏幕锁定或静音开启。但是当用户在后台运行应用程序并且它接收到呼叫时,即使用户没有应答呼叫,在中断结束后也不会恢复背景音频。

The Music app properly resumes background audio if it was interrupted.

音乐应用程序如果被中断,则会正确恢复背景音频。

Am I missing some property or do I need to have a callback or set a background task to continue background audio execution after an interruption? Or this is something that we can't do?

我是否遗漏了一些属性,或者我是否需要进行回调或设置后台任务以在中断后继续执行后台音频执行?或者这是我们做不到的事情?

6 个解决方案

#1


22  

We are resuming our audio after an outbound call and inbound call while the app is in the background.

当应用程序在后台时,我们将在出站呼叫和入站呼叫后恢复音频。

We play audio with AVAudioPlayer and listen to the AVAudioSessionInterruptionNotification. Apple automatically pauses the AVAudioPlayer for you on an interruption and when you tell it to resume after you receive the interruption is over, Apple will set your session active again. See Table 3-2 from Handling Audio Interruptions on recommendations if you are using other types of audio technologies.

我们使用AVAudioPlayer播放音频并收听AVAudioSessionInterruptionNotification。 Apple会在中断时自动暂停AVAudioPlayer,当您在收到中断后告诉它恢复时,Apple会再次激活您的会话。如果您正在使用其他类型的音频技术,请参阅处理音频中断的表3-2。

Subscribe to the notification:

订阅通知:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAudioSessionEvent:) name:AVAudioSessionInterruptionNotification object:nil];

Handle the notification:

处理通知:

- (void) onAudioSessionEvent: (NSNotification *) notification
{
    //Check the type of notification, especially if you are sending multiple AVAudioSession events here
    if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
        NSLog(@"Interruption notification received!");

        //Check to see if it was a Begin interruption
        if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
            NSLog(@"Interruption began!");

        } else {
            NSLog(@"Interruption ended!");
            //Resume your audio
        }
    }
}

#2


5  

In my experience I found I had to "wait a second or two" before attempting to reopen the audio device. I think that after the OS switches back to your app from the call, the phone app is still shutting down.

根据我的经验,我发现在尝试重新打开音频设备之前我必须“等待一两秒钟”。我认为在操作系统从通话切换回您的应用程序后,手机应用程序仍在关闭。

something like this when you return to foreground after knowing your audio session has been stopped:

在知道音频会话已经停止后返回前台时会出现类似的情况:

            dispatch_time_t restartTime = dispatch_time(DISPATCH_TIME_NOW, 
                                                      1.5LL * NSEC_PER_SEC);

            dispatch_after(restartTime, dispatch_get_global_queue(0, 0), ^{ 
                [audioSystem restart]; 
            });

#3


2  

I am using Xamarin so this is C# but the following code is working for me. I first set that my AppDelegate implements the AVAudioSession delegate methods by including IAVAudioSessionDelegate

我正在使用Xamarin,所以这是C#,但以下代码对我有用。我首先通过包含IAVAudioSessionDelegate来设置我的AppDelegate实现AVAudioSession委托方法

            public partial class AppDelegate: UIApplicationDelegate, IAVAudioSessionDelegate

I added a variable to the AppDelegate class for the audio session

我在AppDelegate类中为音频会话添加了一个变量

            public static AVAudioSession AudioSession;

In the override of the FinishedLaunching method:

在FinishedLaunching方法的重写中:

            AudioSession = AVAudioSession.SharedInstance();
            AudioSession.Delegate = this;
            error = AudioSession.SetCategory( AVAudioSessionCategory.Playback, AVAudioSessionCategoryOptions.DuckOthers );
            AudioSession.SetMode( new NSString( "AVAudioSessionModeMoviePlayback" ), out error );

The two relevant delegate methods in AppDelegate.cs are:

AppDelegate.cs中的两个相关委托方法是:

            [Export( "beginInterruption" )]
            public void BeginInterruption()
            {
                 PlayerViewController.BeginSessionInterruption();
            }

            [Export( "endInterruptionWithFlags:" )]
            public void EndInterruption( AVAudioSessionInterruptionFlags flags )
            {  // ignore the flags so we're not dependent on the interrupting event saying that we can resume
                 PlayerViewController.ResumeAfterSessionInterruption();
            }

My AppDelegate also has an override of OnActivated to enable the video tracks if it's a video asset, and an override of DidEnterBackground to disable the media's video tracks but still play the audio.

我的AppDelegate还覆盖了OnActivated以启用视频轨道(如果它是视频资源),并覆盖DidEnterBackground以禁用媒体的视频轨道但仍播放音频。

In the PlayerViewController.BeginSessionInterruption() method, I can't look at Player.Rate to see if the player was running at the time, because the interrupting alarm or phone call has already paused the player. From the "Responding to Interruptions" section of Apple's Audio Session Programming Guide, with my emphasis added:

在PlayerViewController.BeginSessionInterruption()方法中,我无法查看Player.Rate以查看播放器当时是否正在运行,因为中断警报或电话呼叫已暂停播放器。从Apple的音频会话编程指南的“响应中断”部分,我的重点是:

  1. Your app is active, playing back audio.
  2. 您的应用处于活动状态,播放音频。
  3. A phone call arrives. The system activates the phone app’s audio session.
  4. 电话到了。系统会激活手机应用程序的音频会话。
  5. The system deactivates your audio session. At this point, * playback in your app has stopped *.
  6. 系统将停用您的音频会话。此时,*应用中的播放已停止*。
  7. The system invokes your interruption listener callback function indicating that your session has been deactivated. ...
  8. 系统调用您的中断侦听器回调函数,指示您的会话已停用。 ...

My PlayerViewController's Play button has a Paused property to toggle between Play and Pause and draw the appropriate button image. So instead of checking Player.Rate, I look to see if the button's Paused property is false:

我的PlayerViewController的“播放”按钮具有“暂停”属性,可在“播放”和“暂停”之间切换,并绘制相应的按钮图像。因此,我不是检查Player.Rate,而是查看按钮的Paused属性是否为false:

            public void BeginSessionInterruption()
            {
                 PlayerWasRunningOnInterruption = !btnPlay.Paused;
                 TogglePlayPause( true );  // put the Play button into the Paused state to agree with the player being stopped
            }

            public void ResumeAfterSessionInterruption()
            {
                NSError error;
                AppDelegate.AudioSession.SetActive( true, AVAudioSessionSetActiveOptions.NotifyOthersOnDeactivation, out error );  // always reactivate the audio session

                if ( PlayerWasRunningOnInterruption )
                {
// rewind a little bit
// call btnPlayClick to resume the playback as if the user pressed the Play button
                }
            }

#4


0  

if you only detect the call status you can use CTCallCenter, but if you want to detect the interruption (include the call interruption),you can use AVAudioSessionInterruptionNotification, and if you want to support background you should add code like this:

如果您只检测呼叫状态,则可以使用CTCallCenter,但如果要检测中断(包括呼叫中断),则可以使用AVAudioSessionInterruptionNotification,如果要支持后台,则应添加如下代码:

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

before you resume play music in background.hope this can help.

在你继续在background.hope中播放音乐之前,这可以提供帮助。

#5


0  

Only two options works for me:

只有两个选项适合我:

  1. reload AVPlayer or AVAudioPlayer after interruption end

    中断结束后重新加载AVPlayer或AVAudioPlayer

    func interruptionNotification(_ notification: Notification) {
    guard let type = notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? UInt,
      let interruption = AVAudioSessionInterruptionType(rawValue: type) else {
        return
    }
    if interruption == .ended && playerWasPlayingBeforeInterruption {
      player.replaceCurrentItem(with: AVPlayerItem(url: radioStation.url))
      play()
    }
    

    }

    }

  2. use

    使用

AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: .mixWithOthers)

AVAudioSession.sharedInstance()。setCategory(AVAudioSessionCategoryPlayback,with:.mixWithOthers)

#6


0  

Look at:

看着:

https://developer.apple.com/documentation/avfoundation/avaudiosession#//apple_ref/doc/uid/TP40008240-CH1-DontLinkElementID_3

https://developer.apple.com/documentation/avfoundation/avaudiosession#//apple_ref/doc/uid/TP40008240-CH1-DontLinkElementID_3

https://developer.apple.com/documentation/avfoundation/avaudiosession/responding_to_audio_session_interruptions

https://developer.apple.com/documentation/avfoundation/avaudiosession/responding_to_audio_session_interruptions

Code:

码:

private func setupAudioSession() {
    do {

        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: .mixWithOthers)
        try AVAudioSession.sharedInstance().setActive(true)

        setupAudioNotifications()

    } catch {
        print(error)
    }
}

private func setupAudioNotifications() {
    NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption), name: .AVAudioSessionInterruption, object: nil)
}

@objc func handleInterruption(notification: Notification) {

    guard let userInfo = notification.userInfo,
        let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
        let type = AVAudioSessionInterruptionType(rawValue: typeValue) else {
            return
    }

    if type == .began {
        // Interruption began, take appropriate actions
        Player.shared.stop()

    } else if type == .ended {
        if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
            let options = AVAudioSessionInterruptionOptions(rawValue: optionsValue)
            if options.contains(.shouldResume) {
                // Interruption Ended - playback should resume
                Player.shared.start()
            } else {
                // Interruption Ended - playback should NOT resume
            }
        }
    }
}

#1


22  

We are resuming our audio after an outbound call and inbound call while the app is in the background.

当应用程序在后台时,我们将在出站呼叫和入站呼叫后恢复音频。

We play audio with AVAudioPlayer and listen to the AVAudioSessionInterruptionNotification. Apple automatically pauses the AVAudioPlayer for you on an interruption and when you tell it to resume after you receive the interruption is over, Apple will set your session active again. See Table 3-2 from Handling Audio Interruptions on recommendations if you are using other types of audio technologies.

我们使用AVAudioPlayer播放音频并收听AVAudioSessionInterruptionNotification。 Apple会在中断时自动暂停AVAudioPlayer,当您在收到中断后告诉它恢复时,Apple会再次激活您的会话。如果您正在使用其他类型的音频技术,请参阅处理音频中断的表3-2。

Subscribe to the notification:

订阅通知:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAudioSessionEvent:) name:AVAudioSessionInterruptionNotification object:nil];

Handle the notification:

处理通知:

- (void) onAudioSessionEvent: (NSNotification *) notification
{
    //Check the type of notification, especially if you are sending multiple AVAudioSession events here
    if ([notification.name isEqualToString:AVAudioSessionInterruptionNotification]) {
        NSLog(@"Interruption notification received!");

        //Check to see if it was a Begin interruption
        if ([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeBegan]]) {
            NSLog(@"Interruption began!");

        } else {
            NSLog(@"Interruption ended!");
            //Resume your audio
        }
    }
}

#2


5  

In my experience I found I had to "wait a second or two" before attempting to reopen the audio device. I think that after the OS switches back to your app from the call, the phone app is still shutting down.

根据我的经验,我发现在尝试重新打开音频设备之前我必须“等待一两秒钟”。我认为在操作系统从通话切换回您的应用程序后,手机应用程序仍在关闭。

something like this when you return to foreground after knowing your audio session has been stopped:

在知道音频会话已经停止后返回前台时会出现类似的情况:

            dispatch_time_t restartTime = dispatch_time(DISPATCH_TIME_NOW, 
                                                      1.5LL * NSEC_PER_SEC);

            dispatch_after(restartTime, dispatch_get_global_queue(0, 0), ^{ 
                [audioSystem restart]; 
            });

#3


2  

I am using Xamarin so this is C# but the following code is working for me. I first set that my AppDelegate implements the AVAudioSession delegate methods by including IAVAudioSessionDelegate

我正在使用Xamarin,所以这是C#,但以下代码对我有用。我首先通过包含IAVAudioSessionDelegate来设置我的AppDelegate实现AVAudioSession委托方法

            public partial class AppDelegate: UIApplicationDelegate, IAVAudioSessionDelegate

I added a variable to the AppDelegate class for the audio session

我在AppDelegate类中为音频会话添加了一个变量

            public static AVAudioSession AudioSession;

In the override of the FinishedLaunching method:

在FinishedLaunching方法的重写中:

            AudioSession = AVAudioSession.SharedInstance();
            AudioSession.Delegate = this;
            error = AudioSession.SetCategory( AVAudioSessionCategory.Playback, AVAudioSessionCategoryOptions.DuckOthers );
            AudioSession.SetMode( new NSString( "AVAudioSessionModeMoviePlayback" ), out error );

The two relevant delegate methods in AppDelegate.cs are:

AppDelegate.cs中的两个相关委托方法是:

            [Export( "beginInterruption" )]
            public void BeginInterruption()
            {
                 PlayerViewController.BeginSessionInterruption();
            }

            [Export( "endInterruptionWithFlags:" )]
            public void EndInterruption( AVAudioSessionInterruptionFlags flags )
            {  // ignore the flags so we're not dependent on the interrupting event saying that we can resume
                 PlayerViewController.ResumeAfterSessionInterruption();
            }

My AppDelegate also has an override of OnActivated to enable the video tracks if it's a video asset, and an override of DidEnterBackground to disable the media's video tracks but still play the audio.

我的AppDelegate还覆盖了OnActivated以启用视频轨道(如果它是视频资源),并覆盖DidEnterBackground以禁用媒体的视频轨道但仍播放音频。

In the PlayerViewController.BeginSessionInterruption() method, I can't look at Player.Rate to see if the player was running at the time, because the interrupting alarm or phone call has already paused the player. From the "Responding to Interruptions" section of Apple's Audio Session Programming Guide, with my emphasis added:

在PlayerViewController.BeginSessionInterruption()方法中,我无法查看Player.Rate以查看播放器当时是否正在运行,因为中断警报或电话呼叫已暂停播放器。从Apple的音频会话编程指南的“响应中断”部分,我的重点是:

  1. Your app is active, playing back audio.
  2. 您的应用处于活动状态,播放音频。
  3. A phone call arrives. The system activates the phone app’s audio session.
  4. 电话到了。系统会激活手机应用程序的音频会话。
  5. The system deactivates your audio session. At this point, * playback in your app has stopped *.
  6. 系统将停用您的音频会话。此时,*应用中的播放已停止*。
  7. The system invokes your interruption listener callback function indicating that your session has been deactivated. ...
  8. 系统调用您的中断侦听器回调函数,指示您的会话已停用。 ...

My PlayerViewController's Play button has a Paused property to toggle between Play and Pause and draw the appropriate button image. So instead of checking Player.Rate, I look to see if the button's Paused property is false:

我的PlayerViewController的“播放”按钮具有“暂停”属性,可在“播放”和“暂停”之间切换,并绘制相应的按钮图像。因此,我不是检查Player.Rate,而是查看按钮的Paused属性是否为false:

            public void BeginSessionInterruption()
            {
                 PlayerWasRunningOnInterruption = !btnPlay.Paused;
                 TogglePlayPause( true );  // put the Play button into the Paused state to agree with the player being stopped
            }

            public void ResumeAfterSessionInterruption()
            {
                NSError error;
                AppDelegate.AudioSession.SetActive( true, AVAudioSessionSetActiveOptions.NotifyOthersOnDeactivation, out error );  // always reactivate the audio session

                if ( PlayerWasRunningOnInterruption )
                {
// rewind a little bit
// call btnPlayClick to resume the playback as if the user pressed the Play button
                }
            }

#4


0  

if you only detect the call status you can use CTCallCenter, but if you want to detect the interruption (include the call interruption),you can use AVAudioSessionInterruptionNotification, and if you want to support background you should add code like this:

如果您只检测呼叫状态,则可以使用CTCallCenter,但如果要检测中断(包括呼叫中断),则可以使用AVAudioSessionInterruptionNotification,如果要支持后台,则应添加如下代码:

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

before you resume play music in background.hope this can help.

在你继续在background.hope中播放音乐之前,这可以提供帮助。

#5


0  

Only two options works for me:

只有两个选项适合我:

  1. reload AVPlayer or AVAudioPlayer after interruption end

    中断结束后重新加载AVPlayer或AVAudioPlayer

    func interruptionNotification(_ notification: Notification) {
    guard let type = notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? UInt,
      let interruption = AVAudioSessionInterruptionType(rawValue: type) else {
        return
    }
    if interruption == .ended && playerWasPlayingBeforeInterruption {
      player.replaceCurrentItem(with: AVPlayerItem(url: radioStation.url))
      play()
    }
    

    }

    }

  2. use

    使用

AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: .mixWithOthers)

AVAudioSession.sharedInstance()。setCategory(AVAudioSessionCategoryPlayback,with:.mixWithOthers)

#6


0  

Look at:

看着:

https://developer.apple.com/documentation/avfoundation/avaudiosession#//apple_ref/doc/uid/TP40008240-CH1-DontLinkElementID_3

https://developer.apple.com/documentation/avfoundation/avaudiosession#//apple_ref/doc/uid/TP40008240-CH1-DontLinkElementID_3

https://developer.apple.com/documentation/avfoundation/avaudiosession/responding_to_audio_session_interruptions

https://developer.apple.com/documentation/avfoundation/avaudiosession/responding_to_audio_session_interruptions

Code:

码:

private func setupAudioSession() {
    do {

        try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, with: .mixWithOthers)
        try AVAudioSession.sharedInstance().setActive(true)

        setupAudioNotifications()

    } catch {
        print(error)
    }
}

private func setupAudioNotifications() {
    NotificationCenter.default.addObserver(self, selector: #selector(handleInterruption), name: .AVAudioSessionInterruption, object: nil)
}

@objc func handleInterruption(notification: Notification) {

    guard let userInfo = notification.userInfo,
        let typeValue = userInfo[AVAudioSessionInterruptionTypeKey] as? UInt,
        let type = AVAudioSessionInterruptionType(rawValue: typeValue) else {
            return
    }

    if type == .began {
        // Interruption began, take appropriate actions
        Player.shared.stop()

    } else if type == .ended {
        if let optionsValue = userInfo[AVAudioSessionInterruptionOptionKey] as? UInt {
            let options = AVAudioSessionInterruptionOptions(rawValue: optionsValue)
            if options.contains(.shouldResume) {
                // Interruption Ended - playback should resume
                Player.shared.start()
            } else {
                // Interruption Ended - playback should NOT resume
            }
        }
    }
}