我创建了一个
iOS
应用程序OpenGL
,用于从 ffmpeg 渲染 yuv420p。它在 上工作正常iPad
,但在 上iPhone
,它看起来像下面的图像,图片看起来是斜体,下面的右下三角形部分应该在左边。我找不到原因,以前有人遇到过吗?OpenGL
和 iPad 上的不iPhone
一样吗?以下是我的OpenGL
看法。SDL_Overlay
是一个保存来自 ffmpeg 的 YUV 平面数据的结构。
#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