Anthony Léonard | 14e7bf3 | 2017-06-08 08:13:16 -0400 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2017 Savoir-faire Linux Inc. |
| 3 | * Author: Anthony Léonard <anthony.leonard@savoirfairelinux.com> |
| 4 | * |
| 5 | * This program is free software; you can redistribute it and/or modify |
| 6 | * it under the terms of the GNU General Public License as published by |
| 7 | * the Free Software Foundation; either version 3 of the License, or |
| 8 | * (at your option) any later version. |
| 9 | * |
| 10 | * This program is distributed in the hope that it will be useful, |
| 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 13 | * GNU General Public License for more details. |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License |
| 16 | * along with this program; if not, write to the Free Software |
| 17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | */ |
| 19 | |
| 20 | #import "CallLayer.h" |
| 21 | #import <OpenGL/gl3.h> |
| 22 | |
| 23 | static const GLchar* vShaderSrc = R"glsl( |
| 24 | #version 150 |
| 25 | |
| 26 | in vec2 in_Pos; |
| 27 | in vec2 in_TexCoord; |
| 28 | uniform vec2 in_Scaling; |
| 29 | |
| 30 | out vec2 texCoord; |
| 31 | |
| 32 | void main() |
| 33 | { |
| 34 | texCoord = in_TexCoord; |
| 35 | gl_Position = vec4(in_Pos.x*in_Scaling.x, in_Pos.y*in_Scaling.y, 0.0, 1.0); |
| 36 | } |
| 37 | )glsl"; |
| 38 | |
| 39 | static const GLchar* fShaderSrc = R"glsl( |
| 40 | #version 150 |
| 41 | |
| 42 | out vec4 fragColor; |
| 43 | in vec2 texCoord; |
| 44 | |
| 45 | uniform sampler2D tex; |
| 46 | |
| 47 | void main() |
| 48 | { |
| 49 | fragColor = texture(tex, texCoord); |
| 50 | } |
| 51 | )glsl"; |
| 52 | |
| 53 | @implementation CallLayer |
| 54 | |
| 55 | // OpenGL handlers |
| 56 | GLuint tex, vbo, vShader, fShader, sProg, vao; |
| 57 | |
| 58 | // Last frame data and attributes |
| 59 | Video::Frame currentFrame; |
Anthony Léonard | 14e7bf3 | 2017-06-08 08:13:16 -0400 | [diff] [blame] | 60 | BOOL currentFrameDisplayed; |
| 61 | NSLock* currentFrameLk; |
| 62 | |
| 63 | - (id) init |
| 64 | { |
| 65 | self = [super init]; |
| 66 | if (self) { |
| 67 | currentFrameLk = [[NSLock alloc] init]; |
| 68 | [self setVideoRunning:NO]; |
| 69 | } |
| 70 | return self; |
| 71 | } |
| 72 | |
| 73 | // This setter is redefined so we can initialize the OpenGL context when this one is |
| 74 | // setup by the UI (which seems to be done just before the first draw attempt and not in init method); |
| 75 | - (void)setOpenGLContext:(NSOpenGLContext *)openGLContext |
| 76 | { |
| 77 | [super setOpenGLContext:openGLContext]; |
| 78 | |
| 79 | if (openGLContext) { |
| 80 | GLfloat vertices[] = { |
| 81 | -1.0, 1.0, 0.0, 0.0, // Top-left |
| 82 | 1.0, 1.0, 1.0, 0.0, // Top-right |
| 83 | -1.0, -1.0, 0.0, 1.0, // Bottom-left |
| 84 | 1.0, -1.0, 1.0, 1.0 // Bottom-right |
| 85 | }; |
| 86 | |
| 87 | [openGLContext makeCurrentContext]; |
| 88 | |
| 89 | // VAO |
| 90 | glGenVertexArrays(1, &vao); |
| 91 | glBindVertexArray(vao); |
| 92 | |
| 93 | // VBO |
| 94 | glGenBuffers(1, &vbo); |
| 95 | glBindBuffer(GL_ARRAY_BUFFER, vbo); |
| 96 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); |
| 97 | |
| 98 | // Vertex shader |
| 99 | vShader = glCreateShader(GL_VERTEX_SHADER); |
| 100 | glShaderSource(vShader, 1, &vShaderSrc, NULL); |
| 101 | glCompileShader(vShader); |
| 102 | |
| 103 | // Fragment shader |
| 104 | fShader = glCreateShader(GL_FRAGMENT_SHADER); |
| 105 | glShaderSource(fShader, 1, &fShaderSrc, NULL); |
| 106 | glCompileShader(fShader); |
| 107 | |
| 108 | // Program |
| 109 | sProg = glCreateProgram(); |
| 110 | glAttachShader(sProg, vShader); |
| 111 | glAttachShader(sProg, fShader); |
| 112 | glBindFragDataLocation(sProg, 0, "fragColor"); |
| 113 | glLinkProgram(sProg); |
| 114 | glUseProgram(sProg); |
| 115 | |
| 116 | // Vertices position attrib |
| 117 | GLuint inPosAttrib = glGetAttribLocation(sProg, "in_Pos"); |
| 118 | glEnableVertexAttribArray(inPosAttrib); |
| 119 | glVertexAttribPointer(inPosAttrib, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), 0); |
| 120 | |
| 121 | // Texture position attrib |
| 122 | GLuint inTexCoordAttrib = glGetAttribLocation(sProg, "in_TexCoord"); |
| 123 | glEnableVertexAttribArray(inTexCoordAttrib); |
| 124 | glVertexAttribPointer(inTexCoordAttrib, 2, GL_FLOAT, GL_FALSE, 4*sizeof(GLfloat), (void*)(2*sizeof(GLfloat))); |
| 125 | |
| 126 | // Texture |
| 127 | glGenTextures(1, &tex); |
| 128 | glBindTexture(GL_TEXTURE_2D, tex); |
| 129 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); |
| 130 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); |
| 131 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); |
| 132 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | - (NSOpenGLPixelFormat *)openGLPixelFormatForDisplayMask:(uint32_t)mask |
| 137 | { |
| 138 | NSOpenGLPixelFormatAttribute attrs[] = { |
| 139 | NSOpenGLPFANoRecovery, |
| 140 | NSOpenGLPFAColorSize, 24, |
| 141 | NSOpenGLPFAAlphaSize, 8, |
| 142 | NSOpenGLPFADoubleBuffer, |
| 143 | NSOpenGLPFAScreenMask, |
| 144 | mask, |
| 145 | NSOpenGLPFAAccelerated, |
| 146 | NSOpenGLPFAOpenGLProfile, |
| 147 | NSOpenGLProfileVersion3_2Core, |
| 148 | 0 |
| 149 | }; |
| 150 | |
| 151 | NSOpenGLPixelFormat* pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; |
| 152 | |
| 153 | return pixelFormat; |
| 154 | } |
| 155 | |
| 156 | - (BOOL)isAsynchronous |
| 157 | { |
| 158 | return YES; |
| 159 | } |
| 160 | |
| 161 | - (void)drawInOpenGLContext:(NSOpenGLContext *)context pixelFormat:(NSOpenGLPixelFormat *)pixelFormat forLayerTime:(CFTimeInterval)t displayTime:(const CVTimeStamp *)ts |
| 162 | { |
| 163 | GLenum errEnum; |
| 164 | glBindTexture(GL_TEXTURE_2D, tex); |
| 165 | |
| 166 | [currentFrameLk lock]; |
| 167 | if(!currentFrameDisplayed) { |
Anthony Léonard | a9d7c8e | 2018-01-03 14:41:17 -0500 | [diff] [blame] | 168 | if(currentFrame.ptr) { |
| 169 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, currentFrame.width, currentFrame.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, currentFrame.ptr); |
| 170 | } |
Anthony Léonard | 14e7bf3 | 2017-06-08 08:13:16 -0400 | [diff] [blame] | 171 | currentFrameDisplayed = YES; |
| 172 | } |
| 173 | // To ensure that we will not divide by zero |
Anthony Léonard | a9d7c8e | 2018-01-03 14:41:17 -0500 | [diff] [blame] | 174 | if (currentFrame.ptr && currentFrame.width && currentFrame.height) { |
Anthony Léonard | 14e7bf3 | 2017-06-08 08:13:16 -0400 | [diff] [blame] | 175 | // Compute scaling factor to keep the original aspect ratio of the video |
| 176 | CGSize viewSize = self.frame.size; |
| 177 | float viewRatio = viewSize.width/viewSize.height; |
Anthony Léonard | a9d7c8e | 2018-01-03 14:41:17 -0500 | [diff] [blame] | 178 | float frameRatio = ((float)currentFrame.width)/((float)currentFrame.height); |
Anthony Léonard | 14e7bf3 | 2017-06-08 08:13:16 -0400 | [diff] [blame] | 179 | float ratio = viewRatio * (1/frameRatio); |
| 180 | |
| 181 | GLint inScalingUniform = glGetUniformLocation(sProg, "in_Scaling"); |
| 182 | |
Kateryna Kostiuk | 1004caa | 2018-03-29 13:48:07 -0400 | [diff] [blame] | 183 | float multiplier = MAX(frameRatio, ratio); |
| 184 | if((viewRatio >= 1 && frameRatio >= 1) || |
| 185 | (viewRatio < 1 && frameRatio < 1) || |
| 186 | (ratio > 0.5 && ratio < 1.5) ) { |
| 187 | if (ratio > 1.0) |
| 188 | glUniform2f(inScalingUniform, 1.0, 1.0 * ratio); |
| 189 | else |
| 190 | glUniform2f(inScalingUniform, 1.0/ratio, 1.0); |
| 191 | } else { |
| 192 | if (ratio < 1.0) |
| 193 | glUniform2f(inScalingUniform, 1.0, 1.0 * ratio); |
| 194 | else |
| 195 | glUniform2f(inScalingUniform, 1.0/ratio, 1.0); |
| 196 | |
| 197 | } |
Anthony Léonard | 14e7bf3 | 2017-06-08 08:13:16 -0400 | [diff] [blame] | 198 | } |
| 199 | [currentFrameLk unlock]; |
| 200 | |
| 201 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); |
| 202 | glClear(GL_COLOR_BUFFER_BIT); |
| 203 | |
| 204 | if([self videoRunning]) |
| 205 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); |
| 206 | } |
| 207 | |
Anthony Léonard | a9d7c8e | 2018-01-03 14:41:17 -0500 | [diff] [blame] | 208 | - (void) setCurrentFrame:(Video::Frame)framePtr |
Anthony Léonard | 14e7bf3 | 2017-06-08 08:13:16 -0400 | [diff] [blame] | 209 | { |
| 210 | [currentFrameLk lock]; |
| 211 | currentFrame = std::move(framePtr); |
Anthony Léonard | 14e7bf3 | 2017-06-08 08:13:16 -0400 | [diff] [blame] | 212 | currentFrameDisplayed = NO; |
| 213 | [currentFrameLk unlock]; |
| 214 | } |
| 215 | |
| 216 | @end |