Reputation: 481
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?
Upvotes: 47
Views: 103032
Reputation: 1198
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 ...
Upvotes: 22
Reputation: 163
Plus @Deep Gandhi's answer, If you need to have a cache as well, you may need this below; If any improvement you can give, much appreciated!
final class Player {
static let shared = Player()
private var audioPlayer = AVAudioPlayer()
private init() {
let cacheSizeMemory = 50 * 1024 * 1024 // 50 MB
let cacheDiskCapacity = 100 * 1024 * 1024 // 100 MB
let urlCache = URLCache(memoryCapacity: cacheSizeMemory, diskCapacity: cacheDiskCapacity, diskPath: "audioCache")
URLCache.shared = urlCache
}
func play(url: URL) {
if let cachedResponse = URLCache.shared.cachedResponse(for: URLRequest(url: url)) {
playAudio(data: cachedResponse.data)
} else {
fetchDataAndPlay(url: url)
}
}
private func fetchDataAndPlay(url: URL) {
let dataTask = URLSession.shared.dataTask(with: url) { [weak self] data, response, _ in
guard let data = data else { return }
// Store the data in the cache
let cachedResponse = CachedURLResponse(response: response!, data: data)
URLCache.shared.storeCachedResponse(cachedResponse, for: URLRequest(url: url))
// Play the audio using the fetched data
self?.playAudio(data: data)
}
dataTask.resume()
}
private func playAudio(data: Data) {
do {
audioPlayer = try AVAudioPlayer(data: data)
audioPlayer.play()
} catch {
print("Error playing audio: \(error.localizedDescription)")
}
}
}
Upvotes: 0
Reputation: 395
you can create class like:
import AVFoundation
final class Player {
static var share = Player()
private var player = AVPlayer()
private init() {}
func play(url: URL) {
player = AVPlayer(url: url)
player.allowsExternalPlayback = true
player.appliesMediaSelectionCriteriaAutomatically = true
player.automaticallyWaitsToMinimizeStalling = true
player.volume = 1
player.play()
}
}
and call it as:
Player.share.play(url: url)
as it is singleton class, it is easy to manage further operation on that player.
Upvotes: 4
Reputation: 11276
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>
Upvotes: 42
Reputation: 328
Don't forget to unmute silence mode. AvAudioPlayer will not play any sound if silence mode is activated
Upvotes: 3
Reputation: 81
var player : AVPlayer?
func playSound()
{
guard let url = URL(string: "https://file-examples.com/wp-content/uploads/2017/11/file_example_MP3_700KB.mp3")
else
{
print("error to get the mp3 file")
return
}
do{
try AVAudioSession.sharedInstance().setCategory(.playback, mode.default)
try AVAudioSession.sharedInstance().setActive(true)
player = try AVPlayer(url: url as URL)
guard let player = player
else
{
return
}
player.play()
} catch let error {
print(error.localizedDescription)
}
}
Upvotes: 8
Reputation: 3138
SWIFT 4
1 - import AVFoundation
2 - declare player
var player : AVPlayer?
3 - in ViewDidLoad call function and pass the streaming url like String
loadRadio(radioURL: (radioStation?.streamURL)!)
4 - function to play
func loadRadio(radioURL: String) {
guard let url = URL.init(string: radioURL) else { return }
let playerItem = AVPlayerItem.init(url: url)
player = AVPlayer.init(playerItem: playerItem)
player?.play()
startNowPlayingAnimation(true)
played = true
}
For me is the best and simply way to streaming audio in swift.
Upvotes: 13
Reputation: 2498
After some research, and some disappointment since none of the answers solved my problem, I've come to a solution and here's the final result. The accepted answer is half right, you still need to create an AVPlayerLayer
let url = URL(string: "https://www.myinstants.com/media/sounds/cade-o-respeito.mp3" ?? "")
playerItem = AVPlayerItem(url: url!)
player = AVPlayer(playerItem: playerItem)
let playerLayer = AVPlayerLayer(player: player)
playerLayer.frame = CGRect(x: 0, y: 0, width: 10, height: 10)
self.contentView.layoutSublayers(of: playerLayer)
player?.play()
Upvotes: 7
Reputation: 40247
Swift 4
func playSound(soundUrl: String) {
let sound = URL(fileURLWithPath: soundUrl)
do {
let audioPlayer = try AVAudioPlayer(contentsOf: sound)
audioPlayer.prepareToPlay()
audioPlayer.play()
}catch let error {
print("Error: \(error.localizedDescription)")
}
}
Upvotes: 3
Reputation: 13860
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()
}
}
Upvotes: 16
Reputation: 14514
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>
Upvotes: 49
Reputation: 9558
To play a sound in swift2 is
func playSound(soundUrl: String)
{
let sound = NSURL(soundUrl)
do{
let audioPlayer = try AVAudioPlayer(contentsOfURL: sound)
audioPlayer.prepareToPlay()
audioPlayer.play()
}catch {
print("Error..")
}
}
Let me know if it working.
Upvotes: 1