0

opengl rendered on iPhone enter image description here I created an iOS app using OpenGL to render yuv420p from ffmpeg. It works fine on iPad, but on iPhone, it looks like the image below, the picture looks italic and the right bottom triangle part like below should be on the left. I can not found the reason, has anyone met this before? does OpenGL on iPad and iPhone different? below is my OpenGL view. SDL_Overlay is a struct that holds YUV plane data from ffmpeg.

#import "EAGLView.h"

// Uniform index.
enum
{
    UNIFORM_Y,
    UNIFORM_U,
    UNIFORM_V,
    NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];

// Attribute index.
enum
{
    ATTRIB_VERTEX,
    ATTRIB_TEXCOORD,
    NUM_ATTRIBUTES
};

const GLubyte VertexIndexStruct[] = {
    0, 1, 2,
    2, 3, 0
};

@interface EAGLView () {
    // The pixel dimensions of the CAEAGLLayer.
    GLint _backingWidth;
    GLint _backingHeight;

    EAGLContext *m_context;

    GLuint _frameBufferHandle;
    GLuint _colorBufferHandle;

    BOOL _pause;
}
@property GLuint program;

- (void)setupBuffers;
- (BOOL)loadShaders;
- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type URL:(NSURL *)URL;
- (BOOL)linkProgram:(GLuint)prog;
- (BOOL)validateProgram:(GLuint)prog;
@end

@implementation EAGLView

+(Class) layerClass {
    return [CAEAGLLayer class];
}

- (id) initWithCoder:(NSCoder*)coder {
    if ((self = [super initWithCoder:coder])) {
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(applicationDidEnterBackground:)
                                                     name:UIApplicationDidEnterBackgroundNotification
                                                   object:nil];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(applicationWillEnterForeground:)
                                                     name:UIApplicationWillEnterForegroundNotification
                                                   object:nil];
    }

    return self;
}

-(void) applicationDidEnterBackground:(NSNotification *) notification {
    _pause = YES;
}

-(void) applicationWillEnterForeground:(NSNotification *) notification {
    _pause = NO;
}

-(void) destroyFrameBuffer {
    // tear down GL
    if (_frameBufferHandle) {
        glDeleteFramebuffers(1, &_frameBufferHandle);
        _frameBufferHandle = 0;
    }

    if (_colorBufferHandle) {
        glDeleteRenderbuffers(1, &_colorBufferHandle);
        _colorBufferHandle = 0;
    }

    if(self.program) {
        glDeleteProgram(self.program);
    }
}

-(void) setFrame:(CGRect)frame {
    [super setFrame:frame];
    [self setupGL];
}

# pragma mark - OpenGL setup
- (void)setupGL {
    [self destroyFrameBuffer];
    self.contentScaleFactor = [UIScreen mainScreen].scale;
    CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
    eaglLayer.opaque = TRUE;
    [eaglLayer setContentsScale:self.contentScaleFactor];
    eaglLayer.drawableProperties = @{ kEAGLDrawablePropertyRetainedBacking :[NSNumber numberWithBool:NO],
                                      kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8};

    if(m_context) {
        [m_context release];
    }
    m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    if(!m_context) {
        m_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
    }
    if (!m_context || ![EAGLContext setCurrentContext:m_context] || ![self loadShaders]) {
        return;
    }

    [EAGLContext setCurrentContext:m_context];
    [self setupBuffers];
    [self loadShaders];

    glUseProgram(self.program);

    // 0 and 1 are the texture IDs of _lumaTexture and _chromaTexture respectively.
    glUniform1i(uniforms[UNIFORM_Y], 0);
    glUniform1i(uniforms[UNIFORM_U], 1);
    glUniform1i(uniforms[UNIFORM_V], 2);
}

#pragma mark - Utilities
- (void)setupBuffers {
    glDisable(GL_DEPTH_TEST);

    glEnableVertexAttribArray(ATTRIB_VERTEX);
    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);

    glEnableVertexAttribArray(ATTRIB_TEXCOORD);
    glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);

    glGenFramebuffers(1, &_frameBufferHandle);
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferHandle);

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

    CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
    eaglLayer.opaque = TRUE;
    [eaglLayer setContentsScale:self.contentScaleFactor];
    [m_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &_backingWidth);
    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &_backingHeight);
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorBufferHandle);
    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        NSLog(@"Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
    }
}

- (void)dealloc {
    [self destroyFrameBuffer];

    [super dealloc];
}

#pragma mark -  OpenGL ES 2 shader compilation
- (BOOL)loadShaders {
    GLuint vertShader, fragShader;
    NSURL *vertShaderURL, *fragShaderURL;

    // Create the shader program.
    self.program = glCreateProgram();

    // Create and compile the vertex shader.
    vertShaderURL = [[NSBundle mainBundle] URLForResource:@"shader" withExtension:@"vsh"];
    if (![self compileShader:&vertShader type:GL_VERTEX_SHADER URL:vertShaderURL]) {
        NSLog(@"Failed to compile vertex shader");
        return NO;
    }

    // Create and compile fragment shader.
    fragShaderURL = [[NSBundle mainBundle] URLForResource:@"shader" withExtension:@"fsh"];
    if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER URL:fragShaderURL]) {
        NSLog(@"Failed to compile fragment shader");
        return NO;
    }

    // Attach vertex shader to program.
    glAttachShader(self.program, vertShader);

    // Attach fragment shader to program.
    glAttachShader(self.program, fragShader);

    // Bind attribute locations. This needs to be done prior to linking.
    glBindAttribLocation(self.program, ATTRIB_VERTEX, "position");
    glBindAttribLocation(self.program, ATTRIB_TEXCOORD, "texCoord");

    // Link the program.
    if (![self linkProgram:self.program]) {
        NSLog(@"Failed to link program: %d", self.program);

        if (vertShader) {
            glDeleteShader(vertShader);
            vertShader = 0;
        }
        if (fragShader) {
            glDeleteShader(fragShader);
            fragShader = 0;
        }
        if (self.program) {
            glDeleteProgram(self.program);
            self.program = 0;
        }

        return NO;
    }

    // Get uniform locations.
    uniforms[UNIFORM_Y] = glGetUniformLocation(self.program, "SamplerY");
    uniforms[UNIFORM_U] = glGetUniformLocation(self.program, "SamplerU");
    uniforms[UNIFORM_V] = glGetUniformLocation(self.program, "SamplerV");

    // Release vertex and fragment shaders.
    if (vertShader) {
        glDetachShader(self.program, vertShader);
        glDeleteShader(vertShader);
    }
    if (fragShader) {
        glDetachShader(self.program, fragShader);
        glDeleteShader(fragShader);
    }

    return YES;
}

- (BOOL)compileShader:(GLuint *)shader type:(GLenum)type URL:(NSURL *)URL {
    NSError *error;
    NSString *sourceString = [[NSString alloc] initWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error];
    if (sourceString == nil) {
        NSLog(@"Failed to load vertex shader: %@", [error localizedDescription]);
        return NO;
    }

    GLint status;
    const GLchar *source;
    source = (GLchar *)[sourceString UTF8String];

    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);

#if defined(DEBUG)
    GLint logLength;
    glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetShaderInfoLog(*shader, logLength, &logLength, log);
        NSLog(@"Shader compile log:\n%s", log);
        free(log);
    }
#endif

    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
    if (status == 0) {
        glDeleteShader(*shader);
        return NO;
    }

    return YES;
}

- (BOOL)linkProgram:(GLuint)prog {
    GLint status;
    glLinkProgram(prog);

#if defined(DEBUG)
    GLint logLength;
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program link log:\n%s", log);
        free(log);
    }
#endif

    glGetProgramiv(prog, GL_LINK_STATUS, &status);
    if (status == 0) {
        return NO;
    }

    return YES;
}

- (BOOL)validateProgram:(GLuint)prog {
    GLint logLength, status;

    glValidateProgram(prog);
    glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0) {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(prog, logLength, &logLength, log);
        NSLog(@"Program validate log:\n%s", log);
        free(log);
    }

    glGetProgramiv(prog, GL_VALIDATE_STATUS, &status);
    if (status == 0) {
        return NO;
    }

    return YES;
}

-(CGSize) renderSize {
    return CGSizeMake(_backingWidth/self.contentScaleFactor, _backingHeight/self.contentScaleFactor);
}

-(void) render:(SDL_Overlay*) overlay {
    if(_pause) {
        return;
    }
    //Create Y and UV textures from the pixel buffer
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, uniforms[UNIFORM_Y]);
    glTexImage2D(GL_TEXTURE_2D,
                 0,
                 GL_LUMINANCE,
                 overlay->w,
                 overlay->h,
                 0,
                 GL_LUMINANCE,
                 GL_UNSIGNED_BYTE,
                 overlay->data[0]);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    // U-plane.
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, uniforms[UNIFORM_U]);
    glTexImage2D(GL_TEXTURE_2D,
                 0,
                 GL_LUMINANCE,
                 overlay->w/2,
                 overlay->h/2,
                 0,
                 GL_LUMINANCE,
                 GL_UNSIGNED_BYTE,
                 overlay->data[1]);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    // V-plane.
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, uniforms[UNIFORM_V]);

    glTexImage2D(GL_TEXTURE_2D,
                 0,
                 GL_LUMINANCE,
                 overlay->w/2,
                 overlay->h/2,
                 0,
                 GL_LUMINANCE,
                 GL_UNSIGNED_BYTE,
                 overlay->data[2]);


    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glBindFramebuffer(GL_FRAMEBUFFER, _frameBufferHandle);

    CGFloat ratio = (CGFloat)overlay->w / overlay->h;
    GLfloat actualWidth = _backingWidth;
    GLfloat actualHeight = actualWidth / ratio;
    if(actualHeight > _backingHeight) {
        actualHeight = _backingHeight;
        actualWidth = actualHeight * ratio;
    }

    // Set the view port to the entire view.
    glViewport((_backingWidth - actualWidth) / 2,
               (_backingHeight - actualHeight) / 2,
               actualWidth,
               actualHeight);

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

    // Use shader program.
    glUseProgram(self.program);
    /*
     The quad vertex data defines the region of 2D plane onto which we draw our pixel buffers.
     Vertex data formed using (-1,-1) and (1,1) as the bottom left and top right coordinates respectively, covers the entire screen.
     */
    GLfloat quadVertexData [] = {
        1, -1,
        1, 1,
        -1, 1,
        -1, -1
    };

    // Update attribute values.
    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, quadVertexData);
    glEnableVertexAttribArray(ATTRIB_VERTEX);

    /*
     The texture vertices are set up such that we flip the texture vertically. This is so that our top left origin buffers match OpenGL's bottom left texture coordinate system.
     */
    GLfloat quadTextureData[] =  {
        1, 1,
        1, 0,
        0, 0,
        0, 1,
    };

    glVertexAttribPointer(ATTRIB_TEXCOORD, 2, GL_FLOAT, 0, 0, quadTextureData);
    glEnableVertexAttribArray(ATTRIB_TEXCOORD);

//    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    glDrawElements(GL_TRIANGLES, sizeof(VertexIndexStruct) / sizeof(VertexIndexStruct[0]), GL_UNSIGNED_BYTE, &VertexIndexStruct);

    glBindRenderbuffer(GL_RENDERBUFFER, _colorBufferHandle);
    [m_context presentRenderbuffer:GL_RENDERBUFFER];
}

@end
spanfish
  • 191
  • 2
  • 5
  • 1
    Are you loading the same images? Or are they different sizes? Looks like a possible alignment problem with the images. Try setting `GL_UNPACK_ALIGNMENT` to 1 if that's the case. – Reto Koradi Dec 31 '15 at 06:44
  • Setting GL_UNPACK_ALIGNMENT to 1 as @Reto Korai said, it works now, thanks. – spanfish Dec 31 '15 at 07:09
  • Possible duplicate of [OpenGL texture tilted](http://stackoverflow.com/questions/15983607/opengl-texture-tilted) – Reto Koradi Dec 31 '15 at 16:36
  • There is a new problem, now the colour is not correct on iPhone, see picture 2, the color seems to be rendered on the right of where it is expected to be – spanfish Jan 02 '16 at 11:52

0 Answers0