blob: f04a9ebc9c8cbab17fc28e1b811817dda3bcbe4d [file] [log] [blame]
Anthony Léonard14e7bf32017-06-08 08:13:16 -04001/*
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
23static const GLchar* vShaderSrc = R"glsl(
24#version 150
25
26in vec2 in_Pos;
27in vec2 in_TexCoord;
28uniform vec2 in_Scaling;
29
30out vec2 texCoord;
31
32void 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
39static const GLchar* fShaderSrc = R"glsl(
40#version 150
41
42out vec4 fragColor;
43in vec2 texCoord;
44
45uniform sampler2D tex;
46
47void main()
48{
49 fragColor = texture(tex, texCoord);
50}
51)glsl";
52
53@implementation CallLayer
54
55// OpenGL handlers
56GLuint tex, vbo, vShader, fShader, sProg, vao;
57
58// Last frame data and attributes
59Video::Frame currentFrame;
Anthony Léonard14e7bf32017-06-08 08:13:16 -040060BOOL currentFrameDisplayed;
61NSLock* 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éonarda9d7c8e2018-01-03 14:41:17 -0500168 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éonard14e7bf32017-06-08 08:13:16 -0400171 currentFrameDisplayed = YES;
172 }
173 // To ensure that we will not divide by zero
Anthony Léonarda9d7c8e2018-01-03 14:41:17 -0500174 if (currentFrame.ptr && currentFrame.width && currentFrame.height) {
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400175 // 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éonarda9d7c8e2018-01-03 14:41:17 -0500178 float frameRatio = ((float)currentFrame.width)/((float)currentFrame.height);
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400179 float ratio = viewRatio * (1/frameRatio);
180
181 GLint inScalingUniform = glGetUniformLocation(sProg, "in_Scaling");
182
Kateryna Kostiuk1004caa2018-03-29 13:48:07 -0400183 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éonard14e7bf32017-06-08 08:13:16 -0400198 }
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éonarda9d7c8e2018-01-03 14:41:17 -0500208- (void) setCurrentFrame:(Video::Frame)framePtr
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400209{
210 [currentFrameLk lock];
211 currentFrame = std::move(framePtr);
Anthony Léonard14e7bf32017-06-08 08:13:16 -0400212 currentFrameDisplayed = NO;
213 [currentFrameLk unlock];
214}
215
216@end