MP3-Datei von NSData dekodieren

Für meine Anwendung muss ich eine MP3-Datei dekodieren, die in einem NSData-Objekt gespeichert ist.

Aus Sicherheitsgründen ist es nicht wünschenswert, das NSData-Objekt auf die Festplatte zu schreiben und es unter Verwendung einer System-URL-Referenz erneut zu öffnen, selbst wenn es nur für einige Momente lokal gespeichert wird.

Ich würde gerne Extended Audio File Services (oder Audio File Services) dazu nutzen, habe aber Probleme, eine Darstellung der nur im Speicher vorhandenen NSData zu erhalten, die von diesen Audio File Services gelesen werden kann.

Bearbeiten: Ich möchte die MP3-Daten dekodieren, um auf lineare PCM-Audio-Samples zur Bearbeitung zugreifen zu können. Die Wiedergabe von einem NSData-Objekt ist kein Problem.

Mein Code lautet wie folgt:

 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);
        }

Beide Versuche, die URL zu öffnen, führen zu einem OSStatus-Code 43, der "Datei nicht gefunden" ist.

Ich habe überprüft, dass mein Zeiger auf die richtige Adresse im Speicher für die NSData zeigt und dass die Bytes korrekt gelesen werden können.

Gibt es eine Einschränkung für die erweiterten Audiodateidienste, die Verweise auf im Speicher gespeicherte Bytes verbieten?

Vielen Dank für jede Hilfe, die Sie zur Verfügung stellen können.

Bearbeiten: Ich fand heraus, wie es mit Sbooth Vorschlag zu tun ist. Code unten: Diese Funktion nimmt ein NSData-Objekt auf, das eine MP3-Darstellung einer Audiodatei enthält. Es dekodiert es als lineares PCM, sodass Sie die Samples abrufen und dann als AAC neu kodieren können. Ich glaube nicht, dass die MP3-Codierung in CoreAudio auf allen Plattformen (Mobil / Desktop) verfügbar ist. Dieser Code wurde auf meinem Mac getestet und erledigt den Job.

-(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);

}

Und diese Funktionen werden Sie auch brauchen ...

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;
}

Antworten auf die Frage(2)

Ihre Antwort auf die Frage