2012-01-07 3 views
3

Я хочу записывать изображения, созданные с помощью OpenGL, в файл фильма с помощью AVAssetWriter. Возникает проблема, что единственный способ доступа к пикселям из фреймбуфера OpenGL - это использование glReadPixels, которое поддерживает только формат RGBA-пикселей в iOS. Но AVAssetWriter не поддерживает этот формат. Здесь я могу использовать ARGB или BGRA. Поскольку альфа-значения могут быть проигнорированы, я пришел к выводу, что самый быстрый способ конвертировать RGBA в ARGB бы дать glReadPixels буфер сдвинут на один байт:Преобразование RGBA в ARGB (glReadPixels -> AVAssetWriter)

UInt8 *buffer = malloc(width*height*4+1); 
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer+1); 

Проблема в том, что в glReadPixels вызов приводит к аварии EXC_BAD_ACCESS. Если я не сдвигаю буфер на один байт, он отлично работает (но, очевидно, с неправильными цветами в видео-файле). В чем проблема?

+1

не могли бы вы вместо того, чтобы выделить размер + 4, отправить буфер + 4 в glReadPixels вызов, а затем буфер + 3 к AVAssetWriter? Это позволит выровнять GL-вызов, который может быть требованием для iOS. –

ответ

5

Я пришел к выводу, что самый быстрый способ конвертировать RGBA в ARGB бы дать glReadPixels буфер сдвинут на один байт

Это, однако переложить свои значения альфа на 1 пиксель, а также , Вот еще одно предложение:

Измените изображение на текстуру (используя FBO с этой текстурой как цветное вложение). Затем делают эту текстуру на другой фреймбуфером с swizzling пиксельный шейдер:

#version ... 
uniform sampler2D image; 
uniform vec2 image_dim; 
void main() 
{ 
    // we want to address texel centers by absolute fragment coordinates, this 
    // requires a bit of work (OpenGL-ES SL doesn't provide texelFetch function). 
    gl_FragColor.rgba = 
     texture2D(image, vec2((2*gl_FragCoord.x + 1)/(2*image_dim.y), 
           (2*gl_FragCoord.y + 1)/(2*image_dim.y)) 
     ).argb; // this swizzles RGBA into ARGB order if read into a RGBA buffer 
} 
+1

Как я уже говорил, мне не нужна альфа-информация (я создаю h264-видео). Но этот swizzling в шейдере OpenGL - отличная идея! :) –

+2

КПП. Я просто нашел что-то очень интересное: AVAssetWriter быстрее кодирует видео (!) При указании BGRA вместо ARGB. Таким образом, весь вопрос в том, чтобы преобразовать RGBA-> BGRA вместо RGBA-> ARGB, и эта техническая игра с использованием пикселя абсолютно обязательна, потому что предложенный мной метод однобайтового смещения не может преобразовать RGBA-> BGRA! –

+0

@ DominikSeibold, пожалуйста, напишите несколько примеров кода для вашей реализации – anuj

0

Вам потребуется сдвинуть байты, выполнив операцию memcpy или другую операцию копирования. Изменение указателей оставит их неровными, что может или не может быть в пределах возможностей любого базового оборудования (ширина шины DMA, гранулярность плитки и т. Д.)

+0

Я не вижу причин, по которым glReadPixels не может писать в указатель, который не выровнен. Возможно, он не оптимально эффективен, но он действительно не должен разбиваться ... – StilesCrisis

1

Что произойдет, если вы добавите дополнительные 128 байт провисания на конце вашего буфера? Может быть, OpenGL пытается заполнить 4/8/16/etc байты за раз для производительности и имеет ошибку, когда буфер не выровнен или что-то еще. Это было бы не в первый раз, когда оптимизация производительности в OpenGL имела проблемы на граничном фронте :)

+0

Я даже пытался удвоить размер буфера, но он все равно падает. Если я сдвигаю его на 4 байта или одну строку (ширину 4 *), это работает, но, конечно, это не помогает с начальной проблемой. –

+1

После вашего malloc попробуйте добавить NSLog (@ "Buffer at 0x% 08X", buffer); , Затем посмотрите адрес, на который происходит EXC_BAD_ACCESS. Если плохой доступ находится в самом начале вашего буфера, то, вероятно, OpenGL записывает память таким образом, который взорвался при неизменном доступе на ARM. Я не знаю достаточно о ARM, чтобы узнать, что терпит неудачу, когда оно не выровнено (я больше в группе Mac OS). Конечно, если это происходит в другом месте, это все еще интересная точка данных ... – StilesCrisis

+0

Как получить адрес, где встречается EXC_BAD_ACCESS? –

-1

Использование buffer+1 будет означать, что данные не записываются в начале вашей памяти malloc'd, а в один байт, поэтому он будет писать в конце вашей памяти malloc'd, вызывая крушение.

Если glReadPixels iOS будут принимать только GL_RGBA, вам придется пройти и перестроить их самостоятельно, я думаю.

ОБНОВЛЕНИЕ, извините, я пропустил +1 в вашем malloc, StilesCrisis, вероятно, прав насчет причины крушения.

+0

Он добавляет дополнительный байт в свой выделенный буфер, однако. – StilesCrisis

1

Try вызова

glPixelStorei(GL_PACK_ALIGNMENT,1) 

перед тем glReadPixels.

Из docs:

GL_PACK_ALIGNMENT

Определяет требования выравнивания для начала каждого пикселя строки в памяти. Допустимые значения: 1 (байт-выравнивание), 2 (строки, выровненные по четным байтам), 4 (выравнивание по словам) и 8 (строки начинаются с двухсловных границ).

Значение по умолчанию - 4 (см. glGet). Это часто упоминается как «нарушитель спокойствия» в различных «OpenGL pitfalls» типа lists, хотя это, как правило, больше связано с эффектами заполнения строки, чем выравнивание буфера.

Как альтернативный подход, что произойдет, если вы добавите 4 байта malloc, сделайте glReadPixels как выровненный по 4 байта, начиная с буфера + 4, а затем передайте буфер AVAssetWriter + 3 (хотя я не знаю, является ли AVAssetWriter более терпимы к вопросам выравнивания)?

+0

glPixelStorei не помогает. Второе решение работает, но создает дополнительный memcpy- шаг, необходимый, что плохо для производительности. –

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