Вопрос

Я использую GLREADPIXELS, чтобы захватить экранные кадры моей сцены OpenGL, а затем превращать их в видео с помощью AvassetWriter на iOS 4. Моя проблема в том, что мне нужно пройти альфа-канал к видео, которое принимает только kvpixelformattype_32Argb и rgba. Таким образом, в основном мне нужен способ преобразовать мою RGBA в ARGB, другими словами, сначала поставьте альфа-байты.

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

Думал, что я бы опубликовал весь код, так как я могу помочь кому-то еще.

Ваше здоровье

Это было полезно?

Решение

Примечание. Я предполагаю, что 8 бит на канал. Отрегулируйте соответственно, если это не так.

Последнее перемещение альфа-битов, вам нужно выполнить вращение. Отказ Обычно это высказано легко через битовое смещение.

В этом случае вы хотите переместить RGB Bits 8 битов вправо, а биты 24 бита слева. Затем эти два значения должны быть собраны вместе, используя побитовые или, так что становится argb = (rgba >> 8) | (rgba << 24).

Другие советы

Даже лучше, не кодируйте свое видео с помощью ARGB, отправьте свой авасцетарайтер BGRA-кадры. Как я опишу в Этот ответ, При этом позволяет вам кодировать видео 640x480 на 30 кадров в секунду на iPhone 4 и до 20 кадров в секунду для 720p видео. IPhone 4S может пройти до 1080p видео при 30 кадров в секунду с использованием этого.

Кроме того, вы захотите убедиться, что вы используете пиксельный буферный пул вместо того, чтобы каждый раз воссоздать буфер пикселя. Копирование кода из этого ответа вы настраиваете AvassetWriter, используя это:

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

Затем используйте этот код, чтобы захватить каждый кадр, используя 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);

Когда используешь glReadPixels(), вам нужно вздрочь цветы вашей рамки, поэтому я нанял нефтеклен FBO и фрагментный шейдер с следующим кодом, чтобы сделать это:

 varying highp vec2 textureCoordinate;

 uniform sampler2D inputImageTexture;

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

Тем не менее, есть еще более быстрый маршрут на iOS 5.0, чтобы захватить контент OpenGL ES, чем glReadPixels(), который я опишу в Этот ответ. Отказ Приятно в этом процессе состоит в том, что текстуры уже хранят содержимое в формате BGRA Pixel, поэтому вы можете просто кормить капсулированные буферы пикселей прямо на авасцетарайтера без какого-либо конверсии цвета и по-прежнему увидеть большие скорости кодирования.

Я понимаю, что этот вопрос ответил, но я хотел убедиться, что люди знают о VImage, часть ускоренной структуры и доступны в iOS и OSX. Мое понимание состоит в том, что VImage используется основной графикой для выполнения операций вектора CPU на растровых изображениях.

Конкретный API, который вы хотите для преобразования ARGB в RGBA - VimagePerMuteChannels_ArgB8888. Также есть API для преобразования RGB в ARGB / XRGB, чтобы перевернуть изображение, перезаписать канал и многое другое. Это своего рода скрытая драгоценность!

Обновление: Брэд Ларсон написал отличный ответ на тот же вопрос здесь.

Да, его 8 бит на канал, так что это что-то вроде:

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

Я не могу получить это работать

Я уверен, что альфа-значения можно игнорировать. Так что вы можете просто сделать memcpy С массивом Pixel-буфера смещена одним байтом:

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;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top