2015-09-16 145 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