从NSData解码MP3文件

对于我的应用程序,我需要解码存储在NSData对象中的MP3文件。

出于安全原因,将NSData对象写入磁盘并使用系统URL引用重新打开它是不可取的,即使它只在本地存储一段时间。

我想利用扩展audio文件服务(或audio文件服务)来做到这一点,但是我无法获得只能在内存中存在的NSData的表示,这些audio文件服务可以读取这些表示。

编辑:我想解码的MP3数据,所以我可以访问线性PCMaudio样本进行操作。 从NSData对象播放不是一个问题。

我的代码如下:

decryptedData; //an NSData object which has already been initialized const void *dataBytes = decryptedData.bytes; //pointer to the bytes in my NSData object //this creates a CFURLRef from the pointer to the byte data //I have printed out the resulting CFURL and have confirmed that it is indeed reading the bytes correctly CFURLRef audioFileURLFromBytes = CFURLCreateWithBytes (kCFAllocatorDefault, dataBytes, decryptedData.length, kCFStringEncodingASCII, NULL); //attempt to open the the URL using Extended Audio File Services ExtAudioFileRef outExtAudioFile; OSStatus err = 0; err = ExtAudioFileOpenURL(audioFileURLFromBytes, &outExtAudioFile); if (err != noErr) { NSLog(@"ExtAudioFileOpenURL failed with OSStatus Code %i \n", err); } //Attempt to open the URL using Audio File Services AudioFileID audioFile; OSStatus res = 0; res = AudioFileOpenURL(audioFileURLFromBytes, kAudioFileReadPermission, kAudioFileMP3Type, &audioFile); if (res != noErr) { NSLog(@"AudioFileOpenURL failed with OSStatus Code %i \n", res); } 

这两个尝试打开URL导致OSStatus代码43,这是“找不到文件”。

我已经validation了我的指针指向NSData的内存中的正确地址,并且可以正确读取这些字节。

扩展audio文件服务是否有限制,禁止引用存储在内存中的字节?

感谢您的任何帮助,您可以提供。

编辑:我想出了如何使用Sbooth的build议做到这一点。 代码如下:这个函数接受一个包含audio文件的mp3表示的NSData对象。 它将其解码为线性PCM,以便可以获取样本,然后将其重新编码为AAC。 我不认为所有平台(手机/台式机)的CoreAudio都支持MP3编码。 此代码已在我的Mac上进行了testing,并完成了工作。

 -(void) audioFileReaderWithData: (NSData *) audioData { AudioFileID refAudioFileID; ExtAudioFileRef inputFileID; ExtAudioFileRef outputFileID; OSStatus result = AudioFileOpenWithCallbacks(audioData, readProc, 0, getSizeProc, 0, kAudioFileMP3Type, &refAudioFileID); if(result != noErr){ NSLog(@"problem in theAudioFileReaderWithData function: result code %i \n", result); } result = ExtAudioFileWrapAudioFileID(refAudioFileID, false, &inputFileID); if (result != noErr){ NSLog(@"problem in theAudioFileReaderWithData function Wraping the audio FileID: result code %i \n", result); } // Client Audio Format Description AudioStreamBasicDescription clientFormat; memset(&clientFormat, 0, sizeof(clientFormat)); clientFormat.mFormatID = kAudioFormatLinearPCM; clientFormat.mFramesPerPacket = 1; clientFormat.mChannelsPerFrame = 2; clientFormat.mBitsPerChannel = 32; clientFormat.mBytesPerPacket = clientFormat.mBytesPerFrame = 4 * clientFormat.mChannelsPerFrame; clientFormat.mFormatFlags = kAudioFormatFlagsNativeFloatPacked; clientFormat.mSampleRate = 44100; //Output Audio Format Description AudioStreamBasicDescription outputFormat; memset(&outputFormat, 0, sizeof(outputFormat)); outputFormat.mChannelsPerFrame = 2; outputFormat.mSampleRate = 44100; outputFormat.mFormatID = kAudioFormatMPEG4AAC; outputFormat.mFormatFlags = kMPEG4Object_AAC_Main; outputFormat.mBitsPerChannel = 0; outputFormat.mBytesPerFrame = 0; outputFormat.mBytesPerPacket = 0; outputFormat.mFramesPerPacket = 1024; // create the outputFile that we're writing to here.... UInt32 outputFormatSize = sizeof(outputFormat); result = 0; result = AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &outputFormatSize, &outputFormat); if(result != noErr) NSLog(@"could not set the output format with status code %i \n",result); NSMutableString *outputFilePath = [NSMutableString stringWithCapacity: 100]; [outputFilePath setString:@"/Users/You/Desktop/testAudio.m4a"]; NSURL *sourceURL = [NSURL fileURLWithPath:outputFilePath]; result = 0; result = ExtAudioFileCreateWithURL((CFURLRef)sourceURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &outputFileID); if(result != noErr){ NSLog(@"ExtAudioFileCreateWithURL failed for outputFileID with status %i \n", result); } int size = sizeof(clientFormat); result = 0; result = ExtAudioFileSetProperty(inputFileID, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); if(result != noErr) NSLog(@"error on ExtAudioFileSetProperty for input File with result code %i \n", result); size = sizeof(clientFormat); result = 0; result = ExtAudioFileSetProperty(outputFileID, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); if(result != noErr) NSLog(@"error on ExtAudioFileSetProperty for output File with result code %i \n", result); int totalFrames = 0; UInt32 outputFilePacketPosition = 0; //in bytes UInt32 encodedBytes = 0; while (1) { UInt32 bufferByteSize = 22050 * 4 * 2; char srcBuffer[bufferByteSize]; UInt32 numFrames = (bufferByteSize/clientFormat.mBytesPerFrame); AudioBufferList fillBufList; fillBufList.mNumberBuffers = 1; fillBufList.mBuffers[0].mNumberChannels = clientFormat.mChannelsPerFrame; fillBufList.mBuffers[0].mDataByteSize = bufferByteSize; fillBufList.mBuffers[0].mData = srcBuffer; result = 0; result = ExtAudioFileRead(inputFileID, &numFrames, &fillBufList); if (result != noErr) { NSLog(@"Error on ExtAudioFileRead with result code %i \n", result); totalFrames = 0; break; } if (!numFrames) break; totalFrames = totalFrames + numFrames; result = 0; result = ExtAudioFileWrite(outputFileID, numFrames, &fillBufList); if(result!= noErr){ NSLog(@"ExtAudioFileWrite failed with code %i \n", result); } encodedBytes += numFrames * clientFormat.mBytesPerFrame; } //Clean up ExtAudioFileDispose(inputFileID); ExtAudioFileDispose(outputFileID); AudioFileClose(refAudioFileID); } 

而且你还需要这些function

 static OSStatus readProc(void* clientData, SInt64 position, UInt32 requestCount, void* buffer, UInt32* actualCount) { NSData *inAudioData = (NSData *) clientData; size_t dataSize = inAudioData.length; size_t bytesToRead = 0; if(position < dataSize) { size_t bytesAvailable = dataSize - position; bytesToRead = requestCount <= bytesAvailable ? requestCount : bytesAvailable; [inAudioData getBytes: buffer range:NSMakeRange(position, bytesToRead)]; } else { NSLog(@"data was not read \n"); bytesToRead = 0; } if(actualCount) *actualCount = bytesToRead; return noErr; } static SInt64 getSizeProc(void* clientData) { NSData *inAudioData = (NSData *) clientData; size_t dataSize = inAudioData.length; return dataSize; } 

问题是你正试图使用​​ASCII编码从audio字节(MP3帧)创build一个CFURLRef对象。 CFURLCreateWithBytes是用于字节string,而不是二进制数据(即“ http://www.apple.com ”作为char * )。 为了完成你想要使用AudioFileOpenWithCallbacks ,传递你的NSData对象作为refcon,并处理原始的读/寻求在您传入的NSData上的自定义callback。

使用audio队列服务或AVPlayer播放来自stream或内存的audio。