我正在制作一个应用程序,它使用AVPlayer(单个播放/暂停按钮)播放实时音频。应用程序工作正常,但由于这是实时音频,如果有轻微的网络问题,音频停止,播放器无法继续播放,即使按下播放/暂停按钮。恢复应用程序的唯一方法是杀死它并每次重新启动。有人能,请,建议任何东西或替代AVPlayer或我如何缓冲音频,以便如果连接丢失,播放器将缓冲它在同一时间?我是新的IOS编程.感谢
laik7k3q1#
我也遇到了同样的问题。答案是创建一个错误委托,它在每次播放器停止时启动一个选择器(当网络连接中断或流没有正确加载时,错误会改变):下面是我的委托,就在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 } } }
zyfwsgd62#
针对Swift 5进行了更新:
import Foundation import AVFoundation import UIKit class RadioViewController: UIViewController, errorMessageDelegate, sharedInstanceDelegate { // MARK: Properties var firstErrorSkip = true var firstInstanceSkip = true @IBAction func switched(_ sender: UIButton) { toggle() } override func viewDidLoad() { super.viewDidLoad() do { try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback) 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() { } } 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 { RadioPlayer.sharedInstance.play() } } func pauseRadio() { RadioPlayer.sharedInstance.pause() } func resetStream() { print("Reloading interrupted stream"); RadioPlayer.sharedInstance.resetPlayer() //RadioPlayer.sharedInstance = RadioPlayer(); RadioPlayer.sharedInstance.errorDelegate = self RadioPlayer.sharedInstance.instanceDelegate = self if RadioPlayer.sharedInstance.bufferFull() { 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: UIAlertController.Style.alert) alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertAction.Style.default, handler: nil)) self.present(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 } } }
对于收音机播放器:
import Foundation import UIKit import AVFoundation protocol errorMessageDelegate { func errorMessageChanged(newVal: String) } protocol sharedInstanceDelegate { func sharedInstanceChanged(newVal: Bool) } class Globals{ static let radioURL = "enter url here" } class RadioPlayer : NSObject { static let sharedInstance = RadioPlayer() var instanceDelegate:sharedInstanceDelegate? var sharedInstanceBool = false { didSet { if let delegate = self.instanceDelegate { delegate.sharedInstanceChanged(newVal: self.sharedInstanceBool) } } } private var player = AVPlayer(url: NSURL(string: Globals.radioURL)! as URL) private var playerItem = AVPlayerItem?.self private var isPlaying = false var errorDelegate:errorMessageDelegate? = nil var errorMessage = "" { didSet { if let delegate = self.errorDelegate { delegate.errorMessageChanged(newVal: self.errorMessage) } } } override init() { super.init() errorMessage = "" let asset: AVURLAsset = AVURLAsset(url: NSURL(string: Globals.radioURL)! as URL, options: nil) let statusKey = "tracks" asset.loadValuesAsynchronously(forKeys: [statusKey]) { var error: NSError? = nil DispatchQueue.main.async { let status: AVKeyValueStatus = asset.statusOfValue(forKey: 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!) } } } NotificationCenter.default.addObserver( forName: NSNotification.Name.AVPlayerItemFailedToPlayToEndTime, object: nil, queue: nil, using: { 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)! as URL, options: nil) let statusKey = "tracks" asset.loadValuesAsynchronously(forKeys: [statusKey]) { var error: NSError? = nil DispatchQueue.main.async { let status: AVKeyValueStatus = asset.statusOfValue(forKey: 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!) } } } } func bufferFull() -> Bool { return bufferAvailableSeconds() > 45.0 } func bufferAvailableSeconds() -> TimeInterval { // Check if there is a player instance if ((player.currentItem) != nil) { // Get current AVPlayerItem let item: AVPlayerItem = player.currentItem! if (item.status == AVPlayerItem.Status.readyToPlay) { let timeRangeArray: NSArray = item.loadedTimeRanges as NSArray if timeRangeArray.count < 1 { return(CMTimeGetSeconds(CMTime.invalid)) } let aTimeRange: CMTimeRange = (timeRangeArray.object(at:0) as AnyObject).timeRangeValue //let startTime = CMTimeGetSeconds(aTimeRange.end) let loadedDuration = CMTimeGetSeconds(aTimeRange.duration) return (TimeInterval)(loadedDuration); } else { return(CMTimeGetSeconds(CMTime.invalid)) } } else { return(CMTimeGetSeconds(CMTime.invalid)) } } 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 } }
2条答案
按热度按时间laik7k3q1#
我也遇到了同样的问题。答案是创建一个错误委托,它在每次播放器停止时启动一个选择器(当网络连接中断或流没有正确加载时,错误会改变):
下面是我的委托,就在RadioPlayer类的外部和上方:
现在我上课:
现在,在RadioViewController中:
zyfwsgd62#
针对Swift 5进行了更新:
对于收音机播放器: