2016-04-16 16 views
5

Voglio riprodurre l'audio in streaming da Internet. Ho scritto un codice che riproduce lo stream ma non ha alcun buffer, quindi se il segnale è debole, l'applicazione smette di riprodurre l'audio. Questo è il mio codice:Come bufferare l'audio utilizzando AVPlayer in iOS?

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

Non ho idea di come bufferizzare questo flusso. Ho cercato la documentazione Apple ma non riesco a trovare nulla in Swift.

Io trovo qualcosa come AVAudioBuffer ma non so come usarlo e se è corretto per risolvere questo problema.

P.S. C# ha documentazione su MSDN, è qualcosa di simile su Apple in caso di Swift?

+1

documentazione Swift: https://developer.apple.com/library/ios/documentation/ Swift/Concettuale/Swift_Programming_Language/TheBasics.html # // apple_ref/doc/uid/TP40014097-CH5-ID309 // Documentazione iOS: https://developer.apple.com/library/ios/navigation/ – Moritz

risposta

7

La risposta è nella creazione di un delegato errore che ha lanciato un selettore ogni volta che il giocatore si fermò (L'errore cambia quando la connessione di rete viene interrotta o il flusso non è stato caricato correttamente):

Ecco delegati, basta al di fuori e al di sopra la mia classe Radioplayer:

protocol errorMessageDelegate { 
func errorMessageChanged(newVal: String) 
} 

classe:

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

} 

Spero che questo vi aiuterà :)

+0

Grazie! :) Non capisco una cosa. Che cosa è questo: "Globals.radioURL"? Alcune impostazioni globali nel progetto? Dove posso modificarli? –

+1

siete i benvenuti :) E 'solo che url non si dichiara più globalmente di usare ovunque. – Lion

+0

Come posso dichiarare la variabile di uso globale con il prefisso "Globale"? –

0

cambiamento

playerItem = AVPlayerItem?() 

a

playerItem:AVPlayerItem? 
Problemi correlati