iOS revertir el audio a través de AVAssetWriter

Estoy intentando revertir el audio en iOS con AVAsset y AVAssetWriter. El siguiente código funciona, pero el file de salida es más corto que el de input. Por ejemplo, el file de input tiene una duración de 1:59, pero tiene una salida de 1:50 con el mismo contenido de audio.

- (void)reverse:(AVAsset *)asset { AVAssetReader* reader = [[AVAssetReader alloc] initWithAsset:asset error:nil]; AVAssetTrack* audioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0]; NSMutableDictionary* audioReadSettings = [NSMutableDictionary dictionary]; [audioReadSettings setValue:[NSNumber numberWithInt:kAudioFormatLinearPCM] forKey:AVFormatIDKey]; AVAssetReaderTrackOutput* readerOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:audioTrack outputSettings:audioReadSettings]; [reader addOutput:readerOutput]; [reader startReading]; NSDictionary *outputSettings = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt: kAudioFormatMPEG4AAC], AVFormatIDKey, [NSNumber numberWithFloat:44100.0], AVSampleRateKey, [NSNumber numberWithInt:2], AVNumberOfChannelsKey, [NSNumber numberWithInt:128000], AVEncoderBitRateKey, [NSData data], AVChannelLayoutKey, nil]; AVAssetWriterInput *writerInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeAudio outputSettings:outputSettings]; NSString *exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"out.m4a"]; NSURL *exportURL = [NSURL fileURLWithPath:exportPath]; NSError *writerError = nil; AVAssetWriter *writer = [[AVAssetWriter alloc] initWithURL:exportURL fileType:AVFileTypeAppleM4A error:&writerError]; [writerInput setExpectsMediaDataInRealTime:NO]; [writer addInput:writerInput]; [writer startWriting]; [writer startSessionAtSourceTime:kCMTimeZero]; CMSampleBufferRef sample = [readerOutput copyNextSampleBuffer]; NSMutableArray *samples = [[NSMutableArray alloc] init]; while (sample != NULL) { sample = [readerOutput copyNextSampleBuffer]; if (sample == NULL) continue; [samples addObject:(__bridge id)(sample)]; CFRelease(sample); } NSArray* reversedSamples = [[samples reverseObjectEnumerator] allObjects]; for (id reversedSample in reversedSamples) { if (writerInput.readyForMoreMediaData) { [writerInput appendSampleBuffer:(__bridge CMSampleBufferRef)(reversedSample)]; } else { [NSThread sleepForTimeInterval:0.05]; } } [writerInput markAsFinished]; dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0); dispatch_async(queue, ^{ [writer finishWriting]; }); } 

ACTUALIZAR:

Si escribo muestras directamente en el primer loop while – todo está bien (incluso con check writerInput.readyForMoreMediaData ). En este caso, el file de resultados tiene exactamente la misma duración que el original. Pero si escribo las mismas muestras de NSArray invertido, el resultado es más corto.

Imprima el tamaño de cada búfer en el número de muestras (a través del lector de "lectura" Encima mientras bucle), y repita en el script "writing" Input for-loop. De esta manera, puede ver todos los tamaños de búfer y ver si se sumn.

Por ejemplo, si está faltando o omitiendo un búfer if (writerInput.readyForMoreMediaData) es falso, " if (writerInput.readyForMoreMediaData) ", pero luego pasa a la siguiente muestra invertida en Muestras invertidas (ese búfer se elimina efectivamente de la input de escritura)

ACTUALIZACIÓN (según los comentarios): encontré en el código, hay dos problemas:

  1. La configuration de salida es incorrecta (el file de input es mono ( 1 canal), pero la configuration de salida está configurada para 2 canales. Debe ser: [NSNumber numberWithInt:1], AVNumberOfChannelsKey . Mire la información de salida y los files de input:

introduzca la descripción de la imagen aquíintroduzca la descripción de la imagen aquí

  1. El segundo problema es que está invirtiendo 643 buffers de 8192 muestras de audio, en lugar de invertir el índice de cada muestra de audio. Para ver cada búfer, cambié su debugging de mirar el tamaño de cada muestra para ver el tamaño del búfer, que es 8192. Entonces, la línea 76 ahora es: size_t sampleSize = CMSampleBufferGetNumSamples(sample);

El resultado se ve así:

 2015-03-19 22:26:28.171 audioReverse[25012:4901250] Reading [0]: 8192 2015-03-19 22:26:28.172 audioReverse[25012:4901250] Reading [1]: 8192 ... 2015-03-19 22:26:28.651 audioReverse[25012:4901250] Reading [640]: 8192 2015-03-19 22:26:28.651 audioReverse[25012:4901250] Reading [641]: 8192 2015-03-19 22:26:28.651 audioReverse[25012:4901250] Reading [642]: 5056 2015-03-19 22:26:28.651 audioReverse[25012:4901250] Writing [0]: 5056 2015-03-19 22:26:28.652 audioReverse[25012:4901250] Writing [1]: 8192 ... 2015-03-19 22:26:29.134 audioReverse[25012:4901250] Writing [640]: 8192 2015-03-19 22:26:29.135 audioReverse[25012:4901250] Writing [641]: 8192 2015-03-19 22:26:29.135 audioReverse[25012:4901250] Writing [642]: 8192 

Esto muestra que está invirtiendo el order de cada búfer de 8192 muestras, pero en cada búfer el audio sigue "mirando hacia adelante". Podemos ver esto en esta captura de pantalla que tomé de una inversión inversa (muestra por muestra) en comparación con la inversión de su búfer:

introduzca la descripción de la imagen aquí

Creo que su esquema actual puede funcionar si también invierte cada muestra de cada búfer 8192. Personalmente, no recomendaría el uso de los enumeradores NSArray para el procesamiento de la señal, pero puede funcionar si usted opera en el nivel de muestra.