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