Reputation: 5836
I'm trying to playback and mp3 file I retrieve from my server from my app doing the following:
- (IBAction)play:(UIButton *)sender {
dispatch_queue_t downloadQueue = dispatch_queue_create("audio data downloader", NULL);
dispatch_async(downloadQueue, ^{
NSURL *audioURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@/%@.mp3", FONYK_FILES_URL, [self.voicenote valueForKeyPath:@"Fonyker.fonykid"], [self.voicenote valueForKeyPath:@"Voicenote.vnid"]]];
NSData *audioData = [NSData dataWithContentsOfURL:audioURL];
dispatch_async(dispatch_get_main_queue(), ^{
NSError *error = nil;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithData:audioData error:&error];
NSLog(@"%@", error);
audioPlayer.delegate = self;
[audioPlayer play];
});
});
}
The playback fails, meaning no sound is emitted and I get this output when debugging my app step by step:
Catchpoint 2 (exception thrown).Single stepping until exit from function __cxa_throw, which has no line number information.
And this comes up when I NSLog the error in the simulator:
Error Domain=NSOSStatusErrorDomain Code=-50 "The operation couldn’t be completed. (OSStatus error -50.)"
Nothing else shows it just fails, any thoughts?
UPDATE: Modified my code as per J_D's answer and got this in the simulator:
Error loading /System/Library/Extensions/AudioIPCDriver.kext/Contents/Resources/AudioIPCPlugIn.bundle/Contents/MacOS/AudioIPCPlugIn: dlopen(/System/Library/Extensions/AudioIPCDriver.kext/Contents/Resources/AudioIPCPlugIn.bundle/Contents/MacOS/AudioIPCPlugIn, 262): Symbol not found: ___CFObjCIsCollectable
Referenced from: /System/Library/Frameworks/Security.framework/Versions/A/Security
Expected in: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.1.sdk/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
in /System/Library/Frameworks/Security.framework/Versions/A/Security
2012-04-17 15:03:43.054 Fonyk[8238:15307] Error loading /System/Library/Extensions/AudioIPCDriver.kext/Contents/Resources/AudioIPCPlugIn.bundle/Contents/MacOS/AudioIPCPlugIn: dlopen(/System/Library/Extensions/AudioIPCDriver.kext/Contents/Resources/AudioIPCPlugIn.bundle/Contents/MacOS/AudioIPCPlugIn, 262): Symbol not found: ___CFObjCIsCollectable
Referenced from: /System/Library/Frameworks/Security.framework/Versions/A/Security
Expected in: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.1.sdk/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation
in /System/Library/Frameworks/Security.framework/Versions/A/Security
UPDATE: the real issue I had was not creating and AVAudioSession instance set to playback in my code, doing so along with the corrections answer I accepted made it work.
Upvotes: 2
Views: 8947
Reputation: 3586
I see at least one issue with your code, with the line
NSURL *fileURL = [NSURL URLWithString:filePath];
It tries to create an URL with a local file path. It will probably return nil. You should instead use:
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
Now, even with this, your code does not run (I tried it and got the same error you got). I have not used AVAudioPlayer very much myself so I do not know why your specific error happens.
However, I did try a very small example using your code and I was able to play the MP3 correctly. Instead of downloading the data to a file and loading AVAudioPlayer from a file, you can use the initWithData constructor of AVAudioPlayer:
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithData:[NSData dataWithContentsOfURL:audioURL] error:&error];
This works fine on my side, assuming that audioURL is valid. I pointed to a mp4 on my server for my test.
So rewriting your code with this initWithData gives:
- (IBAction)play:(UIButton *)sender {
dispatch_queue_t downloadQueue = dispatch_queue_create("audio data downloader", NULL);
dispatch_async(downloadQueue, ^{
NSURL *audioURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@%@/%@.mp3", FONYK_FILES_URL, [self.voicenote valueForKeyPath:@"Fonyker.fonykid"], [self.voicenote valueForKeyPath:@"Voicenote.vnid"]]];
NSData *audioData = [NSData dataWithContentsOfURL:audioURL];
dispatch_async(dispatch_get_main_queue(), ^{
NSError *error = nil;
AVAudioPlayer *audioPlayer = [[AVAudioPlayer alloc] initWithData:audioData error:&error];
NSLog(@"%@", error);
audioPlayer.delegate = self;
[audioPlayer play];
});
});
}
Just as a last note: You are downloading the entire file before you start playing, which may take a while and consume a lot of heap (or storage if you were to save it on a file)
If you want to stream, you can use a MPMoviePlayerController, but you do not attach the view to your view hierarchy. This way, you will only have audio and will not change any visual of your application.
An example would be:
NSURL *audioURL = [NSURL URLWithString:@"MY_MP3_URL"];
MPMoviePlayerController* player = [[MPMoviePlayerController alloc] initWithContentURL:audioURL];
[player play];
Again, on my side, in the Simulator, it works fine.
Do not forget to release the player. You can also register delegates for important notifications.
Upvotes: 5
Reputation: 16714
You are receiving an EXC_BAD_ACCESS somewhere. I suggest removing all asynchronous code and seeing what happens. Actually, I am a bit confused by your thought process as far as the async calls go.
Note that AVAudioPlayer only works with LOCAL files. It seems like you might be setting it up to play an audio file from a server, which won't work.
Also, why don't you try to NSLog the error variable?
Upvotes: 1