Frage

Im glReadPixels zu greifen Screenshots meiner opengl Szene verwenden und sie dann in ein Video drehen mit AVAssetWriter auf IOS 4. Mein Problem ist, ich brauche den Alpha-Kanal, um das Video zu passieren, die nur kCVPixelFormatType_32ARGB und glReadPixels auf abruft RGBA akzeptiert. Also im Grunde muss ich einen Weg, um meine RGBA zu ARGB zu konvertieren, mit anderen Worten die Alpha setzen Bytes zuerst.

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

dachte, ich würde den ganzen Code schreiben, wie ich jemand anderes helfen kann.

Prost

War es hilfreich?

Lösung

Hinweis: Ich gehe davon aus 8 Bit pro Kanal. Entsprechend anpassen, wenn dies nicht der Fall ist.

Um die Alpha-Bits letzten zu bewegen, müssen Sie Rotation auszuführen. Dies wird in der Regel am leichtesten durch Bitverschiebung ausgedrückt.

In diesem Fall mögen Sie das RGB-Bits 8 Bits nach rechts bewegen, und das A-Bits 24 Bits nach links. Diese beiden Werte sollten dann zusammengesetzt werden mit bitweise OR, so dass argb = (rgba >> 8) | (rgba << 24) wird.

Andere Tipps

Noch besser ist, nicht codiert Ihr Video ARGB verwenden, Ihren AVAssetWriter BGRA Frames senden. Wie ich in diese Antwort beschreiben, dabei können Sie 640x480 Video mit 30 fps auf einem iPhone 4, und bis zu 20 kodieren FPS für 720p Video. Ein iPhone 4S den ganzen Weg bis zu 1080p-Video mit 30 fps mit diesem gehen kann.

Auch Sie werden Sie sicher einen Pixelpufferpool verwenden machen wollen stattdessen einen Pixelpuffer jedes Mal neu zu erstellen. Kopieren Sie den Code aus dieser Antwort, konfigurieren Sie die AVAssetWriter mit dieser:

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

dann diesen Code verwenden, um jeden gerenderten Frame mit glReadPixels() greifen:

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

Wenn glReadPixels() verwenden, müssen Sie die Farben Ihres Rahmens swizzle, so dass ich ein Offscreen FBO und ein Fragment-Shader mit dem folgenden Code verwendet haben, dies zu tun:

 varying highp vec2 textureCoordinate;

 uniform sampler2D inputImageTexture;

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

Es gibt jedoch eine noch schnellere Route auf iOS 5.0 OpenGL ES Inhalt als glReadPixels() zu greifen, die ich in beschreiben diese Antwort . Das Schöne an diesem Verfahren ist, dass die Texturen bereits Speicherinhalt in BGRA Pixelformat, so können Sie nur die Einkapselung Pixelpuffer füttern Recht auf einen AVAssetWriter ohne Farbumwandlung und immer noch große Codierung Geschwindigkeiten sehen.

merke ich, diese Frage beantwortet wurde, aber ich wollte sicher Leute bewusst Vimage, einen Teil des Accelerate-Framework und in iOS und OSX sind machen. Mein Verständnis ist, dass Vimage von Core Graphics verwendet wird CPU-bound Vektoroperationen auf Bitmaps zu tun.

Die spezifische API Sie wollen für ARGB zu RGBA konvertieren ist vImagePermuteChannels_ARGB8888. Es gibt auch APIs RGB zu ARGB / XRGB, zu konvertieren, ein Bild zu drehen, um einen Kanal, und noch viel mehr zu überschreiben. Es ist eine Art ein verstecktes Juwel!

Update:. Brad Larson schrieb eine große Antwort auf im Wesentlichen die gleiche Frage href="https://stackoverflow.com/a/10658683/1614758">

Yep seine 8 Bit pro Kanal, so ist es so etwas wie:

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

Ich kann nicht scheinen, um es zum Laufen zu bringen

Ich bin sicher, dass die Alpha-Werte ignoriert werden können. So einfach memcpy mit dem Pixel-Puffer-Array verschoben um ein Byte zu tun:

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;
}
scroll top