I'm trying to play mp3 from Data file I downloaded with Alamofire. The song is fine, I can play it with AVAudioPlayer(data: Data). How should I play the same Data file with AVPlayer? I could not find how to create PlayerItem from Data or AVAsset from Data.
Also, I've the url for the song path, but that URL does not work with AVPlayer somehow. Music is just not playing.
func startSong(withIndex: Int) {
if let item = getItem(atIndex: withIndex) {
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)
}
let playerItem = item
self.player = AVPlayer(playerItem: playerItem)
} catch let error as NSError {
print(error.localizedDescription)
}
}
}
func getItem(atIndex: Int) -> AVPlayerItem? {
var item: AVPlayerItem
var url: URL
let song = playlist.getSong(index: atIndex)
if PlaylistsService.sharedService.isFileDownloaded(inPath: SongPath.Offline(id: (song?.getID())!).path()) {
url = URL(fileURLWithPath: SongPath.Offline(id: (song?.getID())!).path())
} else if PlaylistsService.sharedService.isFileDownloaded(inPath: SongPath.Temp(id: (song?.getID())!).path()) {
url = URL(fileURLWithPath: SongPath.Temp(id: (song?.getID())!).path())
} else {
self.downloadSong(song: song!)
url = URL(fileURLWithPath: SongPath.Temp(id: (song?.getID())!).path())
}
item = AVPlayerItem(url: url)
return item
}
#IBAction func playPause(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected {
print("Play")
self.player.play()
} else {
print("Pause")
self.player.pause()
}
}
Just found out that AVPlayerItem cannot create object from URL that points to Data file. You need to have URL with file extension ".mp3" for instance. Everything is working now when I've added the extension to path for downloading and playback.
Related
I am having a weird situation and have no clue how to handle this , I am downloading the videos from firestorage and caching into device for future use , meanwhile the background thread is already doing its job , I am passing a video url to the function to play the video. The issue is that sometimes avplayer is playing the right video and sometimes taking some other video url from the cache.
you can find the code in below :
func cacheVideo(for exercise: Exercise) {
print(exercise.imageFileName)
guard let filePath = filePathURL(for: exercise.imageFileName) else { return }
if fileManager.fileExists(atPath: filePath.path) {
// print("already exists")
} else {
exercise.loadRealURL { (url) in
print(url)
self.getFileWith(with: url, saveTo: filePath)
}
}
}
writing file here
func getFileWith(with url: URL, saveTo saveFilePathURL: URL) {
DispatchQueue.global(qos: .background).async {
print(saveFilePathURL.path)
if let videoData = NSData(contentsOf: url) {
videoData.write(to: saveFilePathURL, atomically: true)
DispatchQueue.main.async {
// print("downloaded")
}
} else {
DispatchQueue.main.async {
let error = NSError(domain: "SomeErrorDomain", code: -2001 /* some error code */, userInfo: ["description": "Can't download video"])
print(error.debugDescription)
}
}
}
}
now playing the video using this
func startPlayingVideoOnDemand(url : URL) {
activityIndicatorView.startAnimating()
activityIndicatorView.isHidden = false
print(url)
let cachingPlayerItem = CachingPlayerItem(url: url)
cachingPlayerItem.delegate = self
cachingPlayerItem.download()
// cachingPlayerItem.preferredPeakBitRate = 0
let avasset = AVAsset(url: url)
let playerItem = AVPlayerItem(asset: avasset)
let player = AVPlayer(playerItem: playerItem)
player.automaticallyWaitsToMinimizeStalling = false
initializeVideoLayer(for: player)
}
any suggestions would be highly appreciated.
this was solved because the data model which i was using to download bunch of videos files was accessed in background thread and meanwhile i was trying to assign the url to the same data model class in order to fetch the video and play in avplayer. Hence this was the issue and resolved by simply adding a new attribute into data model for assigning the url to play right away.
I used the function slowPlay to create an action that will play background music. I tried to do the reverse in stop to stop the music. But the music does not stop.
I just want to have a button that plays the music and stops the music. I'm using the code:
#IBAction func play(_ sender: Any) {
slowPlay
}
#IBAction func stop(_ sender: Any) {
slowStop
}
func slowPlay() {
do {
let ap = Bundle.main.path(forResource: "slowslow", ofType: "mp3")
try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: ap!) as URL)
} catch {
////
}
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSessionCategoryPlayback)
} catch {
}
player?.play()
}
func slowStop() {
do {
let ap = Bundle.main.path(forResource: "slowslow", ofType: "mp3")
try player = AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: ap!) as URL)
} catch {
////
}
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSessionCategoryPlayback)
} catch {
}
player?.stop()
}
If you look at your code example you are creating a new instance of the player for both play and stop.
You need to create a class variable for your player, something like:
var player: AVAudioPlayer?
This will allow you to stop like this:
func slowStop() {
player?.stop()
}
Create a single instance for player like blow
var player: AVAudioPlayer?
func slowPlay() {
do {
let ap = Bundle.main.path(forResource: "slowslow", ofType: "mp3")
self.player = try AVAudioPlayer(contentsOf: NSURL(fileURLWithPath: ap!) as URL)
} catch {
////
}
let session = AVAudioSession.sharedInstance()
do {
try session.setCategory(AVAudioSessionCategoryPlayback)
} catch {
//catch error here
}
self.player?.play()
}
func slowStop() {
self.player?.stop()
}
You can add validation to check whether player is valid or not before performing any action.
Below is my code to play AVQueueplayer with some web URL, but it takes almost 10-20 seconds to play first song. I have 10 songs, but for reference and to make it small, I have just kept one song here.
arrSongs = ["http://radiotaj.com/music/1/1836.mp3"]
let index = 0
let strSong = (arrSongs.object(at: index)) as? String
playerItem = AVPlayerItem(url: URL(string:strSong!)!
let playerItems = [playerItem]
player = AVQueuePlayer(items : playerItems as! [AVPlayerItem])
player.play()
Below are the solutions I have already tried
Adding CMTime
Making it pause and play again
It works perfectly fine if file is local.
Any help is greatly appreciated.
Thank you!
You can try the following code, it works for me. Here is the version without Alamofire (replace yourURL with the actual link):
override func viewDidLoad() {
super.viewDidLoad()
downloadFileFromURL(url: URL(string: yourURL)!)
}
func play(url: URL) {
do {
let songData = try Data(contentsOf: url, options: NSData.ReadingOptions.mappedIfSafe)
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
player = try AVAudioPlayer(data: songData, fileTypeHint: AVFileTypeAppleM4A)
player!.prepareToPlay()
player!.play()
} catch {
print(error)
}
}
func downloadFileFromURL(url: URL) {
var downloadTask = URLSessionDownloadTask()
downloadTask = URLSession.shared.downloadTask(with: url, completionHandler: {
customURL, response, error in
self.play(url: customURL!)
})
downloadTask.resume()
}
Using Alamofire is faster, it basically downloads the file into the Documents folder and plays it from here (don't forget to install Alamofire and type outside the class import Alamofire:
func play(url: URL) {
do {
let songData = try Data(contentsOf: url, options: NSData.ReadingOptions.mappedIfSafe)
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
player = try AVAudioPlayer(data: songData, fileTypeHint: AVFileTypeAppleM4A)
player!.prepareToPlay()
player!.play()
} catch {
print(error)
}
}
override func viewDidLoad() {
super.viewDidLoad()
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
var documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
documentsURL.appendPathComponent("song."+"mp3")
return (documentsURL, [.removePreviousFile])
}
Alamofire.download(yourURL, to: destination).response { response in
if response.destinationURL != nil {
songURL = response.destinationURL!
self.play(url: songURL!)
}
}
}
Hey guys so for some reason when you mute the phone the audio is still coming from the app. Any ideas on how to fix?
private func myFunc() {
// Set up the audio session
do {
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
fatalError("Unable to set the audio session because: \(error)")
}
guard
let audioURL = Bundle.main.url(forResource: "lololol", withExtension: "mp3"),
let player = try? AVAudioPlayer(contentsOf: audioURL)
else { fatalError("Unable to create player") }
if let aPlayer = audioPlayer {
if audioPlayer?.isPlaying == false {
audioPlayer = player
player.play()
}
} else {
audioPlayer = player
player.play()
}
}
I solve it by changing AVAudioSessionCategoryPlayback to AVAudioSessionCategoryAmbient.
i try to make iOS app to play some funny sound put when i click play no sound are out from speaker here is the code :
#IBAction func slow(sender: UIButton) {
if let filePath = NSBundle.mainBundle().URLForResource("io", withExtension: "mp3") {
let _a = try! AVAudioPlayer(contentsOfURL: filePath)
// Play
_a.play()
}else
{
print("error")
}
}
there is no error but i can't hear any sound at all
for how that face this problem
#IBAction func slow(sender: UIButton) {
if let path = NSBundle.mainBundle().pathForResource("io", ofType:"mp3") {
let fileURL = NSURL(fileURLWithPath: path)
player = try! AVAudioPlayer(contentsOfURL: fileURL)
player.prepareToPlay()
player.play()
}
else
{
print("error")
}
}
}