27

My app downloads an mp3 from our server and plays it back to the user. The file is 64 kbps (which is well within the acceptable range for iPhone if I understand correctly). I have looked up how to do this on dozens of sites and they all suggest that i do exactly this:

NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:@"http://.../file.mp3"]];
NSError *e = nil;
AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithData:data error&e];
[player setDelegate:self];

When I run the code, player comes back null and I get this error:

2011-02-04 10:44:46.176 MyApp[6052:207] Error loading audio: Error Domain=NSOSStatusErrorDomain Code=1954115647 "The operation couldn’t be completed. (OSStatus error 1954115647.)"
2011-02-04 10:44:49.647 MyApp[6052:207] unsupported file type

I have checked the file and I know that it works. It will play with no problems on Windows Media Player, and Quicktime on mac. I have also uploaded the file to the iPhone emulator and it plays with no problems whatsoever. The file is fine, but for some reason AVAudioPlayer doesn't like it.

Is there something I need to change? Is there some kind of setting for NSData to specify what kind of file it is? Does anyone have any idea?

mtmurdock
  • 12,756
  • 21
  • 65
  • 108

4 Answers4

32

At long last i have found a solution to this problem! Instead of initializing the audio player with the NSData object, I saved the file to the Documents folder, and then initialized the player with the file URL

//download file and play from disk
NSData *audioData = [NSData dataWithContentsOfURL:someURL];
NSString *docDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:@"%@/%@.mp3", docDirPath , fileName];
[audioData writeToFile:filePath atomically:YES];

NSError *error;
NSURL *fileURL = [NSURL fileURLWithPath:filePath];
player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&error];
if (player == nil) {
    NSLog(@"AudioPlayer did not load properly: %@", [error description]);
} else {
    [player play];
}

When the app is done with the file, it can be deleted. Hope that his helps more than just me!

mtmurdock
  • 12,756
  • 21
  • 65
  • 108
14

We were dealing with the same issue - we had to write the file to disk first. What I found eventually was that the data we were downloading had extra, blank bytes at the beginning. When reading from disk using initWithContentsOfURL, AVAudioPlayer knew how to deal with this, but when loading from NSData using initWithData, it did not. Trimming those bytes fixed it.

Don
  • 3,654
  • 1
  • 26
  • 47
  • Oh that's very interesting. Perhaps that should be submitted to Apple as a bug? In the end I opted for the saved file anyways because it fit better with what we were trying to do. Thanks for the Tip! – mtmurdock Mar 19 '11 at 00:26
  • 2
    How exactly do you know which bytes to trim and how did you do that? – typeoneerror Apr 25 '12 at 03:22
  • 3
    @typeoneerror : I took a specific file, looked at it with a hex editor and read the mp3 spec to see how the header was defined. That's when I noticed the discrepancy. Not very general, I know... Programmatically, you could look at the first bytes of the NSData and skip any that are 0. – Don Apr 25 '12 at 16:43
  • @Don, I looked in an editor and noticed that most of the first bytes were 0, but not all. How do I know when it's safe to skip? – user592419 Apr 30 '13 at 14:29
  • @user592419 Looking at http://www.datavoyage.com/mpgscript/mpeghdr.htm, which describes the mp3 frames, the first frame should have the first 11 bytes set. I am not an audio expert, and it's been a while since I looked at this, but maybe try looking for that first frame and ignore everything before it. I'd like to know if that works. – Don Apr 30 '13 at 15:00
  • @Don, I looked more into this and actually it's more involved than I thought. Can you take a look at http://stackoverflow.com/questions/16226281/audioplayer-ios-and-m4a please? – user592419 May 02 '13 at 15:48
11

From iOS 7 AVAudioPlayer has new initializer

NSError *error;
[[AVAudioPlayer alloc] initWithData:soundData fileTypeHint:AVFileTypeMPEGLayer3 error:&error]

The supported UTIs

NSString *const AVFileType3GPP;
NSString *const AVFileType3GPP2;
NSString *const AVFileTypeAIFC;
NSString *const AVFileTypeAIFF;
NSString *const AVFileTypeAMR;
NSString *const AVFileTypeAC3;
NSString *const AVFileTypeMPEGLayer3;
NSString *const AVFileTypeSunAU;
NSString *const AVFileTypeCoreAudioFormat;
NSString *const AVFileTypeAppleM4V;
NSString *const AVFileTypeMPEG4;
NSString *const AVFileTypeAppleM4A;
NSString *const AVFileTypeQuickTimeMovie;
NSString *const AVFileTypeWAVE;
Yankouski Roman
  • 111
  • 1
  • 3
1

If your product name contains space, you will receive the error.
My project's product name was: Panorama 1453 TR. initWithContentsOfURL method cannot fetch file path. so it was not working. you can put a breakPoint to NSURL *fileURL = . After a step you can see fileURL what data has.
I changed to Panorama1453TR , it did work.

kordiseps
  • 391
  • 3
  • 12