Отображение / обработка фоторамок iPhone с помощью OpenGL ES 2.0 - PullRequest
8 голосов
/ 13 февраля 2011

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

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

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

метод. Этот метод - то, где я пытаюсь сделать весь рисунок. К сожалению, я делаю что-то не так ... и рамка камеры не рисует.

Вот мой простой вершинный шейдер:

attribute vec4 position;
attribute vec4 inputTextureCoordinate;

varying vec2 textureCoordinate;

void main()
{
    gl_Position = position;
    textureCoordinate = inputTextureCoordinate.xy;
}

(Я знаю, что он компилируется и снова работает из-за примитивов, которые я могу отрендерить.)

Вот мой упрощенный фрагментный шейдер:

varying highp vec2 textureCoordinate;

uniform sampler2D videoFrame;

void main()
{
    gl_FragColor = texture2D(videoFrame, textureCoordinate);

}

И ... вот где я пытаюсь собрать все это вместе, хаха:

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

    //NSLog(@"Frame...");
    CVImageBufferRef cameraFrame = CMSampleBufferGetImageBuffer(sampleBuffer);  
    CVPixelBufferLockBaseAddress(cameraFrame, 0);
    int bufferHeight = CVPixelBufferGetHeight(cameraFrame);
    int bufferWidth = CVPixelBufferGetWidth(cameraFrame);

    //these er, have to be set
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    // This is necessary for non-power-of-two textures
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    //set the image for the currently bound texture 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferWidth, bufferHeight, 0, GL_BGRA, GL_UNSIGNED_BYTE, CVPixelBufferGetBaseAddress(cameraFrame));

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

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

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, videoFrameTexture);

    // Update uniform values
    glUniform1i(videoFrameUniform, 0);

    glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, squareVertices);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, textureVertices);
    glEnableVertexAttribArray(1);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);
}

Не работает правильно.

Любая помощь будет чрезвычайно оценена, у меня нет идей и я в замешательстве. Заранее спасибо!

** Edit: вот мой код, который я использую для настройки представления, загрузки OpenGL и запуска сеанса захвата.

- (id)initWithFrame:(CGRect)frame {
    NSLog(@"Yo.");

    self = [super initWithFrame:frame];
    if (self) {
        CAEAGLLayer *eaglLayer = (CAEAGLLayer*)[super layer];
        [eaglLayer setOpaque: YES];
        [eaglLayer setFrame: [self bounds]];
        [eaglLayer setContentsScale: 2.0];
        glContext = [[EAGLContext alloc] initWithAPI: kEAGLRenderingAPIOpenGLES2];
        if(!glContext || ![EAGLContext setCurrentContext: glContext])   {
            [self release];
            return nil;
        }

        //endable 2D textures
        glEnable(GL_TEXTURE_2D);

        //generates the frame and render buffers at the pointer locations of the frameBuffer and renderBuffer variables
        glGenFramebuffers(1,  &frameBuffer);
        glGenRenderbuffers(1, &renderBuffer);

        //binds the frame and render buffers, they can now be modified or consumed by later openGL calls
        glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
        glBindRenderbuffer(GL_RENDERBUFFER, renderBuffer);

        //generate storeage for the renderbuffer (Wouldn't be used for offscreen rendering, glRenderbufferStorage() instead)
        [glContext renderbufferStorage:GL_RENDERBUFFER fromDrawable: eaglLayer];

        //attaches the renderbuffer to the framebuffer
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBuffer);

        //sets up the coordinate system
        glViewport(0, 0, frame.size.width, frame.size.height);          

        //|||||||||||||||--Remove this stuff later--||||||||||||||//

        //create the vertex and fragement shaders
        GLint vertexShader, fragmentShader;
        vertexShader = glCreateShader(GL_VERTEX_SHADER);
        fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

        //get their source paths, and the source, store in a char array
        NSString *vertexShaderPath = [[NSBundle mainBundle] pathForResource: @"testShader" ofType: @"vsh"];
        NSString *fragmentShaderPath = [[NSBundle mainBundle] pathForResource: @"testShader" ofType: @"fsh"];       
        const GLchar *vertexSource = (GLchar *)[[NSString stringWithContentsOfFile: vertexShaderPath encoding: NSUTF8StringEncoding error: nil] UTF8String];
        const GLchar *fragmentSource = (GLchar *)[[NSString stringWithContentsOfFile: fragmentShaderPath encoding: NSUTF8StringEncoding error: nil] UTF8String];

        NSLog(@"\n--- Vertex Source ---\n%s\n--- Fragment Source ---\n%s", vertexSource, fragmentSource);

        //associate the source strings with each shader
        glShaderSource(vertexShader, 1, &vertexSource, NULL);       
        glShaderSource(fragmentShader, 1, &fragmentSource, NULL);

        //compile the vertex shader, check for errors
        glCompileShader(vertexShader);
        GLint compiled;
        glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &compiled);
        if(!compiled)   {
            GLint infoLen = 0;
            glGetShaderiv(vertexShader, GL_INFO_LOG_LENGTH, &infoLen);
            GLchar *infoLog = (GLchar *)malloc(sizeof(GLchar) * infoLen);
            glGetShaderInfoLog(vertexShader, infoLen, NULL, infoLog);
            NSLog(@"\n--- Vertex Shader Error ---\n%s", infoLog);
            free(infoLog);
        }   

        //compile the fragment shader, check for errors
        glCompileShader(fragmentShader);
        glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &compiled);
        if(!compiled)   {
            GLint infoLen = 0;
            glGetShaderiv(fragmentShader, GL_INFO_LOG_LENGTH, &infoLen);
            GLchar *infoLog = (GLchar *)malloc(sizeof(GLchar) * infoLen);
            glGetShaderInfoLog(fragmentShader, infoLen, NULL, infoLog);
            NSLog(@"\n--- Fragment Shader Error ---\n%s", infoLog);
            free(infoLog);
        }   

        //create a program and attach both shaders
        testProgram = glCreateProgram();
        glAttachShader(testProgram, vertexShader);
        glAttachShader(testProgram, fragmentShader);

        //bind some attribute locations...
        glBindAttribLocation(testProgram, 0, "position");
        glBindAttribLocation(testProgram, 1, "inputTextureCoordinate");

        //link and use the program, make sure it worked :P
        glLinkProgram(testProgram);
        glUseProgram(testProgram);

        GLint linked;
        glGetProgramiv(testProgram, GL_LINK_STATUS, &linked);
        if(!linked) {
            GLint infoLen = 0;
            glGetProgramiv(testProgram, GL_INFO_LOG_LENGTH, &infoLen);
            GLchar *infoLog = (GLchar *)malloc(sizeof(GLchar) * infoLen);
            glGetProgramInfoLog(testProgram, infoLen, NULL, infoLog);
            NSLog(@"%s", infoLog);
            free(infoLog);
        }

        videoFrameUniform = glGetUniformLocation(testProgram, "videoFrame");

        #if(!TARGET_IPHONE_SIMULATOR)
        //holding an error
        NSError *error = nil;

        //create a new capture session, set the preset, create + add the video camera input
        AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];
        [captureSession setSessionPreset:AVCaptureSessionPreset640x480];
        AVCaptureDevice *videoCamera = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoCamera error:&error];
        [captureSession addInput:videoInput];

        //set up the data ouput object, tell it to discard late video frames for no lag
        AVCaptureVideoDataOutput *dataOutput = [[AVCaptureVideoDataOutput alloc] init];
        dataOutput.alwaysDiscardsLateVideoFrames = YES;

        //create a new dispatch queue for all the sample buffers to be called into.grapher
        dispatch_queue_t queue;
        queue = dispatch_queue_create("cameraQueue", NULL);
        [dataOutput setSampleBufferDelegate:self queue:queue];
        dispatch_release(queue);

        //set some settings on the video data output
        NSString *key = (NSString *)kCVPixelBufferPixelFormatTypeKey; 
        NSNumber *value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA]; 
        NSDictionary* videoSettings = [NSDictionary dictionaryWithObject:value forKey:key]; 
        [dataOutput setVideoSettings:videoSettings];

        //add the data output
        [captureSession addOutput:dataOutput];

        //start the capture session running
        [captureSession startRunning];

        #endif
        //|||||||||||||||--Remove this stuff later--||||||||||||||//

        //draw the view
        [self drawView];
    }
    return self;
}

-(void)drawView {
    //set what color clear is, and then clear the buffer
    glClearColor(0.2f, 0.589f, 0.12f, 1);
    glClear(GL_COLOR_BUFFER_BIT); //HUH?

    [glContext presentRenderbuffer: GL_RENDERBUFFER];
}

Ответы [ 4 ]

13 голосов
/ 14 февраля 2011

Это мое примерное приложение имеет три шейдера, которые выполняют различные уровни обработки и отображения кадров камеры на экране. Я объясняю, как это приложение работает в моем посте здесь , а также в сеансе OpenGL ES 2.0 моего класса в iTunes U .

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

Если это так, то между исходным образцом и вашим приложением должна быть некоторая разница. Похоже, вы упростили мой пример, потянув часть кода, который был у меня в методе -drawFrame, в конец метода делегата, который должен работать нормально, так что это не проблема. Я предполагаю, что остальные настройки OpenGL идентичны тем, что были в этом примере, поэтому сцена настроена правильно.

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

Кроме того, вы связываете рендер-буфер, но вам может понадобиться

[context presentRenderbuffer:GL_RENDERBUFFER];

после последней строки, чтобы содержимое попадало на экран (где context - ваш экземпляр EAGLContext).

1 голос
/ 18 июля 2011

Это потому, что контекст OpenGl не является общим для разных потоков. Так добавь следующая функция для вашего (void) captureOutput: function.

dispatch_async(dispatch_get_main_queue(),
               ^{                       
                   //your code for displaying
               });
}    
0 голосов
/ 05 апреля 2011

Ваш буфер изображения коренастый или плоский? Я просто прошёл мимо и не прочитал здесь каждый последний фрагмент документации Apple, но я читал, что этот вопрос определяет, что вы найдете по базовому адресу буфера.

0 голосов
/ 24 марта 2011

Может ли это быть проблемой с потоками?Делегат captureOutput может вызываться в другом потоке из initWithFrame.Конечно, если вы хотите обновить отображение с помощью UIKit, вам нужно убедиться, что это происходит в главном потоке, например, в вашей функции captureOutput вы должны сделать что-то вроде:

// trigger the processing of this frame on the main thread
[self performSelectorOnMainThread:@selector(processFrame) withObject:nil waitUntilDone:NO];

И затем поместить UIобновить код в функцию processFrame.Я знаю, что вы не используете UIKit для своего дисплея, но вы также не можете разделить контекст OpenGL между потоками, так что, возможно, это все еще применимо к вашему случаю.В любом случае, просто мысль.

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