Более быстрая альтернатива glReadPixels в iPhone OpenGL ES 2.0



есть ли более быстрый способ доступа к буферу кадров, чем использование glReadPixels? Мне понадобится доступ только для чтения к небольшой прямоугольной области рендеринга в буфере кадров для дальнейшей обработки данных в CPU. Производительность важна, потому что я должен выполнять эту операцию неоднократно. Я искал в интернете и нашел какой-то подход, например, используя объект Pixel Buffer и glMapBuffer, но кажется, что OpenGL ES 2.0 не поддерживает их.

656   2  

2 ответов:

начиная с iOS 5.0, теперь есть более быстрый способ захватить данные из OpenGL ES. Это не совсем очевидно, но оказывается, что поддержка кэша текстур, добавленная в iOS 5.0, не просто работает для быстрой загрузки кадров камеры в OpenGL ES, но ее можно использовать в обратном порядке, чтобы получить быстрый доступ к необработанным пикселям в текстуре OpenGL ES.

вы можете воспользоваться этим, чтобы захватить пиксели для рендеринга OpenGL ES с помощью объекта framebuffer (FBO) с прикрепленной текстурой, с помощью эта текстура была предоставлена из кэша текстур. После того, как вы визуализируете свою сцену в этом FBO, пиксели BGRA для этой сцены будут содержаться в вашем CVPixelBufferRef, поэтому не нужно будет вытаскивать их с помощью glReadPixels().

это гораздо, гораздо быстрее, чем при использовании glReadPixels() в моих тестах. Я нашел это на моем iPhone 4,glReadPixels() было узким местом в чтении 720p видеокадров для кодирования на диск. Это ограничило кодирование от того, чтобы иметь место в чем-либо большем, чем 8-9 КАДР. Замена этого быстрым чтением кэша текстур позволяет мне теперь кодировать видео 720p со скоростью 20 кадров в секунду, и узкое место переместилось с чтения пикселей на обработку OpenGL ES и фактические части кодирования фильма конвейера. На iPhone 4S это позволяет записывать видео 1080p с полной частотой 30 кадров в секунду.

моя реализация может быть найдена в классе GPUImageMovieWriter в моем открытом исходном коде GPUImage рамки, но это было вдохновлено Дэннис nazir словам, это статья на эту тему и образец приложения ChromaKey от Apple (который был доступен только на WWDC 2011).

Я начинаю с настройки моего AVAssetWriter, добавления ввода и настройки ввода буфера пикселей. Для настройки входного буфера пикселей используется следующий код:

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

как только у меня это есть, я настраиваю FBO, который я буду отображать свои видеокадры, используя следующий код:

if ([GPUImageOpenGLESContext supportsFastTextureUpload])
{
    CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, (__bridge void *)[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context], NULL, &coreVideoTextureCache);
    if (err) 
    {
        NSAssert(NO, @"Error at CVOpenGLESTextureCacheCreate %d");
    }

    CVPixelBufferPoolCreatePixelBuffer (NULL, [assetWriterPixelBufferInput pixelBufferPool], &renderTarget);

    CVOpenGLESTextureRef renderTexture;
    CVOpenGLESTextureCacheCreateTextureFromImage (kCFAllocatorDefault, coreVideoTextureCache, renderTarget,
                                                  NULL, // texture attributes
                                                  GL_TEXTURE_2D,
                                                  GL_RGBA, // opengl format
                                                  (int)videoSize.width,
                                                  (int)videoSize.height,
                                                  GL_BGRA, // native iOS format
                                                  GL_UNSIGNED_BYTE,
                                                  0,
                                                  &renderTexture);

    glBindTexture(CVOpenGLESTextureGetTarget(renderTexture), CVOpenGLESTextureGetName(renderTexture));
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, CVOpenGLESTextureGetName(renderTexture), 0);
}

это тянет буфер пикселей из бассейна связанный с моим вводом Asset writer, создает и связывает с ним текстуру и использует эту текстуру в качестве цели для моего FBO.

как только я отрисовал кадр, я блокирую базовый адрес пиксельного буфера:

CVPixelBufferLockBaseAddress(pixel_buffer, 0);

а затем просто введите его в мой писатель активов для кодирования:

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

if (![GPUImageOpenGLESContext supportsFastTextureUpload])
{
    CVPixelBufferRelease(pixel_buffer);
}

обратите внимание, что здесь я ничего не читаю вручную. Кроме того, текстуры изначально находятся в формате BGRA, который оптимизирован для использования AVAssetWriters, когда кодирование видео, так что нет никакой необходимости делать какие-либо цвета swizzling здесь. Необработанные пиксели BGRA просто подаются в кодер, чтобы сделать фильм.

Помимо использования этого в AVAssetWriter, у меня есть некоторый код в ответ что я использовал для извлечения необработанных пикселей. Он также испытывает значительное ускорение на практике по сравнению с использованием glReadPixels(), хотя и меньше, чем я вижу с пулом буферов пикселей, который я использую с AVAssetWriter.

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

Что касается того, что атисман упомянул о черном экране, у меня тоже была эта проблема. Действительно убедитесь, что все в порядке с текстурой и другими настройками. Я пытался захватить слой OpenGL AIR, который я сделал в конце концов, проблема заключалась в том, что когда я случайно не установил "depthAndStencil" в true в манифесте приложений, моя текстура FBO была наполовину высотой(экран был разделен пополам и зеркально отражен, я думаю, из-за материала wrap texture param). И мое видео было черный.

Это было довольно неприятно, так как на основе того, что Брэд публикует, он должен был просто работать, как только у меня были некоторые данные в текстуре. К сожалению, это не так, все должно быть "правильно" для его работы - данные в текстуре не являются гарантией для просмотра равных данных в видео. Как только я добавил depthAndStencil, моя текстура закрепилась на полную высоту, и я начал получать видеозапись прямо из слоя OpenGL AIR, без glReadPixels или чего-то еще :)

да, то, что описывает Брэд, действительно работает без необходимости воссоздавать буферы на каждом кадре, вам просто нужно убедиться, что ваша настройка правильная. Если вы получаете черноту, попробуйте сыграть с размерами видео / текстуры или некоторыми другими настройками (настройка вашего FBO?).

Comments

    Ничего не найдено.