【问题标题】:Optimization of CVPixelBufferRefCVPixelBufferRef的优化
【发布时间】:2012-06-14 12:51:37
【问题描述】:

我正在做一个项目,在这个项目中我从 UIImage 生成一个视频,代码是我在这里找到的,为了优化它,我已经苦苦挣扎了几天(对于大约 300 张图像,大约需要 5 分钟模拟器,并且由于内存而在设备上简单地崩溃)。

我将从今天的工作代码开始(我使用 arc):

-(void) writeImageAsMovie:(NSArray *)array toPath:(NSString*)path size:(CGSize)size duration:(int)duration 
{
    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];

    AVAssetWriterInputPixelBufferAdaptor *adaptor = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:writerInput sourcePixelBufferAttributes:nil];
    NSParameterAssert(writerInput);
    NSParameterAssert([videoWriter canAddInput:writerInput]);
    [videoWriter addInput:writerInput];


    //Start a session:
    [videoWriter startWriting];
    [videoWriter startSessionAtSourceTime:kCMTimeZero];

    CVPixelBufferRef buffer = NULL;
    buffer = [self newPixelBufferFromCGImage:[[self.frames objectAtIndex:0] CGImage]];

    CVPixelBufferPoolCreatePixelBuffer (NULL, adaptor.pixelBufferPool, &buffer);

    [adaptor appendPixelBuffer:buffer withPresentationTime:kCMTimeZero];

    dispatch_queue_t mediaInputQueue =  dispatch_queue_create("mediaInputQueue", NULL);
    int frameNumber = [self.frames count];

    [writerInput requestMediaDataWhenReadyOnQueue:mediaInputQueue usingBlock:^{
        NSLog(@"Entering block with frames: %i", [self.frames count]);
        if(!self.frames || [self.frames count] == 0)
        {
            return;
        }
        int i = 1;
        while (1)
        {
            if (i == frameNumber) 
            {
                break;
            }
            if ([writerInput isReadyForMoreMediaData]) 
            {
                freeMemory();
                NSLog(@"inside for loop %d (%i)",i, [self.frames count]);
                UIImage *image = [self.frames objectAtIndex:i];
                CGImageRef imageRef = [image CGImage];
                CVPixelBufferRef sampleBuffer = [self newPixelBufferFromCGImage:imageRef];
                CMTime frameTime = CMTimeMake(1, TIME_STEP);

                CMTime lastTime=CMTimeMake(i, TIME_STEP); 

                CMTime presentTime=CMTimeAdd(lastTime, frameTime);       

                if (sampleBuffer) 
                {
                    [adaptor appendPixelBuffer:sampleBuffer withPresentationTime:presentTime];
                    i++;
                    CVPixelBufferRelease(sampleBuffer);
                } 
                else 
                {
                    break;
                }
            }
        }

        [writerInput markAsFinished];
        [videoWriter finishWriting];
        self.frames = nil;

        CVPixelBufferPoolRelease(adaptor.pixelBufferPool);

    }];
}

现在是获取像素缓冲区的函数,我正在努力解决这个问题:

- (CVPixelBufferRef) newPixelBufferFromCGImage: (CGImageRef) image
{
    CVPixelBufferRef pxbuffer = NULL;

    int width = CGImageGetWidth(image)*2;
    int height = CGImageGetHeight(image)*2;

    NSMutableDictionary *attributes = [NSMutableDictionary dictionaryWithObjectsAndKeys:[NSNumber kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, [NSNumber numberWithInt:width], kCVPixelBufferWidthKey, [NSNumber numberWithInt:height], kCVPixelBufferHeightKey, nil];
    CVPixelBufferPoolRef pixelBufferPool; 
    CVReturn theError = CVPixelBufferPoolCreate(kCFAllocatorDefault, NULL, (__bridge CFDictionaryRef) attributes, &pixelBufferPool);
    NSParameterAssert(theError == kCVReturnSuccess);
    CVReturn status = CVPixelBufferPoolCreatePixelBuffer(NULL, pixelBufferPool, &pxbuffer);
    NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);

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

    CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(pxdata, width,
                                             height, 8, width*4, rgbColorSpace, 
                                             kCGImageAlphaNoneSkipFirst);
    NSParameterAssert(context);
    CGContextDrawImage(context, CGRectMake(0, 0, width, 
                                       height), image);
    CGColorSpaceRelease(rgbColorSpace);
    CGContextRelease(context);

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

    return pxbuffer;
}

第一个奇怪的事情:正如你在这个函数中看到的那样,我必须将宽度和高度乘以2,否则结果视频全乱了,我不明白为什么(如果可以的话我可以发布截图有帮助;像素似乎来自我的图像,但宽度不正确,并且视频的半底部有一个大的黑色方块。

另一个问题是它需要大量的内存;我认为像素缓冲区无法很好地释放,但我不明白为什么。

最后,速度很慢,但是我有两个想法可以改进它,但我没有使用。

  • 首先是避免使用 UIImage 来创建我的像素缓冲区,因为我自己使用 (uint8_t *) 数据生成 UIImage。我尝试使用“CVPixelBufferCreateWithBytes”,但它不起作用。以下是我的尝试:

    OSType pixFmt = CVPixelBufferGetPixelFormatType(pxbuffer);
    CVPixelBufferCreateWithBytes(kCFAllocatorDefault, width, height, pixFmt, self.composition.srcImage.resultImageData, width*2, NULL, NULL, (__bridge CFDictionaryRef) attributes, &pxbuffer);
    

(参数与上述函数相同;我的图像数据以每像素 16 位编码,我找不到一个好的 OSType 参数给函数。) 如果有人知道如何使用它(也许 16 位/像素数据不可能?),它将帮助我避免真正无用的转换。

  • 第二件事是我想在我的视频中避免使用 kCVPixelFormatType_32ARGB。我想使用更少位/像素的东西会更快,但是当我尝试它时(我已经尝试了所有 kCVPixelFormatType_16XXXXX 格式,使用 5 位/组件和 kCGImageAlphaNoneSkipFirst 创建的上下文),要么它崩溃,要么生成视频不包含任何内容(使用 kCVPixelFormatType_16BE555)。

我知道我只在一篇文章中问了很多问题,但我有点迷失在这段代码中,我尝试了这么多组合,但没有一个能奏效......

【问题讨论】:

  • 这最多需要 10 秒。听起来您正在泄漏并复制图像数据。你是如何加载图像的?也许有一种更直接的方法可以将它们加载到 CVPixelBuffer 中。
  • 我昨天才设法得到不泄漏的东西!但是,我不太确定问题出在哪里,因为我一直在同时搜索各个方向...我使用了这里的代码:codethink.no-ip.org/wordpress/archives/673,我对其进行了很多修改并以一些没有的东西结束泄漏......它仍然不完美,因为我从(int *)数据创建图像并稍后将它们再次转换为,但至少它不会因为内存问题而崩溃......
  • 你有这个代码的最终版本吗?我有同样的问题。如果您能分享,我将不胜感激。谢谢。
  • 遇到同样的问题可以帮忙吗?

标签: objective-c ios avassetwriter


【解决方案1】:

I have to multiply the width and height by 2, otherwise, the result video is all messed up, and I can't understand why

点与像素?高 dpi 视网膜屏幕每点的像素数是其两倍。

【讨论】:

    猜你喜欢
    • 2016-01-08
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 2015-01-13
    • 2014-06-13
    • 1970-01-01
    • 1970-01-01
    • 2013-05-18
    相关资源
    最近更新 更多