【问题标题】:How to resume audio after interruption in Swift?Swift中断后如何恢复音频?
【发布时间】:2016-12-12 12:24:08
【问题描述】:

我正在按照here 的说明进行操作,我已经整理了这个测试项目来处理音频播放的中断。具体来说,我使用默认 iphone 时钟应用程序中的闹钟作为中断。似乎正在调用中断处理程序,但没有超过 let = interruptionType 行,因为“wrong type”出现了两次。

import UIKit
import AVFoundation

class ViewController: UIViewController {

    var player = AVAudioPlayer()

    let audioPath = NSBundle.mainBundle().pathForResource("rachmaninov-romance-sixhands-alianello", ofType: "mp3")!

    func handleInterruption(notification: NSNotification) {

        guard let interruptionType = notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? AVAudioSessionInterruptionType else { print("wrong type"); return }

        switch interruptionType {

        case .Began:
            print("began")
            // player is paused and session is inactive. need to update UI)
            player.pause()
            print("audio paused")

        default:
            print("ended")
            /**/
            if let option = notification.userInfo?[AVAudioSessionInterruptionOptionKey] as? AVAudioSessionInterruptionOptions where option == .ShouldResume {
                // ok to resume playing, re activate session and resume playing
                // need to update UI
                player.play()
                print("audio resumed")
            }
            /**/
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.

        do {
            try player = AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: audioPath))
            player.numberOfLoops = -1 // play indefinitely
            player.prepareToPlay()
            //player.delegate = player

        } catch {
            // process error here
        }

        // enable play in background https://stackoverflow.com/a/30280699/1827488 but this audio still gets interrupted by alerts
        do {
            try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
            print("AVAudioSession Category Playback OK")
            do {
                try AVAudioSession.sharedInstance().setActive(true)
                print("AVAudioSession is Active")
            } catch let error as NSError {
                print(error.localizedDescription)
            }
        } catch let error as NSError {
            print(error.localizedDescription)
        }

        // add observer to handle audio interruptions
        // using 'object: nil' does not have a noticeable effect
        let theSession = AVAudioSession.sharedInstance()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ViewController.handleInterruption(_:)), name: AVAudioSessionInterruptionNotification, object: theSession)

        // start playing audio
        player.play()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

此外,根据here 的想法,我已将处理程序修改为

func handleInterruption(notification: NSNotification) {

        //guard let interruptionType = notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? AVAudioSessionInterruptionType else { print("wrong type"); return }

        if notification.name != AVAudioSessionInterruptionNotification
            || notification.userInfo == nil{
            return
        }

        var info = notification.userInfo!
        var intValue: UInt = 0
        (info[AVAudioSessionInterruptionTypeKey] as! NSValue).getValue(&intValue)
        if let interruptionType = AVAudioSessionInterruptionType(rawValue: intValue) {

            switch interruptionType {

            case .Began:
                print("began")
                // player is paused and session is inactive. need to update UI)
                player.pause()
                print("audio paused")

            default:
                print("ended")
                /** /
                if let option = notification.userInfo?[AVAudioSessionInterruptionOptionKey] as? AVAudioSessionInterruptionOptions where option == .ShouldResume {
                    // ok to resume playing, re activate session and resume playing
                    // need to update UI
                    player.play()
                    print("audio resumed")
                }
                / **/
                player.play()
                print("audio resumed")
            }
        }
    }

结果是所有“开始”、“音频暂停”、“结束”和“音频恢复”都显示在控制台中,但实际上并未恢复音频播放。

注意:我将player.play() 移到注释掉的where option == .ShouldResume if 语句之外,因为当.Ended 中断发生时,if 条件不成立。

【问题讨论】:

  • 抱歉,我的陈述有误,已更正。中断处理程序被调用。
  • 好的,这将是我第一次在上面放任何东西,所以我可能需要几分钟以上...请继续关注
  • 感谢您对 CW 答案的编辑!如果您想自己重新发布整个答案,我很乐意删除 CW 版本并为您的版本投票 - 辛苦了。
  • @halfer,感谢您的建议。我喜欢这里的社区精神。
  • 这绝对是为那些坚持自己答案的人而存在的——我们中的很多人,作为海报的一部分。

标签: swift audio resume interruption


【解决方案1】:

(代表问题作者发布,在问题中发布后)

找到解决方案!在讨论here之后,将其插入viewDidLoad()

do {
    try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, withOptions: AVAudioSessionCategoryOptions.MixWithOthers)
} catch {        
}

在闹钟中断点击“确定”后,音频继续播放。与之前提到的不同,该解决方案不需要中断处理程序(@Leo Dabus 已将其删除)。

但是,如果您使用中断处理程序,则不得在 handleInterruption() 内调用 .play(),因为这样做并不能保证继续播放,并且似乎会阻止调用 audioPlayerEndInterruption()(请参阅 docs)。相反,.play() 必须在 audioPlayerEndInterruption()(其 3 个版本中的任何一个)内调用以保证恢复。

此外,AVAudioSession 必须提供@Simon Newstead 指出的选项.MixWithOthers,如果您希望您的应用在您的应用处于后台时中断后继续播放。似乎如果用户希望应用程序在进入后台时继续播放,那么假设用户也希望应用程序在应用程序处于后台时中断后继续播放是合乎逻辑的。事实上,这正是 Apple Music 应用所表现出来的行为。

【讨论】:

    【解决方案2】:

    @rockhammers 的建议对我有用。 Here

    课前

    let theSession = AVAudioSession.sharedInstance()
    

    在 viewDidLoad 中

        NotificationCenter.default.addObserver(self, selector: #selector(ViewController.handleInterruption(notification:)), name: NSNotification.Name.AVAudioSessionInterruption, object: theSession)
    

    然后是函数

    func handleInterruption(notification: NSNotification) {
        print("handleInterruption")
        guard let value = (notification.userInfo?[AVAudioSessionInterruptionTypeKey] as? NSNumber)?.uintValue,
            let interruptionType =  AVAudioSessionInterruptionType(rawValue: value)
            else {
                print("notification.userInfo?[AVAudioSessionInterruptionTypeKey]", notification.userInfo?[AVAudioSessionInterruptionTypeKey])
                return }
        switch interruptionType {
        case .began:
            print("began")
            vox.pause()
            music.pause()
            print("audioPlayer.playing", vox.isPlaying)
            /**/
            do {
                try theSession.setActive(false)
                print("AVAudioSession is inactive")
            } catch let error as NSError {
                print(error.localizedDescription)
            }
            pause()
        default :
            print("ended")
            if let optionValue = (notification.userInfo?[AVAudioSessionInterruptionOptionKey] as? NSNumber)?.uintValue, AVAudioSessionInterruptionOptions(rawValue: optionValue) == .shouldResume {
                print("should resume")
                // ok to resume playing, re activate session and resume playing
                /**/
                do {
                    try theSession.setActive(true)
                    print("AVAudioSession is Active again")
                    vox.play()
                    music.play()
                } catch let error as NSError {
                    print(error.localizedDescription)
                }
                play()
            }
        }
    }
    

    【讨论】:

    • 您好,我面临一个问题,默认代码对我不起作用,它不在 if 语句中。你能帮我么?它向我显示了这个错误。 WKProcessAssertionBackgroundTaskManager:忽略启动新后台任务的请求,因为 RunningBoard 已经启动了过期计时器
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-14
    • 2016-10-01
    相关资源
    最近更新 更多