2011-02-10 19 views
9

Necesito convertir un archivo WAVE en un archivo AAC codificado M4A en iOS. Soy consciente de que la codificación AAC no es compatible con dispositivos más antiguos o en el simulador. Lo estoy probando antes de ejecutar el código. Pero todavía no puedo hacer que funcione.¿Cómo puedo hacer que funcione la codificación AAC con ExtAudioFile en iOS?

Miré en el propio ejemplo de iPhoneExtAudioFileConvertTest de Apple y pensé que lo seguí exactamente, ¡pero todavía no tuve suerte!

Actualmente, obtengo un -50 (= error en la lista de parámetros de usuario) al intentar establecer el formato del cliente en el archivo de destino. En el archivo fuente, funciona.

A continuación se muestra mi código. Cualquier ayuda es muy apreciada, ¡gracias!

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

¿Alguna vez funciona esto? Estoy buscando convertir un .wav a .acc también. – RyanG

+0

Hola Ryan, revisa mi propia respuesta a esto a continuación. – Sebastian

Respuesta

0

¿Estás seguro de que las frecuencias de muestra coinciden? ¿Puedes imprimir los valores de clientFormat y outputFormat en el punto donde recibes el error? De lo contrario, creo que es posible que necesite un AudioConverter.

11

Respondió mi propia pregunta: ¡Tenía que pasarle este problema a mi colega y él lo hizo funcionar! Nunca tuve la oportunidad de analizar mi problema original, pero pensé que lo publicaría aquí para completar. El siguiente método se llama desde dentro de un NSThread. Los parámetros se ajustan a través de la 'threadDictionary' y creó un delegado personalizada para transmitir la realimentación del progreso (lo siento, SO no entiende el formato correctamente, se supone que lo siguiente es un bloque de implementación del método):

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

Se ve bien, ¿por qué no marcar esto como la respuesta correcta? – newenglander

+0

Gracias. ¡Es genial! – John

0

Probé el código en la respuesta de Sebastian y, aunque funcionaba para archivos sin comprimir (aif, wav, caf), no lo hizo para un archivo comprimido con pérdida (mp3). También tuve un código de error de -50, pero en ExtAudioFileRead en lugar de ExtAudioFileSetProperty. De este question aprendí que este error significa un problema con los parámetros de la función. Resulta que el búfer para leer el archivo de audio tenían un tamaño de 0 bytes, el resultado de esta línea:

int bufferSize = (bufferSizeInFrames * sourceFormat.mBytesPerFrame); 

conmutación para utilizar el los bytes por trama desde clientFormat lugar (sourceFormat 's valor de 0) trabajado para mí:

int bufferSize = (bufferSizeInFrames * clientFormat.mBytesPerFrame); 

Esta línea también se encontraba en el código de pregunta, pero no creo que era el problema (pero no tenía demasiado texto de un comentario).

+0

iOS no tiene un compresor mp3 –

Cuestiones relacionadas