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

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

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

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

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

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

订阅通知:

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

处理通知:

 - (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 } } } 

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

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

  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]; }); 

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

  public partial class AppDelegate: UIApplicationDelegate, IAVAudioSessionDelegate 

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

  public static AVAudioSession AudioSession; 

在FinishedLaunching方法的重写中:

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

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(); } 

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

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

  1. 您的应用处于活动状态,播放音频。
  2. 电话到了。 系统会激活手机应用程序的音频会话。
  3. 系统将停用您的音频会话。 此时, *应用中的播放已停止*
  4. 系统调用您的中断侦听器回调函数,指示您的会话已停用。 …

我的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 } } 

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

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

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

只有两个选项适合我:

  1. 中断结束后重新加载AVPlayerAVAudioPlayer

     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. 使用

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

看着:

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

码:

 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 } } } }