2013-05-10 4 views
12

Я делаю запись видео с использованием AVFoundation. Мне нужно обрезать видео до 320x280. Я получаю CMSampleBufferRef и конвертирует его в UIImage, используя приведенный ниже код.Преобразование UIImage в CMSampleBufferRef

CGImageRef _cgImage = [self imageFromSampleBuffer:sampleBuffer]; 
UIImage *_uiImage = [UIImage imageWithCGImage:_cgImage]; 
CGImageRelease(_cgImage); 
_uiImage = [_uiImage resizedImageWithSize:CGSizeMake(320, 280)]; 

CMSampleBufferRef croppedBuffer = /* NEED HELP WITH THIS */ 

[_videoInput appendSampleBuffer:sampleBuffer]; 
// _videoInput is a AVAssetWriterInput 

The imageFromSampleBuffer: метод выглядит следующим образом:

- (CGImageRef) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer // Create a CGImageRef from sample buffer data 
{ 
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); 
    CVPixelBufferLockBaseAddress(imageBuffer,0);  // Lock the image buffer 

    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0); // Get information of the image 
    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); 
    size_t width = CVPixelBufferGetWidth(imageBuffer); 
    size_t height = CVPixelBufferGetHeight(imageBuffer); 
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 

    CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
    CGImageRef newImage = CGBitmapContextCreateImage(newContext); 
    CGContextRelease(newContext); 

    CGColorSpaceRelease(colorSpace); 
    CVPixelBufferUnlockBaseAddress(imageBuffer,0); 
    /* CVBufferRelease(imageBuffer); */ // do not call this! 

    return newImage; 
} 

Теперь я должен преобразовать уменьшенное изображение обратно в CMSampleBufferRef писать в AVAssetWriterInput.

Как конвертировать UIImage в CMSampleBufferRef?

Спасибо всем!

ответ

9

Хотя вы могли бы создавать свои собственные буферы с образцами Core Media с нуля, вероятно, проще использовать AVPixelBufferAdaptor.

Вы описываете формат буфера источника пикселя в словаре inputSettings и передать в адаптер инициализаторе:

NSMutableDictionary* inputSettingsDict = [NSMutableDictionary dictionary]; 
[inputSettingsDict setObject:[NSNumber numberWithInt:pixelFormat] forKey:(NSString*)kCVPixelBufferPixelFormatTypeKey]; 
[inputSettingsDict setObject:[NSNumber numberWithUnsignedInteger:(NSUInteger)(image.uncompressedSize/image.rect.size.height)] forKey:(NSString*)kCVPixelBufferBytesPerRowAlignmentKey]; 
[inputSettingsDict setObject:[NSNumber numberWithDouble:image.rect.size.width] forKey:(NSString*)kCVPixelBufferWidthKey]; 
[inputSettingsDict setObject:[NSNumber numberWithDouble:image.rect.size.height] forKey:(NSString*)kCVPixelBufferHeightKey]; 
[inputSettingsDict setObject:[NSNumber numberWithBool:YES] forKey:(NSString*)kCVPixelBufferCGImageCompatibilityKey]; 
[inputSettingsDict setObject:[NSNumber numberWithBool:YES] forKey:(NSString*)kCVPixelBufferCGBitmapContextCompatibilityKey]; 
AVAssetWriterInputPixelBufferAdaptor* pixelBufferAdapter = [[AVAssetWriterInputPixelBufferAdaptor alloc] initWithAssetWriterInput:assetWriterInput sourcePixelBufferAttributes:inputSettingsDict]; 

Затем вы можете добавить CVPixelBuffers к адаптеру:

[pixelBufferAdapter appendPixelBuffer:completePixelBuffer withPresentationTime:pixelBufferTime] 

pixelbufferAdaptor принимает CVPixelBuffers , поэтому вам нужно преобразовать UIImages в pixelBuffers, который описан здесь: https://stackoverflow.com/a/3742212/100848
Передайте CGImage имущество вашего UIImage - newPixelBufferFromCGImage.

+2

К сожалению, документация для этого AVPixelBufferAdaptor отсутствует и плохо документирована как 99% Apple пишет. Хотя эта вещь работает для фреймов, которые вы читаете из массива, она не работает для видеопотоков в реальном времени. – SpaceDog

8

Это функция, которую я использую в моей рамках GPUImage, чтобы изменить размер входящего CMSampleBufferRef и место масштабируется результатов в CVPixelBufferRef, что вы предоставляете:

void GPUImageCreateResizedSampleBuffer(CVPixelBufferRef cameraFrame, CGSize finalSize, CMSampleBufferRef *sampleBuffer) 
{ 
    // CVPixelBufferCreateWithPlanarBytes for YUV input 

    CGSize originalSize = CGSizeMake(CVPixelBufferGetWidth(cameraFrame), CVPixelBufferGetHeight(cameraFrame)); 

    CVPixelBufferLockBaseAddress(cameraFrame, 0); 
    GLubyte *sourceImageBytes = CVPixelBufferGetBaseAddress(cameraFrame); 
    CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, sourceImageBytes, CVPixelBufferGetBytesPerRow(cameraFrame) * originalSize.height, NULL); 
    CGColorSpaceRef genericRGBColorspace = CGColorSpaceCreateDeviceRGB(); 
    CGImageRef cgImageFromBytes = CGImageCreate((int)originalSize.width, (int)originalSize.height, 8, 32, CVPixelBufferGetBytesPerRow(cameraFrame), genericRGBColorspace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst, dataProvider, NULL, NO, kCGRenderingIntentDefault); 

    GLubyte *imageData = (GLubyte *) calloc(1, (int)finalSize.width * (int)finalSize.height * 4); 

    CGContextRef imageContext = CGBitmapContextCreate(imageData, (int)finalSize.width, (int)finalSize.height, 8, (int)finalSize.width * 4, genericRGBColorspace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); 
    CGContextDrawImage(imageContext, CGRectMake(0.0, 0.0, finalSize.width, finalSize.height), cgImageFromBytes); 
    CGImageRelease(cgImageFromBytes); 
    CGContextRelease(imageContext); 
    CGColorSpaceRelease(genericRGBColorspace); 
    CGDataProviderRelease(dataProvider); 

    CVPixelBufferRef pixel_buffer = NULL; 
    CVPixelBufferCreateWithBytes(kCFAllocatorDefault, finalSize.width, finalSize.height, kCVPixelFormatType_32BGRA, imageData, finalSize.width * 4, stillImageDataReleaseCallback, NULL, NULL, &pixel_buffer); 
    CMVideoFormatDescriptionRef videoInfo = NULL; 
    CMVideoFormatDescriptionCreateForImageBuffer(NULL, pixel_buffer, &videoInfo); 

    CMTime frameTime = CMTimeMake(1, 30); 
    CMSampleTimingInfo timing = {frameTime, frameTime, kCMTimeInvalid}; 

    CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, pixel_buffer, YES, NULL, NULL, videoInfo, &timing, sampleBuffer); 
    CFRelease(videoInfo); 
    CVPixelBufferRelease(pixel_buffer); 
} 

Это не займет у вас весь путь к созданию CMSampleBufferRef, но, как указывает Weichsel, вам нужен только CVPixelBufferRef для кодирования видео.

Однако, если вы действительно хотите сделать это, обрезайте видео и запишите его, перейдя к UIImage и из него, это будет очень медленный способ сделать это. Вместо этого я могу рекомендовать использовать что-то вроде GPUImage для захвата видео с помощью входа GPUImageVideoCamera (или GPUImageMovie, если обрезать ранее записанный фильм), подавая его в GPUImageCropFilter и беря результат в GPUImageMovieWriter. Таким образом, видео никогда не затрагивает Core Graphics и аппаратное ускорение, как можно больше. Это будет намного быстрее, чем описано выше.

+0

Хорошо, как тогда мы можем добавить текст или рисовать линию, используя только CVPixelBuffer? – user924

1
- (CVPixelBufferRef)CVPixelBufferRefFromUiImage:(UIImage *)img { 

    CGSize size = img.size; 
    CGImageRef image = [img CGImage]; 

    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: 
          [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, 
          [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil]; 
    CVPixelBufferRef pxbuffer = NULL; 
    CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, size.width, size.height, kCVPixelFormatType_32ARGB, (__bridge CFDictionaryRef) options, &pxbuffer); 

    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, kCGImageAlphaPremultipliedFirst); 
    NSParameterAssert(context); 

    CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image); 

    CGColorSpaceRelease(rgbColorSpace); 
    CGContextRelease(context); 

    CVPixelBufferUnlockBaseAddress(pxbuffer, 0); 

    return pxbuffer; 
} 
+0

, пожалуйста, добавьте некоторое объяснение своего ответа – Mostafiz

Смежные вопросы