Szybsza alternatywa dla glReadPixels w iPhone OpenGL ES 2.0

Czy jest jakiś szybszy sposób na dostęp do bufora ramki niż użycie glReadPixels? Potrzebowałbym dostępu tylko do odczytu do małego prostokątnego obszaru renderowania w buforze ramki, aby dalej przetwarzać dane w procesorze CPU. Wydajność jest ważna, ponieważ muszę wykonywać tę operację wielokrotnie. Przeszukałem internet i znalazłem jakieś podejście, takie jak użycie obiektu bufora pikseli i glMapBuffer, ale wydaje się, że OpenGL ES 2.0 ich nie obsługuje.

Author: Brad Larson, 2012-03-04

2 answers

[[8]} od wersji IOS 5.0 istnieje teraz szybszy sposób pobierania danych z OpenGL ES. To nie jest łatwo oczywiste, ale okazuje się, że wsparcie texture cache dodane w iOS 5.0 nie tylko działa na szybkie przesyłanie ramek aparatu do OpenGL ES, ale może być używany w odwrotnej, aby uzyskać szybki dostęp do surowych pikseli w tekstury OpenGL ES.

Możesz skorzystać z tego, aby pobrać piksele do renderowania OpenGL ES za pomocą obiektu bufora ramki (FBO) z dołączoną teksturą, z tą teksturą po dostarczeniu z bufora tekstur. Po renderowaniu sceny do tego FBO, piksele BGRA dla tej sceny zostaną zawarte w Twoim CVPixelBufferRef, więc nie będzie potrzeby ich ściągać za pomocą glReadPixels().

Jest to znacznie, znacznie szybsze niż używanie glReadPixels() w moich benchmarkach. Odkryłem, że na moim iPhone 4, glReadPixels() było wąskim gardłem w odczycie klatek wideo 720p do kodowania na dysk. Ograniczało to możliwość kodowania z prędkością większą niż 8-9 FPS. Zastąpienie tego fast texture Cache reads pozwala mi kodować wideo 720p przy 20 kl. / s, a wąskie gardło przeniosło się z odczytu pikseli do przetwarzania OpenGL ES i rzeczywistych części kodowania filmów w potoku. W przypadku iPhone ' a 4S pozwala to na zapis wideo 1080p z pełną prędkością 30 klatek na sekundę.

Moja implementacja znajduje się w klasie GPUImageMovieWriter w ramach frameworka open source GPUImage, ale została zainspirowana artykułem Dennisa Muhlesteina na ten temat i artykułem Apple ' a Chromakey sample application (który został udostępniony dopiero na WWDC 2011).

Zaczynam od skonfigurowania mojego Avassetwritera, dodania wejścia i skonfigurowania wejścia bufora pikseli. Do Ustawienia Wejścia bufora pikseli używany jest następujący kod:

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

Kiedy już to mam, konfiguruję FBO, do którego będę renderował moje klatki wideo, używając następującego kodu:

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

To pobiera bufor pikseli z puli skojarzonej z moim wpisem asset writer, tworzy i kojarzy teksturę z nim i używa tej tekstury jako celu dla mojego FBO.

Po wyrenderowaniu ramki blokuję adres bazowy bufora pikseli:

CVPixelBufferLockBaseAddress(pixel_buffer, 0);

A następnie po prostu wprowadź go do mojego edytora zasobów, aby został zakodowany:

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

Zauważ, że w żadnym momencie nie czytam niczego ręcznie. Ponadto tekstury są natywnie w formacie BGRA, który jest tym, co Avassetwriterzy są zoptymalizowani do użycia podczas kodowania wideo, więc nie ma potrzeby robienia tutaj żadnych kolorów. Surowe piksele BGRA są po prostu karmione do enkodera, aby zrobić film.

Oprócz użycia tego w AVAssetWriter, mam trochę kodu w tej odpowiedzi , którego używałem do ekstrakcji pikseli raw. W praktyce występuje również znaczne przyspieszenie w porównaniu z używaniem glReadPixels(), chociaż mniej niż w przypadku puli buforów pikseli, której używam w AVAssetWriter.

[8]}szkoda, że nic z tego nie jest nigdzie udokumentowane, ponieważ zapewnia ogromny wzrost wydajności przechwytywania wideo.
 129
Author: Brad Larson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:31

Odnośnie tego, co atisman wspomniał o czarnym ekranie, też miałem ten problem. Upewnij się, że wszystko jest w porządku z teksturą i innymi ustawieniami. Próbowałem uchwycić warstwę OpenGL AIR, co zrobiłem w końcu, problem polegał na tym, że kiedy nie ustawiłem "depthAndStencil" na true przez przypadek w manifeście aplikacji, Moja Tekstura FBO była w połowie wysokości (Ekran został podzielony na pół i lustrzane, chyba z powodu param tekstury zawijania rzeczy). A mój filmik był czarny.

To było dość frustrujące, ponieważ na podstawie tego, co Brad zamieszcza, powinno zadziałać, gdy tylko będę miał jakieś dane w teksturze. Niestety tak nie jest, wszystko musi być "właściwe", aby działało - dane w teksturze nie są gwarancją zobaczenia równych danych w filmie. Po dodaniu depthAndStencil moja Tekstura stała się na pełnej wysokości i zacząłem nagrywać wideo prosto z warstwy OpenGL AIR, bez glReadPixels czy coś :)

So yeah, what Brad opisuje naprawdę działa bez potrzeby odtwarzania buforów na każdej ramce, wystarczy upewnić się, że konfiguracja jest prawidłowa. Jeśli dostajesz czerń, spróbuj zagrać z rozmiarami wideo/tekstur lub innymi ustawieniami (konfiguracja Twojego FBO?).

 0
Author: user2979732,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-09 21:31:28