opengles_display.c 19 KB
Newer Older
Yann Diorcet's avatar
Yann Diorcet committed
1
/*
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 opengles_display.m
 Copyright (C) 2011 Belledonne Communications, Grenoble, France

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#include "opengles_display.h"
#include "mediastreamer2/mscommon.h"
#include "shaders.h"

24 25 26 27
#ifdef __cplusplus
extern "C"{
#endif

28
enum ImageType {
29 30 31
	REMOTE_IMAGE = 0,
	PREVIEW_IMAGE,
	MAX_IMAGE
32
};
33 34 35 36

/* helper functions */
static void check_GL_errors(const char* context);
static bool_t load_shaders(GLuint* program, GLint* uniforms);
37
static void allocate_gl_textures(struct opengles_display* gldisp, int w, int h, enum ImageType type);
38 39
static void load_orthographic_matrix(float left, float right, float bottom, float top, float near, float far, float* mat);
static unsigned int align_on_power_of_2(unsigned int value);
40
static bool_t update_textures_with_yuv(struct opengles_display* gldisp, enum ImageType type);
41 42


43
//#define CHECK_GL_ERROR
44 45 46 47 48 49

#ifdef CHECK_GL_ERROR
	#define GL_OPERATION(x)	\
		(x); \
		check_GL_errors(#x);
#else
50 51
	#define GL_OPERATION(x) \
		(x);
52 53 54
#endif

enum {
55 56 57 58 59 60
	UNIFORM_PROJ_MATRIX = 0,
	UNIFORM_ROTATION,
	UNIFORM_TEXTURE_Y,
	UNIFORM_TEXTURE_U,
	UNIFORM_TEXTURE_V,
	NUM_UNIFORMS
61 62 63
};

enum {
64 65 66
	ATTRIB_VERTEX = 0,
	ATTRIB_UV,
	NUM_ATTRIBS
67 68 69
};

enum {
70 71 72
	Y,
	U,
	V
73 74
};

75 76
#define TEXTURE_BUFFER_SIZE 3

77
struct opengles_display {
78 79
	/* input: yuv image to display */
	ms_mutex_t yuv_mutex;
80 81
	mblk_t *yuv[MAX_IMAGE];
	bool_t new_yuv_image[TEXTURE_BUFFER_SIZE][MAX_IMAGE];
82 83 84

	/* GL resources */
	bool_t glResourcesInitialized;
85
	GLuint program, textures[TEXTURE_BUFFER_SIZE][MAX_IMAGE][3];
86
	GLint uniforms[NUM_UNIFORMS];
87
	MSVideoSize allocatedTexturesSize[MAX_IMAGE];
88

89
	int texture_index;
90

91 92 93 94 95
	/* GL view size */
	GLint backingWidth;
	GLint backingHeight;

	/* runtime data */
96
	float uvx[MAX_IMAGE], uvy[MAX_IMAGE];
97
	MSVideoSize yuv_size[MAX_IMAGE];
98 99

	/* coordinates of for zoom-in */
100 101 102
	float zoom_factor;
	float zoom_cx;
	float zoom_cy;
103 104 105 106 107 108 109 110 111 112
};

struct opengles_display* ogl_display_new() {
	struct opengles_display* result =
		(struct opengles_display*) malloc(sizeof(struct opengles_display));
	if (result == 0) {
		ms_error("Could not allocate OpenGL display structure\n");
		return 0;
	}
	memset(result, 0, sizeof(struct opengles_display));
113 114
	result->zoom_factor = 1;
	result->zoom_cx = result->zoom_cy = 0;
115
	result->texture_index = 0;
116 117

	ms_mutex_init(&result->yuv_mutex, NULL);
118
	ms_message("%s : %p\n", __FUNCTION__, result);
119 120 121 122
	return result;
}

void ogl_display_free(struct opengles_display* gldisp) {
123
	int i;
124

125 126 127 128
	if (!gldisp) {
		ms_error("%s called with null struct opengles_display", __FUNCTION__);
		return;
	}
129

130 131 132 133 134 135
	for(i=0; i<MAX_IMAGE; i++) {
		if (gldisp->yuv[i]) {
			ms_free(gldisp->yuv[i]);
			gldisp->yuv[i] = NULL;
		}
	}
136 137 138 139 140
	ms_mutex_destroy(&gldisp->yuv_mutex);

	free(gldisp);
}

141
void ogl_display_set_size(struct opengles_display* gldisp, int width, int height) {
142
	gldisp->backingWidth = width;
143
	gldisp->backingHeight = height;
144
	ms_message("resize opengles_display (%d x %d, gl initialized:%d)", width, height, gldisp->glResourcesInitialized);
145

146
	GL_OPERATION(glViewport(0, 0, gldisp->backingWidth, gldisp->backingHeight));
147

148
	check_GL_errors("ogl_display_set_size");
149 150
}

151
void ogl_display_init(struct opengles_display* gldisp, int width, int height) {
152
	int i,j;
153
	static bool_t version_displayed = FALSE;
154
	if (!gldisp) {
155 156 157 158
		ms_error("%s called with null struct opengles_display", __FUNCTION__);
		return;
	}

159
	ms_message("init opengles_display (%d x %d, gl initialized:%d)", width, height, gldisp->glResourcesInitialized);
160 161

	GL_OPERATION(glDisable(GL_DEPTH_TEST))
162
	GL_OPERATION(glClearColor(0, 0, 0, 1))
163

164
	ogl_display_set_size(gldisp, width, height);
165

166 167 168
	if (gldisp->glResourcesInitialized)
		return;

169 170 171 172 173 174 175
	for(j=0; j<TEXTURE_BUFFER_SIZE; j++) {
		// init textures
		for(i=0; i<MAX_IMAGE; i++) {
			GL_OPERATION(glGenTextures(3, gldisp->textures[j][i]))
			gldisp->allocatedTexturesSize[i].width = gldisp->allocatedTexturesSize[i].height = 0;
		}
	}
176

177 178 179 180 181 182 183 184 185
	if (!version_displayed) {
		version_displayed = TRUE;
		ms_message("OpenGL version string: %s", glGetString(GL_VERSION));
		ms_message("OpenGL extensions: %s",glGetString(GL_EXTENSIONS));
		ms_message("OpenGL vendor: %s", glGetString(GL_VENDOR));
		ms_message("OpenGL renderer: %s", glGetString(GL_RENDERER));
		ms_message("OpenGL version: %s", glGetString(GL_VERSION));
		ms_message("OpenGL GLSL version: %s", glGetString(GL_SHADING_LANGUAGE_VERSION));
	}
186
	load_shaders(&gldisp->program, gldisp->uniforms);
187

188
	GL_OPERATION(glUseProgram(gldisp->program))
189 190

	gldisp->glResourcesInitialized = TRUE;
191

192
	check_GL_errors("ogl_display_init");
193 194
}

195
void ogl_display_uninit(struct opengles_display* gldisp, bool_t freeGLresources) {
196
	int i,j;
197

198
	if (!gldisp) {
199 200 201
		ms_error("%s called with null struct opengles_display", __FUNCTION__);
		return;
	}
202
	ms_message("uninit opengles_display (gl initialized:%d)\n", gldisp->glResourcesInitialized);
203 204 205 206 207 208
	for(i=0; i<MAX_IMAGE; i++) {
		if (gldisp->yuv[i]) {
			ms_free(gldisp->yuv[i]);
			gldisp->yuv[i] = NULL;
		}
	}
209

210
	if (gldisp->glResourcesInitialized && freeGLresources) {
211 212 213 214 215 216 217
		// destroy gl resources
		for(j=0; j<TEXTURE_BUFFER_SIZE; j++) {
			for(i=0; i<MAX_IMAGE; i++) {
				GL_OPERATION(glDeleteTextures(3, gldisp->textures[j][i]));
				gldisp->allocatedTexturesSize[i].width = gldisp->allocatedTexturesSize[i].height = 0;
			}
		}
218
		GL_OPERATION(glDeleteProgram(gldisp->program));
219
	}
220

221
	gldisp->glResourcesInitialized = FALSE;
222

223
	check_GL_errors("ogl_display_uninit");
224 225
}

226
static void ogl_display_set_yuv(struct opengles_display* gldisp, mblk_t *yuv, enum ImageType type) {
227
	int j;
228 229 230 231
	if (!gldisp) {
		ms_error("%s called with null struct opengles_display", __FUNCTION__);
		return;
	}
232
	ms_mutex_lock(&gldisp->yuv_mutex);
233
	if (gldisp->yuv[type])
234 235
		freemsg(gldisp->yuv[type]);
	gldisp->yuv[type] = dupmsg(yuv);
236 237 238
	for(j = 0; j < TEXTURE_BUFFER_SIZE; ++j) {
		gldisp->new_yuv_image[j][type] = TRUE;
	}
239 240

	ms_mutex_unlock(&gldisp->yuv_mutex);
241 242
}

243 244 245 246 247 248 249 250
void ogl_display_set_yuv_to_display(struct opengles_display* gldisp, mblk_t *yuv) {
	ogl_display_set_yuv(gldisp, yuv, REMOTE_IMAGE);
}

void ogl_display_set_preview_yuv_to_display(struct opengles_display* gldisp, mblk_t *yuv) {
	ogl_display_set_yuv(gldisp, yuv, PREVIEW_IMAGE);
}

251
static void ogl_display_render_type(struct opengles_display* gldisp, enum ImageType type, bool_t clear, float vpx, float vpy, float vpw, float vph, int orientation) {
252 253 254 255 256 257 258 259 260
	float uLeft, uRight, vTop, vBottom;
	GLfloat squareUvs[8];
	GLfloat squareVertices[8];
	int screenW, screenH;
	int x,y,w,h;
	GLfloat mat[16];
	float rad;

	if (!gldisp) {
261 262 263
		ms_error("%s called with null struct opengles_display", __FUNCTION__);
		return;
	}
264
	if (!gldisp->yuv[type] || !gldisp->glResourcesInitialized) {
265
		return;
266
	}
267

268
	ms_mutex_lock(&gldisp->yuv_mutex);
269
	if (gldisp->new_yuv_image[gldisp->texture_index][type]) {
270
		update_textures_with_yuv(gldisp, type);
271
		gldisp->new_yuv_image[gldisp->texture_index][type] = FALSE;
272 273
	}
	ms_mutex_unlock(&gldisp->yuv_mutex);
274

275

276 277
	uLeft = vBottom = 0.0f;
	uRight = gldisp->uvx[type];
278 279 280 281 282 283 284 285 286 287 288 289
	vTop = gldisp->uvy[type];

	squareUvs[0] = uLeft;
	squareUvs[1] =  vTop;
	squareUvs[2] = uRight;
	squareUvs[3] =  vTop;
	squareUvs[4] = uLeft;
	squareUvs[5] =  vBottom;
	squareUvs[6] = uRight;
	squareUvs[7] = vBottom;


290 291 292
	if (clear) {
		GL_OPERATION(glClear(GL_COLOR_BUFFER_BIT))
	}
293 294


295
	// drawing surface dimensions
296 297
	screenW = gldisp->backingWidth;
	screenH = gldisp->backingHeight;
298 299
	if (orientation == 90 || orientation == 270) {
		screenW = screenH;
300
		screenH = gldisp->backingWidth;
301 302 303
	}

	// Fill the smallest dimension, then compute the other one using the image ratio
Simon Morlat's avatar
Simon Morlat committed
304
	if (screenW <= screenH) {
305
		float ratio = (gldisp->yuv_size[type].height) / (float)(gldisp->yuv_size[type].width);
Simon Morlat's avatar
Simon Morlat committed
306
		w = screenW * vpw;
307
		h = w * ratio;
308 309 310 311
		if (h > screenH) {
			w *= screenH /(float) h;
			h = screenH;
		}
312
		x = vpx * gldisp->backingWidth;
313
		y = vpy * gldisp->backingHeight;
314
	} else {
315
		float ratio = gldisp->yuv_size[type].width / (float)gldisp->yuv_size[type].height;
Simon Morlat's avatar
Simon Morlat committed
316
		h = screenH * vph;
317
		w = h * ratio;
318 319 320 321
		if (w > screenW) {
			h *= screenW / (float)w;
			w = screenW;
		}
322 323
		x = vpx * screenW;
		y = vpy * screenH;
324
	}
325

326 327 328 329 330 331 332 333
	squareVertices[0] = (x - w * 0.5) / screenW - 0.;
	squareVertices[1] = (y - h * 0.5) / screenH - 0.;
	squareVertices[2] = (x + w * 0.5) / screenW - 0.;
	squareVertices[3] = (y - h * 0.5) / screenH - 0.;
	squareVertices[4] = (x - w * 0.5) / screenW - 0.;
	squareVertices[5] = (y + h * 0.5) / screenH - 0.;
	squareVertices[6] = (x + w * 0.5) / screenW - 0.;
	squareVertices[7] = (y + h * 0.5) / screenH - 0.;
334

335 336 337 338
	#define VP_SIZE 1.0f
	if (type == REMOTE_IMAGE) {
		float scale_factor = 1.0 / gldisp->zoom_factor;
		float vpDim = (VP_SIZE * scale_factor) / 2;
339

340 341 342 343 344 345 346 347 348
		#define ENSURE_RANGE_A_INSIDE_RANGE_B(a, aSize, bMin, bMax) \
		if (2*aSize >= (bMax - bMin)) \
			a = 0; \
		else if ((a - aSize < bMin) || (a + aSize > bMax)) {  \
			float diff; \
			if (a - aSize < bMin) diff = bMin - (a - aSize); \
			else diff = bMax - (a + aSize); \
			a += diff; \
		}
349

350 351
		ENSURE_RANGE_A_INSIDE_RANGE_B(gldisp->zoom_cx, vpDim, squareVertices[0], squareVertices[2])
		ENSURE_RANGE_A_INSIDE_RANGE_B(gldisp->zoom_cy, vpDim, squareVertices[1], squareVertices[7])
352

353
		load_orthographic_matrix(
354 355 356 357
			gldisp->zoom_cx - vpDim,
			gldisp->zoom_cx + vpDim,
			gldisp->zoom_cy - vpDim,
			gldisp->zoom_cy + vpDim,
358 359 360 361
			0, 0.5, mat);
	} else {
		load_orthographic_matrix(- VP_SIZE * 0.5, VP_SIZE * 0.5, - VP_SIZE * 0.5, VP_SIZE * 0.5, 0, 0.5, mat);
	}
362

363
	GL_OPERATION(glUniformMatrix4fv(gldisp->uniforms[UNIFORM_PROJ_MATRIX], 1, GL_FALSE, mat))
364 365 366

	rad = (2.0 * 3.14157 * orientation / 360.0); // Convert orientation to radian

367
	GL_OPERATION(glUniform1f(gldisp->uniforms[UNIFORM_ROTATION], rad))
368

369
	GL_OPERATION(glActiveTexture(GL_TEXTURE0))
370
	GL_OPERATION(glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][Y]))
371
	GL_OPERATION(glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_Y], 0))
372
	GL_OPERATION(glActiveTexture(GL_TEXTURE1))
373
	GL_OPERATION(glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][U]))
374
	GL_OPERATION(glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_U], 1))
375
	GL_OPERATION(glActiveTexture(GL_TEXTURE2))
376
	GL_OPERATION(glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][V]))
377
	GL_OPERATION(glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_V], 2))
378

379 380 381 382
	GL_OPERATION(glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices))
	GL_OPERATION(glEnableVertexAttribArray(ATTRIB_VERTEX))
	GL_OPERATION(glVertexAttribPointer(ATTRIB_UV, 2, GL_FLOAT, 1, 0, squareUvs))
	GL_OPERATION(glEnableVertexAttribArray(ATTRIB_UV))
383 384 385

	GL_OPERATION(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4))

386
	check_GL_errors("ogl_display_render_type");
387

388
}
389

390
void ogl_display_render(struct opengles_display* gldisp, int orientation) {
391 392 393
	ogl_display_render_type(gldisp, REMOTE_IMAGE, TRUE, 0, 0, 1, 1, orientation);
	// preview image already have the correct orientation
	ogl_display_render_type(gldisp, PREVIEW_IMAGE, FALSE, 0.4f, -0.4f, 0.2f, 0.2f, 0);
394

395
	gldisp->texture_index = (gldisp->texture_index + 1) % TEXTURE_BUFFER_SIZE;
396 397 398
}

static void check_GL_errors(const char* context) {
399
	 int maxIterations=10;
400 401 402 403 404 405 406 407 408 409 410 411 412
	GLenum error;
	while (((error = glGetError()) != GL_NO_ERROR) && maxIterations > 0)
	{
		switch(error)
		{
			case GL_INVALID_ENUM:  ms_error("[%2d]GL error: '%s' -> GL_INVALID_ENUM\n", maxIterations, context); break;
			case GL_INVALID_VALUE: ms_error("[%2d]GL error: '%s' -> GL_INVALID_VALUE\n", maxIterations, context); break;
			case GL_INVALID_OPERATION: ms_error("[%2d]GL error: '%s' -> GL_INVALID_OPERATION\n", maxIterations, context); break;
			case GL_OUT_OF_MEMORY: ms_error("[%2d]GL error: '%s' -> GL_OUT_OF_MEMORY\n", maxIterations, context); break;
			case GL_INVALID_FRAMEBUFFER_OPERATION: ms_error("[%2d]GL error: '%s' -> GL_INVALID_FRAMEBUFFER_OPERATION\n", maxIterations, context); break;
			default:
				ms_error("[%2d]GL error: '%s' -> %x\n", maxIterations, context, error);
		}
413
		  maxIterations--;
414
	}
415 416 417
}

static bool_t load_shaders(GLuint* program, GLint* uniforms) {
418
	GLuint vertShader, fragShader;
419 420
#include "yuv2rgb.vs.h"
#include "yuv2rgb.fs.h"
Simon Morlat's avatar
Simon Morlat committed
421 422
	(void)yuv2rgb_vs_len;
	(void)yuv2rgb_fs_len;
423

424
	*program = glCreateProgram();
425

426 427 428 429
	if (!compileShader(&vertShader, GL_VERTEX_SHADER, (const char*)yuv2rgb_vs))
		return FALSE;
	if (!compileShader(&fragShader, GL_FRAGMENT_SHADER, (const char*)yuv2rgb_fs))
		return FALSE;
430

431 432
	GL_OPERATION(glAttachShader(*program, vertShader))
	GL_OPERATION(glAttachShader(*program, fragShader))
433

434 435
	GL_OPERATION(glBindAttribLocation(*program, ATTRIB_VERTEX, "position"))
	GL_OPERATION(glBindAttribLocation(*program, ATTRIB_UV, "uv"))
436

437 438
	if (!linkProgram(*program))
		return FALSE;
439

440 441 442 443 444
	uniforms[UNIFORM_PROJ_MATRIX] = glGetUniformLocation(*program, "proj_matrix");
	uniforms[UNIFORM_ROTATION] = glGetUniformLocation(*program, "rotation");
	uniforms[UNIFORM_TEXTURE_Y] = glGetUniformLocation(*program, "t_texture_y");
	uniforms[UNIFORM_TEXTURE_U] = glGetUniformLocation(*program, "t_texture_u");
	uniforms[UNIFORM_TEXTURE_V] = glGetUniformLocation(*program, "t_texture_v");
445

446 447
	glDeleteShader(vertShader);
	glDeleteShader(fragShader);
448

449
	return TRUE;
450

451
	check_GL_errors("load_shaders");
452 453 454 455
}

static void load_orthographic_matrix(float left, float right, float bottom, float top, float near, float far, float* mat)
{
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477
	float r_l = right - left;
	float t_b = top - bottom;
	float f_n = far - near;
	float tx = - (right + left) / (right - left);
	float ty = - (top + bottom) / (top - bottom);
	float tz = - (far + near) / (far - near);

	mat[0] = (2.0f / r_l);
	mat[1] = mat[2] = mat[3] = 0.0f;

	mat[4] = 0.0f;
	mat[5] = (2.0f / t_b);
	mat[6] = mat[7] = 0.0f;

	mat[8] = mat[9] = 0.0f;
	mat[10] = -2.0f / f_n;
	mat[11] = 0.0f;

	mat[12] = tx;
	mat[13] = ty;
	mat[14] = tz;
	mat[15] = 1.0f;
478 479
}

480
static void allocate_gl_textures(struct opengles_display* gldisp, int w, int h, enum ImageType type) {
481 482
	int j;
	for(j=0; j<TEXTURE_BUFFER_SIZE; j++) {
483
	GL_OPERATION(glActiveTexture(GL_TEXTURE0))
484
	GL_OPERATION(glBindTexture(GL_TEXTURE_2D, gldisp->textures[j][type][Y]))
485 486 487 488 489 490 491
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR))
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR))
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE))
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE))
	GL_OPERATION(glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0))

	GL_OPERATION(glActiveTexture(GL_TEXTURE1))
492
	GL_OPERATION(glBindTexture(GL_TEXTURE_2D, gldisp->textures[j][type][U]))
493 494 495 496 497 498 499
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR))
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR))
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE))
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE))
	GL_OPERATION(glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w >> 1, h >> 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0))

	GL_OPERATION(glActiveTexture(GL_TEXTURE2))
500
	GL_OPERATION(glBindTexture(GL_TEXTURE_2D, gldisp->textures[j][type][V]))
501 502 503 504 505 506
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR))
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR))
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE))
	GL_OPERATION(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE))
	GL_OPERATION(glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w >> 1, h >> 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0))

507
	}
508 509
	gldisp->allocatedTexturesSize[type].width =  w;
	gldisp->allocatedTexturesSize[type].height =  h;
510

511
	ms_message("%s: allocated new textures[%d] (%d x %d)\n", __FUNCTION__, type, w, h);
512

513
	check_GL_errors("allocate_gl_textures");
514 515 516 517 518 519 520 521 522 523 524 525 526
}

static unsigned int align_on_power_of_2(unsigned int value) {
	int i;
	/* browse all power of 2 value, and find the one just >= value */
	for(i=0; i<32; i++) {
		unsigned int c = 1 << i;
		if (value <= c)
			return c;
	}
	return 0;
}

527
static bool_t update_textures_with_yuv(struct opengles_display* gldisp, enum ImageType type) {
528 529 530
	unsigned int aligned_yuv_w, aligned_yuv_h;
	MSPicture yuvbuf;

531
	ms_yuv_buf_init_from_mblk(&yuvbuf, gldisp->yuv[type]);
532 533 534 535 536 537 538 539 540

	if (yuvbuf.w == 0 || yuvbuf.h == 0) {
		ms_warning("Incoherent image size: %dx%d\n", yuvbuf.w, yuvbuf.h);
		return FALSE;
	}
	aligned_yuv_w = align_on_power_of_2(yuvbuf.w);
	aligned_yuv_h = align_on_power_of_2(yuvbuf.h);

	/* check if we need to adjust texture sizes */
541 542 543
	if (aligned_yuv_w != gldisp->allocatedTexturesSize[type].width ||
		aligned_yuv_h != gldisp->allocatedTexturesSize[type].height) {
		allocate_gl_textures(gldisp, aligned_yuv_w, aligned_yuv_h, type);
544
	}
545 546
	gldisp->uvx[type] = yuvbuf.w / (float)(gldisp->allocatedTexturesSize[type].width+1);
	gldisp->uvy[type] = yuvbuf.h / (float)(gldisp->allocatedTexturesSize[type].height+1);
547 548 549

	/* upload Y plane */
	GL_OPERATION(glActiveTexture(GL_TEXTURE0))
550
	GL_OPERATION(glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][Y]))
551 552 553 554 555 556 557
	GL_OPERATION(glTexSubImage2D(GL_TEXTURE_2D, 0,
			0, 0, yuvbuf.w, yuvbuf.h,
			GL_LUMINANCE, GL_UNSIGNED_BYTE, yuvbuf.planes[Y]))
	GL_OPERATION(glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_Y], 0))

	/* upload U plane */
	GL_OPERATION(glActiveTexture(GL_TEXTURE1))
558
	GL_OPERATION(glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][U]))
559 560 561 562 563 564 565
	GL_OPERATION(glTexSubImage2D(GL_TEXTURE_2D, 0,
			0, 0, yuvbuf.w >> 1, yuvbuf.h >> 1,
			GL_LUMINANCE, GL_UNSIGNED_BYTE, yuvbuf.planes[U]))
	GL_OPERATION(glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_U], 1))

	/* upload V plane */
	GL_OPERATION(glActiveTexture(GL_TEXTURE2))
566
	GL_OPERATION(glBindTexture(GL_TEXTURE_2D, gldisp->textures[gldisp->texture_index][type][V]))
567 568 569 570 571
	GL_OPERATION(glTexSubImage2D(GL_TEXTURE_2D, 0,
			0, 0, yuvbuf.w >> 1, yuvbuf.h >> 1,
			GL_LUMINANCE, GL_UNSIGNED_BYTE, yuvbuf.planes[V]))
	GL_OPERATION(glUniform1i(gldisp->uniforms[UNIFORM_TEXTURE_V], 2))

572 573
	gldisp->yuv_size[type].width = yuvbuf.w;
	gldisp->yuv_size[type].height = yuvbuf.h;
574

575
	check_GL_errors("update_textures_with_yuv");
576

577 578 579
	return TRUE;
}

580 581 582 583
void ogl_display_zoom(struct opengles_display* gldisp, float* params) {
	gldisp->zoom_factor = params[0];
	gldisp->zoom_cx = params[1] - 0.5;
	gldisp->zoom_cy = params[2] - 0.5;
584 585
}

586
#ifdef ANDROID
587
JNIEXPORT void JNICALL Java_org_linphone_mediastream_video_display_OpenGLESDisplay_init(JNIEnv * env, jobject obj, jint ptr, jint width, jint height) {
588 589 590 591
	struct opengles_display* d = (struct opengles_display*) ptr;
	ogl_display_init(d, width, height);
}

592
JNIEXPORT void JNICALL Java_org_linphone_mediastream_video_display_OpenGLESDisplay_render(JNIEnv * env, jobject obj, jint ptr) {
593
	struct opengles_display* d = (struct opengles_display*) ptr;
594
	ogl_display_render(d, 0);
595 596
}
#endif
597 598 599 600

#ifdef __cplusplus
}
#endif