1 /* $Id$ */
2 /*
3 * Copyright (C) 2013-2014 Teluu Inc. (http://www.teluu.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 2 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 #include <pjmedia-videodev/videodev_imp.h>
20 #include <pj/assert.h>
21 #include <pj/log.h>
22
23 #if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \
24 defined(PJMEDIA_VIDEO_DEV_HAS_OPENGL) && \
25 PJMEDIA_VIDEO_DEV_HAS_OPENGL != 0
26
27 #include <pjmedia-videodev/opengl_dev.h>
28 #ifdef PJMEDIA_VIDEO_DEV_HAS_OPENGL_ES
29 # if PJ_ANDROID
30 # include <GLES2/gl2.h>
31 # include <GLES2/gl2ext.h>
32 # undef GL_RGBA
33 # define GL_RGBA GL_BGRA_EXT
34 # define GL_BGRA GL_BGRA_EXT
35 # else
36 # include <OpenGLES/ES2/gl.h>
37 # include <OpenGLES/ES2/glext.h>
38 # endif
39 #else
40 # include <GL/gl.h>
41 # include <GL/glext.h>
42 #endif
43
44 #define THIS_FILE "opengl_dev.c"
45 #define DEFAULT_CLOCK_RATE 90000
46 #define DEFAULT_WIDTH 480
47 #define DEFAULT_HEIGHT 360
48 #define DEFAULT_FPS 15
49
50 #if PJ_ANDROID
51 # define LOG(a) PJ_LOG(3, (THIS_FILE, a))
52 #else
53 # define LOG(a)
54 #endif
55
56 enum {
57 ATTRIB_VERTEX,
58 ATTRIB_TEXTUREPOSITON,
59 NUM_ATTRIBUTES
60 };
61
62 /* Vertex and fragment shaders */
63 static const GLchar *vertSrc = " \
64 attribute vec4 position; \
65 attribute vec4 inTexCoord; \
66 varying vec2 texCoord; \
67 void main() \
68 { \
69 gl_Position = position; \
70 texCoord = inTexCoord.xy; \
71 } \
72 ";
73 static const GLchar *fragSrc = " \
74 varying highp vec2 texCoord; \
75 uniform sampler2D videoFrame; \
76 void main() \
77 { \
78 gl_FragColor = texture2D(videoFrame, texCoord); \
79 } \
80 ";
81
82 /* OpenGL buffers structure. */
83 struct gl_buffers {
84 GLuint frameBuf;
85 GLuint rendBuf;
86 GLuint rendTex;
87 GLuint directProg;
88
89 int rendBufW;
90 int rendBufH;
91 pj_bool_t direct;
92 };
93
94 /* Supported formats */
95 static pjmedia_format_id opengl_fmts[] = {PJMEDIA_FORMAT_BGRA};
96
97 /* opengl device info */
98 struct opengl_dev_info
99 {
100 pjmedia_vid_dev_info info;
101 };
102
103 /* opengl factory */
104 struct opengl_factory
105 {
106 pjmedia_vid_dev_factory base;
107 pj_pool_t *pool;
108 pj_pool_factory *pf;
109
110 unsigned dev_count;
111 struct opengl_dev_info *dev_info;
112 };
113
114 /* Prototypes */
115 static pj_status_t opengl_factory_init(pjmedia_vid_dev_factory *f);
116 static pj_status_t opengl_factory_destroy(pjmedia_vid_dev_factory *f);
117 static pj_status_t opengl_factory_refresh(pjmedia_vid_dev_factory *f);
118 static unsigned opengl_factory_get_dev_count(pjmedia_vid_dev_factory *f);
119 static pj_status_t opengl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
120 unsigned index,
121 pjmedia_vid_dev_info *info);
122 static pj_status_t opengl_factory_default_param(pj_pool_t *pool,
123 pjmedia_vid_dev_factory *f,
124 unsigned index,
125 pjmedia_vid_dev_param *param);
126 static pj_status_t
127 opengl_factory_create_stream(pjmedia_vid_dev_factory *f,
128 pjmedia_vid_dev_param *param,
129 const pjmedia_vid_dev_cb *cb,
130 void *user_data,
131 pjmedia_vid_dev_stream **p_vid_strm);
132
133 /* Operations */
134 static pjmedia_vid_dev_factory_op factory_op =
135 {
136 &opengl_factory_init,
137 &opengl_factory_destroy,
138 &opengl_factory_get_dev_count,
139 &opengl_factory_get_dev_info,
140 &opengl_factory_default_param,
141 &opengl_factory_create_stream,
142 &opengl_factory_refresh
143 };
144
145 /****************************************************************************
146 * OpenGL utility functions
147 */
148 /* Compile a shader from the provided source(s) */
compile_shader(GLenum target,GLsizei count,const GLchar ** sources,GLuint * shader)149 GLint compile_shader(GLenum target, GLsizei count, const GLchar **sources,
150 GLuint *shader)
151 {
152 GLint status;
153
154 *shader = glCreateShader(target);
155 glShaderSource(*shader, count, sources, NULL);
156 glCompileShader(*shader);
157
158 glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
159
160 return status;
161 }
162
163 /* Create program, compile shader, link program, bind attributes */
create_program(const GLchar * vertSource,const GLchar * fragSource,GLsizei attribNameCnt,const GLchar ** attribNames,const GLint * attribLocations,GLuint * program)164 GLint create_program(const GLchar *vertSource, const GLchar *fragSource,
165 GLsizei attribNameCnt, const GLchar **attribNames,
166 const GLint *attribLocations, GLuint *program)
167 {
168 GLuint vertShader = 0, fragShader = 0, prog = 0, i;
169 GLint status;
170
171 /* Create shader program */
172 prog = glCreateProgram();
173 *program = prog;
174
175 /* Create and compile vertex shader */
176 status = compile_shader(GL_VERTEX_SHADER, 1, &vertSource, &vertShader);
177 if (status == 0) {
178 LOG("Unable to compile vertex shader");
179 return status;
180 }
181
182 /* Create and compile fragment shader */
183 status = compile_shader(GL_FRAGMENT_SHADER, 1, &fragSource, &fragShader);
184 if (status == 0) {
185 LOG("Unable to compile fragment shader");
186 return status;
187 }
188
189 /* Attach vertex shader to program */
190 glAttachShader(prog, vertShader);
191
192 /* Attach fragment shader to program */
193 glAttachShader(prog, fragShader);
194
195 /* Bind attribute locations prior to linking */
196 for (i = 0; i < attribNameCnt; i++) {
197 glBindAttribLocation(prog, attribLocations[i], attribNames[i]);
198 }
199
200 /* Link program */
201 glLinkProgram(prog);
202 glGetProgramiv(prog, GL_LINK_STATUS, &status);
203 if (status == 0) {
204 LOG("Unable to link program");
205 return status;
206 }
207
208 /* Release vertex and fragment shaders */
209 if (vertShader)
210 glDeleteShader(vertShader);
211 if (fragShader)
212 glDeleteShader(fragShader);
213
214 return status;
215 }
216
pjmedia_vid_dev_opengl_create_buffers(pj_pool_t * pool,pj_bool_t direct,gl_buffers ** glb)217 void pjmedia_vid_dev_opengl_create_buffers(pj_pool_t *pool, pj_bool_t direct,
218 gl_buffers **glb)
219 {
220 gl_buffers *glbuf = PJ_POOL_ZALLOC_T(pool, gl_buffers);
221
222 *glb = glbuf;
223 glDisable(GL_DEPTH_TEST);
224
225 if (!(glbuf->direct = direct)) {
226 glGenFramebuffers(1, &glbuf->frameBuf);
227 glBindFramebuffer(GL_FRAMEBUFFER, glbuf->frameBuf);
228
229 glGenRenderbuffers(1, &glbuf->rendBuf);
230 glBindRenderbuffer(GL_RENDERBUFFER, glbuf->rendBuf);
231 }
232
233 glGenTextures(1, &glbuf->rendTex);
234 }
235
pjmedia_vid_dev_opengl_init_buffers(gl_buffers * glb)236 pj_status_t pjmedia_vid_dev_opengl_init_buffers(gl_buffers *glb)
237 {
238 /* Attributes */
239 GLint attribLocation[NUM_ATTRIBUTES] = { ATTRIB_VERTEX,
240 ATTRIB_TEXTUREPOSITON };
241 GLchar *attribName[NUM_ATTRIBUTES] = { "position", "texCoord" };
242
243 if (!glb->direct ) {
244 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,
245 &glb->rendBufW);
246 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT,
247 &glb->rendBufH);
248
249 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
250 GL_RENDERBUFFER, glb->rendBuf);
251 if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
252 LOG("Unable to create frame buffer");
253 return -1;
254 }
255 }
256
257 create_program(vertSrc, fragSrc, NUM_ATTRIBUTES,
258 (const GLchar **)&attribName[0], attribLocation,
259 &glb->directProg);
260
261 if (!glb->directProg) {
262 LOG("Unable to create program");
263 return -2;
264 }
265
266 return PJ_SUCCESS;
267 }
268
pjmedia_vid_dev_opengl_draw(gl_buffers * glb,unsigned int width,unsigned int height,void * pixels)269 pj_status_t pjmedia_vid_dev_opengl_draw(gl_buffers *glb, unsigned int width,
270 unsigned int height, void *pixels)
271 {
272 static const GLfloat squareVertices[] = {
273 -1.0f, -1.0f,
274 1.0f, -1.0f,
275 -1.0f, 1.0f,
276 1.0f, 1.0f,
277 };
278 GLfloat textureVertices[] = {
279 0, 1, 1, 1, 0, 0, 1, 0
280 };
281
282 glBindTexture(GL_TEXTURE_2D, glb->rendTex);
283
284 /* Set texture parameters */
285 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
286 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
287 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
288 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
289
290 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (GLsizei)width, (GLsizei)height,
291 0, GL_BGRA, GL_UNSIGNED_BYTE, (GLvoid *)pixels);
292
293 glFlush();
294
295 /* Do we render directly to the screen? */
296 glBindFramebuffer(GL_FRAMEBUFFER, (glb->direct? 0: glb->frameBuf));
297
298 /* Set the view port to the entire view */
299 glViewport(0, 0, (glb->direct? width: glb->rendBufW),
300 (glb->direct? height: glb->rendBufH));
301
302 /* Draw the texture on the screen with OpenGL ES 2 */
303 /* Use program */
304 glUseProgram(glb->directProg);
305
306 /* Update attribute values */
307 glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
308 glEnableVertexAttribArray(ATTRIB_VERTEX);
309 glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0,
310 textureVertices);
311 glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);
312
313 /* Update uniform values if there are any */
314 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
315
316 /* Present */
317 if (!glb->direct)
318 glBindRenderbuffer(GL_RENDERBUFFER, glb->rendBuf);
319
320 return PJ_SUCCESS;
321 }
322
pjmedia_vid_dev_opengl_destroy_buffers(gl_buffers * glb)323 void pjmedia_vid_dev_opengl_destroy_buffers(gl_buffers *glb)
324 {
325 if (glb->frameBuf) {
326 glDeleteFramebuffers(1, &glb->frameBuf);
327 glb->frameBuf = 0;
328 }
329
330 if (glb->rendBuf) {
331 glDeleteRenderbuffers(1, &glb->rendBuf);
332 glb->rendBuf = 0;
333 }
334
335 if (glb->rendTex) {
336 glDeleteTextures(1, &glb->rendTex);
337 glb->rendTex = 0;
338 }
339
340 if (glb->directProg) {
341 glDeleteProgram(glb->directProg);
342 glb->directProg = 0;
343 }
344 }
345
346 /****************************************************************************
347 * Factory operations
348 */
349 /*
350 * Init opengl video driver.
351 */
pjmedia_opengl_factory(pj_pool_factory * pf)352 pjmedia_vid_dev_factory* pjmedia_opengl_factory(pj_pool_factory *pf)
353 {
354 struct opengl_factory *f;
355 pj_pool_t *pool;
356
357 pool = pj_pool_create(pf, "opengl rend", 512, 512, NULL);
358 f = PJ_POOL_ZALLOC_T(pool, struct opengl_factory);
359 f->pf = pf;
360 f->pool = pool;
361 f->base.op = &factory_op;
362
363 return &f->base;
364 }
365
366 /* API: init factory */
opengl_factory_init(pjmedia_vid_dev_factory * f)367 static pj_status_t opengl_factory_init(pjmedia_vid_dev_factory *f)
368 {
369 struct opengl_factory *qf = (struct opengl_factory*)f;
370 struct opengl_dev_info *qdi;
371 unsigned l;
372
373 /* Initialize input and output devices here */
374 qf->dev_info = (struct opengl_dev_info*)
375 pj_pool_calloc(qf->pool, 1, sizeof(struct opengl_dev_info));
376
377 qf->dev_count = 0;
378 qdi = &qf->dev_info[qf->dev_count++];
379 pj_bzero(qdi, sizeof(*qdi));
380 strcpy(qdi->info.name, "OpenGL renderer");
381 strcpy(qdi->info.driver, "OpenGL");
382 qdi->info.dir = PJMEDIA_DIR_RENDER;
383 qdi->info.has_callback = PJ_FALSE;
384 qdi->info.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
385 qdi->info.fmt_cnt = PJ_ARRAY_SIZE(opengl_fmts);
386 qdi->info.caps |= pjmedia_vid_dev_opengl_imp_get_cap();
387
388 for (l = 0; l < PJ_ARRAY_SIZE(opengl_fmts); l++) {
389 pjmedia_format *fmt = &qdi->info.fmt[l];
390 pjmedia_format_init_video(fmt, opengl_fmts[l], DEFAULT_WIDTH,
391 DEFAULT_HEIGHT, DEFAULT_FPS, 1);
392 }
393
394 PJ_LOG(4, (THIS_FILE, "OpenGL device initialized"));
395
396 return PJ_SUCCESS;
397 }
398
399 /* API: destroy factory */
opengl_factory_destroy(pjmedia_vid_dev_factory * f)400 static pj_status_t opengl_factory_destroy(pjmedia_vid_dev_factory *f)
401 {
402 struct opengl_factory *qf = (struct opengl_factory*)f;
403 pj_pool_t *pool = qf->pool;
404
405 qf->pool = NULL;
406 pj_pool_release(pool);
407
408 return PJ_SUCCESS;
409 }
410
411 /* API: refresh the list of devices */
opengl_factory_refresh(pjmedia_vid_dev_factory * f)412 static pj_status_t opengl_factory_refresh(pjmedia_vid_dev_factory *f)
413 {
414 PJ_UNUSED_ARG(f);
415 return PJ_SUCCESS;
416 }
417
418 /* API: get number of devices */
opengl_factory_get_dev_count(pjmedia_vid_dev_factory * f)419 static unsigned opengl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
420 {
421 struct opengl_factory *qf = (struct opengl_factory*)f;
422 return qf->dev_count;
423 }
424
425 /* API: get device info */
opengl_factory_get_dev_info(pjmedia_vid_dev_factory * f,unsigned index,pjmedia_vid_dev_info * info)426 static pj_status_t opengl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
427 unsigned index,
428 pjmedia_vid_dev_info *info)
429 {
430 struct opengl_factory *qf = (struct opengl_factory*)f;
431
432 PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
433
434 pj_memcpy(info, &qf->dev_info[index].info, sizeof(*info));
435
436 return PJ_SUCCESS;
437 }
438
439 /* API: create default device parameter */
opengl_factory_default_param(pj_pool_t * pool,pjmedia_vid_dev_factory * f,unsigned index,pjmedia_vid_dev_param * param)440 static pj_status_t opengl_factory_default_param(pj_pool_t *pool,
441 pjmedia_vid_dev_factory *f,
442 unsigned index,
443 pjmedia_vid_dev_param *param)
444 {
445 struct opengl_factory *qf = (struct opengl_factory*)f;
446 struct opengl_dev_info *di = &qf->dev_info[index];
447
448 PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
449
450 PJ_UNUSED_ARG(pool);
451
452 pj_bzero(param, sizeof(*param));
453 if (di->info.dir & PJMEDIA_DIR_RENDER) {
454 param->dir = PJMEDIA_DIR_RENDER;
455 param->rend_id = index;
456 param->cap_id = PJMEDIA_VID_INVALID_DEV;
457 } else {
458 return PJMEDIA_EVID_INVDEV;
459 }
460
461 param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
462 param->clock_rate = DEFAULT_CLOCK_RATE;
463 pj_memcpy(¶m->fmt, &di->info.fmt[0], sizeof(param->fmt));
464
465 return PJ_SUCCESS;
466 }
467
468 /* API: create stream */
469 static pj_status_t
opengl_factory_create_stream(pjmedia_vid_dev_factory * f,pjmedia_vid_dev_param * param,const pjmedia_vid_dev_cb * cb,void * user_data,pjmedia_vid_dev_stream ** p_vid_strm)470 opengl_factory_create_stream(pjmedia_vid_dev_factory *f,
471 pjmedia_vid_dev_param *param,
472 const pjmedia_vid_dev_cb *cb,
473 void *user_data,
474 pjmedia_vid_dev_stream **p_vid_strm)
475 {
476 struct opengl_factory *qf = (struct opengl_factory*)f;
477 pj_pool_t *pool;
478 const pjmedia_video_format_info *vfi;
479
480 PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
481 PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
482 param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
483 (param->dir == PJMEDIA_DIR_CAPTURE ||
484 param->dir == PJMEDIA_DIR_RENDER),
485 PJ_EINVAL);
486
487 vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
488 if (!vfi)
489 return PJMEDIA_EVID_BADFORMAT;
490
491 /* Create and Initialize stream descriptor */
492 pool = pj_pool_create(qf->pf, "opengl-dev", 4000, 4000, NULL);
493 PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
494
495 return pjmedia_vid_dev_opengl_imp_create_stream(pool, param, cb,
496 user_data, p_vid_strm);
497 }
498
499 #endif /* PJMEDIA_VIDEO_DEV_HAS_OPENGL */
500