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(&param->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