Domanda

Im usando glReadPixels a Screen Grab colpi di mia scena OpenGL e poi li trasforma in un video utilizzando AVAssetWriter su IOS 4. Il mio problema è che ho bisogno di passare il canale alfa per il video, che accetta solo kCVPixelFormatType_32ARGB e glReadPixels su recupera RGBA. Quindi, in pratica ho bisogno di un modo per convertire il mio RGBA a ARGB, in altre parole, mettere l'alfa byte prima.

int depth = 4;
unsigned char buffer[width * height * depth];  
glReadPixels(0,0,width, height, GL_RGBA, GL_UNSIGNED_BYTE, &buffer);

CGDataProviderRef ref = CGDataProviderCreateWithData(NULL, &buffer), width*height*depth, NULL );

CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedLast;

CGImageRef image = CGImageCreate(width, height, 8, 32, width*depth, CGColorSpaceCreateDeviceRGB(), bitmapInfo, ref, NULL, true, kCGRenderingIntentDefault);

UIWindow* parentWindow = [self window];

NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey, [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey, nil];

CVPixelBufferRef pxbuffer = NULL;
CVReturn status = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32ARGB, (CFDictionaryRef) options, &pxbuffer);

NSParameterAssert(status == kCVReturnSuccess);
NSParameterAssert(pxbuffer != NULL);

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

CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pxdata, width, height, 8, depth*width, rgbColorSpace, kCGImageAlphaPremultipliedFirst);

NSParameterAssert(context);

CGContextConcatCTM(context, parentWindow.transform);
CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);

CGColorSpaceRelease(rgbColorSpace);
CGContextRelease(context);

CVPixelBufferUnlockBaseAddress(pxbuffer, 0);

return pxbuffer; // chuck pixel buffer into AVAssetWriter

pensato che avrei inviare l'intero codice come io possa aiutare qualcun altro.

Saluti

È stato utile?

Soluzione

Nota: sto assumendo 8 bit per canale. Regolare di conseguenza se questo non è il caso.

Per spostare i bit alfa scorso, è necessario eseguire di rotazione . Questo è di solito espressa più facilmente attraverso po 'di spostamento.

In questo caso, si desidera spostare i bit RGB 8 bit a destra, ei bit A 24 bit a sinistra. Questi due valori devono poi essere messi insieme utilizzando OR bit a bit, in modo che diventi argb = (rgba >> 8) | (rgba << 24).

Altri suggerimenti

Ancora meglio, non codificano il video utilizzando ARGB, inviare i fotogrammi AVAssetWriter BGRA. Come ho descritto in questa risposta , così facendo consente di codificare i video di 640x480 a 30 FPS su un iPhone 4, e fino al 20 FPS per video 720p. Un iPhone 4S può andare tutta la strada fino a 1080p video a 30 FPS usando questo.

Inoltre, si vuole fare in modo di utilizzare un pool di buffer di pixel invece di ricreare un pixel di buffer ogni volta. Copiare il codice da questa risposta, si configura l'AVAssetWriter utilizzare questo:

NSError *error = nil;

assetWriter = [[AVAssetWriter alloc] initWithURL:movieURL fileType:AVFileTypeAppleM4V error:&error];
if (error != nil)
{
    NSLog(@"Error: %@", error);
}


NSMutableDictionary * outputSettings = [[NSMutableDictionary alloc] init];
[outputSettings setObject: AVVideoCodecH264 forKey: AVVideoCodecKey];
[outputSettings setObject: [NSNumber numberWithInt: videoSize.width] forKey: AVVideoWidthKey];
[outputSettings setObject: [NSNumber numberWithInt: videoSize.height] forKey: AVVideoHeightKey];


assetWriterVideoInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeVideo outputSettings:outputSettings];
assetWriterVideoInput.expectsMediaDataInRealTime = YES;

// You need to use BGRA for the video in order to get realtime encoding. I use a color-swizzling shader to line up glReadPixels' normal RGBA output with the movie input's BGRA.
NSDictionary *sourcePixelBufferAttributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey,
                                                       [NSNumber numberWithInt:videoSize.width], kCVPixelBufferWidthKey,
                                                       [NSNumber numberWithInt:videoSize.height], kCVPixelBufferHeightKey,
                                                       nil];

assetWriterPixelBufferInput = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:assetWriterVideoInput sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary];

[assetWriter addInput:assetWriterVideoInput];

quindi utilizzare questo codice per afferrare ciascun frame rendering utilizzando glReadPixels():

CVPixelBufferRef pixel_buffer = NULL;

CVReturn status = CVPixelBufferPoolCreatePixelBuffer (NULL, [assetWriterPixelBufferInput pixelBufferPool], &pixel_buffer);
if ((pixel_buffer == NULL) || (status != kCVReturnSuccess))
{
    return;
}
else
{
    CVPixelBufferLockBaseAddress(pixel_buffer, 0);
    GLubyte *pixelBufferData = (GLubyte *)CVPixelBufferGetBaseAddress(pixel_buffer);
    glReadPixels(0, 0, videoSize.width, videoSize.height, GL_RGBA, GL_UNSIGNED_BYTE, pixelBufferData);
}

// May need to add a check here, because if two consecutive times with the same value are added to the movie, it aborts recording
CMTime currentTime = CMTimeMakeWithSeconds([[NSDate date] timeIntervalSinceDate:startTime],120);

if(![assetWriterPixelBufferInput appendPixelBuffer:pixel_buffer withPresentationTime:currentTime]) 
{
    NSLog(@"Problem appending pixel buffer at time: %lld", currentTime.value);
} 
else 
{
//        NSLog(@"Recorded pixel buffer at time: %lld", currentTime.value);
}
CVPixelBufferUnlockBaseAddress(pixel_buffer, 0);

CVPixelBufferRelease(pixel_buffer);

Quando si utilizza glReadPixels(), è necessario Swizzle i colori della vostra cornice, quindi ho impiegato un fuori campo FBO e uno shader frammento con il seguente codice per fare questo:

 varying highp vec2 textureCoordinate;

 uniform sampler2D inputImageTexture;

 void main()
 {
     gl_FragColor = texture2D(inputImageTexture, textureCoordinate).bgra;
 }

Tuttavia, v'è un percorso ancora più veloce su iOS 5.0 per afferrare contenuti OpenGL ES di glReadPixels(), che ho descritto in questa risposta . La cosa bella di questo processo è che le texture già Content Store in formato pixel BGRA, quindi si può solo alimentare i buffer incapsulante pixel diritto ad un AVAssetWriter senza alcuna conversione del colore e ancora vedere le velocità di codifica grande.

mi rendo conto questa domanda è stato risposto, ma ho voluto assicurarsi che le persone sono consapevoli di Vimage, parte del quadro Accelerate e disponibile in iOS e OSX. La mia comprensione è che Vimage è utilizzato da Core Graphics per fare operazioni vettoriali CPU-bound su bitmap.

L'API specifica che si desidera per convertire ARGB a RGBA è vImagePermuteChannels_ARGB8888. Ci sono anche le API per convertire RGB a ARGB / XRGB, per ribaltare un'immagine, per sovrascrivere un canale, e molto altro ancora. E 'una specie di un tesoro nascosto!

Aggiornamento:. Brad Larson ha scritto una grande risposta alla essenzialmente la stessa domanda qui

Sì suoi 8 bit per canale, così è qualcosa di simile:

int depth = 4;
int width = 320;
int height = 480;

unsigned char buffer[width * height * depth]; 

glReadPixels(0,0,width, height, GL_RGBA, GL_UNSIGNED_BYTE, &buffer);

for(int i = 0; i < width; i++){
   for(int j = 0; j < height; j++){     
    buffer[i*j] = (buffer[i*j] >> 8) | (buffer[i*j] << 24);
    }
}

I cant sembrano farlo funzionare

sono sicuro che le alfa-valori possono essere ignorati. Quindi, si può solo fare memcpy con la matrice di pixel-buffer spostato di un byte:

void *buffer = malloc(width*height*4);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &buffer);
…
memcpy(pxdata + 1, buffer, width*height*4 - 1);
+ (UIImage *) createARGBImageFromRGBAImage: (UIImage *)image {
    CGSize dimensions = [image size];

    NSUInteger bytesPerPixel = 4;
    NSUInteger bytesPerRow = bytesPerPixel * dimensions.width;
    NSUInteger bitsPerComponent = 8;

    unsigned char *rgba = malloc(bytesPerPixel * dimensions.width * dimensions.height);
    unsigned char *argb = malloc(bytesPerPixel * dimensions.width * dimensions.height);

    CGColorSpaceRef colorSpace = NULL;
    CGContextRef context = NULL;

    colorSpace = CGColorSpaceCreateDeviceRGB();
    context = CGBitmapContextCreate(rgba, dimensions.width, dimensions.height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault); // kCGBitmapByteOrder32Big
    CGContextDrawImage(context, CGRectMake(0, 0, dimensions.width, dimensions.height), [image CGImage]);
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    for (int x = 0; x < dimensions.width; x++) {
        for (int y = 0; y < dimensions.height; y++) {
            NSUInteger offset = ((dimensions.width * y) + x) * bytesPerPixel;
            argb[offset + 0] = rgba[offset + 3];
            argb[offset + 1] = rgba[offset + 0];
            argb[offset + 2] = rgba[offset + 1];
            argb[offset + 3] = rgba[offset + 2];
        }
    }

    colorSpace = CGColorSpaceCreateDeviceRGB();
    context = CGBitmapContextCreate(argb, dimensions.width, dimensions.height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrderDefault); // kCGBitmapByteOrder32Big
    CGImageRef imageRef = CGBitmapContextCreateImage(context);
    image = [UIImage imageWithCGImage: imageRef];
    CGImageRelease(imageRef);
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    free(rgba);
    free(argb);

    return image;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top