ios objective-c memory-leaks audio-recording avassetwriter

ios - appendSampleBuffer con un audio AVAssetWriterInput "pierde" la memoria hasta que endSessionAtSourceTime



objective-c memory-leaks (1)

Ya que ha estado esperando un mes por una respuesta, le daré una respuesta poco ideal pero viable.

Podría usar las funciones ExtendedAudioFile para escribir un archivo separado. Luego, simplemente puede reproducir el video y el audio junto con una composición AV. Creo que podrías usar AVFoundation para componer la cafetería y el video juntos sin volver a codificarlos si los necesitas al final de la grabación.

Eso lo pondrá en marcha y luego podrá resolver la pérdida de memoria cuando lo desee.

Tengo una extraña "fuga" de memoria con AVAssetWriterInput appendSampleBuffer . Estoy escribiendo video y audio al mismo tiempo, así que tengo un AVAssetWriter con dos entradas, una para video y otra para audio:

self.videoWriter = [[[AVAssetWriter alloc] initWithURL:[self.currentVideo currentVideoClipLocalURL] fileType:AVFileTypeMPEG4 error:&error] autorelease]; ... self.videoWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings]; self.videoWriterInput.expectsMediaDataInRealTime = YES; [self.videoWriter addInput:self.videoWriterInput]; ... self.audioWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio outputSettings:audioSettings]; self.audioWriterInput.expectsMediaDataInRealTime = YES; [self.videoWriter addInput:self.audioWriterInput];

Empiezo a escribir y todo funciona bien en la superficie. El video y el audio se escriben y se alinean, etc. Sin embargo, puse mi código en el instrumento de asignaciones y noté lo siguiente:

Los bytes de audio se retienen en la memoria, como demostraré en un segundo. Esa es la rampa en la memoria. Los bytes de audio solo se liberan después de que llame a [self.videoWriter endSessionAtSourceTime:...] , que se ve como la caída dramática en el uso de la memoria. Aquí está mi código de escritura de audio, que se envía como un bloque a una cola en serie:

@autoreleasepool { // The objects that will hold the audio data CMSampleBufferRef sampleBuffer; CMBlockBufferRef blockBuffer1; CMBlockBufferRef blockBuffer2; size_t nbytes = numSamples * asbd_.mBytesPerPacket; OSStatus status = noErr; status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault, data, nbytes, kCFAllocatorNull, NULL, 0, nbytes, kCMBlockBufferAssureMemoryNowFlag, &blockBuffer1); if (status != noErr) { NLog(@"CMBlockBufferCreateWithMemoryBlock error at buffer 1"); return; } status = CMBlockBufferCreateContiguous(kCFAllocatorDefault, blockBuffer1, kCFAllocatorDefault, NULL, 0, nbytes, kCMBlockBufferAssureMemoryNowFlag | kCMBlockBufferAlwaysCopyDataFlag, &blockBuffer2); if (status != noErr) { NSLog(@"CMBlockBufferCreateWithMemoryBlock error at buffer 2"); CFRelease(blockBuffer1); return; } // Finally, create the CMSampleBufferRef status = CMAudioSampleBufferCreateWithPacketDescriptions(kCFAllocatorDefault, blockBuffer2, YES, // Yes data is ready NULL, // No callback needed to make data ready NULL, audioFormatDescription_, 1, timestamp, NULL, &sampleBuffer); if (status != noErr) { NSLog(@"CMAudioSampleBufferCreateWithPacketDescriptions error."); CFRelease(blockBuffer1); CFRelease(blockBuffer2); return; } if ([self.audioWriterInput isReadyForMoreMediaData]) { if (![self.audioWriterInput appendSampleBuffer:sampleBuffer]) { NSLog(@"Couldn''t append audio sample buffer: %d", numAudioCallbacks_); } } else { NSLog(@"AudioWriterInput isn''t ready for more data."); } // One release per create CFRelease(blockBuffer1); CFRelease(blockBuffer2); CFRelease(sampleBuffer); }

Como puede ver, estoy liberando cada búfer una vez por creación. He rastreado la "fuga" hasta la línea donde se anexan los buffers de audio:

[self.audioWriterInput appendSampleBuffer:sampleBuffer]

Lo comprobé a mí mismo al comentar esa línea, después de lo cual obtengo el siguiente gráfico de Asignaciones "sin fugas" (aunque el video grabado ahora no tiene audio, por supuesto):

Intenté otra cosa, que es volver a agregar la línea appendSamplebuffer y, en su lugar, a appendSamplebuffer de doble liberación:

CFRelease(blockBuffer1); CFRelease(blockBuffer2); CFRelease(blockBuffer2); // Double release to test the hypothesis that appendSamplebuffer is retaining this CFRelease(sampleBuffer);

Hacer esto no causó una doble blockBuffer2 , lo que indica que la cuenta de blockBuffer2 de blockBuffer2 en ese punto es 2. Esto produjo el mismo gráfico de asignaciones "sin fugas", con la excepción de que cuando llamé [self.videoWriter endSessionAtSourceTime:...] , Recibo un bloqueo de una versión doble (lo que indica que self.videoWriter está intentando liberar todos sus punteros a los blockBuffer2 que se han pasado).

Si por el contrario, intento lo siguiente:

CFRelease(blockBuffer1); CFRelease(blockBuffer2); CMSampleBufferInvalidate(sampleBuffer); // Invalidate sample buffer CFRelease(sampleBuffer);

luego [self.audioWriterInput appendSampleBuffer:sampleBuffer] y la llamada a añadir marcos de video comienza a fallar para cada llamada después de eso.

Así que mi conclusión es que AVAssetWriter o AVAssetWriterInput retiene blockBuffer2 hasta que el video haya terminado de grabarse. Obviamente, esto puede causar problemas de memoria reales si el video se está grabando durante el tiempo suficiente. ¿Estoy haciendo algo mal?

Edición: los bytes de audio que estoy recibiendo son en formato PCM, mientras que el formato de video que estoy escribiendo es MPEG4 y el formato de audio para ese video es MPEG4AAC. ¿Es posible que el escritor de videos esté realizando el formato PCM -> AAC sobre la marcha, y es por eso que se está amortiguando?