2011-02-10 17 views
9

Ho bisogno di convertire un file WAVE in un file M4A codificato AAC su iOS. Sono consapevole che la codifica AAC non è supportata su dispositivi precedenti o nel simulatore. Lo sto testando prima di eseguire il codice. Ma non riesco ancora a farlo funzionare.Come posso ottenere la codifica AAC con ExtAudioFile su iOS per funzionare?

ho guardato in molto proprio iPhoneExtAudioFileConvertTest esempio di Apple e ho pensato ho seguito esattamente, ma ancora senza fortuna!

Attualmente, viene visualizzato un errore -50 (= errore nell'elenco dei parametri utente) durante il tentativo di impostare il formato client sul file di destinazione. Nel file sorgente, funziona.

Di seguito è riportato il mio codice. Qualsiasi aiuto è molto apprezzato, grazie!

UInt32 size; 

// Open a source audio file. 
ExtAudioFileRef sourceAudioFile; 
ExtAudioFileOpenURL((CFURLRef)sourceURL, &sourceAudioFile); 

// Get the source data format 
AudioStreamBasicDescription sourceFormat; 
size = sizeof(sourceFormat); 
result = ExtAudioFileGetProperty(sourceAudioFile, kExtAudioFileProperty_FileDataFormat, &size, &sourceFormat); 

// Define the output format (AAC). 
AudioStreamBasicDescription outputFormat; 
outputFormat.mFormatID = kAudioFormatMPEG4AAC; 
outputFormat.mSampleRate = 44100; 
outputFormat.mChannelsPerFrame = 2; 

// Use AudioFormat API to fill out the rest of the description. 
size = sizeof(outputFormat); 
AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &outputFormat); 

// Make a destination audio file with this output format. 
ExtAudioFileRef destAudioFile; 
ExtAudioFileCreateWithURL((CFURLRef)destURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &destAudioFile); 

// Create canonical PCM client format. 
AudioStreamBasicDescription clientFormat; 
clientFormat.mSampleRate = sourceFormat.mSampleRate; 
clientFormat.mFormatID = kAudioFormatLinearPCM; 
clientFormat.mFormatFlags = kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; 
clientFormat.mChannelsPerFrame = 2; 
clientFormat.mBitsPerChannel = 16; 
clientFormat.mBytesPerFrame = 4; 
clientFormat.mBytesPerPacket = 4; 
clientFormat.mFramesPerPacket = 1; 

// Set the client format in source and destination file. 
size = sizeof(clientFormat); 
ExtAudioFileSetProperty(sourceAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); 
size = sizeof(clientFormat); 
ExtAudioFileSetProperty(destAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); 

// Make a buffer 
int bufferSizeInFrames = 8000; 
int bufferSize = (bufferSizeInFrames * sourceFormat.mBytesPerFrame); 
UInt8 * buffer = (UInt8 *)malloc(bufferSize); 
AudioBufferList bufferList; 
bufferList.mNumberBuffers = 1; 
bufferList.mBuffers[0].mNumberChannels = clientFormat.mChannelsPerFrame; 
bufferList.mBuffers[0].mData = buffer; 
bufferList.mBuffers[0].mDataByteSize = (bufferSize); 

while(TRUE) 
{ 
    // Try to fill the buffer to capacity. 
    UInt32 framesRead = bufferSizeInFrames; 
    ExtAudioFileRead(sourceAudioFile, &framesRead, &bufferList); 

    // 0 frames read means EOF. 
    if(framesRead == 0) 
     break; 

    // Write. 
    ExtAudioFileWrite(destAudioFile, framesRead, &bufferList); 
} 

free(buffer); 

// Close the files. 
ExtAudioFileDispose(sourceAudioFile); 
ExtAudioFileDispose(destAudioFile); 
+0

Hai mai funzionato? Sto anche cercando di convertire un file .wav in .acc. – RyanG

+0

Hey Ryan, controlla la mia risposta a questo sotto. – Sebastian

risposta

0

Sei sicuro la frequenza di campionamento partita? È possibile stampare i valori per clientFormat e outputFormat nel momento in cui si verifica l'errore? In caso contrario, penso che potrebbe essere necessario un AudioConverter.

11

Ho risposto alla mia domanda: ho dovuto passare questo problema al mio collega e l'ha fatto funzionare! Non ho mai avuto la possibilità di analizzare il mio problema originale, ma ho pensato, lo posterei qui per motivi di completezza. Il seguente metodo è chiamato all'interno di un NSThread. I parametri sono impostati tramite il 'threadDictionary' e ha creato un delegato personalizzato per trasmettere un feedback corso (scusate, SO non capisce la formattazione correttamente, la seguente si suppone che sia un blocco di implementazione del metodo):

- (void)encodeToAAC 
{ 
    RXAudioEncoderStatusType encoderStatus; 
    OSStatus result = noErr; 
    BOOL success = NO; 
    BOOL cancelled = NO; 
    UInt32 size; 

    ExtAudioFileRef sourceAudioFile,destAudioFile; 
    AudioStreamBasicDescription sourceFormat,outputFormat, clientFormat; 

    SInt64 totalFrames; 
    unsigned long long encodedBytes, totalBytes; 

    int bufferSizeInFrames, bufferSize; 
    UInt8 * buffer; 
    AudioBufferList bufferList; 

    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; 

    NSFileManager * fileManager = [[[NSFileManager alloc] init] autorelease]; 

    NSMutableDictionary * threadDict = [[NSThread currentThread] threadDictionary]; 

    NSObject<RXAudioEncodingDelegate> * delegate = (NSObject<RXAudioEncodingDelegate> *)[threadDict objectForKey:@"Delegate"]; 

    NSString *sourcePath = (NSString *)[threadDict objectForKey:@"SourcePath"]; 
    NSString *destPath = (NSString *)[threadDict objectForKey:@"DestinationPath"]; 

    NSURL * sourceURL = [NSURL fileURLWithPath:sourcePath]; 
    NSURL * destURL = [NSURL fileURLWithPath:destPath]; 

    // Open a source audio file. 
    result = ExtAudioFileOpenURL((CFURLRef)sourceURL, &sourceAudioFile); 
    if(result != noErr) 
    { 
     DLog(@"Error in ExtAudioFileOpenURL: %ld", result); 
     goto bailout; 
    } 

    // Get the source data format 
    size = sizeof(sourceFormat); 
    result = ExtAudioFileGetProperty(sourceAudioFile, kExtAudioFileProperty_FileDataFormat, &size, &sourceFormat); 
    if(result != noErr) 
    { 
     DLog(@"Error in ExtAudioFileGetProperty: %ld", result); 
     goto bailout; 
    } 

    // Define the output format (AAC). 
    memset(&outputFormat, 0, sizeof(outputFormat)); 
    outputFormat.mFormatID = kAudioFormatMPEG4AAC; 
    outputFormat.mSampleRate = 44100; 
    outputFormat.mFormatFlags = kMPEG4Object_AAC_Main; 
    outputFormat.mChannelsPerFrame = 2; 
    outputFormat.mBitsPerChannel = 0; 
    outputFormat.mBytesPerFrame = 0; 
    outputFormat.mBytesPerPacket = 0; 
    outputFormat.mFramesPerPacket = 1024; 


    // Use AudioFormat API to fill out the rest of the description. 
    //size = sizeof(outputFormat); 
    //AudioFormatGetProperty(kAudioFormatProperty_FormatInfo, 0, NULL, &size, &outputFormat); 

    // Make a destination audio file with this output format. 
    result = ExtAudioFileCreateWithURL((CFURLRef)destURL, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &destAudioFile); 
    if(result != noErr) 
    { 
     DLog(@"Error creating destination file: %ld", result); 
     goto bailout; 
    } 

    // Create the canonical PCM client format. 
    memset(&clientFormat, 0, sizeof(clientFormat)); 
    clientFormat.mSampleRate = sourceFormat.mSampleRate; 
    clientFormat.mFormatID = kAudioFormatLinearPCM; 
    clientFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; //kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger; 
    clientFormat.mChannelsPerFrame = 2; 
    clientFormat.mBitsPerChannel = 16; 
    clientFormat.mBytesPerFrame = 4; 
    clientFormat.mBytesPerPacket = 4; 
    clientFormat.mFramesPerPacket = 1; 

    // Set the client format in source and destination file. 
    size = sizeof(clientFormat); 
    result = ExtAudioFileSetProperty(sourceAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); 
    if(result != noErr) 
    { 
     DLog(@"Error while setting client format in source file: %ld", result); 
     goto bailout; 
    } 
    size = sizeof(clientFormat); 
    result = ExtAudioFileSetProperty(destAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat); 
    if(result != noErr) 
    { 
     DLog(@"Error while setting client format in destination file: %ld", result); 
     goto bailout; 
    } 

    // Make a buffer 
    bufferSizeInFrames = 8000; 
    bufferSize = (bufferSizeInFrames * sourceFormat.mBytesPerFrame); 
    buffer = (UInt8 *)malloc(bufferSize); 

    bufferList.mNumberBuffers = 1; 
    bufferList.mBuffers[0].mNumberChannels = clientFormat.mChannelsPerFrame; 
    bufferList.mBuffers[0].mData = buffer; 
    bufferList.mBuffers[0].mDataByteSize = (bufferSize); 

    // Obtain total number of audio frames to encode 
    size = sizeof(totalFrames); 
    result = ExtAudioFileGetProperty(sourceAudioFile, kExtAudioFileProperty_FileLengthFrames, &size, &totalFrames); 
    if(result != noErr) 
    { 
     DLog(@"Error in ExtAudioFileGetProperty, could not get kExtAudioFileProperty_FileLengthFrames from sourceFile: %ld", result); 
     goto bailout; 
    } 

    encodedBytes = 0; 
    totalBytes = totalFrames * sourceFormat.mBytesPerFrame; 
    [threadDict setValue:[NSValue value:&totalBytes withObjCType:@encode(unsigned long long)] forKey:@"TotalBytes"]; 

    if (delegate != nil) 
     [self performSelectorOnMainThread:@selector(didStartEncoding) withObject:nil waitUntilDone:NO]; 

    while(TRUE) 
    { 
     // Try to fill the buffer to capacity. 
     UInt32 framesRead = bufferSizeInFrames; 
     result = ExtAudioFileRead(sourceAudioFile, &framesRead, &bufferList); 
     if(result != noErr) 
     { 
      DLog(@"Error in ExtAudioFileRead: %ld", result); 
      success = NO; 
      break; 
     } 

     // 0 frames read means EOF. 
     if(framesRead == 0) { 
      success = YES; 
      break; 
     } 

     // Write. 
     result = ExtAudioFileWrite(destAudioFile, framesRead, &bufferList); 
     if(result != noErr) 
     { 
      DLog(@"Error in ExtAudioFileWrite: %ld", result); 
      success = NO; 
      break; 
     } 

     encodedBytes += framesRead * sourceFormat.mBytesPerFrame; 

     if (delegate != nil) 
      [self performSelectorOnMainThread:@selector(didEncodeBytes:) withObject:[NSValue value:&encodedBytes withObjCType:@encode(unsigned long long)] waitUntilDone:NO]; 

     if ([[NSThread currentThread] isCancelled]) { 
      cancelled = YES; 
      DLog(@"Encoding was cancelled."); 
      success = NO; 
      break; 
     } 
    } 

    free(buffer); 

    // Close the files. 
    ExtAudioFileDispose(sourceAudioFile); 
    ExtAudioFileDispose(destAudioFile); 

bailout: 
    encoderStatus.result = result; 
    [threadDict setValue:[NSValue value:&encoderStatus withObjCType:@encode(RXAudioEncoderStatusType)] forKey:@"EncodingError"]; 

    // Report to the delegate if one exists 
    if (delegate != nil) 
     if (success) 
      [self performSelectorOnMainThread:@selector(didEncodeFile) withObject:nil waitUntilDone:YES]; 
     else if (cancelled) 
      [self performSelectorOnMainThread:@selector(encodingCancelled) withObject:nil waitUntilDone:YES]; 
     else 
      [self performSelectorOnMainThread:@selector(failedToEncodeFile) withObject:nil waitUntilDone:YES]; 

    // Clear the partially encoded file if encoding failed or is cancelled midway 
    if ((cancelled || !success) && [fileManager fileExistsAtPath:destPath]) 
     [fileManager removeItemAtURL:destURL error:NULL]; 

    [threadDict setValue:[NSNumber numberWithBool:NO] forKey:@"isEncoding"]; 

    [pool release]; 
} 
+0

Sembra buono, perché non contrassegnarlo come la risposta corretta? – newenglander

+0

Grazie. È ottimo! – John

0

Ho provato il codice nella risposta di Sebastian e mentre ha funzionato per i file non compressi (aif, wav, caf), non ha funzionato per un file compresso con perdita (mp3). Ho anche avuto un codice di errore di -50, ma in ExtAudioFileRead anziché ExtAudioFileSetProperty. Da questo question ho appreso che questo errore indica un problema con i parametri della funzione. Risulta il buffer per la lettura del file audio aveva una dimensione di 0 byte, il risultato di questa linea:

int bufferSize = (bufferSizeInFrames * sourceFormat.mBytesPerFrame); 

Commutazione per utilizzare il byte per frame da clientFormat invece (sourceFormat s' valore era 0) lavorato per me:

int bufferSize = (bufferSizeInFrames * clientFormat.mBytesPerFrame); 

questa linea è stata anche nel codice domanda, ma non credo che era il problema (ma ho avuto troppo testo di un commento).

+0

iOS non ha un compressore mp3 –

Problemi correlati