Запись видео iOS - AVAssetWriterInputPixelBufferAdaptor и CVPixelBuffer различное цветовое пространство - PullRequest
0 голосов
/ 24 июня 2019

Я пытаюсь записать видео на файл из видеопотока в iOS. Видеопоток поступает из openFrameworks и хочет сохранить видео в MP4, используя iOS AVFoundation (AVAssetWriter, AVAssetWriterInput, AVAssetWriterInputPixelBufferAdaptor). Я успешно сохраняю видео, но есть несоответствие между цветовым пространством входного буфера и буфером, который сохраняется AVAssetWriterInputPixelBufferAdaptor.

Это входной кадр и это то, что сохраняется

В буфере определенно содержатся пиксели RGBA (где последний байт равен A, это всегда ff)

Можно ли указать другой формат буфера в AVAssetWriterInputPixelBufferAdaptor? Или единственный вариант - изменить буфер для соответствия ARGB? Какова наилучшая стратегия для изменения буфера из одного цветового пространства в другое?

Я пытался изменить bufferAttributes при создании AVAssetWriterInputPixelBufferAdaptor, но затем я получаю ошибки.

NSDictionary* bufferAttributes = 
  [NSDictionary dictionaryWithObjectsAndKeys:
    [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], 
    kCVPixelBufferPixelFormatTypeKey, nil
  ];

Если я изменю kCVPixelBufferPixelFormatTypeKey на kCVPixelFormatType_32RGBA, я получаю сообщение об ошибке при вызове CVPixelBufferPoolCreatePixelBuffer.

Я основал свою программу на коде этого репозитория github .


    // SETUP
    videoWriter = [[AVAssetWriter alloc] initWithURL:[self tempFileURL] fileType:AVFileTypeQuickTimeMovie error:&error];
        NSParameterAssert(videoWriter);

        //Configure video
        NSDictionary* videoCompressionProps = [NSDictionary dictionaryWithObjectsAndKeys:
                                               [NSNumber numberWithDouble:1024.0*1024.0], AVVideoAverageBitRateKey,
                                               nil ];

        NSDictionary* videoSettings = [NSDictionary dictionaryWithObjectsAndKeys:
                                       AVVideoCodecH264, AVVideoCodecKey,
                                       [NSNumber numberWithInt:size.width], AVVideoWidthKey,
                                       [NSNumber numberWithInt:size.height], AVVideoHeightKey,
                                       videoCompressionProps, AVVideoCompressionPropertiesKey,
                                       nil];

        videoWriterInput = [[AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:videoSettings] retain];

        NSParameterAssert(videoWriterInput);
        videoWriterInput.expectsMediaDataInRealTime = YES;
        NSDictionary* bufferAttributes = [NSDictionary dictionaryWithObjectsAndKeys:
                                          [NSNumber numberWithInt:kCVPixelFormatType_32ARGB], kCVPixelBufferPixelFormatTypeKey, nil];

        avAdaptor = [[AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:videoWriterInput sourcePixelBufferAttributes:bufferAttributes] retain];

        //add input
        [videoWriter addInput:videoWriterInput];
        [videoWriter startWriting];
        [videoWriter startSessionAtSourceTime:CMTimeMake(0, 1000)];



    //ADD VIDEO FRAME
    float millisElapsed = [[NSDate date] timeIntervalSinceDate:startedAt] * 1000.0;
        CMTime time = CMTimeMake((int)millisElapsed, 1000);

        if (![videoWriterInput isReadyForMoreMediaData]) {
            NSLog(@"Not ready for video data");
        }
        else {
            @synchronized (self) {

                UIImage* newFrame = [videoFrame retain];
                CVPixelBufferRef pixelBuffer = NULL;
                CGImageRef cgImage = CGImageCreateCopy([newFrame CGImage]);
                CFDataRef image = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));

                int status = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, avAdaptor.pixelBufferPool, &pixelBuffer);
                if(status != 0){
                    //could not get a buffer from the pool
                    NSLog(@"Error creating pixel buffer:  status=%d", status);
                }

                // set image data into pixel buffer
                CVPixelBufferLockBaseAddress( pixelBuffer, 0 );
                uint8_t* destPixels = (uint8_t*) CVPixelBufferGetBaseAddress(pixelBuffer);
                CFDataGetBytes(image, CFRangeMake(0, CFDataGetLength(image)), destPixels);  //XXX:  will work if the pixel buffer is contiguous and has the same bytesPerRow as the input data



                if(status == 0){

                    BOOL success = [avAdaptor appendPixelBuffer:pixelBuffer withPresentationTime:time];
                    if (!success)
                        NSLog(@"Warning:  Unable to write buffer to video");
                }

                //clean up
                [newFrame release];
                CVPixelBufferUnlockBaseAddress( pixelBuffer, 0 );
                CVPixelBufferRelease( pixelBuffer );
                CFRelease(image);
                CGImageRelease(cgImage);

          }
       }
    }

Я также пытался преобразовать CGImage в CVPixelBufferRef, используя этот код, но производительность низкая, и это приводит к утечке памяти.


     NSDictionary *options = @{
                                  (NSString*)kCVPixelBufferCGImageCompatibilityKey : @YES,
                                  (NSString*)kCVPixelBufferCGBitmapContextCompatibilityKey : @YES,
                                  };

        CVPixelBufferRef pxbuffer = NULL;
        CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, CGImageGetWidth(image),
                                              CGImageGetHeight(image), kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options,
                                              &pxbuffer);
        if (status!=kCVReturnSuccess) {
            NSLog(@"Operation failed");
        }
        NSParameterAssert(status == kCVReturnSuccess && pxbuffer != NULL);

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

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

        CGContextConcatCTM(context, CGAffineTransformMakeRotation(0));
        CGAffineTransform flipVertical = CGAffineTransformMake( 1, 0, 0, -1, 0, CGImageGetHeight(image) );
        CGContextConcatCTM(context, flipVertical);
        CGAffineTransform flipHorizontal = CGAffineTransformMake( -1.0, 0.0, 0.0, 1.0, CGImageGetWidth(image), 0.0 );
        CGContextConcatCTM(context, flipHorizontal);

        CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image),
                                               CGImageGetHeight(image)), image);
        CGColorSpaceRelease(rgbColorSpace);
        CGContextRelease(context);

        CVPixelBufferUnlockBaseAddress(pxbuffer, 0); 

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...