Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to play mp3 audio from URL in ios swift

I am getting mp3 url as a response of an API call.

I want to play that audio, so how can I do that?

here is my response

{
    content = "En este primer programa se tratar\U00e1n asuntos tan importante como este y aquel sin descuidar un poco de todo lo dem\U00e1s";
    file = "http://radio.spainmedia.es/wp-content/uploads/2015/12/ogilvy.mp3";
    image = "http://radio.spainmedia.es/wp-content/uploads/2015/12/tapas.jpg";
    number = 0001;
    subtitle = Titulareando;
    title = "Tapa 1";
}

here is my code:

 @IBAction func btnplayaudio(sender: AnyObject) {
    let urlstring = "http://radio.spainmedia.es/wp-content/uploads/2015/12/tailtoddle_lo4.mp3"
    let url = NSURL(string: urlstring)
    print("the url = \(url!)")
    
    play(url!)

}

func play(url:NSURL) {
    print("playing \(url)")
    
    do {
        self.player = try AVAudioPlayer(contentsOfURL: url)
        player.prepareToPlay()
        player.volume = 1.0
        player.play()
    } catch let error as NSError {
        self.player = nil
        print(error.localizedDescription)
    } catch {
        print("AVAudioPlayer init failed")
    }
    
}

Where am I going wrong?

like image 623
Govind Avatar asked Jan 02 '16 06:01

Govind


4 Answers

I tried the following:-

let urlstring = "http://radio.spainmedia.es/wp-content/uploads/2015/12/tailtoddle_lo4.mp3"
let url = NSURL(string: urlstring)
print("the url = \(url!)")
downloadFileFromURL(url!)

Add the below methods:-

func downloadFileFromURL(url:NSURL){

    var downloadTask:NSURLSessionDownloadTask
    downloadTask = NSURLSession.sharedSession().downloadTaskWithURL(url, completionHandler: { [weak self](URL, response, error) -> Void in
        self?.play(URL)
    })
    downloadTask.resume()
}

And your play method as it is:-

func play(url:NSURL) {
    print("playing \(url)")
    do {
        self.player = try AVAudioPlayer(contentsOfURL: url)
        player.prepareToPlay()
        player.volume = 1.0
        player.play()
    } catch let error as NSError {
        //self.player = nil
        print(error.localizedDescription)
    } catch {
        print("AVAudioPlayer init failed")
    }
}

Download the mp3 file and then try to play it, somehow AVAudioPlayer does not download your mp3 file for you. I am able to download the audio file and player plays it.

Remember to add this in your info.plist since you are loading from a http source and you need the below to be set for iOS 9+

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
</plist>
like image 39
Satheesh Avatar answered Nov 02 '22 10:11

Satheesh


Use AVPlayer instead of AVAudioPlayer to play remote content. As per documentation AVAudioPlayer needs mp3 file to play Audio. AVAudioPlayer not provide support for streaming.

Try this code , its working fine for me

func play(url:NSURL) {
    print("playing \(url)")

    do {

        let playerItem = AVPlayerItem(URL: url)

        self.player = try AVPlayer(playerItem:playerItem)
        player!.volume = 1.0
        player!.play()
    } catch let error as NSError {
        self.player = nil
        print(error.localizedDescription)
    } catch {
        print("AVAudioPlayer init failed")
    }
}

Please keep in mind to set App Transport Security(ATS) in info.plist file.

<key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
like image 81
technerd Avatar answered Nov 02 '22 10:11

technerd


Create a complete Audio player with a progress bar and other stuff ... swift 5 and Xcode 12.1

var player: AVPlayer?
var playerItem:AVPlayerItem?
fileprivate let seekDuration: Float64 = 10


@IBOutlet weak var labelOverallDuration: UILabel!
@IBOutlet weak var labelCurrentTime: UILabel!
@IBOutlet weak var playbackSlider: UISlider!
@IBOutlet weak var ButtonPlay: UIButton!

//call this mehtod to init audio player 
func initAudioPlayer{
    let url = URL(string: "https://argaamplus.s3.amazonaws.com/eb2fa654-bcf9-41de-829c-4d47c5648352.mp3")
    let playerItem:AVPlayerItem = AVPlayerItem(url: url!)
    player = AVPlayer(playerItem: playerItem)
    
    playbackSlider.minimumValue = 0
    
    //To get overAll duration of the audio
    let duration : CMTime = playerItem.asset.duration
    let seconds : Float64 = CMTimeGetSeconds(duration)
    labelOverallDuration.text = self.stringFromTimeInterval(interval: seconds)
    
    //To get the current duration of the audio
    let currentDuration : CMTime = playerItem.currentTime()
    let currentSeconds : Float64 = CMTimeGetSeconds(currentDuration)
    labelCurrentTime.text = self.stringFromTimeInterval(interval: currentSeconds)
    
    playbackSlider.maximumValue = Float(seconds)
    playbackSlider.isContinuous = true
    
    
    
    player!.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(1, preferredTimescale: 1), queue: DispatchQueue.main) { (CMTime) -> Void in
        if self.player!.currentItem?.status == .readyToPlay {
            let time : Float64 = CMTimeGetSeconds(self.player!.currentTime());
            self.playbackSlider.value = Float ( time );
            self.labelCurrentTime.text = self.stringFromTimeInterval(interval: time)
        }
        let playbackLikelyToKeepUp = self.player?.currentItem?.isPlaybackLikelyToKeepUp
        if playbackLikelyToKeepUp == false{
            print("IsBuffering")
            self.ButtonPlay.isHidden = true
            //        self.loadingView.isHidden = false
        } else {
            //stop the activity indicator
            print("Buffering completed")
            self.ButtonPlay.isHidden = false
            //        self.loadingView.isHidden = true
        }
    }
   
   //change the progress value
    playbackSlider.addTarget(self, action: #selector(playbackSliderValueChanged(_:)), for: .valueChanged)
    
    //check player has completed playing audio
    NotificationCenter.default.addObserver(self, selector: #selector(self.finishedPlaying(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: playerItem)}


@objc func playbackSliderValueChanged(_ playbackSlider:UISlider) {
    let seconds : Int64 = Int64(playbackSlider.value)
    let targetTime:CMTime = CMTimeMake(value: seconds, timescale: 1)
    player!.seek(to: targetTime)
    if player!.rate == 0 {
        player?.play()
    }
}

@objc func finishedPlaying( _ myNotification:NSNotification) {
    ButtonPlay.setImage(UIImage(named: "play"), for: UIControl.State.normal)
    //reset player when finish   
    playbackSlider.value = 0
    let targetTime:CMTime = CMTimeMake(value: 0, timescale: 1)
    player!.seek(to: targetTime)
}

@IBAction func playButton(_ sender: Any) {
    print("play Button")
    if player?.rate == 0
    {
        player!.play()
        self.ButtonPlay.isHidden = true
        //        self.loadingView.isHidden = false
        ButtonPlay.setImage(UIImage(systemName: "pause"), for: UIControl.State.normal)
    } else {
        player!.pause()
        ButtonPlay.setImage(UIImage(systemName: "play"), for: UIControl.State.normal)
    }
    
}


func stringFromTimeInterval(interval: TimeInterval) -> String {
    let interval = Int(interval)
    let seconds = interval % 60
    let minutes = (interval / 60) % 60
    let hours = (interval / 3600)
    return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
}



@IBAction func seekBackWards(_ sender: Any) {
    if player == nil { return }
    let playerCurrenTime = CMTimeGetSeconds(player!.currentTime())
    var newTime = playerCurrenTime - seekDuration
    if newTime < 0 { newTime = 0 }
    player?.pause()
    let selectedTime: CMTime = CMTimeMake(value: Int64(newTime * 1000 as Float64), timescale: 1000)
    player?.seek(to: selectedTime)
    player?.play()

}


@IBAction func seekForward(_ sender: Any) {
    if player == nil { return }
    if let duration = player!.currentItem?.duration {
       let playerCurrentTime = CMTimeGetSeconds(player!.currentTime())
       let newTime = playerCurrentTime + seekDuration
       if newTime < CMTimeGetSeconds(duration)
       {
          let selectedTime: CMTime = CMTimeMake(value: Int64(newTime * 1000 as 
       Float64), timescale: 1000)
          player!.seek(to: selectedTime)
       }
       player?.pause()
       player?.play()
      }
}

The end result looks like this ...

enter image description here

like image 16
Wahab Khan Jadon Avatar answered Nov 02 '22 11:11

Wahab Khan Jadon


Since AVPlayer is very limited (for example you cannot change url there without regenerate whole AVPlayer I think you should use AVQueuePlayer:

From the docs:

AVQueuePlayer is a subclass of AVPlayer used to play a number of items in sequence. Using this class you can create and manage a queue of player items comprised of local or progressively downloaded file-based media, such as QuickTime movies or MP3 audio files, as well as media served using HTTP Live Streaming.

So in Swift 3 it will work like this:

lazy var playerQueue : AVQueuePlayer = {
    return AVQueuePlayer()
}()

...

func playTrackOrPlaylist{
    if let url = track.streamURL() { //this is an URL fetch from somewhere. In this if you make sure that URL is valid
        let playerItem = AVPlayerItem.init(url: url)
        self.playerQueue.insert(playerItem, after: nil)
        self.playerQueue.play()
    }
}
like image 14
Jakub Avatar answered Nov 02 '22 11:11

Jakub