2015-09-16 105 views
3

我正在制作一个使用AVPlayer(单个播放/暂停按钮)流式传输实时音频的应用程序。应用程序工作正常,但是由于这是现场音频,如果出现轻微的网络问题,音频会停止,并且播放器不会继续播放,即使按下播放/暂停按钮也是如此。恢复该应用程序的唯一方法是杀死它并每次重新启动。AVPlayer实时流的音频缓冲

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

+0

也许这个问题可以帮助你:http://stackoverflow.com/questions/6880817/ios-avplayer-trigger-streaming-is-out-of-buffer –

回答

2

我有这个相同的问题。答案是创建一个错误委托,每次播放器停止时都会启动一个选择器(错误在网络连接中断或流加载不正常时发生变化):

这是我的代表,只是在外面和上面我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 
     } 
    } 

} 
+0

非常感谢你!我也会试一试。 – rulebreaker4