Для моего приложения мне нужно декодировать MP3-файл, который хранится в объекте NSData.Декодировать MP3-файл из NSData
По соображениям безопасности нежелательно записывать объект NSData на диск и повторно открывать его с использованием ссылки на системный URL-адрес, даже если его локально хранится в течение нескольких секунд.
Я бы хотел использовать расширенные службы аудиофайлов (или службы аудиофайлов), но мне трудно получить представление NSData, которое существует только в памяти, которое может быть прочитано этими аудио Файловые службы.
Редактирование: Я хочу декодировать данные MP3, чтобы я мог получить доступ к линейным образцам аудио PCM для манипуляций. Воспроизведение с объекта 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, который является «файл не найден».
Я проверил, что мой указатель указывает на правильный адрес в памяти для NSData и что байты могут быть считаны правильно.
Есть ли некоторые ограничения для расширенных аудиофайлов, которые запрещают ссылки на байты, хранящиеся в памяти?
Спасибо за любую помощь, которую вы можете предоставить.
Редактировать: Я понял, как это сделать, используя предложение Sbooth. Код ниже: Эта функция принимает объект NSData, содержащий mp3-представление аудиофайла. Он декодирует его как линейный PCM, поэтому вы можете получить образцы, а затем перекодировать его как AAC. Я не думаю, что кодирование MP3 доступно в CoreAudio на всех платформах (мобильный/рабочий). Этот код был протестирован на моем Mac и выполнил эту работу.
-(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);
}
И вам нужны эти функции, а ...
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;
}
Это другая тема, где он или она просто хочет прямой ответ не показывая всю картину того, чего они хотят достичь. –
@TBlue Приношу свои извинения, если моя проблема и вопрос не были достаточно ясными. В основном это происходит следующим образом: 1) Через отдельный процесс дешифрования у меня есть mp3-файл в памяти, хранящийся в объекте NSData. 2) Я бы обработал необработанные линейные образцы PCM, декодированные из этого объекта NSData. 3) Я хочу избежать записи NSData в mp3-файл на диске по соображениям безопасности. Я понимаю, что это кажется странным способом заниматься вещами, но проблема безопасности - это проблема. Это имеет смысл? – rmigneco
Да. Это намного яснее, чем было. –