实时流AVPlayer音频缓冲

时间:2015-09-16 16:32:17

标签: swift ios8 xcode6 swift2 ios9

我正在制作一个使用AVPlayer流式传输音频的应用程序(单个播放/暂停按钮)。应用程序工作正常,但由于这是Live Audio,如果有轻微的网络问题,音频停止和播放器不会继续,甚至按播放/暂停按钮。只有恢复应用程序的方法是杀死它和每次重启。

请有人,建议AVPlayer的任何或替代方案,或者我如何缓冲音频,如果连接丢失,播放器会同时缓冲它?我是IOS编程的新手。赞赏

1 个答案:

答案 0 :(得分:4)

我遇到了同样的问题。答案是创建一个错误委托,每次播放器停止时启动选择器(当网络连接中断或流没有正确加载时错误发生变化):

以下是我的代表,就在我的RadioPlayer类之外和之上:

protocol errorMessageDelegate {
    func errorMessageChanged(newVal: String)
}

protocol sharedInstanceDelegate {
    func sharedInstanceChanged(newVal: Bool)
}

现在我的班级:

import Foundation
import AVFoundation
import UIKit

class RadioPlayer : NSObject {

    static let sharedInstance = RadioPlayer()
    var instanceDelegate:sharedInstanceDelegate? = nil
    var sharedInstanceBool = false {
        didSet {
            if let delegate = self.instanceDelegate {
                delegate.sharedInstanceChanged(self.sharedInstanceBool)
            }
        }
    }
    private var player = AVPlayer(URL: NSURL(string: Globals.radioURL)!)
    private var playerItem = AVPlayerItem?()
    private var isPlaying = false

    var errorDelegate:errorMessageDelegate? = nil
    var errorMessage = "" {
        didSet {
            if let delegate = self.errorDelegate {
                delegate.errorMessageChanged(self.errorMessage)
            }
        }
    }

    override init() {
        super.init()

        errorMessage = ""

        let asset: AVURLAsset = AVURLAsset(URL: NSURL(string: Globals.radioURL)!, options: nil)

        let statusKey = "tracks"

        asset.loadValuesAsynchronouslyForKeys([statusKey], completionHandler: {
            var error: NSError? = nil

            dispatch_async(dispatch_get_main_queue(), {
                let status: AVKeyValueStatus = asset.statusOfValueForKey(statusKey, error: &error)

                if status == AVKeyValueStatus.Loaded{

                    let playerItem = AVPlayerItem(asset: asset)

                    self.player = AVPlayer(playerItem: playerItem)
                    self.sharedInstanceBool = true

                } else {
                    self.errorMessage = error!.localizedDescription
                    print(error!)
                }

            })


        })

        NSNotificationCenter.defaultCenter().addObserverForName(
            AVPlayerItemFailedToPlayToEndTimeNotification,
            object: nil,
            queue: nil,
            usingBlock: { notification in
                print("Status: Failed to continue")
                self.errorMessage = "Stream was interrupted"
        })

        print("Initializing new player")

    }

    func resetPlayer() {
        errorMessage = ""

        let asset: AVURLAsset = AVURLAsset(URL: NSURL(string: Globals.radioURL)!, options: nil)

        let statusKey = "tracks"

        asset.loadValuesAsynchronouslyForKeys([statusKey], completionHandler: {
            var error: NSError? = nil

            dispatch_async(dispatch_get_main_queue(), {
                let status: AVKeyValueStatus = asset.statusOfValueForKey(statusKey, error: &error)

                if status == AVKeyValueStatus.Loaded{

                    let playerItem = AVPlayerItem(asset: asset)
                    //playerItem.addObserver(self, forKeyPath: "status", options: NSKeyValueObservingOptions.New, context: &ItemStatusContext)

                    self.player = AVPlayer(playerItem: playerItem)
                    self.sharedInstanceBool = true

                } else {
                    self.errorMessage = error!.localizedDescription
                    print(error!)
                }

            })
        })
    }

    func bufferFull() -> Bool {
        return bufferAvailableSeconds() > 45.0
    }

    func bufferAvailableSeconds() -> NSTimeInterval {
        // Check if there is a player instance
        if ((player.currentItem) != nil) {

            // Get current AVPlayerItem
            let item: AVPlayerItem = player.currentItem!
            if (item.status == AVPlayerItemStatus.ReadyToPlay) {

                let timeRangeArray: NSArray = item.loadedTimeRanges
                if timeRangeArray.count < 1 { return(CMTimeGetSeconds(kCMTimeInvalid)) }
                let aTimeRange: CMTimeRange = timeRangeArray.objectAtIndex(0).CMTimeRangeValue
                //let startTime = CMTimeGetSeconds(aTimeRange.end)
                let loadedDuration = CMTimeGetSeconds(aTimeRange.duration)

                return (NSTimeInterval)(loadedDuration);
            }
            else {
                return(CMTimeGetSeconds(kCMTimeInvalid))
            }
        } 
        else {
            return(CMTimeGetSeconds(kCMTimeInvalid))
        }
    }

    func play() {
        player.play()
        isPlaying = true
        print("Radio is \(isPlaying ? "" : "not ")playing")
    }

    func pause() {
        player.pause()
        isPlaying = false
        print("Radio is \(isPlaying ? "" : "not ")playing")
    }

    func currentlyPlaying() -> Bool {
        return isPlaying
    }

}

现在,在RadioViewController中:

import UIKit
import AVFoundation

class RadioViewController: UIViewController, errorMessageDelegate, sharedInstanceDelegate {

    // MARK: Properties

    var firstErrorSkip = true
    var firstInstanceSkip = true

    @IBOutlet weak var listenLabel: UILabel!
    @IBOutlet weak var radioSwitch: UIImageView!

    @IBAction func back(sender: AnyObject) {
        print("Dismissing radio view")
        if let navigationController = self.navigationController
        {
            navigationController.popViewControllerAnimated(true)
        }
    }

    @IBAction func switched(sender: AnyObject) {
        toggle()
    }

    override func viewDidLoad() {
        super.viewDidLoad()

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

        RadioPlayer.sharedInstance.errorDelegate = self
        RadioPlayer.sharedInstance.instanceDelegate = self

        if RadioPlayer.sharedInstance.currentlyPlaying() {
            radioSwitch.image = UIImage(named: "Radio_Switch_Active")
            listenLabel.text = "Click to Pause Radio Stream:"
        }

    }

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

    func toggle() {
        if RadioPlayer.sharedInstance.currentlyPlaying() {
            pauseRadio()
        } else {
            playRadio()
        }
    }

    func playRadio() {
        firstErrorSkip = false
        firstInstanceSkip = false

        if RadioPlayer.sharedInstance.errorMessage != "" || RadioPlayer.sharedInstance.bufferFull() {
            resetStream()
        } else {
            radioSwitch.image = UIImage(named: "Radio_Switch_Active")
            listenLabel.text = "Click to Pause Radio Stream:"
            RadioPlayer.sharedInstance.play()
        }
    }

    func pauseRadio() {
        RadioPlayer.sharedInstance.pause()
        radioSwitch.image = UIImage(named: "Radio_Switch_Inactive")
        listenLabel.text = "Click to Play Radio Stream:"
    }

    func resetStream() {
        print("Reloading interrupted stream");
        RadioPlayer.sharedInstance.resetPlayer()
        //RadioPlayer.sharedInstance = RadioPlayer();
        RadioPlayer.sharedInstance.errorDelegate = self
        RadioPlayer.sharedInstance.instanceDelegate = self
        if RadioPlayer.sharedInstance.bufferFull() {
            radioSwitch.image = UIImage(named: "Radio_Switch_Active")
            listenLabel.text = "Click to Pause Radio Stream:"
            RadioPlayer.sharedInstance.play()
        } else {
            playRadio()
        }
    }

    func errorMessageChanged(newVal: String) {
        if !firstErrorSkip {
            print("Error changed to '\(newVal)'")
            if RadioPlayer.sharedInstance.errorMessage != "" {
                print("Showing Error Message")
                let alertController = UIAlertController(title: "Stream Failure", message: RadioPlayer.sharedInstance.errorMessage, preferredStyle: UIAlertControllerStyle.Alert)
                alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))

                self.presentViewController(alertController, animated: true, completion: nil)

                pauseRadio()

            }
        } else {
            print("Skipping first init")
            firstErrorSkip = false
        }
    }

    func sharedInstanceChanged(newVal: Bool) {
        if !firstInstanceSkip {
        print("Detected New Instance")
            if newVal {
                RadioPlayer.sharedInstance.play()
            }
        } else {
            firstInstanceSkip = false
        }
    }

}