Index: /pjproject/trunk/pjmedia/build/Makefile
===================================================================
--- /pjproject/trunk/pjmedia/build/Makefile	(revision 4811)
+++ /pjproject/trunk/pjmedia/build/Makefile	(revision 4812)
@@ -107,5 +107,5 @@
 export PJMEDIA_VIDEODEV_SRCDIR = ../src/pjmedia-videodev
 export PJMEDIA_VIDEODEV_OBJS +=  errno.o videodev.o avi_dev.o ffmpeg_dev.o \
-				colorbar_dev.o v4l2_dev.o
+				colorbar_dev.o v4l2_dev.o opengl_dev.o
 export PJMEDIA_VIDEODEV_CFLAGS += $(_CFLAGS)
 export PJMEDIA_VIDEODEV_CXXFLAGS += $(_CXXFLAGS)
Index: /pjproject/trunk/pjmedia/build/os-auto.mak.in
===================================================================
--- /pjproject/trunk/pjmedia/build/os-auto.mak.in	(revision 4811)
+++ /pjproject/trunk/pjmedia/build/os-auto.mak.in	(revision 4812)
@@ -215,5 +215,5 @@
 #
 ifeq ($(AC_PJMEDIA_VIDEO),iphone_os)
-export PJMEDIA_VIDEODEV_OBJS += ios_dev.o
+export PJMEDIA_VIDEODEV_OBJS += ios_dev.o ios_opengl_dev.o
 endif
 
Index: /pjproject/trunk/pjmedia/include/pjmedia-videodev/opengl_dev.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia-videodev/opengl_dev.h	(revision 4812)
+++ /pjproject/trunk/pjmedia/include/pjmedia-videodev/opengl_dev.h	(revision 4812)
@@ -0,0 +1,49 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013-2014 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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
+ */
+#ifndef PJMEDIA_VIDEODEV_OPENGL_DEV_H__
+#define PJMEDIA_VIDEODEV_OPENGL_DEV_H__
+
+#include <pjmedia-videodev/videodev_imp.h>
+
+/* OpenGL implementation on each platform needs to implement this and
+ * stream operations.
+ */
+pj_status_t
+pjmedia_vid_dev_opengl_imp_create_stream(pj_pool_t *pool,
+                                         pjmedia_vid_dev_param *param,
+                                         const pjmedia_vid_dev_cb *cb,
+                                         void *user_data,
+                                         pjmedia_vid_dev_stream **p_vid_strm);
+
+/* OpenGL buffers opaque structure. */
+typedef struct gl_buffers gl_buffers;
+
+/* Create OpenGL buffers. */
+void        pjmedia_vid_dev_opengl_create_buffers(pj_pool_t *pool,
+                                                  gl_buffers **glb);
+/* Initialize OpenGL buffers. */
+pj_status_t pjmedia_vid_dev_opengl_init_buffers(gl_buffers *glb);
+/* Render a texture. */
+pj_status_t pjmedia_vid_dev_opengl_draw(gl_buffers *glb,
+                                        unsigned int texture,
+                                        unsigned int name);
+/* Destroy OpenGL buffers. */
+void        pjmedia_vid_dev_opengl_destroy_buffers(gl_buffers *glb);
+
+#endif    /* PJMEDIA_VIDEODEV_OPENGL_DEV_H__ */
Index: /pjproject/trunk/pjmedia/src/pjmedia-videodev/ios_opengl_dev.m
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia-videodev/ios_opengl_dev.m	(revision 4812)
+++ /pjproject/trunk/pjmedia/src/pjmedia-videodev/ios_opengl_dev.m	(revision 4812)
@@ -0,0 +1,429 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013-2014 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+
+#if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \
+    defined(PJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL) && \
+    PJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL != 0
+
+#include <pjmedia-videodev/opengl_dev.h>
+#import <UIKit/UIKit.h>
+
+#define THIS_FILE		"ios_opengl_dev.c"
+
+typedef struct iosgl_fmt_info
+{
+    pjmedia_format_id   pjmedia_format;
+    UInt32		iosgl_format;
+} iosgl_fmt_info;
+
+/* Supported formats */
+static iosgl_fmt_info iosgl_fmts[] =
+{
+    {PJMEDIA_FORMAT_BGRA, kCVPixelFormatType_32BGRA} ,
+};
+
+@interface GLView : UIView
+{
+@public
+    struct iosgl_stream *stream;
+}
+
+@end
+
+/* Video stream. */
+struct iosgl_stream
+{
+    pjmedia_vid_dev_stream  base;		/**< Base stream       */
+    pjmedia_vid_dev_param   param;		/**< Settings	       */
+    pj_pool_t		   *pool;		/**< Memory pool       */
+    
+    pjmedia_vid_dev_cb	    vid_cb;		/**< Stream callback   */
+    void		   *user_data;          /**< Application data  */
+    
+    pj_status_t             status;
+    pj_timestamp            frame_ts;
+    unsigned                ts_inc;
+    
+    gl_buffers                  *gl_buf;
+    GLView                      *gl_view;
+    EAGLContext                 *ogl_context;
+    CVOpenGLESTextureCacheRef    vid_texture;
+    CVImageBufferRef             pb;
+    CVOpenGLESTextureRef         texture;
+};
+
+
+/* Prototypes */
+static pj_status_t iosgl_stream_get_param(pjmedia_vid_dev_stream *strm,
+                                          pjmedia_vid_dev_param *param);
+static pj_status_t iosgl_stream_get_cap(pjmedia_vid_dev_stream *strm,
+                                        pjmedia_vid_dev_cap cap,
+                                        void *value);
+static pj_status_t iosgl_stream_set_cap(pjmedia_vid_dev_stream *strm,
+                                        pjmedia_vid_dev_cap cap,
+                                        const void *value);
+static pj_status_t iosgl_stream_start(pjmedia_vid_dev_stream *strm);
+static pj_status_t iosgl_stream_put_frame(pjmedia_vid_dev_stream *strm,
+                                          const pjmedia_frame *frame);
+static pj_status_t iosgl_stream_stop(pjmedia_vid_dev_stream *strm);
+static pj_status_t iosgl_stream_destroy(pjmedia_vid_dev_stream *strm);
+
+/* Operations */
+static pjmedia_vid_dev_stream_op stream_op =
+{
+    &iosgl_stream_get_param,
+    &iosgl_stream_get_cap,
+    &iosgl_stream_set_cap,
+    &iosgl_stream_start,
+    NULL,
+    &iosgl_stream_put_frame,
+    &iosgl_stream_stop,
+    &iosgl_stream_destroy
+};
+
+static iosgl_fmt_info* get_iosgl_format_info(pjmedia_format_id id)
+{
+    unsigned i;
+    
+    for (i = 0; i < PJ_ARRAY_SIZE(iosgl_fmts); i++) {
+        if (iosgl_fmts[i].pjmedia_format == id)
+            return &iosgl_fmts[i];
+    }
+    
+    return NULL;
+}
+
+@implementation GLView
+
++ (Class) layerClass
+{
+    return [CAEAGLLayer class];
+}
+
+- (void) init_buffers
+{
+    /* Initialize OpenGL ES 2 */
+    CAEAGLLayer *eagl_layer = (CAEAGLLayer *)[stream->gl_view layer];
+    eagl_layer.opaque = YES;
+    eagl_layer.drawableProperties =
+    [NSDictionary dictionaryWithObjectsAndKeys:
+     [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking,
+     kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat,
+     nil];
+    
+    stream->ogl_context = [[EAGLContext alloc] initWithAPI:
+                           kEAGLRenderingAPIOpenGLES2];
+    if (!stream->ogl_context ||
+        ![EAGLContext setCurrentContext:stream->ogl_context])
+    {
+        stream->status = PJMEDIA_EVID_SYSERR;
+        return;
+    }
+    
+    /* Create GL buffers */
+    pjmedia_vid_dev_opengl_create_buffers(stream->pool, &stream->gl_buf);
+    
+    [stream->ogl_context renderbufferStorage:GL_RENDERBUFFER
+                         fromDrawable:(CAEAGLLayer *)[stream->gl_view layer]];
+    
+    /* Init GL buffers */
+    stream->status = pjmedia_vid_dev_opengl_init_buffers(stream->gl_buf);
+}
+
+- (void)render
+{
+    if (![EAGLContext setCurrentContext:stream->ogl_context]) {
+        /* Failed to set context */
+        return;
+    }
+    
+    pjmedia_vid_dev_opengl_draw(stream->gl_buf,
+        (unsigned int)CVOpenGLESTextureGetTarget(stream->texture),
+        (unsigned int)CVOpenGLESTextureGetName(stream->texture));
+}
+
+@end
+
+/* API: create stream */
+pj_status_t
+pjmedia_vid_dev_opengl_imp_create_stream(pj_pool_t *pool,
+                                         pjmedia_vid_dev_param *param,
+                                         const pjmedia_vid_dev_cb *cb,
+                                         void *user_data,
+                                         pjmedia_vid_dev_stream **p_vid_strm)
+{
+    struct iosgl_stream *strm;
+    const pjmedia_video_format_detail *vfd;
+    pj_status_t status = PJ_SUCCESS;
+    iosgl_fmt_info *ifi;
+    
+    if (!(ifi = get_iosgl_format_info(param->fmt.id)))
+        return PJMEDIA_EVID_BADFORMAT;
+    
+    strm = PJ_POOL_ZALLOC_T(pool, struct iosgl_stream);
+    pj_memcpy(&strm->param, param, sizeof(*param));
+    strm->pool = pool;
+    pj_memcpy(&strm->vid_cb, cb, sizeof(*cb));
+    strm->user_data = user_data;
+    
+    vfd = pjmedia_format_get_video_format_detail(&strm->param.fmt, PJ_TRUE);
+    strm->ts_inc = PJMEDIA_SPF2(param->clock_rate, &vfd->fps, 1);
+    
+    if (param->dir & PJMEDIA_DIR_RENDER) {
+        CVReturn err;
+        UIWindow *window;
+        CGRect rect;
+        
+	if ((param->flags & PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) &&
+            param->window.info.ios.window)
+        {
+            /* Get output window handle provided by the application */
+	    window = (UIWindow *)param->window.info.ios.window;
+            rect = window.bounds;
+        } else {
+            rect = CGRectMake(0, 0, strm->param.disp_size.w,
+                              strm->param.disp_size.h);
+        }
+        
+	strm->gl_view = [[GLView alloc] initWithFrame:rect];
+	if (!strm->gl_view)
+	    return PJ_ENOMEM;
+        strm->gl_view->stream = strm;
+
+        /* Perform OpenGL buffer initializations in the main thread. */
+        strm->status = PJ_SUCCESS;
+        [strm->gl_view performSelectorOnMainThread:@selector(init_buffers)
+                       withObject:nil waitUntilDone:YES];
+        if ((status = strm->status) != PJ_SUCCESS) {
+            PJ_LOG(3, (THIS_FILE, "Unable to create and init OpenGL buffers"));
+            goto on_error;
+        }
+        
+        /*  Create a new CVOpenGLESTexture cache */
+        err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL,
+                                           strm->ogl_context, NULL,
+                                           &strm->vid_texture);
+        if (err) {
+            PJ_LOG(3, (THIS_FILE, "Unable to create OpenGL texture cache %d",
+                       err));
+            status = PJMEDIA_EVID_SYSERR;
+            goto on_error;
+        }
+
+        PJ_LOG(4, (THIS_FILE, "iOS OpenGL ES renderer successfully created"));
+    }
+    
+    /* Apply the remaining settings */
+    /*
+     if (param->flags & PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
+     iosgl_stream_set_cap(&strm->base,
+     PJMEDIA_VID_DEV_CAP_INPUT_SCALE,
+     &param->fmt);
+     }
+     */
+    /* Done */
+    strm->base.op = &stream_op;
+    *p_vid_strm = &strm->base;
+    
+    return PJ_SUCCESS;
+    
+on_error:
+    iosgl_stream_destroy((pjmedia_vid_dev_stream *)strm);
+    
+    return status;
+}
+
+/* API: Get stream info. */
+static pj_status_t iosgl_stream_get_param(pjmedia_vid_dev_stream *s,
+                                          pjmedia_vid_dev_param *pi)
+{
+    struct iosgl_stream *strm = (struct iosgl_stream*)s;
+    
+    PJ_ASSERT_RETURN(strm && pi, PJ_EINVAL);
+    
+    pj_memcpy(pi, &strm->param, sizeof(*pi));
+
+    if (iosgl_stream_get_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW,
+                           &pi->window) == PJ_SUCCESS)
+    {
+        pi->flags |= PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+    }
+    
+    return PJ_SUCCESS;
+}
+
+/* API: get capability */
+static pj_status_t iosgl_stream_get_cap(pjmedia_vid_dev_stream *s,
+                                        pjmedia_vid_dev_cap cap,
+                                        void *pval)
+{
+    struct iosgl_stream *strm = (struct iosgl_stream*)s;
+    
+    PJ_UNUSED_ARG(strm);
+    
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+    
+    if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
+        return PJMEDIA_EVID_INVCAP;
+    } else if (cap == PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW) {
+        pjmedia_vid_dev_hwnd *wnd = (pjmedia_vid_dev_hwnd *)pval;
+        wnd->info.ios.window = strm->gl_view;
+        return PJ_SUCCESS;
+    } else {
+	return PJMEDIA_EVID_INVCAP;
+    }
+}
+
+/* API: set capability */
+static pj_status_t iosgl_stream_set_cap(pjmedia_vid_dev_stream *s,
+                                        pjmedia_vid_dev_cap cap,
+                                        const void *pval)
+{
+    struct iosgl_stream *strm = (struct iosgl_stream*)s;
+    
+    PJ_UNUSED_ARG(strm);
+    
+    PJ_ASSERT_RETURN(s && pval, PJ_EINVAL);
+    
+    if (cap==PJMEDIA_VID_DEV_CAP_INPUT_SCALE) {
+	return PJ_SUCCESS;
+    }
+    
+    return PJMEDIA_EVID_INVCAP;
+}
+
+/* API: Start stream. */
+static pj_status_t iosgl_stream_start(pjmedia_vid_dev_stream *strm)
+{
+    struct iosgl_stream *stream = (struct iosgl_stream*)strm;
+    
+    PJ_UNUSED_ARG(stream);
+    
+    PJ_LOG(4, (THIS_FILE, "Starting ios opengl stream"));
+    
+    return PJ_SUCCESS;
+}
+
+/* API: Put frame from stream */
+static pj_status_t iosgl_stream_put_frame(pjmedia_vid_dev_stream *strm,
+                                          const pjmedia_frame *frame)
+{
+    struct iosgl_stream *stream = (struct iosgl_stream*)strm;
+    CVReturn err;
+
+    err = CVPixelBufferCreateWithBytes(kCFAllocatorDefault,
+                                       stream->param.disp_size.w,
+                                       stream->param.disp_size.h,
+                                       kCVPixelFormatType_32BGRA,
+                                       frame->buf,
+                                       stream->param.disp_size.w * 4,
+                                       NULL, NULL, NULL, &stream->pb);
+    if (err) {
+        PJ_LOG(3, (THIS_FILE, "Unable to create pixel buffer %d", err));
+        return PJMEDIA_EVID_SYSERR;
+    }
+
+    /* Create a CVOpenGLESTexture from the CVImageBuffer */
+    err=CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
+                                                     stream->vid_texture,
+                                                     stream->pb, NULL,
+                                                     GL_TEXTURE_2D, GL_RGBA,
+                                                     stream->param.disp_size.w,
+                                                     stream->param.disp_size.h,
+                                                     GL_BGRA,
+                                                     GL_UNSIGNED_BYTE,
+                                                     0, &stream->texture);
+    if (!stream->texture || err) {
+        PJ_LOG(3, (THIS_FILE, "Unable to create OpenGL texture %d", err));
+        CVPixelBufferRelease(stream->pb);
+        return PJMEDIA_EVID_SYSERR;
+    }
+    
+    /* Perform OpenGL drawing in the main thread. */
+    [stream->gl_view performSelectorOnMainThread:@selector(render)
+                           withObject:nil waitUntilDone:YES];
+    //    dispatch_async(dispatch_get_main_queue(),
+    //                   ^{[stream->gl_view render];});
+    
+    [stream->ogl_context presentRenderbuffer:GL_RENDERBUFFER];
+ 
+    /* Flush the CVOpenGLESTexture cache and release the texture */
+    CVOpenGLESTextureCacheFlush(stream->vid_texture, 0);
+    CFRelease(stream->texture);
+    CVPixelBufferRelease(stream->pb);
+    
+    return PJ_SUCCESS;
+}
+
+/* API: Stop stream. */
+static pj_status_t iosgl_stream_stop(pjmedia_vid_dev_stream *strm)
+{
+    struct iosgl_stream *stream = (struct iosgl_stream*)strm;
+    
+    PJ_UNUSED_ARG(stream);
+    
+    PJ_LOG(4, (THIS_FILE, "Stopping ios opengl stream"));
+
+    return PJ_SUCCESS;
+}
+
+
+/* API: Destroy stream. */
+static pj_status_t iosgl_stream_destroy(pjmedia_vid_dev_stream *strm)
+{
+    struct iosgl_stream *stream = (struct iosgl_stream*)strm;
+    
+    PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
+    
+    iosgl_stream_stop(strm);
+    
+    if (stream->vid_texture) {
+        CFRelease(stream->vid_texture);
+        stream->vid_texture = NULL;
+    }
+
+    if ([EAGLContext currentContext] == stream->ogl_context)
+        [EAGLContext setCurrentContext:nil];
+    
+    if (stream->ogl_context) {
+        [stream->ogl_context release];
+        stream->ogl_context = NULL;
+    }
+    
+    if (stream->gl_view) {
+        UIView *view = stream->gl_view;
+        dispatch_async(dispatch_get_main_queue(),
+                      ^{
+                          [view removeFromSuperview];
+                          [view release];
+                       });
+        stream->gl_view = NULL;
+    }
+    
+    pjmedia_vid_dev_opengl_destroy_buffers(stream->gl_buf);
+    
+    pj_pool_release(stream->pool);
+    
+    return PJ_SUCCESS;
+}
+
+#endif	/* PJMEDIA_VIDEO_DEV_HAS_IOS_OPENGL */
Index: /pjproject/trunk/pjmedia/src/pjmedia-videodev/opengl_dev.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia-videodev/opengl_dev.c	(revision 4812)
+++ /pjproject/trunk/pjmedia/src/pjmedia-videodev/opengl_dev.c	(revision 4812)
@@ -0,0 +1,475 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2013-2014 Teluu Inc. (http://www.teluu.com)
+ *
+ * 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 <pjmedia-videodev/videodev_imp.h>
+#include <pj/assert.h>
+#include <pj/log.h>
+
+#if defined(PJMEDIA_HAS_VIDEO) && PJMEDIA_HAS_VIDEO != 0 && \
+    defined(PJMEDIA_VIDEO_DEV_HAS_OPENGL) && \
+    PJMEDIA_VIDEO_DEV_HAS_OPENGL != 0
+
+#include <pjmedia-videodev/opengl_dev.h>
+#ifdef PJMEDIA_VIDEO_DEV_HAS_OPENGL_ES
+#   include <OpenGLES/ES2/gl.h>
+#   include <OpenGLES/ES2/glext.h>
+#else
+#   include <GL/gl.h>
+#   include <GL/glext.h>
+#endif
+
+#define THIS_FILE		"opengl_dev.c"
+#define DEFAULT_CLOCK_RATE	90000
+#define DEFAULT_WIDTH		480
+#define DEFAULT_HEIGHT		360
+#define DEFAULT_FPS		15
+
+#define LOG(a)
+
+enum {
+    ATTRIB_VERTEX,
+    ATTRIB_TEXTUREPOSITON,
+    NUM_ATTRIBUTES
+};
+
+/* Vertex and fragment shaders */
+static const GLchar *vertSrc = " \
+attribute vec4 position; \
+attribute vec4 inTexCoord; \
+varying vec2 texCoord; \
+void main() \
+{ \
+    gl_Position = position; \
+    texCoord = inTexCoord.xy; \
+} \
+";
+static const GLchar *fragSrc = " \
+varying highp vec2 texCoord; \
+uniform sampler2D videoFrame; \
+void main() \
+{ \
+    gl_FragColor = texture2D(videoFrame, texCoord); \
+} \
+";
+
+/* OpenGL buffers structure. */
+struct gl_buffers {
+    GLuint  frameBuf;
+    GLuint  rendBuf;
+    GLuint  directProg;
+    
+    int     rendBufW;
+    int     rendBufH;
+};
+
+/* Supported formats */
+static pjmedia_format_id opengl_fmts[] = {PJMEDIA_FORMAT_BGRA};
+
+/* opengl device info */
+struct opengl_dev_info
+{
+    pjmedia_vid_dev_info	 info;
+};
+
+/* opengl factory */
+struct opengl_factory
+{
+    pjmedia_vid_dev_factory	 base;
+    pj_pool_t			*pool;
+    pj_pool_factory		*pf;
+    
+    unsigned			 dev_count;
+    struct opengl_dev_info	*dev_info;
+};
+
+/* Prototypes */
+static pj_status_t opengl_factory_init(pjmedia_vid_dev_factory *f);
+static pj_status_t opengl_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t opengl_factory_refresh(pjmedia_vid_dev_factory *f);
+static unsigned    opengl_factory_get_dev_count(pjmedia_vid_dev_factory *f);
+static pj_status_t opengl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+                                               unsigned index,
+                                               pjmedia_vid_dev_info *info);
+static pj_status_t opengl_factory_default_param(pj_pool_t *pool,
+                                                pjmedia_vid_dev_factory *f,
+                                                unsigned index,
+                                                pjmedia_vid_dev_param *param);
+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);
+
+/* Operations */
+static pjmedia_vid_dev_factory_op factory_op =
+{
+    &opengl_factory_init,
+    &opengl_factory_destroy,
+    &opengl_factory_get_dev_count,
+    &opengl_factory_get_dev_info,
+    &opengl_factory_default_param,
+    &opengl_factory_create_stream,
+    &opengl_factory_refresh
+};
+
+/****************************************************************************
+ * OpenGL utility functions
+ */
+/* Compile a shader from the provided source(s) */
+GLint compile_shader(GLenum target, GLsizei count, const GLchar **sources,
+                     GLuint *shader)
+{
+    GLint status;
+    
+    *shader = glCreateShader(target);
+    glShaderSource(*shader, count, sources, NULL);
+    glCompileShader(*shader);
+    
+    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);
+    
+    return status;
+}
+
+/* Create program, compile shader, link program, bind attributes */
+GLint create_program(const GLchar *vertSource, const GLchar *fragSource,
+                     GLsizei attribNameCnt, const GLchar **attribNames,
+                     const GLint *attribLocations, GLuint *program)
+{
+    GLuint vertShader = 0, fragShader = 0, prog = 0, i;
+    GLint status;
+    
+    /* Create shader program */
+    prog = glCreateProgram();
+    *program = prog;
+    
+    /* Create and compile vertex shader */
+    status = compile_shader(GL_VERTEX_SHADER, 1, &vertSource, &vertShader);
+    if (status == 0) {
+        LOG("Unable to compile vertex shader");
+        return status;
+    }
+    
+    /* Create and compile fragment shader */
+    status = compile_shader(GL_FRAGMENT_SHADER, 1, &fragSource, &fragShader);
+    if (status == 0) {
+        LOG("Unable to compile fragment shader");
+        return status;
+    }
+    
+    /* Attach vertex shader to program */
+    glAttachShader(prog, vertShader);
+    
+    /* Attach fragment shader to program */
+    glAttachShader(prog, fragShader);
+    
+    /* Bind attribute locations prior to linking */
+    for (i = 0; i < attribNameCnt; i++) {
+        glBindAttribLocation(prog, attribLocations[i], attribNames[i]);
+    }
+    
+    /* Link program */
+    glLinkProgram(prog);
+    glGetProgramiv(prog, GL_LINK_STATUS, &status);
+    if (status == 0) {
+        LOG("Unable to link program");
+        return status;
+    }
+    
+    /* Release vertex and fragment shaders */
+    if (vertShader)
+        glDeleteShader(vertShader);
+    if (fragShader)
+        glDeleteShader(fragShader);
+    
+    return status;
+}
+
+void pjmedia_vid_dev_opengl_create_buffers(pj_pool_t *pool, gl_buffers **glb)
+{
+    gl_buffers *glbuf = PJ_POOL_ZALLOC_T(pool, gl_buffers);
+    
+    *glb = glbuf;
+    glDisable(GL_DEPTH_TEST);
+    
+    glGenFramebuffers(1, &glbuf->frameBuf);
+    glBindFramebuffer(GL_FRAMEBUFFER, glbuf->frameBuf);
+    
+    glGenRenderbuffers(1, &glbuf->rendBuf);
+    glBindRenderbuffer(GL_RENDERBUFFER, glbuf->rendBuf);
+}
+
+pj_status_t pjmedia_vid_dev_opengl_init_buffers(gl_buffers *glb)
+{
+    /* Attributes */
+    GLint attribLocation[NUM_ATTRIBUTES] = { ATTRIB_VERTEX,
+        ATTRIB_TEXTUREPOSITON };
+    GLchar *attribName[NUM_ATTRIBUTES] = { "position", "texCoord" };
+    
+    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH,
+                                 &glb->rendBufW);
+    glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT,
+                                 &glb->rendBufH);
+    
+    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
+                              GL_RENDERBUFFER, glb->rendBuf);
+    if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
+        LOG("Unable to create frame buffer");
+        return -1;
+    }
+    
+    create_program(vertSrc, fragSrc, NUM_ATTRIBUTES,
+                   (const GLchar **)&attribName[0], attribLocation,
+                   &glb->directProg);
+    
+    if (!glb->directProg) {
+        LOG("Unable to create program");
+        return -2;
+    }
+    
+    return PJ_SUCCESS;
+}
+
+pj_status_t pjmedia_vid_dev_opengl_draw(gl_buffers *glb, unsigned int texture,
+                                        unsigned int name)
+{
+    static const GLfloat squareVertices[] = {
+        -1.0f, -1.0f,
+        1.0f, -1.0f,
+        -1.0f,  1.0f,
+        1.0f,  1.0f,
+    };
+    GLfloat textureVertices[] = {
+        0, 1, 1, 1, 0, 0, 1, 0
+    };
+    GLenum tex = (GLenum) texture;
+    GLuint nam = (GLuint) name;
+
+    glBindTexture(tex, nam);
+    
+    /* Set texture parameters */
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    
+    glBindFramebuffer(GL_FRAMEBUFFER, glb->frameBuf);
+    
+    /* Set the view port to the entire view */
+    glViewport(0, 0, glb->rendBufW, glb->rendBufH);
+    
+    /* Draw the texture on the screen with OpenGL ES 2 */
+    /* Use program */
+    glUseProgram(glb->directProg);
+    
+    /* Update attribute values */
+    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, squareVertices);
+    glEnableVertexAttribArray(ATTRIB_VERTEX);
+    glVertexAttribPointer(ATTRIB_TEXTUREPOSITON, 2, GL_FLOAT, 0, 0,
+                          textureVertices);
+    glEnableVertexAttribArray(ATTRIB_TEXTUREPOSITON);
+    
+    /* Update uniform values if there are any */
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    
+    /* Present */
+    glBindRenderbuffer(GL_RENDERBUFFER, glb->rendBuf);
+    
+    return PJ_SUCCESS;
+}
+
+void pjmedia_vid_dev_opengl_destroy_buffers(gl_buffers *glb)
+{
+    if (glb->frameBuf) {
+        glDeleteFramebuffers(1, &glb->frameBuf);
+        glb->frameBuf = 0;
+    }
+    
+    if (glb->rendBuf) {
+        glDeleteRenderbuffers(1, &glb->rendBuf);
+        glb->rendBuf = 0;
+    }
+    
+    if (glb->directProg) {
+        glDeleteProgram(glb->directProg);
+        glb->directProg = 0;
+    }
+}
+
+/****************************************************************************
+ * Factory operations
+ */
+/*
+ * Init opengl video driver.
+ */
+pjmedia_vid_dev_factory* pjmedia_opengl_factory(pj_pool_factory *pf)
+{
+    struct opengl_factory *f;
+    pj_pool_t *pool;
+    
+    pool = pj_pool_create(pf, "opengl rend", 512, 512, NULL);
+    f = PJ_POOL_ZALLOC_T(pool, struct opengl_factory);
+    f->pf = pf;
+    f->pool = pool;
+    f->base.op = &factory_op;
+    
+    return &f->base;
+}
+
+/* API: init factory */
+static pj_status_t opengl_factory_init(pjmedia_vid_dev_factory *f)
+{
+    struct opengl_factory *qf = (struct opengl_factory*)f;
+    struct opengl_dev_info *qdi;
+    unsigned i, l;
+    
+    /* Initialize input and output devices here */
+    qf->dev_info = (struct opengl_dev_info*)
+    pj_pool_calloc(qf->pool, 1, sizeof(struct opengl_dev_info));
+    
+    qf->dev_count = 0;
+    qdi = &qf->dev_info[qf->dev_count++];
+    pj_bzero(qdi, sizeof(*qdi));
+    strcpy(qdi->info.name, "OpenGL renderer");
+    strcpy(qdi->info.driver, "OpenGL");
+    qdi->info.dir = PJMEDIA_DIR_RENDER;
+    qdi->info.has_callback = PJ_FALSE;
+    qdi->info.caps = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
+
+    for (i = 0; i < qf->dev_count; i++) {
+	qdi = &qf->dev_info[i];
+	qdi->info.fmt_cnt = PJ_ARRAY_SIZE(opengl_fmts);
+	qdi->info.caps |= PJMEDIA_VID_DEV_CAP_FORMAT;
+	
+	for (l = 0; l < PJ_ARRAY_SIZE(opengl_fmts); l++) {
+	    pjmedia_format *fmt = &qdi->info.fmt[l];
+	    pjmedia_format_init_video(fmt,
+				      opengl_fmts[l],
+				      DEFAULT_WIDTH,
+				      DEFAULT_HEIGHT,
+				      DEFAULT_FPS, 1);
+	}
+    }
+    
+    PJ_LOG(4, (THIS_FILE, "OpenGL initialized with %d devices",
+	       qf->dev_count));
+    
+    return PJ_SUCCESS;
+}
+
+/* API: destroy factory */
+static pj_status_t opengl_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+    struct opengl_factory *qf = (struct opengl_factory*)f;
+    pj_pool_t *pool = qf->pool;
+    
+    qf->pool = NULL;
+    pj_pool_release(pool);
+    
+    return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t opengl_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+    PJ_UNUSED_ARG(f);
+    return PJ_SUCCESS;
+}
+
+/* API: get number of devices */
+static unsigned opengl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
+{
+    struct opengl_factory *qf = (struct opengl_factory*)f;
+    return qf->dev_count;
+}
+
+/* API: get device info */
+static pj_status_t opengl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
+                                              unsigned index,
+                                              pjmedia_vid_dev_info *info)
+{
+    struct opengl_factory *qf = (struct opengl_factory*)f;
+    
+    PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
+    
+    pj_memcpy(info, &qf->dev_info[index].info, sizeof(*info));
+    
+    return PJ_SUCCESS;
+}
+
+/* API: create default device parameter */
+static pj_status_t opengl_factory_default_param(pj_pool_t *pool,
+                                               pjmedia_vid_dev_factory *f,
+                                               unsigned index,
+                                               pjmedia_vid_dev_param *param)
+{
+    struct opengl_factory *qf = (struct opengl_factory*)f;
+    struct opengl_dev_info *di = &qf->dev_info[index];
+    
+    PJ_ASSERT_RETURN(index < qf->dev_count, PJMEDIA_EVID_INVDEV);
+    
+    PJ_UNUSED_ARG(pool);
+    
+    pj_bzero(param, sizeof(*param));
+    if (di->info.dir & PJMEDIA_DIR_RENDER) {
+	param->dir = PJMEDIA_DIR_RENDER;
+	param->rend_id = index;
+	param->cap_id = PJMEDIA_VID_INVALID_DEV;
+    } else {
+	return PJMEDIA_EVID_INVDEV;
+    }
+    
+    param->flags = PJMEDIA_VID_DEV_CAP_FORMAT;
+    param->clock_rate = DEFAULT_CLOCK_RATE;
+    pj_memcpy(&param->fmt, &di->info.fmt[0], sizeof(param->fmt));
+    
+    return PJ_SUCCESS;
+}
+
+/* API: create stream */
+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)
+{
+    struct opengl_factory *qf = (struct opengl_factory*)f;
+    pj_pool_t *pool;
+    const pjmedia_video_format_info *vfi;
+    
+    PJ_ASSERT_RETURN(f && param && p_vid_strm, PJ_EINVAL);
+    PJ_ASSERT_RETURN(param->fmt.type == PJMEDIA_TYPE_VIDEO &&
+		     param->fmt.detail_type == PJMEDIA_FORMAT_DETAIL_VIDEO &&
+                     (param->dir == PJMEDIA_DIR_CAPTURE ||
+                      param->dir == PJMEDIA_DIR_RENDER),
+		     PJ_EINVAL);
+    
+    vfi = pjmedia_get_video_format_info(NULL, param->fmt.id);
+    if (!vfi)
+        return PJMEDIA_EVID_BADFORMAT;
+    
+    /* Create and Initialize stream descriptor */
+    pool = pj_pool_create(qf->pf, "opengl-dev", 4000, 4000, NULL);
+    PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM);
+
+    return pjmedia_vid_dev_opengl_imp_create_stream(pool, param, cb,
+                                                    user_data, p_vid_strm);
+}
+
+#endif	/* PJMEDIA_VIDEO_DEV_HAS_OPENGL */
Index: /pjproject/trunk/pjmedia/src/pjmedia-videodev/videodev.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia-videodev/videodev.c	(revision 4811)
+++ /pjproject/trunk/pjmedia/src/pjmedia-videodev/videodev.c	(revision 4812)
@@ -96,4 +96,8 @@
 #if PJMEDIA_VIDEO_DEV_HAS_IOS
 pjmedia_vid_dev_factory* pjmedia_ios_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_OPENGL
+pjmedia_vid_dev_factory* pjmedia_opengl_factory(pj_pool_factory *pf);
 #endif
 
@@ -381,4 +385,7 @@
     vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_ios_factory;
 #endif
+#if PJMEDIA_VIDEO_DEV_HAS_OPENGL
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_opengl_factory;
+#endif
 #if PJMEDIA_VIDEO_DEV_HAS_DSHOW
     vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_dshow_factory;
