What is wrong with this URL for AVAudioPlayer in Swift? - ios

I tried to create AVAudioPlayer like this
let url = URL(string: "https://storage.googleapis.com/preview-public/project/e45d3194bb7f4768984fd53acc833600/fa13293c27314b448a815ebd42176684/audio-gnvY.m4a")!
do {
let player = try AVAudioPlayer(contentsOf: url)
} catch (let err) {
print("err", err)
}
I get this error: Error Domain=NSOSStatusErrorDomain Code=2003334207 "(null)".
I also tried
do {
let _ = try url.checkResourceIsReachable()
} catch (let err) {
print("err", err)
}
and I get this error: Domain=NSCocoaErrorDomain Code=262 "The file couldn’t be opened because the specified URL type isn’t supported.".
I can't figure out what could possibly be wrong with the url.
Images load normally from same bucket although if I check checkResourceIsReachable() on image URL it returns same error. (But I load image with CGImageSourceCreateWithURL(url as CFURL which might not have same checks as AVAudioPlayer.) Anyway I'm lost. Any ideas?

AVAudioPlayer is for local file: URLs only. It doesn’t accept remote URLs, which would need to be streamed. For that, use AVPlayer.

i have demo in try to run this code audio is working perfect
let audiourl = URL(string: "https://storage.googleapis.com/preview-public/project/e45d3194bb7f4768984fd53acc833600/fa13293c27314b448a815ebd42176684/audio-gnvY.m4a")
let player = AVPlayer(url: audiourl!)
let playViewController = AVPlayerViewController()
playViewController.player = player
self.present(playViewController, animated: true){
playViewController.player!.play()
}

In AVAudioPlayer, URL is the path to a local file.
https://developer.apple.com/documentation/foundation/url

Related

Agora cannot record call with iOS Swift

I am following the doc here: https://docs.agora.io/en/Voice/rtc_recording_apple?platform=iOS and implementing a basic recording. This is my code:
func startRecording(){
let filename = getDocumentsDirectory().appendingPathComponent("\(APP_NAME)\(now()).WAV")
let str = String(describing: filename)
self.recordingPath = str
agoraKit?.startAudioRecording(str, quality: .high)
}
func stopRecording(){
agoraKit?.stopAudioRecording()
// get audio file
guard let audioUrl = URL(string: self.recordingPath) as? URL else { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0 ) { [weak self] in
// getdata
do {
let myData = try Data(contentsOf: audioUrl)
print(myData.count, myData)
} catch {
print(error)
}
}
}
private func getDocumentsDirectory() -> URL {
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return paths[0]
}
But I am getting error: The file “Whisper1608949569.WAV” couldn’t be opened because there is no such file
Full message:
file:///var/mobile/Containers/Data/Application/1F682ABD-153C-4DFD-BFF4-
02C1CE6F9A4C/Documents/Whisper1608949569.WAV
Error Domain=NSCocoaErrorDomain Code=260 "The file “Whisper1608949569.WAV” couldn’t be opened because there is no such file." UserInfo={NSFilePath=/var/mobile/Containers/Data/Application/1F682ABD-153C-4DFD-BFF4-02C1CE6F9A4C/Documents/Whisper1608949569.WAV, NSUnderlyingError=0x281e33f60 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Am I not accessing the file correctly?
This is how I have initialized the agora client:
self.agoraKit = AgoraRtcEngineKit.sharedEngine(withAppId: AppID, delegate: self)
agoraKit?.delegate = self
agoraKit?.enableWebSdkInteroperability(true)
// sample loudest speaker every second
agoraKit?.enableAudioVolumeIndication(1000, smooth: 3, report_vad: true)
agoraKit?.enableAudio()
// config for livecast to start
agoraKit?.setChannelProfile(.liveBroadcasting)
// set framrate and HD/SD
agoraKit?.setVideoEncoderConfiguration( CONFIG_PRODUCTION )
//agoraKit?.setDefaultAudioRouteToSpeakerphone(true)
I just checked out the documentation referenced from the doc you're using and it says the method startAudioRecording(filepath, quality: quality) is now deprecated, and you should instead use this method with the additional sampleRate parameter:
https://docs.agora.io/en/Voice/API%20Reference/oc/Classes/AgoraRtcEngineKit.html#//api/name/startAudioRecording:sampleRate:quality:
Also check that the returned value of startAudioRecording and stopAudioRecording returns 0, meaning success.
If your channel name contains special characters (colons, slashes) recording will silently fail and no file will be produced.
It seems Agora uses the channel name when creating the temporary file.

Can't play a sound file downloaded from an S3 bucket

I am facing a weird problem with an .m4a sound file on an iOS app written in Swift.
Here is what happens:
I record a sound file on the device using the app. Let us call it xyz.m4a.
I check that I can play this sound file, and it is working.
I then upload the sound file to an AWS S3 bucket, from my app.
I check on the AWS console that the file is uploaded as expected and I download it using a browser (on my computer), checking again that I can still play the sound as expected. Up to this point nothing is wrong, I can play the sound.
I then use my app to download the file to my iOS device (iPhone 6). All seems OK here too.
Finally I try to play the downloaded file from within my app and this is where things go wrong. I cannot play it.
Here is what I think is the relevant code, please let me know if you think I should add more.
Code to download the file from the AWS S3 bucket:
let transferUtility = AWSS3TransferUtility.default()
transferUtility.downloadData(
fromBucket: "mybucket",
key: "xyz.m4a",
expression: expression,
completionHandler: completionHandler
).continueWith {
(task) -> AnyObject! in if let error = task.error {
print("Error: \(error.localizedDescription)")
}
if let _ = task.result {
// Do something with downloadTask.
print("task.result -- OK!")
let downloadOutput = task.result
print("downloadOutput:\(String(describing: downloadOutput))")
}
return nil;
}
Here is the completionHandler part:
var completionHandler: AWSS3TransferUtilityDownloadCompletionHandlerBlock?
completionHandler = {
[weak self] (task, URL, data, error) -> Void in
DispatchQueue.main.async(execute: {
print("transfer completion OK!")
let localFileName = "xyz.m4a",
playFileURL = self?.getDocumentsURL().appendingPathComponent(localFileName)
FileManager.default.createFile(atPath: (playFileURL?.path)!,
contents: data,
attributes: nil)
if FileManager.default.fileExists(atPath: (playFileURL?.path)!) {
print("playFileURL present!") // Confirm that the file is here!
}
})
}
Code to play the downloaded sound file:
let fileName = "xyz.m4a",
audioFile = getDocumentsURL().appendingPathComponent(fileName)
if !FileManager.default.fileExists(atPath: audioFile.path) {
print("We have no file !!!!!") // Just in case the file was not found!
}
// This code assumes the file exists.
do {try soundSession.setCategory(AVAudioSessionCategoryPlayback)
try soundSession.setActive(true)
if audioPlayer == nil {
audioPlayer = try AVAudioPlayer(contentsOf: audioFile,
fileTypeHint: AVFileType.m4a.rawValue)
guard let _ = audioPlayer else {return}
audioPlayer.delegate = self
}
audioPlayer.play()
} catch let error {
print("Error:\n" + error.localizedDescription)
}
Finally here is the error I get:
Error:
The operation couldn’t be completed. (OSStatus error 1685348671.)
I hope someone can see something wrong an tell me where the problem is.

AVAudioPlayer not playing m4a or mp3 filetype from a website

I am trying to locate a URL which is only pure .m4a sound with my application. I have the URL to the audio and theoretically download it. Then, with the downloaded fileURL to the sound, I try to play it with the AVAudioPlayer, yet it does not play any sound. Here is my code:
In the URL retrieval function, I call: (urls defined as a URL(string: url), url being the retrieve URL string)
downloadSound(url: urls!)
Here is my downloadSound() function:
func downloadSound(url:URL){
var downloadTask:URLSessionDownloadTask
downloadTask = URLSession.shared.downloadTask(with: url, completionHandler: { [weak self](URL, response, error) -> Void in
self?.playSound(url: URL!)
})
downloadTask.resume()
}
And lastly the playSound function:
func playSound(url:URL) {
print("The url is \(url)")
let player = try! AVAudioPlayer(contentsOf: url)
player.play()
Everything is being called as the print("The url is \(url)") returns me the path of the file (I am not actually able to track the file, however).
Here is the general path of the sound on the simulator:
file:///Users/[...]/Library/Developer/CoreSimulator/Devices/116C311A-C7F3-44EC-9762-2FAA0F9FE966/data/Containers/Data/Application/60BFCDE7-AC02-4196-8D1A-24EC646C4622/tmp/CFNetworkDownload_7VDpsV.tmp
Whereas running it on a phone returns:
file:///private/var/mobile/Containers/Data/Application/C75C1F1D-77E9-4795-9A38-3F0756D30547/tmp/CFNetworkDownload_T1XlPb.tmp
Thank you in advance.
I had the same problem and I choosed an alternative solution as app doc said:
A file URL for the temporary file. Because the file is temporary, you
must either open the file for reading or move it to a permanent
location in your app’s sandbox container directory before returning
from this delegate method.
The idea is just to copy from tmp directory to document directory and play from document directory.
Create a member variable:
var player = AVAudioPlayer()
Now implement your downloadSound method as below:
func downloadSound(url:URL){
let docUrl:URL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL!
let desURL = docUrl.appendingPathComponent("tmpsong.m4a")
var downloadTask:URLSessionDownloadTask
downloadTask = URLSession.shared.downloadTask(with: url, completionHandler: { [weak self](URLData, response, error) -> Void in
do{
let isFileFound:Bool? = FileManager.default.fileExists(atPath: desURL.path)
if isFileFound == true{
print(desURL) //delete tmpsong.m4a & copy
try FileManager.default.removeItem(atPath: desURL.path)
try FileManager.default.copyItem(at: URLData!, to: desURL)
} else {
try FileManager.default.copyItem(at: URLData!, to: desURL)
}
let sPlayer = try AVAudioPlayer(contentsOf: desURL!)
self?.player = sPlayer
self?.player.prepareToPlay()
self?.player.play()
}catch let err {
print(err.localizedDescription)
}
})
downloadTask.resume()
}
This is just a sample solution.

Using AVAudioPlayer with Dynamic URL in Swift 3 causing Thread Errors

I am new to Swift and making an audio app using AVAudioPlayer. I am using a remote URL mp3 file for the audio, and this works when it's static.
For my use case, I want to pull a URL for an mp3 file from a JSON array and then pass it into the AVAudioPlayer to run.
If I move the AVAudioPlayer block into the ViewDidLoad and make the mp3 file a static URL, it will run fine.
Then, when I move this code into my block that extracts an mp3 url from JSON, I can print the URL successfully. But when I pass it into my audio player, problems arise. Here's the code.
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "http://www.example.com/example.json")
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
guard let data = data, error == nil else { return }
let json: Any?
do{
json = try JSONSerialization.jsonObject(with: data, options: [])
}
catch{
return
}
guard let data_list = json as? [[String:Any]] else {
return
}
if let foo = data_list.first(where: {$0["episode"] as? String == "Example Preview"}) {
self.audiotest = (foo["audio"] as? String)!
print(self.audiotest) // this prints
// where i'm passing it into the audio player
if let audioUrl = URL(string: self.audiotest) {
// then lets create your document folder url
let documentsDirectoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
// lets create your destination file url
let destinationUrl = documentsDirectoryURL.appendingPathComponent(audioUrl.lastPathComponent)
//let url = Bundle.main.url(forResource: destinationUrl, withExtension: "mp3")!
do {
audioPlayer = try AVAudioPlayer(contentsOf: destinationUrl)
} catch let error {
print(error.localizedDescription)
}
} // end player
// ....
Specifically, I get an error Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value when clicking a play button IBAction that is connected to the audio player. Finally, that action function looks like this:
#IBAction func playPod(_ sender: Any) {
audioPlayer.play()
}
Do you know where I'm going wrong? I'm confused as to why I can't print the URL and also get a response that the URL is nil in the same block, but maybe that's an asynchronous thing.
The problem is that you didn't save the mp3 file to documents and trying to play it
this line
audioPlayer = try AVAudioPlayer(contentsOf: destinationUrl)
assumes that there is a saved mp3 file in that path , but acutally there is no files you appended the audio extension on the fly
besides for steaming audio from a remote server, use AVPlayer instead of AVAudioPLayer.
AVPlayer Documentation
Also try this with urls parsed from json
var urlStr = (foo["audio"] as? String)!
self.audiotest = urlStr.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)

OSStatus error 2003334207 when using AVAudioPlayer

I am trying to play an MP3 file (works when played via VLC/iTunes) when a button is pressed. Here is my code:
var audioPlayer: AVAudioPlayer!
#IBAction func playEpisode(sender: AnyObject) {
println("now playing")
let indexPath = NSIndexPath(forRow: sender.tag, inSection: 0)
let data: CDEpisode = fetchedResultsController.objectAtIndexPath(indexPath!) as! CDEpisode
var err: NSError?
let url = NSURL(string: data.localPath)
println("The url is \(url)")
audioPlayer = AVAudioPlayer(contentsOfURL: url, error: &err)
if audioPlayer == nil {
if let e = err {
println(e.localizedDescription)
}
}
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
audioPlayer.play()
}
Here is the log:
now playing
The url is Optional(file:///var/mobile/Containers/Data/Application/4747A71E-A63F-4EFC-B2DF-8B361361080B/Documents/serial-s01-e12.mp3)
The operation couldn’t be completed. (OSStatus error 2003334207.)
fatal error: unexpectedly found nil while unwrapping an Optional value
The EXC_BREAKPOINT happens on the audioPlayer.delegate = self.
Other threads on StackoOverflow do not help. Any ideas?
Thanks
Edit: I have tried passing a localURL to contentsOfURL (instead of a CDEpisode object) and it still fails.
This is probably caused by trying to load a file that doesn't exist. If that helps someone adding the call to url.checkResourceIsReachable() will log more descriptive message.
Example code:
do {
let url = URL(fileURLWithPath: dbObject.path)
let isReachable = try url.checkResourceIsReachable()
// ... you can set breaking points after that line, and if you stopped at them it means file exist.
} catch let e {
print("couldnt load file \(e.localizedDescription)")
}
It looks like your trying to unwrap a variable that has a nil value. You should safely unwrap your variables to prevent this.
if let data: CDEpisode = fetchedResultsController.objectAtIndexPath(indexPath!) as! CDEpisode
{
var err: NSError?
let url = NSURL(string: data.localPath)
println("The url is \(url)")
//rest of code
}
You will still need to figure out why it is returning nil but this is a safer way to unwrap variables and prevent crashing as there would need to be more context to resolve that issue.
Some questions to look into:
Are you sure the fetchedResultsController is returning an object at
all?
Are you sure it is of CDEpisode?
try this one
var player: AVAudioPlayer = AVAudioPlayer()
#IBAction func playX(_ sender: Any) {
let urlstring = "https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3"
let url = URL(string: urlstring)
let data = try! Data(contentsOf: url!)
player = try! AVAudioPlayer(data: data)
player.prepareToPlay()
player.volume = 1.0
player.play()
}
You're checking if audioPlayer is nil but then you go on to use it as if it wasn't anyway. You probably want something like:
if audioPlayer == nil {
if let e = err {
println(e.localizedDescription)
}
} else {
audioPlayer.delegate = self
audioPlayer.prepareToPlay()
audioPlayer.play()
}
And do something to actually handle the error case rather than just printing the error.
In my case I was having the same issue and I found out that I needed to set this before start recording
try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayAndRecord)
Hope it helps anyone
I also got this problem, after checking up the audio file url, found that it is stored in Cache directory. so audio player probably couldn't find audio file according to your "url".
please ensure, the url path is under Document directory.
As others have said, the audio player couldn't find the file.
This helped me: Document directory path change when rebuild application
Basically, you cannot load files with an absolute reference, as the sandbox environment regenerates the absolute file url each time. So you will need to add a small bit of code (see above) to get the correct urls to use.

Resources