2010-10-16 9 views
2

Im используя glReadPixels, чтобы захватить снимки экрана моей сцены opengl, а затем превратить их в видео с помощью AVAssetWriter на IOS 4. Моя проблема в том, что мне нужно передать альфа-канал видео, которое принимает только kCVPixelFormatType_32ARGB и glReadPixels при получении RGBA. Поэтому в основном мне нужен способ конвертировать RGBA в ARGB, другими словами, сначала поставить альфа-байты.IPhone RGBA to 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 

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

Приветствия

ответ

6

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

Чтобы переместить альфа-бит в последний раз, вам необходимо выполнить вращение. Это обычно легче всего выразить с помощью сдвига битов.

В этом случае вы хотите переместить бит RGB на 8 бит вправо, а бит A - 24 бита. Эти два значения затем должны быть объединены с помощью побитового ИЛИ, так что это будет argb = (rgba >> 8) | (rgba << 24).

0

Yep его 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); 
    } 
} 

Я не могу показаться, чтобы получить его работу

+1

Проблема заключается в том, что буфер имеет тип unsigned char, но он должен иметь 4-байтовый тип, такой как unsigned int или UInt32 соответственно. –

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

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

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

Почему игнорируются альфа-значения? Всегда ли это будет верно? – kevlar

2

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

Кроме того, вы хотите, чтобы вы использовали пул пиксельных буферов вместо воссоздания буфера пикселей каждый раз. При копировании кода из этого ответа, вы настроить 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(), вам нужно Swizzle цвета ваш кадра, поэтому я использовал закадровый FBO и фрагмент шейдер с помощью следующего кода, чтобы сделать это:

varying highp vec2 textureCoordinate; 

uniform sampler2D inputImageTexture; 

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

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

2

Я понимаю, что на этот вопрос был дан ответ, но я хотел убедиться, что люди знают об vImage, части структуры Accelerate и доступны в iOS и OSX. Я понимаю, что vImage используется Core Graphics для выполнения векторных операций, связанных с процессором, в растровых изображениях.

Конкретный API, который вы хотите преобразовать ARGB в RGBA, - vImagePermuteChannels_ARGB8888. Существуют также API-интерфейсы для преобразования RGB в ARGB/XRGB, для переворота изображения, для перезаписи канала и т. Д. Это своего рода скрытый камень!

Обновление: Брэд Ларсон написал отличный ответ по существу по тому же вопросу here.

 Смежные вопросы

  • Нет связанных вопросов^_^