2

参考

我已经通过 SO 中的可能很好的链接从图像的 NSArray 创建视频文件。最有用的一个是这个SO Question Links

问题

  • 现在 VideoToolBox 中存在内存泄漏。(我在 iOS 5.1 模拟器中运行应用程序时附上了仪器的屏幕截图)仪器屏幕截图
  • 我的应用程序在创建此视频时使用了 346 MB 的内存。主要是因为这个方法。
 
    (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 中的视频?
  • 创建视频时如何减少我的应用程序内存占用?
4

1 回答 1

1

VideoToolBox 中没有真正的泄漏。在模拟器中运行时看起来确实有,但在设备上运行时不会泄漏。您问题的第 2 部分和第 3 部分的答案是肯定的,有更好的方法。您可以使用像素缓冲池来获得更好的性能和更少的内存分配,但您必须“恰到好处”地做到这一点。

我不会在剪切和粘贴上浪费您的时间,因为您已经体验过尝试通过 SO 复制其他人的代码的缺点。这种电影编码操作非常复杂,Apple API 也不是那么容易使用。最好看一下工作代码的实际完成版本,而不是尝试剪切和粘贴您的成功之路。基本上,您应该调用 CVPixelBufferPoolCreatePixelBuffer() 而不是 CVPixelBufferCreate() 来创建一个像素缓冲区,但如果它已经存在,则重用和现有的像素缓冲区。

看看AVAssetWriterConvertFromMaxvid.m类,它是我在 github 上的 AVAnimator 库的一部分。本课程展示了一个像素缓冲池的真实工作示例,以及您的代码需要如何与辅助线程的运行循环进行交互,以避免在某些编码情况下卡住。

于 2013-06-06T23:25:15.437 回答