Как я могу изменить пример GLCameraRipple для обработки в фоновом потоке? - PullRequest
3 голосов
/ 03 февраля 2012

Я пытаюсь изменить пример приложения GLCameraRipple от Apple для обработки видеокадров в фоновом потоке. В этом примере он обрабатывает каждый кадр в главном потоке, используя следующий код:

// Set dispatch to be on the main thread so OpenGL can do things with the data
[dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];

Если я изменю этот код для обработки в фоновом потоке:

dispatch_queue_t videoQueue = dispatch_queue_create("com.test.queue", NULL);
[dataOutput setSampleBufferDelegate:self queue:videoQueue];

тогда программа вылетает.

Когда я пытаюсь создать второй EAGLContext с общим доступом, как указано в документации Apple, тогда я вижу только зеленый или черный экран.

Как я могу изменить этот пример приложения для запуска в фоновом потоке?

1 Ответ

2 голосов
/ 03 февраля 2012

Это было довольно интересно после того, как я возился с образцом. Проблема здесь заключается в функции CVOpenGLESTextureCacheCreateTextureFromImage(). Если вы посмотрите на консоль, когда получите зеленую текстуру, вы увидите что-то похожее на следующее:

Ошибка при CVOpenGLESTextureCacheCreateTextureFromImage -6661

-6661, согласно заголовкам (единственное место, где я мог найти документацию по этим новым функциям), это ошибка kCVReturnInvalidArgument. Что-то явно не так с одним из аргументов этой функции.

Оказывается, это проблема CVImageBufferRef. Похоже, что это освобождается или иным образом изменяется, пока происходит блок, который обрабатывает это обновление кэша текстуры.

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

- (void)captureOutput:(AVCaptureOutput *)captureOutput 
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
       fromConnection:(AVCaptureConnection *)connection
{
    if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
    {
        return;
    }

    CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    CFRetain(pixelBuffer);

    dispatch_async(openGLESContextQueue, ^{
        [EAGLContext setCurrentContext:_context];

        // Rest of your processing

        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
        CFRelease(pixelBuffer);

        dispatch_semaphore_signal(frameRenderingSemaphore);
    });
}

Создавая CVImageBufferRef в главном потоке, блокируя байты, на которые он указывает, и сохраняя его, затем передавая его асинхронному блоку, который, похоже, исправляет эту ошибку. Полный проект, демонстрирующий эту модификацию, можно загрузить с здесь .

Я должен сказать здесь одну вещь: это, похоже, ничего вам не даст. Если вы посмотрите, как настроен пример GLCameraRipple, самая тяжелая операция в приложении, вычисление эффекта ряби, уже отправлена ​​в фоновую очередь. При этом также используется новый путь быстрой загрузки для предоставления данных камеры в OpenGL ES, так что это не является узким местом при запуске в главном потоке.

В своем профилировании инструментов на двухъядерном iPhone 4S я не вижу существенной разницы в скорости рендеринга или использовании процессора между стандартной версией этого примера приложения и моей модифицированной, которая выполняет загрузку кадров в фоновой очереди. Тем не менее, это была интересная проблема для диагностики.

...