2013-05-10 26 views
12

Ich mache Videoaufnahmen mit AVFoundation. Ich muss das Video auf 320x280 zuschneiden. Ich bekomme die CMSampleBufferRef und konvertiert es in UIImage mit dem folgenden Code.Convert UIImage zu 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: Methode sieht wie folgt aus:

- (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; 
} 

Jetzt muss ich die Größe geänderte Bild zu CMSampleBufferRef in AVAssetWriterInput zu schreiben zurück konvertieren.

Wie kann ich UIImage-CMSampleBufferRef konvertieren?

Danke allen!

Antwort

9

Während Sie Ihre eigenen Core Media-Beispielpuffer von Grund auf neu erstellen können, ist es wahrscheinlich einfacher, eine AVPixelBufferAdaptor zu verwenden.

Sie beschreiben die Quellenpixelpufferformat im inputSettings Wörterbuch und dass initializer an den Adapter übergeben:

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]; 

Sie können dann CVPixelBuffers an Ihren Adapter anfügen:

[pixelBufferAdapter appendPixelBuffer:completePixelBuffer withPresentationTime:pixelBufferTime] 

Die pixelbufferAdaptor akzeptiert CVPixelBuffers , also müssen Sie Ihre UIImages in pixelBuffers umwandeln, die hier beschrieben wird: https://stackoverflow.com/a/3742212/100848 Übergeben Sie die CGImage Eigenschaft Ihres UIImage bis newPixelBufferFromCGImage.

+2

Leider ist die Dokumentation für diesen AVPixelBufferAdaptor nicht vorhanden und schlecht dokumentiert, wie 99% Apple schreibt. Während dieses Ding für Frames funktioniert, die Sie von einem Array lesen, schlägt es für Echtzeit-Videostreams fehl. – SpaceDog

8

Dies ist eine Funktion, die ich in meinem GPUImage Rahmen verwenden, um einen eingehenden CMSampleBufferRef und legen die skalierten Ergebnisse innerhalb einer CVPixelBufferRef, um die Größe, die Sie liefern:

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); 
} 

Es Sie den ganzen Weg nicht, um die Schaffung ein CMSampleBufferRef, aber wie Weichsel darauf hinweist, benötigen Sie nur den CVPixelBufferRef zum Verschlüsseln des Videos.

Wenn Sie jedoch wirklich wollen, ist hier das Schneiden von Videos und das Aufzeichnen von Videos, das gehen zu und von einem UIImage wird ein sehr langsamer Weg sein, dies zu tun. Stattdessen empfehle ich, etwas wie GPUImage zu verwenden, um Video mit einem GPUImageVideoCamera-Eingang (oder GPUImageMovie, wenn ein zuvor aufgenommener Film beschnitten wird) aufzunehmen, dieses in einen GPUImageCropFilter einzufügen und das Ergebnis an einen GPUImageMovieWriter zu übergeben. Auf diese Weise berührt das Video niemals Core Graphics und die Hardwarebeschleunigung wird so weit wie möglich genutzt. Es wird viel schneller als das, was Sie oben beschreiben.

+0

ok wie dann können wir Text oder zeichnen Linie mit nur 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

bitte fügen Sie eine Erklärung Ihrer Antwort hinzu – Mostafiz