Проблемы с OpenGL при использовании обработки изображений для фильтрации видео - PullRequest
0 голосов
/ 05 марта 2012

Я пытаюсь использовать Framework Брэда Ларсона в качестве эталона для получения некоторых базовых тестов обработки изображений. До сих пор я реализовал фильтр сепии, и он вроде работает, проблема в том, что я получаю обработанное изображение и необработанное изображение каждый цикл рендеринга (например, если я устанавливаю fps на 1 сек, первая секунда рендеринга, вторая нет, третий да и тд). И я провел часы, и я не могу понять, почему.

У меня есть 2 окна, слой предварительного просмотра видео и glkview, где я рендерил свой буфер.

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

Просмотр сделал Способ загрузки:

- (void)viewDidLoad
{
    [super viewDidLoad];

    session = [[AVCaptureSession alloc] init];

    // Add inputs and outputs.
    if ([session canSetSessionPreset:AVCaptureSessionPreset640x480]) {
        session.sessionPreset = AVCaptureSessionPreset640x480;
    }
    else {
        // Handle the failure.
        NSLog(@"Cannot set session preset to 640x480");
    }

    AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    NSError *error = nil;
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:device error:&error];

    if (!input) {
        // Handle the error appropriately.
        NSLog(@"Could create input: %@", error);
    }

    if ([session canAddInput:input]) {
        [session addInput:input];
    }
    else {
        // Handle the failure.
        NSLog(@"Could not add input");
    }




    captureVideoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:session];

    [captureVideoPreviewLayer setVideoGravity:AVLayerVideoGravityResizeAspect];

    [captureVideoPreviewLayer setBounds:videoLayer.layer.bounds];
    [captureVideoPreviewLayer setPosition:videoLayer.layer.position];

    [videoLayer.layer addSublayer:captureVideoPreviewLayer];

    [session startRunning];



    // OPENGL stuff


    self.imageView.context = [[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context];


    if ([self createFilterProgram]) {
        NSLog(@"Filter Program Created");
    }


    if ([self createOutputProgram]) {
        NSLog(@"Output Program Created");
    }

    if (CVOpenGLESTextureCacheCreate != NULL)
    {
        [GPUImageOpenGLESContext useImageProcessingContext];
        CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, (__bridge void *)[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context], NULL, &coreVideoTextureCache);
        if (err) 
        {
            NSAssert(NO, @"Error at CVOpenGLESTextureCacheCreate %d");
        }
        else
            NSLog(@"Initial CVOpenGLESTextureCache created");

        // Need to remove the initially created texture
        if (outputTexture)
        {
            glDeleteTextures(1, &outputTexture);
            outputTexture = 0;
        }
    }

    // DATA OUTPUT
    dataOutput = [[AVCaptureVideoDataOutput alloc] init];

    if ([session canAddOutput:dataOutput]) {
        [session addOutput:dataOutput];

        dataOutput.videoSettings = 
        [NSDictionary dictionaryWithObject: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA]
                                    forKey: (id)kCVPixelBufferPixelFormatTypeKey];

        AVCaptureConnection *connection = [dataOutput connectionWithMediaType:AVMediaTypeVideo];

        [connection setVideoMaxFrameDuration:CMTimeMake(1, 1)];
        [connection setVideoMinFrameDuration:CMTimeMake(1, 1)];

    }
    else {
        // Handle the failure.
        NSLog(@"Could not add output");
    }
    // DATA OUTPUT END

    //dispatch_queue_t queue = dispatch_queue_create("MyQueue", NULL);
    [dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
    //dispatch_release(queue);

}

и метод делегата из захвата видео:

- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection 
{

    NSLog(@"Render");
    //CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
    CVImageBufferRef cameraFrame = CMSampleBufferGetImageBuffer(sampleBuffer);
    int bufferWidth = CVPixelBufferGetWidth(cameraFrame);
    int bufferHeight = CVPixelBufferGetHeight(cameraFrame);

    if (CVOpenGLESTextureCacheCreate != NULL)
    {
        NSLog(@"Not Null");
        CVPixelBufferLockBaseAddress(cameraFrame, 0);

        [GPUImageOpenGLESContext useImageProcessingContext];
        CVOpenGLESTextureRef texture = NULL;
        CVReturn err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault, coreVideoTextureCache, cameraFrame, NULL, GL_TEXTURE_2D, GL_RGBA, bufferWidth, bufferHeight, GL_BGRA, GL_UNSIGNED_BYTE, 0, &texture);

        if (!texture || err) {
            NSLog(@"CVOpenGLESTextureCacheCreateTextureFromImage failed (error: %d)", err);  
            return;
        }

        outputTexture = CVOpenGLESTextureGetName(texture);
        glBindTexture(CVOpenGLESTextureGetTarget(texture), outputTexture);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);


        // "setInputSize" method

        CGSize sizeOfFBO = CGSizeMake(bufferWidth, bufferHeight);

        // "newFrameReady" method

        static const GLfloat squareVertices[] = {
            -1.0f, -1.0f,
            1.0f, -1.0f,
            -1.0f,  1.0f,
            1.0f,  1.0f,
        };

        static const GLfloat squareTextureCoordinates[] = {
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f,  1.0f,
            1.0f,  1.0f,
        };

        [GPUImageOpenGLESContext useImageProcessingContext];

        // "setFilterFBO" method
        if (!filterFramebuffer)
        {
            NSLog(@"New Filter Frame buffer");
            glActiveTexture(GL_TEXTURE1);
            glGenFramebuffers(1, &filterFramebuffer);
            glBindFramebuffer(GL_FRAMEBUFFER, filterFramebuffer);

            NSLog(@"Filter size: %f, %f", sizeOfFBO.width, sizeOfFBO.height);

            glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, (int)sizeOfFBO.width, (int)sizeOfFBO.height);
            glBindTexture(GL_TEXTURE_2D, outputTexture);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)sizeOfFBO.width, (int)sizeOfFBO.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, outputTexture, 0);

            GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);

            NSAssert(status == GL_FRAMEBUFFER_COMPLETE, @"Incomplete filter FBO: %d", status);
        }

        glBindFramebuffer(GL_FRAMEBUFFER, filterFramebuffer);
        glViewport(0, 0, (int)sizeOfFBO.width, (int)sizeOfFBO.height);

        // END "setFilterFBO" method

        glUseProgram(filterProgram); // This Program MUST be the filter one 

        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glActiveTexture(GL_TEXTURE2);
        glBindTexture(GL_TEXTURE_2D, outputTexture);

        glUniform1i(uniforms[filterInputTextureUniform], 2);    

        glVertexAttribPointer(position, 2, GL_FLOAT, 0, 0, squareVertices);
        glVertexAttribPointer(inputTextureCoordinate, 2, GL_FLOAT, 0, 0, squareTextureCoordinates);

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        static const GLfloat squareVertices2[] = {
            -1.0f, -1.0f,
            1.0f, -1.0f,
            -1.0f,  1.0f,
            1.0f,  1.0f,
        };

        static const GLfloat textureCoordinates2[] = {
            0.0f, 1.0f,
            1.0f, 1.0f,
            0.0f, 0.0f,
            1.0f, 0.0f,
        };


        [GPUImageOpenGLESContext useImageProcessingContext];

        //[self setDisplayFramebuffer];
        if (!displayFramebuffer)
        {
            NSLog(@"New Display Frame buffer");
            glGenFramebuffers(1, &displayFramebuffer);
            glBindFramebuffer(GL_FRAMEBUFFER, displayFramebuffer);

            glGenRenderbuffers(1, &displayRenderbuffer);
            glBindRenderbuffer(GL_RENDERBUFFER, displayRenderbuffer);

            [[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context] renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)imageView.layer];

            glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
            glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
            NSLog(@"Backing width: %d, height: %d", backingWidth, backingHeight);

            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, displayRenderbuffer);

            GLuint framebufferCreationStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
            NSAssert(framebufferCreationStatus == GL_FRAMEBUFFER_COMPLETE, @"Failure with display framebuffer generation");
        }

        glBindFramebuffer(GL_FRAMEBUFFER, displayFramebuffer);

        glViewport(0, 0, backingWidth, backingHeight);

        glUseProgram(outputProgram);

        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        glActiveTexture(GL_TEXTURE4);     
        glBindTexture(GL_TEXTURE_2D, outputTexture);
        glUniform1i(uniformsOutput[outputTextureUniform], 4);   

        glVertexAttribPointer(outputPosition, 2, GL_FLOAT, 0, 0, squareVertices2);
        glVertexAttribPointer(outputTextureCoordinate, 2, GL_FLOAT, 0, 0, textureCoordinates2);

        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        glBindRenderbuffer(GL_RENDERBUFFER, displayRenderbuffer);
        [[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] presentBufferForDisplay];        

        //glBindRenderbuffer(GL_RENDERBUFFER, 0);

        CVPixelBufferUnlockBaseAddress(cameraFrame, 0);

        glBindTexture(outputTexture, 0);

        // Flush the CVOpenGLESTexture cache and release the texture
        CVOpenGLESTextureCacheFlush(coreVideoTextureCache, 0);
        CFRelease(texture);
        outputTexture = 0;
    }

    else
        NSLog(@"No support for fast CVOpenGLESTextureCacheCreate");
}

Это оригинальная структура Брэда (что довольно круто) http://www.sunsetlakesoftware.com/2012/02/12/introducing-gpuimage-framework

Если кто-нибудь знает, почему это может происходить, я был бы признателен за помощь.

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

  • Рендеринг 2 раза в течение цикла (который я отбросил, потому что это происходит каждые 1 секунду, и даже если это произошло очень быстро в последний раз останется обработанный)
  • Включение или отключение состояния OpenGL (которое, я думаю, может быть причина, но не могу найти где)
  • Неправильное использование буферов (я использую ту же самую переменную буфера для обоих шагов это может вызвать проблемы)
  • Это может иметь отношение к контексту (я считаю, что контекст где все рисуется, так что, если я работаю над glkview контекст, я мог бы перезаписать изображение)

Пожалуйста, исправьте меня, если некоторые из этих идей неверны, это моя первая попытка обработки с использованием OpenGL

Спасибо!

EDIT:

Как предложил Брэд, я использовал профилировщик OpenGL и получил следующие результаты:

image

Похоже, этот инструмент отлично подходит для оптимизации кода OpenGL.

...