2013-05-10 20 views
12

Sto registrando video utilizzando AVFoundation. Devo ritagliare il video su 320x280. Sto ottenendo il CMSampleBufferRef e convertendolo in UIImmagine usando il codice qui sotto.Converti UIImage in 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 

Il imageFromSampleBuffer: metodo assomiglia a questo:

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

Ora devo convertire l'immagine ridimensionata torna a CMSampleBufferRef a scrivere in AVAssetWriterInput.

Come faccio a convertire UIImage-CMSampleBufferRef?

Grazie a tutti!

risposta

9

Mentre è possibile creare da zero i propri buffer di esempio Core Media, è probabilmente più semplice utilizzare uno AVPixelBufferAdaptor.

descriveresti il ​​formato del buffer pixel di origine nel dizionario inputSettings e passare che all'adattatore initializer:

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

È quindi possibile aggiungere CVPixelBuffers alla scheda:

[pixelBufferAdapter appendPixelBuffer:completePixelBuffer withPresentationTime:pixelBufferTime] 

Il pixelbufferAdaptor accetta CVPixelBuffers , quindi devi convertire i tuoi UIImages in pixelBuffers, che è qui descritto: https://stackoverflow.com/a/3742212/100848
Passa la proprietà CGImage del tuo UIImage a newPixelBufferFromCGImage.

+2

sfortunatamente la documentazione di questo AVPixelBufferAdaptor è inesistente e scarsamente documentata come il 99% di Apple scrive. Mentre questa cosa funziona per i frame che leggi da un array, fallisce per i flussi video in tempo reale. – SpaceDog

8

Questa è una funzione che uso nel mio quadro GPUImage per ridimensionare un CMSampleBufferRef in arrivo e mettere i risultati in scala all'interno di una CVPixelBufferRef che forniti:

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

Non ci vuole tutta la strada per la creazione di un CMSampleBufferRef, ma come sottolinea weichsel, è necessario solo il CVPixelBufferRef per codificare il video.

Tuttavia, se quello che vuoi veramente fare qui è ritagliare il video e registrarlo, andare e tornare da un UIImage sarà un modo molto lento per farlo. Invece, posso consigliare di utilizzare qualcosa come GPUImage per acquisire video con un input GPUImageVideoCamera (o GPUImageMovie se ritaglia un film registrato in precedenza), inserendolo in un GPUImageCropFilter e portando il risultato a un GPUImageMovieWriter. In questo modo, il video non tocca mai Core Graphics e l'accelerazione hardware viene utilizzata il più possibile. Sarà molto più veloce di quello che descrivi sopra.

+0

ok come allora possiamo aggiungere del testo o disegnare una linea usando solo 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

per favore aggiungi qualche spiegazione della tua risposta – Mostafiz