iOS: утечка памяти в VideoToolBox при создании видеофайла из списка изображений

Справка

Я прошел через хорошие ссылки в SO для создания видеофайла из NSArray изображений. Одним из наиболее полезных было это SO-ссылки

ВОПРОС

  • Теперь в VideoToolBox происходит утечка памяти. (я подключил экранный снимок инструмента во время работы приложения в iOS 5.1 Simulator) Экран экрана инструмента
  • Мое приложение использует 346 МБ памяти при создании этого видео. Главным образом из-за этого метода.
 (BOOL)appendPixelBuffer:(CVPixelBufferRef)pixelBuffer withPresentationTime:(CMTime)presentationTime 

Этот метод класса AVAssetWriterInputPixelBufferAdaptor сохранит все CVPixelBufferRef до тех пор, пока видео не будет создано.

КОД

Я создал класс ImageToVideo.m NSOperation для создания видео с изображений.

 #import "ImageToVideo.h" @implementation ImageToVideo //global pixcel buffer. CVPixelBufferRef pxbuffer = NULL; - (void) pixelBufferFromCGImage:(CGImageRef)image size:(CGSize)size { NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil]; CVBufferRelease(pxbuffer); CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, size.width, size.height, kCVPixelFormatType_32ARGB, (CFDictionaryRef) options, &pxbuffer); options = nil; [options release]; status=status;//Added to make the stupid compiler not show a stupid warning. NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL); CVPixelBufferLockBaseAddress(pxbuffer, 0); void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer); NSParameterAssert(pxdata != NULL); CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef context = CGBitmapContextCreate(pxdata, size.width, size.height, 8, 4*size.width, rgbColorSpace, kCGImageAlphaNoneSkipFirst); NSParameterAssert(context); CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image); CGColorSpaceRelease(rgbColorSpace); CGContextRelease(context); CVPixelBufferUnlockBaseAddress(pxbuffer, 0); } - (void)writeImageAsMovietoPath:(NSString*)path size:(CGSize)size { NSError *error = nil; AVAssetWriter *videoWriter = [[AVAssetWriter alloc] initWithURL: [NSURL fileURLWithPath:path] fileType:AVFileTypeQuickTimeMovie error:&error]; NSParameterAssert(videoWriter); NSDictionary *videoSettings = [NSDictionary dictionaryWithObjectsAndKeys: AVVideoCodecH264, AVVideoCodecKey, [NSNumber numberWithInt:size.width], AVVideoWidthKey, [NSNumber numberWithInt:size.height], AVVideoHeightKey, nil]; AVAssetWriterInput* writerInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings] retain]; AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:nil]; NSParameterAssert(writerInput); NSParameterAssert([videoWriter canAddInput:writerInput]); [videoWriter addInput:writerInput]; NSMutableArray *photoImages = [[[NSMutableArray alloc] init] autorelease]; //Creating a image Array for (int i = 0; i = [photoImages count]) { CVBufferRelease(pxbuffer); pxbuffer = NULL; } else { //creating the pixcel buffer. [self pixelBufferFromCGImage:[[photoImages objectAtIndex:i] CGImage] size:CGSizeMake(640, 980)]; } if (pxbuffer) { // append buffer [adaptor appendPixelBuffer:pxbuffer withPresentationTime:presentTime]; CVBufferRelease(pxbuffer); pxbuffer = NULL; i++; } else { //Finish the session: [writerInput markAsFinished]; [videoWriter finishWriting]; CVPixelBufferPoolRelease(adaptor.pixelBufferPool); [videoWriter release]; [writerInput release]; NSLog (@"Done"); break; } } } //release the photoImage array. [photoImages removeAllObjects]; } - (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo { // Unable to save the image if (error) { UIAlertView *alert; alert = [[UIAlertView alloc] initWithTitle:@"Error" message:@"Unable to save image to Photo Album." delegate:self cancelButtonTitle:@"Ok" otherButtonTitles:nil]; [alert show]; [alert release]; } } - (void) main { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSLog(@"Operation Started"); NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"Documents/movie.mp4"]]; CGSize size = CGSizeMake(640, 960); [self writeImageAsMovietoPath:path size:size] ; [pool drain]; } @end 

Вопрос

  • Почему в VideoToolBox происходит утечка памяти?
  • Есть ли лучший способ конвертировать список изображений в видео в iOS?
  • Как я могу уменьшить объем памяти приложения при создании видео?

В VideoToolBox нет реальной утечки. Это похоже на то, что при запуске в симуляторе, но при запуске на устройстве утечки не будет. Ответ на части 2 и 3 вашего вопроса: да, есть лучший способ. Вы можете использовать пул пиксельных буферов для повышения производительности и меньшего распределения памяти, но вы должны сделать это «правильно».

Я не собираюсь тратить время на резку и склеивание, так как вы уже испытали недостаток, пытаясь скопировать код других людей через SO. Эта операция кодирования фильмов очень сложна, и API Apple не так просты в использовании. Было бы лучше взглянуть на фактическую готовую версию рабочего кода вместо того, чтобы пытаться вырезать и вставить свой путь к успеху. В принципе, вы должны вызвать CVPixelBufferPoolCreatePixelBuffer () вместо CVPixelBufferCreate (), чтобы создать буфер пикселей, но повторно использовать и существующий из пула, если он уже существует.

Посмотрите на класс AVAssetWriterConvertFromMaxvid.m, он является частью моей библиотеки AVAnimator на github. Этот класс показывает реальный рабочий пример пула буферов пикселей и то, как ваш код должен взаимодействовать с циклом запуска вторичного потока, чтобы избежать застревания в определенных ситуациях кодирования.