1 /*
2 opengles_display.c
3 Copyright (C) 2017 Belledonne Communications, Grenoble, France
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (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 #include "opengles_display.h"
21 #include "mediastreamer2/mscommon.h"
22 #include "shader_util.h"
23
24 #ifdef __cplusplus
25 extern "C"{
26 #endif
27
28 enum ImageType {
29 REMOTE_IMAGE = 0,
30 PREVIEW_IMAGE,
31 MAX_IMAGE
32 };
33
34 // #define CHECK_GL_ERROR
35
36 #ifdef CHECK_GL_ERROR
37 #define GL_OPERATION(f, x) \
38 do { \
39 (f->x); \
40 check_GL_errors(f, #x); \
41 } while (0);
42
43 #define GL_OPERATION_RET(f, x, ret) \
44 do { \
45 ret = (f->x); \
46 check_GL_errors(f, #x); \
47 } while (0);
48 #else
49 #define GL_OPERATION(f, x) (f->x);
50
51 #define GL_OPERATION_RET(f, x, ret) ret = (f->x);
52 #endif
53
54 enum {
55 UNIFORM_PROJ_MATRIX = 0,
56 UNIFORM_ROTATION,
57 UNIFORM_TEXTURE_Y,
58 UNIFORM_TEXTURE_U,
59 UNIFORM_TEXTURE_V,
60 NUM_UNIFORMS
61 };
62
63 enum {
64 ATTRIB_VERTEX = 0,
65 ATTRIB_UV,
66 NUM_ATTRIBS
67 };
68
69 enum {
70 Y,
71 U,
72 V
73 };
74
75 #define TEXTURE_BUFFER_SIZE 3
76
77 // -----------------------------------------------------------------------------
78
79 struct opengles_display {
80 /* input: yuv image to display */
81 ms_mutex_t yuv_mutex;
82 mblk_t *yuv[MAX_IMAGE];
83 bool_t new_yuv_image[TEXTURE_BUFFER_SIZE][MAX_IMAGE];
84
85 /* GL resources */
86 bool_t glResourcesInitialized;
87 GLuint program, textures[TEXTURE_BUFFER_SIZE][MAX_IMAGE][3];
88 GLint uniforms[NUM_UNIFORMS];
89 MSVideoSize allocatedTexturesSize[MAX_IMAGE];
90
91 int texture_index;
92
93 /* GL view size */
94 GLint backingWidth;
95 GLint backingHeight;
96
97 /* runtime data */
98 float uvx[MAX_IMAGE], uvy[MAX_IMAGE];
99 MSVideoSize yuv_size[MAX_IMAGE];
100
101 /* coordinates of for zoom-in */
102 float zoom_factor;
103 float zoom_cx;
104 float zoom_cy;
105
106 OpenGlFunctions *default_functions;
107 const OpenGlFunctions *functions;
108 };
109
110 // -----------------------------------------------------------------------------
111
clean_GL_errors(const OpenGlFunctions * f)112 static void clean_GL_errors (const OpenGlFunctions *f) {
113 while (f->glGetError() != GL_NO_ERROR);
114 }
115
check_GL_errors(const OpenGlFunctions * f,const char * context)116 static void check_GL_errors (const OpenGlFunctions *f, const char* context) {
117 GLenum error;
118 while ((error = f->glGetError()) != GL_NO_ERROR) {
119 switch(error) {
120 case GL_INVALID_ENUM: ms_error("GL error: '%s' -> GL_INVALID_ENUM\n", context); break;
121 case GL_INVALID_VALUE: ms_error("GL error: '%s' -> GL_INVALID_VALUE\n", context); break;
122 case GL_INVALID_OPERATION: ms_error("GL error: '%s' -> GL_INVALID_OPERATION\n", context); break;
123 case GL_OUT_OF_MEMORY: ms_error("GL error: '%s' -> GL_OUT_OF_MEMORY\n", context); break;
124 case GL_INVALID_FRAMEBUFFER_OPERATION: ms_error("GL error: '%s' -> GL_INVALID_FRAMEBUFFER_OPERATION\n", context); break;
125 default:
126 ms_error("GL error: '%s' -> %x\n", context, error);
127 }
128 }
129 }
130
align_on_power_of_2(unsigned int value)131 static unsigned int align_on_power_of_2(unsigned int value) {
132 int i;
133 /* browse all power of 2 value, and find the one just >= value */
134 for(i = 0; i < 32; i++) {
135 unsigned int c = 1 << i;
136 if (value <= c)
137 return c;
138 }
139 return 0;
140 }
141
load_orthographic_matrix(float left,float right,float bottom,float top,float _near,float _far,float * mat)142 static void load_orthographic_matrix (float left, float right, float bottom, float top, float _near, float _far, float *mat) {
143 float r_l = right - left;
144 float t_b = top - bottom;
145 float f_n = _far - _near;
146 float tx = - (right + left) / (right - left);
147 float ty = - (top + bottom) / (top - bottom);
148 float tz = - (_far + _near) / (_far - _near);
149
150 mat[0] = (2.0f / r_l);
151 mat[1] = mat[2] = mat[3] = 0.0f;
152
153 mat[4] = 0.0f;
154 mat[5] = (2.0f / t_b);
155 mat[6] = mat[7] = 0.0f;
156
157 mat[8] = mat[9] = 0.0f;
158 mat[10] = -2.0f / f_n;
159 mat[11] = 0.0f;
160
161 mat[12] = tx;
162 mat[13] = ty;
163 mat[14] = tz;
164 mat[15] = 1.0f;
165 }
166
167 // -----------------------------------------------------------------------------
168
print_program_info(const OpenGlFunctions * f,GLuint * program)169 static void print_program_info (const OpenGlFunctions *f, GLuint *program) {
170 GLint logLength;
171 char *msg;
172
173 GL_OPERATION(f, glGetProgramiv(*program, GL_INFO_LOG_LENGTH, &logLength));
174 if (logLength > 0) {
175 msg = ms_new(char, logLength);
176
177 GL_OPERATION(f, glGetProgramInfoLog(*program, logLength, &logLength, msg));
178 ms_message("OpenGL program info: %s", msg);
179
180 ms_free(msg);
181 } else
182 ms_message("OpenGL program info: [NO INFORMATION]");
183 }
184
load_shaders(const OpenGlFunctions * f,GLuint * program,GLint * uniforms)185 static bool_t load_shaders (const OpenGlFunctions *f, GLuint *program, GLint *uniforms) {
186 GLuint vertShader, fragShader;
187
188 #include "yuv2rgb.vs.h"
189 #include "yuv2rgb.fs.h"
190 (void)yuv2rgb_vs_len;
191 (void)yuv2rgb_fs_len;
192
193 *program = f->glCreateProgram();
194 if (!glueCompileShader(f, GL_VERTEX_SHADER, 1, (const char*)yuv2rgb_vs, &vertShader))
195 return FALSE;
196 if (!glueCompileShader(f, GL_FRAGMENT_SHADER, 1, (const char*)yuv2rgb_fs, &fragShader))
197 return FALSE;
198
199 GL_OPERATION(f, glAttachShader(*program, vertShader))
200 GL_OPERATION(f, glAttachShader(*program, fragShader))
201
202 GL_OPERATION(f, glBindAttribLocation(*program, ATTRIB_VERTEX, "position"))
203 GL_OPERATION(f, glBindAttribLocation(*program, ATTRIB_UV, "uv"))
204
205 if (!glueLinkProgram(f, *program))
206 return FALSE;
207
208 GL_OPERATION_RET(f, glGetUniformLocation(*program, "proj_matrix"), uniforms[UNIFORM_PROJ_MATRIX])
209 GL_OPERATION_RET(f, glGetUniformLocation(*program, "rotation"), uniforms[UNIFORM_ROTATION])
210 GL_OPERATION_RET(f, glGetUniformLocation(*program, "t_texture_y"), uniforms[UNIFORM_TEXTURE_Y])
211 GL_OPERATION_RET(f, glGetUniformLocation(*program, "t_texture_u"), uniforms[UNIFORM_TEXTURE_U])
212 GL_OPERATION_RET(f, glGetUniformLocation(*program, "t_texture_v"), uniforms[UNIFORM_TEXTURE_V])
213
214 GL_OPERATION(f, glDeleteShader(vertShader))
215 GL_OPERATION(f, glDeleteShader(fragShader))
216
217 check_GL_errors(f, "load_shaders");
218 print_program_info(f, program);
219
220 return TRUE;
221 }
222
223 // -----------------------------------------------------------------------------
224
allocate_gl_textures(struct opengles_display * gldisp,int w,int h,enum ImageType type)225 static void allocate_gl_textures (struct opengles_display *gldisp, int w, int h, enum ImageType type) {
226 const OpenGlFunctions *f = gldisp->functions;
227 int j;
228
229 for(j = 0; j< TEXTURE_BUFFER_SIZE; j++) {
230 GL_OPERATION(f, glActiveTexture(GL_TEXTURE0))
231 GL_OPERATION(f, glBindTexture(GL_TEXTURE_2D, gldisp->textures[j][type][Y]))
232 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR))
233 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR))
234 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE))
235 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE))
236 GL_OPERATION(f, glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0))
237
238 GL_OPERATION(f, glActiveTexture(GL_TEXTURE1))
239 GL_OPERATION(f, glBindTexture(GL_TEXTURE_2D, gldisp->textures[j][type][U]))
240 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR))
241 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR))
242 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE))
243 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE))
244 GL_OPERATION(f, glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w >> 1, h >> 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0))
245
246 GL_OPERATION(f, glActiveTexture(GL_TEXTURE2))
247 GL_OPERATION(f, glBindTexture(GL_TEXTURE_2D, gldisp->textures[j][type][V]))
248 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR))
249 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR))
250 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE))
251 GL_OPERATION(f, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE))
252 GL_OPERATION(f, glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w >> 1, h >> 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0))
253 }
254
255 gldisp->allocatedTexturesSize[type].width = w;
256 gldisp->allocatedTexturesSize[type].height = h;
257
258 ms_message("%s: allocated new textures[%d] (%d x %d)\n", __FUNCTION__, type, w, h);
259
260 check_GL_errors(f, "allocate_gl_textures");
261 }
262
pixel_unpack_alignment(uint8_t * ptr,int datasize)263 static int pixel_unpack_alignment(uint8_t *ptr, int datasize) {
264 uintptr_t num_ptr = (uintptr_t) ptr;
265 int alignment_ptr = !(num_ptr % 4) ? 4 : !(num_ptr % 2) ? 2 : 1;
266 int alignment_data = !(datasize % 4) ? 4 : !(datasize % 2) ? 2 : 1;
267 return (alignment_ptr <= alignment_data) ? alignment_ptr : alignment_data;
268 }
269
ogl_display_set_yuv(struct opengles_display * gldisp,mblk_t * yuv,enum ImageType type)270 static void ogl_display_set_yuv (struct opengles_display *gldisp, mblk_t *yuv, enum ImageType type) {
271 int j;
272
273 if (!gldisp) {
274 ms_error("%s called with null struct opengles_display", __FUNCTION__);
275 return;
276 }
277
278 ms_mutex_lock(&gldisp->yuv_mutex);
279
280 if (gldisp->yuv[type]) {
281 freemsg(gldisp->yuv[type]);
282 gldisp->yuv[type] = NULL;
283 }
284
285 if (yuv) {
286 gldisp->yuv[type] = dupmsg(yuv);
287 for(j = 0; j < TEXTURE_BUFFER_SIZE; ++j) {
288 gldisp->new_yuv_image[j][type] = TRUE;
289 }
290 }
291
292 ms_mutex_unlock(&gldisp->yuv_mutex);
293 }
294
update_textures_with_yuv(struct opengles_display * gldisp,enum ImageType type)295 static bool_t update_textures_with_yuv (struct opengles_display *gldisp, enum ImageType type) {
296 const OpenGlFunctions *f = gldisp->functions;
297
298 unsigned int aligned_yuv_w, aligned_yuv_h;
299 int alignment = 0;
300 MSPicture yuvbuf;
301
302 ms_yuv_buf_init_from_mblk(&yuvbuf, gldisp->yuv[type]);
303
304 if (yuvbuf.w == 0 || yuvbuf.h == 0) {
305 ms_warning("Incoherent image size: %dx%d\n", yuvbuf.w, yuvbuf.h);
306 return FALSE;
307 }
308
309 aligned_yuv_w = align_on_power_of_2(yuvbuf.w);
310 aligned_yuv_h = align_on_power_of_2(yuvbuf.h);
311
312 /* check if we need to adjust texture sizes */
313 if (
314 aligned_yuv_w != (unsigned int)gldisp->allocatedTexturesSize[type].width ||
315 aligned_yuv_h != (unsigned int)gldisp->allocatedTexturesSize[type].height
316 )
317 allocate_gl_textures(gldisp, aligned_yuv_w, aligned_yuv_h, type);
318
319 /* We must add 2 to width and height due to a precision issue with the division */
320 gldisp->uvx[type] = yuvbuf.w / (float)(gldisp->allocatedTexturesSize[type].width + 2);
321 gldisp->uvy[type] = yuvbuf.h / (float)(gldisp->allocatedTexturesSize[type].height + 2);
322
323 /* alignment of pointers and datasize */
324 {
325 int alig_Y = pixel_unpack_alignment(yuvbuf.planes[Y], yuvbuf.w * yuvbuf.h);
326 int alig_U = pixel_unpack_alignment(yuvbuf.planes[U], yuvbuf.w >> 1);
327 int alig_V = pixel_unpack_alignment(yuvbuf.planes[V], yuvbuf.w >> 1);
328 alignment = (alig_U > alig_V)
329 ? ((alig_V > alig_Y) ? alig_Y : alig_V)
330 : ((alig_U > alig_Y) ? alig_Y : alig_U);
331 }
332
333 /* upload Y plane */
334 GL_OPERATION(f, glActiveTexture(GL_TEXTURE0))
335 GL_OPERATION(f, glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][Y]))
336 GL_OPERATION(f, glPixelStorei(GL_UNPACK_ALIGNMENT, alignment))
337 GL_OPERATION(f, glTexSubImage2D(GL_TEXTURE_2D, 0,
338 0, 0, yuvbuf.w, yuvbuf.h,
339 GL_LUMINANCE, GL_UNSIGNED_BYTE, yuvbuf.planes[Y]))
340 GL_OPERATION(f, glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_Y], 0))
341
342 /* upload U plane */
343 GL_OPERATION(f, glActiveTexture(GL_TEXTURE1))
344 GL_OPERATION(f, glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][U]))
345 GL_OPERATION(f, glPixelStorei(GL_UNPACK_ALIGNMENT, alignment))
346 GL_OPERATION(f, glTexSubImage2D(GL_TEXTURE_2D, 0,
347 0, 0, yuvbuf.w >> 1, yuvbuf.h >> 1,
348 GL_LUMINANCE, GL_UNSIGNED_BYTE, yuvbuf.planes[U]))
349 GL_OPERATION(f, glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_U], 1))
350
351 /* upload V plane */
352 GL_OPERATION(f, glActiveTexture(GL_TEXTURE2))
353 GL_OPERATION(f, glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][V]))
354 GL_OPERATION(f, glPixelStorei(GL_UNPACK_ALIGNMENT, alignment))
355 GL_OPERATION(f, glTexSubImage2D(GL_TEXTURE_2D, 0,
356 0, 0, yuvbuf.w >> 1, yuvbuf.h >> 1,
357 GL_LUMINANCE, GL_UNSIGNED_BYTE, yuvbuf.planes[V]))
358 GL_OPERATION(f, glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_V], 2))
359 gldisp->yuv_size[type].width = yuvbuf.w;
360 gldisp->yuv_size[type].height = yuvbuf.h;
361
362 check_GL_errors(f, "update_textures_with_yuv");
363
364 return TRUE;
365 }
366
ogl_display_render_type(struct opengles_display * gldisp,enum ImageType type,bool_t clear,float vpx,float vpy,float vpw,float vph,int orientation)367 static void ogl_display_render_type(
368 struct opengles_display *gldisp,
369 enum ImageType type,
370 bool_t clear,
371 float vpx,
372 float vpy,
373 float vpw,
374 float vph,
375 int orientation
376 ) {
377 const OpenGlFunctions *f = gldisp->functions;
378
379 float uLeft, uRight, vTop, vBottom;
380 GLfloat squareUvs[8];
381 GLfloat squareVertices[8];
382 GLfloat screenW, screenH;
383 GLfloat x,y,w,h;
384 GLfloat mat[16];
385 float rad;
386
387 if (!gldisp) {
388 ms_error("%s called with null struct opengles_display", __FUNCTION__);
389 return;
390 }
391
392 if (!gldisp->yuv[type] || !gldisp->glResourcesInitialized)
393 return;
394
395 ms_mutex_lock(&gldisp->yuv_mutex);
396
397 if (gldisp->new_yuv_image[gldisp->texture_index][type]) {
398 update_textures_with_yuv(gldisp, type);
399 gldisp->new_yuv_image[gldisp->texture_index][type] = FALSE;
400 }
401
402 ms_mutex_unlock(&gldisp->yuv_mutex);
403
404 uLeft = vBottom = 0.0f;
405 uRight = gldisp->uvx[type];
406 vTop = gldisp->uvy[type];
407
408 squareUvs[0] = uLeft;
409 squareUvs[1] = vTop;
410 squareUvs[2] = uRight;
411 squareUvs[3] = vTop;
412 squareUvs[4] = uLeft;
413 squareUvs[5] = vBottom;
414 squareUvs[6] = uRight;
415 squareUvs[7] = vBottom;
416
417 if (clear)
418 GL_OPERATION(f, glClear(GL_COLOR_BUFFER_BIT))
419
420 // drawing surface dimensions
421 screenW = (GLfloat)gldisp->backingWidth;
422 screenH = (GLfloat)gldisp->backingHeight;
423 if (orientation == 90 || orientation == 270) {
424 screenW = screenH;
425 screenH = (GLfloat)gldisp->backingWidth;
426 }
427
428 // Fill the smallest dimension, then compute the other one using the image ratio
429 if (screenW <= screenH) {
430 float ratio = gldisp->yuv_size[type].height / (float)(gldisp->yuv_size[type].width);
431 w = screenW * vpw;
432 h = w * ratio;
433 if (h > screenH) {
434 w *= screenH / (float)h;
435 h = screenH;
436 }
437 } else {
438 float ratio = gldisp->yuv_size[type].width / (float)(gldisp->yuv_size[type].height);
439 h = screenH * vph;
440 w = h * ratio;
441 if (w > screenW) {
442 h *= screenW / (float)w;
443 w = screenW;
444 }
445 }
446
447 x = vpx * screenW;
448 y = vpy * screenH;
449
450 squareVertices[0] = (x - w * 0.5f) / screenW;
451 squareVertices[1] = (y - h * 0.5f) / screenH;
452 squareVertices[2] = (x + w * 0.5f) / screenW;
453 squareVertices[3] = (y - h * 0.5f) / screenH;
454 squareVertices[4] = (x - w * 0.5f) / screenW;
455 squareVertices[5] = (y + h * 0.5f) / screenH;
456 squareVertices[6] = (x + w * 0.5f) / screenW;
457 squareVertices[7] = (y + h * 0.5f) / screenH;
458
459 #define VP_SIZE 1.0f
460 if (type == REMOTE_IMAGE) {
461 float scale_factor = 1.0f / gldisp->zoom_factor;
462 float vpDim = (VP_SIZE * scale_factor) / 2;
463
464 #define ENSURE_RANGE_A_INSIDE_RANGE_B(a, aSize, bMin, bMax) \
465 if (2*aSize >= (bMax - bMin)) \
466 a = 0; \
467 else if ((a - aSize < bMin) || (a + aSize > bMax)) { \
468 float diff; \
469 if (a - aSize < bMin) diff = bMin - (a - aSize); \
470 else diff = bMax - (a + aSize); \
471 a += diff; \
472 }
473
474 ENSURE_RANGE_A_INSIDE_RANGE_B(gldisp->zoom_cx, vpDim, squareVertices[0], squareVertices[2])
475 ENSURE_RANGE_A_INSIDE_RANGE_B(gldisp->zoom_cy, vpDim, squareVertices[1], squareVertices[7])
476
477 load_orthographic_matrix(
478 gldisp->zoom_cx - vpDim,
479 gldisp->zoom_cx + vpDim,
480 gldisp->zoom_cy - vpDim,
481 gldisp->zoom_cy + vpDim,
482 0, 0.5, mat
483 );
484 } else {
485 load_orthographic_matrix(- VP_SIZE * 0.5, VP_SIZE * 0.5, - VP_SIZE * 0.5, VP_SIZE * 0.5, 0, 0.5, mat);
486 }
487
488 GL_OPERATION(f, glUniformMatrix4fv(gldisp->uniforms[UNIFORM_PROJ_MATRIX], 1, GL_FALSE, mat))
489
490 rad = (2.0f * 3.14157f * orientation / 360.0f); // Convert orientation to radian
491
492 GL_OPERATION(f, glUniform1f(gldisp->uniforms[UNIFORM_ROTATION], rad))
493
494 GL_OPERATION(f, glActiveTexture(GL_TEXTURE0))
495 GL_OPERATION(f, glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][Y]))
496 GL_OPERATION(f, glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_Y], 0))
497 GL_OPERATION(f, glActiveTexture(GL_TEXTURE1))
498 GL_OPERATION(f, glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][U]))
499 GL_OPERATION(f, glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_U], 1))
500 GL_OPERATION(f, glActiveTexture(GL_TEXTURE2))
501 GL_OPERATION(f, glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][V]))
502 GL_OPERATION(f, glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_V], 2))
503
504 GL_OPERATION(f, glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices))
505 GL_OPERATION(f, glEnableVertexAttribArray(ATTRIB_VERTEX))
506 GL_OPERATION(f, glVertexAttribPointer(ATTRIB_UV, 2, GL_FLOAT, 1, 0, squareUvs))
507 GL_OPERATION(f, glEnableVertexAttribArray(ATTRIB_UV))
508
509 GL_OPERATION(f, glDrawArrays(GL_TRIANGLE_STRIP, 0, 4))
510
511 check_GL_errors(f, "ogl_display_render_type");
512 }
513
514 // -----------------------------------------------------------------------------
515
ogl_display_new(void)516 struct opengles_display *ogl_display_new (void) {
517 struct opengles_display *result = (struct opengles_display*)malloc(sizeof(struct opengles_display));
518 if (result == 0) {
519 ms_error("Could not allocate OpenGL display structure\n");
520 return 0;
521 }
522
523 memset(result, 0, sizeof(struct opengles_display));
524
525 result->zoom_factor = 1;
526 result->zoom_cx = result->zoom_cy = 0;
527 result->texture_index = 0;
528
529 ms_mutex_init(&result->yuv_mutex, NULL);
530 ms_message("%s : %p\n", __FUNCTION__, result);
531
532 return result;
533 }
534
ogl_display_free(struct opengles_display * gldisp)535 void ogl_display_free (struct opengles_display *gldisp) {
536 int i;
537
538 if (!gldisp) {
539 ms_error("%s called with null struct opengles_display", __FUNCTION__);
540 return;
541 }
542
543 for(i = 0; i < MAX_IMAGE; i++) {
544 if (gldisp->yuv[i]) {
545 freemsg(gldisp->yuv[i]);
546 gldisp->yuv[i] = NULL;
547 }
548 }
549
550 ms_mutex_destroy(&gldisp->yuv_mutex);
551
552 free(gldisp);
553 }
554
ogl_display_set_size(struct opengles_display * gldisp,int width,int height)555 void ogl_display_set_size (struct opengles_display *gldisp, int width, int height) {
556 const OpenGlFunctions *f = gldisp->functions;
557
558 gldisp->backingWidth = width;
559 gldisp->backingHeight = height;
560 ms_message("resize opengles_display (%d x %d, gl initialized:%d)", width, height, gldisp->glResourcesInitialized);
561
562 GL_OPERATION(f, glViewport(0, 0, gldisp->backingWidth, gldisp->backingHeight))
563 check_GL_errors(f, "ogl_display_set_size");
564 }
565
ogl_display_init(struct opengles_display * gldisp,const OpenGlFunctions * f,int width,int height)566 void ogl_display_init (struct opengles_display *gldisp, const OpenGlFunctions *f, int width, int height) {
567 static bool_t version_displayed = FALSE;
568 int i, j;
569
570 if (!gldisp) {
571 ms_error("%s called with null struct opengles_display", __FUNCTION__);
572 return;
573 }
574
575 // Create default functions if necessary. (No opengl functions given.)
576 if (!gldisp->default_functions && !f) {
577 gldisp->default_functions = ms_new(OpenGlFunctions, 1);
578 opengl_functions_default_init(gldisp->default_functions);
579 }
580
581 // Update gl functions.
582 gldisp->functions = f ? f : gldisp->default_functions;
583 f = gldisp->functions;
584
585 ms_message("init opengles_display (%d x %d, gl initialized:%d)", width, height, gldisp->glResourcesInitialized);
586
587 clean_GL_errors(f);
588
589 GL_OPERATION(f, glDisable(GL_DEPTH_TEST))
590 GL_OPERATION(f, glClearColor(0, 0, 0, 0))
591
592 ogl_display_set_size(gldisp, width, height);
593
594 if (gldisp->glResourcesInitialized)
595 return;
596
597 for(j = 0; j < TEXTURE_BUFFER_SIZE; j++) {
598 // init textures
599 for(i = 0; i < MAX_IMAGE; i++) {
600 GL_OPERATION(f, glGenTextures(3, gldisp->textures[j][i]))
601 gldisp->allocatedTexturesSize[i].width = gldisp->allocatedTexturesSize[i].height = 0;
602 }
603 }
604
605 if (!version_displayed) {
606 version_displayed = TRUE;
607 ms_message("OpenGL version string: %s", f->glGetString(GL_VERSION));
608 ms_message("OpenGL extensions: %s", f->glGetString(GL_EXTENSIONS));
609 ms_message("OpenGL vendor: %s", f->glGetString(GL_VENDOR));
610 ms_message("OpenGL renderer: %s", f->glGetString(GL_RENDERER));
611 ms_message("OpenGL version: %s", f->glGetString(GL_VERSION));
612 ms_message("OpenGL GLSL version: %s", f->glGetString(GL_SHADING_LANGUAGE_VERSION));
613 }
614
615 load_shaders(gldisp->functions, &gldisp->program, gldisp->uniforms);
616
617 gldisp->glResourcesInitialized = TRUE;
618
619 check_GL_errors(f, "ogl_display_init");
620 }
621
ogl_display_uninit(struct opengles_display * gldisp,bool_t freeGLresources)622 void ogl_display_uninit (struct opengles_display *gldisp, bool_t freeGLresources) {
623 int i, j;
624 const OpenGlFunctions *f;
625
626 if (!gldisp) {
627 ms_error("%s called with null struct opengles_display", __FUNCTION__);
628 return;
629 }
630
631 ms_message("uninit opengles_display (gl initialized:%d)\n", gldisp->glResourcesInitialized);
632
633 for(i = 0; i < MAX_IMAGE; i++) {
634 if (gldisp->yuv[i]) {
635 freemsg(gldisp->yuv[i]);
636 gldisp->yuv[i] = NULL;
637 }
638 }
639
640 f = gldisp->functions;
641
642 if (gldisp->glResourcesInitialized && freeGLresources) {
643 // destroy gl resources
644 for(j = 0; j < TEXTURE_BUFFER_SIZE; j++) {
645 for(i = 0; i < MAX_IMAGE; i++) {
646 GL_OPERATION(f, glDeleteTextures(3, gldisp->textures[j][i]));
647 gldisp->allocatedTexturesSize[i].width = gldisp->allocatedTexturesSize[i].height = 0;
648 }
649 }
650
651 GL_OPERATION(f, glDeleteProgram(gldisp->program));
652 }
653
654 if (f) check_GL_errors(f, "ogl_display_uninit");
655
656 if (gldisp->default_functions) {
657 ms_free(gldisp->default_functions);
658 gldisp->default_functions = NULL;
659 }
660
661 gldisp->glResourcesInitialized = FALSE;
662 }
663
ogl_display_set_yuv_to_display(struct opengles_display * gldisp,mblk_t * yuv)664 void ogl_display_set_yuv_to_display(struct opengles_display *gldisp, mblk_t *yuv) {
665 ogl_display_set_yuv(gldisp, yuv, REMOTE_IMAGE);
666 }
667
ogl_display_set_preview_yuv_to_display(struct opengles_display * gldisp,mblk_t * yuv)668 void ogl_display_set_preview_yuv_to_display (struct opengles_display *gldisp, mblk_t *yuv) {
669 ogl_display_set_yuv(gldisp, yuv, PREVIEW_IMAGE);
670 }
671
ogl_display_render(struct opengles_display * gldisp,int orientation)672 void ogl_display_render (struct opengles_display *gldisp, int orientation) {
673 const OpenGlFunctions *f = gldisp->functions;
674
675 clean_GL_errors(f);
676
677 GL_OPERATION(f, glUseProgram(gldisp->program))
678
679 ogl_display_render_type(gldisp, REMOTE_IMAGE, TRUE, 0, 0, 1, 1, orientation);
680
681 // preview image already have the correct orientation
682 ogl_display_render_type(gldisp, PREVIEW_IMAGE, FALSE, 0.4f, -0.4f, 0.2f, 0.2f, 0);
683
684 gldisp->texture_index = (gldisp->texture_index + 1) % TEXTURE_BUFFER_SIZE;
685 }
686
ogl_display_zoom(struct opengles_display * gldisp,float * params)687 void ogl_display_zoom (struct opengles_display *gldisp, float *params) {
688 gldisp->zoom_factor = params[0];
689 gldisp->zoom_cx = params[1] - 0.5f;
690 gldisp->zoom_cy = params[2] - 0.5f;
691 }
692
693 // -----------------------------------------------------------------------------
694
695 #ifdef __ANDROID__
Java_org_linphone_mediastream_video_display_OpenGLESDisplay_init(JNIEnv * env,jobject obj,jlong ptr,jint width,jint height)696 JNIEXPORT void JNICALL Java_org_linphone_mediastream_video_display_OpenGLESDisplay_init (JNIEnv * env, jobject obj, jlong ptr, jint width, jint height) {
697 struct opengles_display* d = (struct opengles_display*) ptr;
698 ogl_display_init(d, NULL, width, height);
699 }
700
Java_org_linphone_mediastream_video_display_OpenGLESDisplay_render(JNIEnv * env,jobject obj,jlong ptr)701 JNIEXPORT void JNICALL Java_org_linphone_mediastream_video_display_OpenGLESDisplay_render (JNIEnv * env, jobject obj, jlong ptr) {
702 struct opengles_display* d = (struct opengles_display*) ptr;
703 ogl_display_render(d, 0);
704 }
705 #endif
706
707 #ifdef __cplusplus
708 }
709 #endif
710