使用AVFoundation使用UIImages生成电影

时间:2014-05-12 20:51:46

标签: iphone uiimage avfoundation ios7.1 movie

在我之前,很多人都在关于这个主题的堆栈溢出中分享了他们的知识。由于贡献,我能够接管大部分技巧和代码片段。这一切都很好,除了工作记忆常常很难。我正在研究的这个延时应用程序能够用2000高清图像生成一部电影,但是自从iOS 7.1以来,它无法生成超过240个高清图像的视频。 240张图片似乎是iPhone 5s的限制。我想知道是否有人也遇到过这些问题,是否有人找到了解决方案。现在来源代码。

此部分迭代应用程序文档目录中保存的uiimages。

if ([adaptor.assetWriterInput isReadyForMoreMediaData])
{
            CMTime frameTime = CMTimeMake(1, fps);
            CMTime lastTime=CMTimeMake(i, fps);
            CMTime presentTime=CMTimeAdd(lastTime, frameTime);

            NSString *imageFilePath = [NSString stringWithFormat:@"%@/%@",folderPathName, imageFileName];
            image = [UIImage imageWithContentsOfFile:imageFilePath] ;
            cgimage = [image CGImage];

            buffer = (CVPixelBufferRef)[self pixelBufferFromCGImage: cgimage];
            bool result = [adaptor appendPixelBuffer:buffer withPresentationTime:presentTime];


            if (result == NO)
            {
                NSLog(@"failed to append buffer %i", i);    
                _videoStatus = 0;
                success = NO;

                return success;
            }

     //buffer has to be released here or memory pressure will occur 
            if(buffer != NULL)
            {
                CVBufferRelease(buffer);
                buffer = NULL;
            }
}

这是本地方法,似乎最麻烦。它从cgimage获取像素缓冲区引用。

- (CVPixelBufferRef) pixelBufferFromCGImage: (CGImageRef) image 
{

NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                         [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                         [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                         nil];



CVPixelBufferRef pxbuffer = NULL;
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, CGImageGetWidth(image),
                                      CGImageGetHeight(image), kCVPixelFormatType_32ARGB, (CFDictionaryRef) CFBridgingRetain(options),
                                      &pxbuffer);

NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);
CVPixelBufferLockBaseAddress(pxbuffer, 0);    
void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
 NSParameterAssert(pxdata != NULL);

CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata, CGImageGetWidth(image),
                                             CGImageGetHeight(image), 8, 4*CGImageGetWidth(image), rgbColorSpace,                                               (CGBitmapInfo)kCGImageAlphaNoneSkipFirst);

NSParameterAssert(context);
CGContextConcatCTM(context, CGAffineTransformMakeRotation(M_PI));

float width = CGImageGetWidth(image);
float height = CGImageGetHeight(image);

CGContextDrawImage(context, CGRectMake(0, 0, width,
                                           height), image);

CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context);


CVPixelBufferUnlockBaseAddress(pxbuffer, 0);


return pxbuffer;
}

我花了很多时间在这上面而不是前进。非常感谢帮助。如果需要更多细节,我很乐意提供。

2 个答案:

答案 0 :(得分:2)

我使用非常相似的代码,尽管原因略有不同(我使用AVAssetReader,将帧作为图像抓取并操纵它们)。然而,净结果应该是相似的 - 我在没有问题的情况下迭代1000张图像。

我注意到我做的两件事情是不同的:

  1. 当您释放缓冲区时,您正在使用CVBufferRelease,我正在使用 CVPixelBufferRelease。
  2. 您没有使用CGImageRelease释放CGImage。
  3. 尝试重写此内容:

     //buffer has to be released here or memory pressure will occur 
            if(buffer != NULL)
            {
                CVBufferRelease(buffer);
                buffer = NULL;
            }
    

    为:

     //buffer has to be released here or memory pressure will occur 
            if(buffer != NULL)
            {
                CVPixelBufferRelease(buffer);
                buffer = NULL;
            }
            CGImageRelease(cgImage);
    

    让我知道这是怎么回事。

    编辑:这是我的代码示例,获取并发布CGImageRef。 Image是从读取器缓冲区中提取并过滤的CIImage创建的。

                    CGImageRef finalImage = [context createCGImage:outputImage fromRect:[outputImage extent]];
    
                // 2. Grab the size
                CGSize size = CGSizeMake(CGImageGetWidth(finalImage), CGImageGetHeight(finalImage));
    
                // 3. Convert the CGImage to a PixelBuffer
                CVPixelBufferRef pxBuffer = NULL;
                pxBuffer = [self pixelBufferFromCGImage: finalImage andSize: size];
    
                // 4. Write things back out.
                // Calculate the frame time
                CMTime frameTime = CMTimeMake(1, 30);
                CMTime presentTime=CMTimeAdd(currentTime, frameTime);
    
                [_ugcAdaptor appendPixelBuffer:pxBuffer withPresentationTime:presentTime];
    
                CGImageRelease(finalImage);
    
                CVPixelBufferRelease(pxBuffer);
    

答案 1 :(得分:1)

最后我找到了解决问题的方法,我的代码中有两点需要改变。

  1. 我将方法(CVPixelBufferRef)pixelBufferFromImage:(UIImage *)image 的参数类型从CGImageRef更改为UIImage。现在的原因主要是简化代码,以便更容易实现即将到来的修正。
  2. Autoreleasepool被引入此方法。现在这是解决方案的实际关键。 CGImageRef cgimage = [image CGImage]; ,该方法的所有其他组件必须包含在Autoreleasepool中。
  3. 代码看起来像这样。

    - (CVPixelBufferRef) pixelBufferFromImage: (UIImage*) image withOrientation:(ImageOrientation)orientation
    {
     @autoreleasepool
     {    
     CGImageRef cgimage = [image CGImage];
    
     NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                             nil];
    
     CVPixelBufferRef pxbuffer = NULL;
    
     float width =  CGImageGetWidth(cgimage);
     float height = CGImageGetHeight(cgimage);
    
    
     CVPixelBufferCreate(kCFAllocatorDefault,width,
                                          height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef)(options),
                                          &pxbuffer);
    
     CVPixelBufferLockBaseAddress(pxbuffer, 0);
    
     void *pxdata = CVPixelBufferGetBaseAddress(pxbuffer);
    
     NSParameterAssert(pxdata != NULL);
    
     CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
     CGContextRef context = CGBitmapContextCreate(pxdata, width,
                                                 height, 8, 4*width, rgbColorSpace,
                                                  (CGBitmapInfo)kCGImageAlphaNoneSkipFirst);
    
    
      CGContextConcatCTM(context, CGAffineTransformMakeRotation(-M_PI/2));
    
      CGContextDrawImage(context, CGRectMake(-height, 0, height, width), cgimage);
    
      CGColorSpaceRelease(rgbColorSpace);
      CGContextRelease(context);
    
      CVPixelBufferUnlockBaseAddress(pxbuffer, 0);
    
      return pxbuffer;
     }
    }
    

    使用此解决方案,以相当慢的速度生成超过2000张图像的高清电影,但它似乎非常可靠,这是最重要的。