Оптимизация CVPixelBufferRef

Я работаю над проектом, в котором я генерирую видео из UIImage с кодом, который я нашел здесь, и я уже несколько дней пытаюсь его оптимизировать (примерно для 300 изображений). , на симуляторе это занимает минут 5, а на устройстве просто вылетает из-за памяти).

Начну с рабочего кода, который у меня есть сегодня (я работаю с дугой):

-(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, иначе в результирующем видео все перепуталось, и я не могу понять почему (могу выложить скрины, если поможет; пиксели вроде как с моего изображения, но ширина не правильная, и есть большой черный квадрат re на половину нижней части видео).

Другая проблема заключается в том, что требуется очень большой объем памяти; Я думаю, что буфер пикселей не может быть хорошо освобожден, но я не понимаю, почему.

Наконец, он очень медленный, но у меня есть две идеи по его улучшению, которые я не использую.

  • Во-первых, избегать использования UIImage для создания моих пиксельных буферов, поскольку я сам генерирую UIImage с данными (uint8_t *). Я пытался использовать CVPixelBufferCreateWithBytes, но это не сработало.Вот как я это пробовал:

    OSType pixFmt = CVPixelBufferGetPixelFormatType(pxbuffer);
    CVPixelBufferCreateWithBytes(kCFAllocatorDefault, ширина, высота, pixFmt, self.composition.srcImage.resultImageData, ширина*2, NULL, NULL, атрибуты (__bridge CFDictionaryRef), &pxbuffer);
    

(Аргументы те же, что и для вышеперечисленных функций; данные моего изображения закодированы в 16 бит на пиксель, и я не смог найти хороший аргумент OSType для передачи функции.) Если кто-то знает, как его использовать (может быть, это невозможно с данными 16 бит/пиксель?), это поможет мне избежать действительно бесполезного преобразования.

  • Во-вторых, я хотел бы избежать kCVPixelFormatType_32ARGB для своего видео. Я предполагаю, что было бы быстрее использовать что-то с меньшим количеством битов на пиксель, но когда я пытаюсь это сделать (я пробовал все форматы kCVPixelFormatType_16XXXXX, с контекстом, созданным с 5 битами на компонент и kCGImageAlphaNoneSkipFirst), либо он падает, либо результирующее видео ничего не содержит (с kCVPixelFormatType_16BE555).

Я знаю, что много прошу всего в одном посте, но я как-то потерялся в этом коде, я перепробовал столько комбинаций, и ни одна из них не сработала...

6
задан Grhyll 14 June 2012 в 12:51
поделиться