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?