2013-06-26 3 views
6

Для моего приложения мне нужно декодировать 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; 
} 
+0

Это другая тема, где он или она просто хочет прямой ответ не показывая всю картину того, чего они хотят достичь. –

+0

@TBlue Приношу свои извинения, если моя проблема и вопрос не были достаточно ясными. В основном это происходит следующим образом: 1) Через отдельный процесс дешифрования у меня есть mp3-файл в памяти, хранящийся в объекте NSData. 2) Я бы обработал необработанные линейные образцы PCM, декодированные из этого объекта NSData. 3) Я хочу избежать записи NSData в mp3-файл на диске по соображениям безопасности. Я понимаю, что это кажется странным способом заниматься вещами, но проблема безопасности - это проблема. Это имеет смысл? – rmigneco

+0

Да. Это намного яснее, чем было. –

ответ

3

Проблема заключается в том, что вы пытаетесь создать объект CFURLRef из аудиобайтов (фреймов MP3) с использованием кодировки ASCII. CFURLCreateWithBytes предназначен для использования с байтовыми строками, а не двоичными данными (то есть «http://www.apple.com» в качестве char *). Для того, чтобы сделать то, что вы хотите использовать AudioFileOpenWithCallbacks, передать свой NSData объект как REFCON и обрабатывать сырое чтение/поиск в пользовательских обратных вызовах работают на NSData, что вы прошли в.

+0

Спасибо! Я смог понять это с помощью AudioFileOpenWithCallbacks. Я отредактировал мой оригинальный вопрос, чтобы включить ответ. – rmigneco

-1

Audio Queue Services Использование или AVPlayer для воспроизведения звука из потока или памяти.

+0

Спасибо, я должен был быть более конкретным в моей просьбе. Мне требуется выборочный уровень доступа к аудио. Таким образом, декодирование mp3 для получения линейных образцов PCM. – rmigneco

+0

Службы очереди аудио должны предоставить вам доступ к образцам аудио –