如何在iOS中使用AVPlayer缓冲音频?

我想从互联网播放流音频。 我编写了播放流的代码,但它没有任何缓冲区,所以如果信号弱,应用程序停止播放音频。 这是我的代码:

import UIKit import AVFoundation import MediaPlayer import AudioToolbox class ViewController: UIViewController { var playerItem:AVPlayerItem? var player:AVPlayer? @IBOutlet weak var PlayButton: UIButton! override func viewDidLoad() { super.viewDidLoad() var buffer = AVAudioBuffer () let url = NSURL (string: "http://radio.afera.com.pl/afera64.aac") playerItem = AVPlayerItem(URL: url!) player = AVPlayer(playerItem: playerItem!) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } @IBAction func PlayButtonTapped(sender: AnyObject) { if ((player!.rate != 0) && (player!.error == nil)) { player!.pause() PlayButton.setImage(UIImage(named:"Play"), forState: UIControlState.Normal) } else { player!.play() PlayButton.setImage(UIImage(named:"Stop"), forState:UIControlState.Normal) } } } 

我不知道如何缓冲此流。 我搜索了苹果文档但在Swift中找不到任何东西。

我发现类似AVAudioBuffer但我不知道如何使用它,如果它正确解决这个问题。

PS C#有关于MSDN文档,在Swift的情况下Apple是类似的吗?

答案是创建一个错误委托,每次播放器停止时启动选择器(当网络连接中断或流未正确加载时错误发生变化):

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

 protocol errorMessageDelegate { func errorMessageChanged(newVal: String) } 

类:

 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 } } protocol sharedInstanceDelegate { func sharedInstanceChanged(newVal: Bool) } 

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

希望这会有所帮助:)

更改

 playerItem = AVPlayerItem?() 

 playerItem:AVPlayerItem?