* #27232: jni: added pjproject checkout as regular git content

We will remove it once the next release of pjsip (with Android support)
comes out and is merged into SFLphone.
diff --git a/jni/pjproject-android/.svn/pristine/12/12ec8c76e85660b9b4311611a7576de86c565320.svn-base b/jni/pjproject-android/.svn/pristine/12/12ec8c76e85660b9b4311611a7576de86c565320.svn-base
new file mode 100644
index 0000000..658d091
--- /dev/null
+++ b/jni/pjproject-android/.svn/pristine/12/12ec8c76e85660b9b4311611a7576de86c565320.svn-base
@@ -0,0 +1,877 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2008-2011 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/errno.h>
+#include <pj/log.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
+
+
+#define THIS_FILE   "videodev.c"
+
+#define DEFINE_CAP(name, info)	{name, info}
+
+/* Capability names */
+static struct cap_info
+{
+    const char *name;
+    const char *info;
+} cap_infos[] = 
+{
+    DEFINE_CAP("format",        "Video format"),
+    DEFINE_CAP("scale",         "Input dimension"),
+    DEFINE_CAP("window",        "Window handle"),
+    DEFINE_CAP("resize",        "Renderer resize"),
+    DEFINE_CAP("position",      "Renderer position"),
+    DEFINE_CAP("hide",          "Renderer hide"),
+    DEFINE_CAP("preview",       "Input preview"),
+    DEFINE_CAP("orientation",   "Video orientation"),
+    DEFINE_CAP("switch",        "Switch device"),
+    DEFINE_CAP("wndflags",      "Window flags")
+};
+
+
+/*
+ * The device index seen by application and driver is different. 
+ *
+ * At application level, device index is index to global list of device.
+ * At driver level, device index is index to device list on that particular
+ * factory only.
+ */
+#define MAKE_DEV_ID(f_id, index)   (((f_id & 0xFFFF) << 16) | (index & 0xFFFF))
+#define GET_INDEX(dev_id)	   ((dev_id) & 0xFFFF)
+#define GET_FID(dev_id)		   ((dev_id) >> 16)
+#define DEFAULT_DEV_ID		    0
+
+
+/* extern functions to create factories */
+#if PJMEDIA_VIDEO_DEV_HAS_NULL_VIDEO
+pjmedia_vid_dev_factory* pjmedia_null_video_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
+pjmedia_vid_dev_factory* pjmedia_dshow_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
+pjmedia_vid_dev_factory* pjmedia_cbar_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_SDL
+pjmedia_vid_dev_factory* pjmedia_sdl_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG
+pjmedia_vid_dev_factory* pjmedia_ffmpeg_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_V4L2
+pjmedia_vid_dev_factory* pjmedia_v4l2_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_QT
+pjmedia_vid_dev_factory* pjmedia_qt_factory(pj_pool_factory *pf);
+#endif
+
+#if PJMEDIA_VIDEO_DEV_HAS_IOS
+pjmedia_vid_dev_factory* pjmedia_ios_factory(pj_pool_factory *pf);
+#endif
+
+#define MAX_DRIVERS	16
+#define MAX_DEVS	64
+
+
+/* driver structure */
+struct driver
+{
+    /* Creation function */
+    pjmedia_vid_dev_factory_create_func_ptr create;
+    /* Factory instance */
+    pjmedia_vid_dev_factory *f;
+    char		     name[32];	    /* Driver name		    */
+    unsigned		     dev_cnt;	    /* Number of devices	    */
+    unsigned		     start_idx;	    /* Start index in global list   */
+    int			     cap_dev_idx;   /* Default capture device.	    */
+    int			     rend_dev_idx;  /* Default render device	    */
+};
+
+/* The video device subsystem */
+static struct vid_subsys
+{
+    unsigned	     init_count;	/* How many times init() is called  */
+    pj_pool_factory *pf;		/* The pool factory.		    */
+
+    unsigned	     drv_cnt;		/* Number of drivers.		    */
+    struct driver    drv[MAX_DRIVERS];	/* Array of drivers.		    */
+
+    unsigned	     dev_cnt;		/* Total number of devices.	    */
+    pj_uint32_t	     dev_list[MAX_DEVS];/* Array of device IDs.		    */
+
+} vid_subsys;
+
+/* API: get capability name/info */
+PJ_DEF(const char*) pjmedia_vid_dev_cap_name(pjmedia_vid_dev_cap cap,
+					     const char **p_desc)
+{
+    const char *desc;
+    unsigned i;
+
+    if (p_desc==NULL) p_desc = &desc;
+
+    for (i=0; i<PJ_ARRAY_SIZE(cap_infos); ++i) {
+	if ((1 << i)==cap)
+	    break;
+    }
+
+    if (i==PJ_ARRAY_SIZE(cap_infos)) {
+	*p_desc = "??";
+	return "??";
+    }
+
+    *p_desc = cap_infos[i].info;
+    return cap_infos[i].name;
+}
+
+static pj_status_t get_cap_pointer(const pjmedia_vid_dev_param *param,
+				   pjmedia_vid_dev_cap cap,
+				   void **ptr,
+				   unsigned *size)
+{
+#define FIELD_INFO(name)    *ptr = (void*)&param->name; \
+			    *size = sizeof(param->name)
+
+    switch (cap) {
+    case PJMEDIA_VID_DEV_CAP_FORMAT:
+	FIELD_INFO(fmt);
+	break;
+    case PJMEDIA_VID_DEV_CAP_INPUT_SCALE:
+	FIELD_INFO(disp_size);
+	break;
+    case PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW:
+	FIELD_INFO(window);
+	break;
+    case PJMEDIA_VID_DEV_CAP_OUTPUT_RESIZE:
+	FIELD_INFO(disp_size);
+	break;
+    case PJMEDIA_VID_DEV_CAP_OUTPUT_POSITION:
+	FIELD_INFO(window_pos);
+	break;
+    case PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE:
+	FIELD_INFO(window_hide);
+	break;
+    case PJMEDIA_VID_DEV_CAP_INPUT_PREVIEW:
+	FIELD_INFO(native_preview);
+	break;
+    case PJMEDIA_VID_DEV_CAP_ORIENTATION:
+	FIELD_INFO(orient);
+	break;
+    /* The PJMEDIA_VID_DEV_CAP_SWITCH does not have an entry in the
+     * param (it doesn't make sense to open a stream and tell it
+     * to switch immediately).
+     */
+    case PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW_FLAGS:
+	FIELD_INFO(window_flags);
+	break;
+    default:
+	return PJMEDIA_EVID_INVCAP;
+    }
+
+#undef FIELD_INFO
+
+    return PJ_SUCCESS;
+}
+
+/* API: set cap value to param */
+PJ_DEF(pj_status_t)
+pjmedia_vid_dev_param_set_cap( pjmedia_vid_dev_param *param,
+			       pjmedia_vid_dev_cap cap,
+			       const void *pval)
+{
+    void *cap_ptr;
+    unsigned cap_size;
+    pj_status_t status;
+
+    status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pj_memcpy(cap_ptr, pval, cap_size);
+    param->flags |= cap;
+
+    return PJ_SUCCESS;
+}
+
+/* API: get cap value from param */
+PJ_DEF(pj_status_t)
+pjmedia_vid_dev_param_get_cap( const pjmedia_vid_dev_param *param,
+			       pjmedia_vid_dev_cap cap,
+			       void *pval)
+{
+    void *cap_ptr;
+    unsigned cap_size;
+    pj_status_t status;
+
+    status = get_cap_pointer(param, cap, &cap_ptr, &cap_size);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if ((param->flags & cap) == 0) {
+	pj_bzero(cap_ptr, cap_size);
+	return PJMEDIA_EVID_INVCAP;
+    }
+
+    pj_memcpy(pval, cap_ptr, cap_size);
+    return PJ_SUCCESS;
+}
+
+/* Internal: init driver */
+static pj_status_t init_driver(unsigned drv_idx, pj_bool_t refresh)
+{
+    struct driver *drv = &vid_subsys.drv[drv_idx];
+    pjmedia_vid_dev_factory *f;
+    unsigned i, dev_cnt;
+    pj_status_t status;
+
+    if (!refresh) {
+        /* Create the factory */
+        f = (*drv->create)(vid_subsys.pf);
+        if (!f)
+            return PJ_EUNKNOWN;
+
+        /* Call factory->init() */
+        status = f->op->init(f);
+        if (status != PJ_SUCCESS) {
+            f->op->destroy(f);
+            return status;
+        }
+    } else {
+	f = drv->f;
+    }
+
+    /* Get number of devices */
+    dev_cnt = f->op->get_dev_count(f);
+    if (dev_cnt + vid_subsys.dev_cnt > MAX_DEVS) {
+	PJ_LOG(4,(THIS_FILE, "%d device(s) cannot be registered because"
+			      " there are too many devices",
+			      vid_subsys.dev_cnt + dev_cnt - MAX_DEVS));
+	dev_cnt = MAX_DEVS - vid_subsys.dev_cnt;
+    }
+
+    /* enabling this will cause pjsua-lib initialization to fail when there
+     * is no video device installed in the system, even when pjsua has been
+     * run with --null-video
+     *
+    if (dev_cnt == 0) {
+	f->op->destroy(f);
+	return PJMEDIA_EVID_NODEV;
+    }
+    */
+
+    /* Fill in default devices */
+    drv->rend_dev_idx = drv->cap_dev_idx = -1;
+    for (i=0; i<dev_cnt; ++i) {
+	pjmedia_vid_dev_info info;
+
+	status = f->op->get_dev_info(f, i, &info);
+	if (status != PJ_SUCCESS) {
+	    f->op->destroy(f);
+	    return status;
+	}
+
+	if (drv->name[0]=='\0') {
+	    /* Set driver name */
+	    pj_ansi_strncpy(drv->name, info.driver, sizeof(drv->name));
+	    drv->name[sizeof(drv->name)-1] = '\0';
+	}
+
+	if (drv->rend_dev_idx < 0 && (info.dir & PJMEDIA_DIR_RENDER)) {
+	    /* Set default render device */
+	    drv->rend_dev_idx = i;
+	}
+	if (drv->cap_dev_idx < 0 && (info.dir & PJMEDIA_DIR_CAPTURE)) {
+	    /* Set default capture device */
+	    drv->cap_dev_idx = i;
+	}
+
+        if (drv->rend_dev_idx >= 0 && drv->cap_dev_idx >= 0) {
+	    /* Done. */
+	    break;
+	}
+    }
+
+    /* Register the factory */
+    drv->f = f;
+    drv->f->sys.drv_idx = drv_idx;
+    drv->start_idx = vid_subsys.dev_cnt;
+    drv->dev_cnt = dev_cnt;
+
+    /* Register devices to global list */
+    for (i=0; i<dev_cnt; ++i) {
+	vid_subsys.dev_list[vid_subsys.dev_cnt++] = MAKE_DEV_ID(drv_idx, i);
+    }
+
+    return PJ_SUCCESS;
+}
+
+/* Internal: deinit driver */
+static void deinit_driver(unsigned drv_idx)
+{
+    struct driver *drv = &vid_subsys.drv[drv_idx];
+
+    if (drv->f) {
+	drv->f->op->destroy(drv->f);
+	drv->f = NULL;
+    }
+
+    drv->dev_cnt = 0;
+    drv->rend_dev_idx = drv->cap_dev_idx = -1;
+}
+
+/* API: Initialize the video device subsystem. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_subsys_init(pj_pool_factory *pf)
+{
+    unsigned i;
+    pj_status_t status = PJ_SUCCESS;
+
+    /* Allow init() to be called multiple times as long as there is matching
+     * number of shutdown().
+     */
+    if (vid_subsys.init_count++ != 0) {
+	return PJ_SUCCESS;
+    }
+
+    /* Register error subsystem */
+    pj_register_strerror(PJMEDIA_VIDEODEV_ERRNO_START, 
+			 PJ_ERRNO_SPACE_SIZE, 
+			 &pjmedia_videodev_strerror);
+
+    /* Init */
+    vid_subsys.pf = pf;
+    vid_subsys.drv_cnt = 0;
+    vid_subsys.dev_cnt = 0;
+
+    /* Register creation functions */
+#if PJMEDIA_VIDEO_DEV_HAS_V4L2
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_v4l2_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_QT
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_qt_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_IOS
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_ios_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_DSHOW
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_dshow_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_FFMPEG
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_ffmpeg_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_CBAR_SRC
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_cbar_factory;
+#endif
+#if PJMEDIA_VIDEO_DEV_HAS_SDL
+    vid_subsys.drv[vid_subsys.drv_cnt++].create = &pjmedia_sdl_factory;
+#endif
+
+    /* Initialize each factory and build the device ID list */
+    for (i=0; i<vid_subsys.drv_cnt; ++i) {
+	status = init_driver(i, PJ_FALSE);
+	if (status != PJ_SUCCESS) {
+	    deinit_driver(i);
+	    continue;
+	}
+    }
+
+    return vid_subsys.dev_cnt ? PJ_SUCCESS : status;
+}
+
+/* API: register a video device factory to the video device subsystem. */
+PJ_DEF(pj_status_t)
+pjmedia_vid_register_factory(pjmedia_vid_dev_factory_create_func_ptr adf,
+                             pjmedia_vid_dev_factory *factory)
+{
+    pj_bool_t refresh = PJ_FALSE;
+    pj_status_t status;
+
+    if (vid_subsys.init_count == 0)
+	return PJMEDIA_EVID_INIT;
+
+    vid_subsys.drv[vid_subsys.drv_cnt].create = adf;
+    vid_subsys.drv[vid_subsys.drv_cnt].f = factory;
+
+    if (factory) {
+        /* Call factory->init() */
+        status = factory->op->init(factory);
+        if (status != PJ_SUCCESS) {
+            factory->op->destroy(factory);
+            return status;
+        }
+        refresh = PJ_TRUE;
+    }
+
+    status = init_driver(vid_subsys.drv_cnt, refresh);
+    if (status == PJ_SUCCESS) {
+	vid_subsys.drv_cnt++;
+    } else {
+	deinit_driver(vid_subsys.drv_cnt);
+    }
+
+    return status;
+}
+
+/* API: unregister a video device factory from the video device subsystem. */
+PJ_DEF(pj_status_t)
+pjmedia_vid_unregister_factory(pjmedia_vid_dev_factory_create_func_ptr adf,
+                               pjmedia_vid_dev_factory *factory)
+{
+    unsigned i, j;
+
+    if (vid_subsys.init_count == 0)
+	return PJMEDIA_EVID_INIT;
+
+    for (i=0; i<vid_subsys.drv_cnt; ++i) {
+	struct driver *drv = &vid_subsys.drv[i];
+
+	if ((factory && drv->f==factory) || (adf && drv->create == adf)) {
+	    for (j = drv->start_idx; j < drv->start_idx + drv->dev_cnt; j++)
+	    {
+		vid_subsys.dev_list[j] = (pj_uint32_t)PJMEDIA_VID_INVALID_DEV;
+	    }
+
+	    deinit_driver(i);
+	    pj_bzero(drv, sizeof(*drv));
+	    return PJ_SUCCESS;
+	}
+    }
+
+    return PJMEDIA_EVID_ERR;
+}
+
+/* API: get the pool factory registered to the video device subsystem. */
+PJ_DEF(pj_pool_factory*) pjmedia_vid_dev_subsys_get_pool_factory(void)
+{
+    return vid_subsys.pf;
+}
+
+/* API: Shutdown the video device subsystem. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_subsys_shutdown(void)
+{
+    unsigned i;
+
+    /* Allow shutdown() to be called multiple times as long as there is
+     * matching number of init().
+     */
+    if (vid_subsys.init_count == 0) {
+	return PJ_SUCCESS;
+    }
+    --vid_subsys.init_count;
+
+    if (vid_subsys.init_count == 0) {
+        for (i=0; i<vid_subsys.drv_cnt; ++i) {
+	    deinit_driver(i);
+        }
+
+        vid_subsys.pf = NULL;
+    }
+    return PJ_SUCCESS;
+}
+
+/* API: Refresh the list of video devices installed in the system. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_refresh(void)
+{
+    unsigned i;
+    
+    vid_subsys.dev_cnt = 0;
+    for (i=0; i<vid_subsys.drv_cnt; ++i) {
+	struct driver *drv = &vid_subsys.drv[i];
+	
+	if (drv->f && drv->f->op->refresh) {
+	    pj_status_t status = drv->f->op->refresh(drv->f);
+	    if (status != PJ_SUCCESS) {
+		PJ_PERROR(4, (THIS_FILE, status, "Unable to refresh device "
+						 "list for %s", drv->name));
+	    }
+	}
+	init_driver(i, PJ_TRUE);
+    }
+    return PJ_SUCCESS;
+}
+
+/* API: Get the number of video devices installed in the system. */
+PJ_DEF(unsigned) pjmedia_vid_dev_count(void)
+{
+    return vid_subsys.dev_cnt;
+}
+
+/* Internal: convert local index to global device index */
+static pj_status_t make_global_index(unsigned drv_idx, 
+				     pjmedia_vid_dev_index *id)
+{
+    if (*id < 0) {
+	return PJ_SUCCESS;
+    }
+
+    /* Check that factory still exists */
+    PJ_ASSERT_RETURN(vid_subsys.drv[drv_idx].f, PJ_EBUG);
+
+    /* Check that device index is valid */
+    PJ_ASSERT_RETURN(*id>=0 && *id<(int)vid_subsys.drv[drv_idx].dev_cnt, 
+		     PJ_EBUG);
+
+    *id += vid_subsys.drv[drv_idx].start_idx;
+    return PJ_SUCCESS;
+}
+
+/* Internal: lookup device id */
+static pj_status_t lookup_dev(pjmedia_vid_dev_index id,
+			      pjmedia_vid_dev_factory **p_f,
+			      unsigned *p_local_index)
+{
+    int f_id, index;
+
+    if (id < 0) {
+	unsigned i;
+
+	if (id <= PJMEDIA_VID_INVALID_DEV)
+	    return PJMEDIA_EVID_INVDEV;
+
+	for (i=0; i<vid_subsys.drv_cnt; ++i) {
+	    struct driver *drv = &vid_subsys.drv[i];
+	    if (id==PJMEDIA_VID_DEFAULT_CAPTURE_DEV && 
+		drv->cap_dev_idx >= 0) 
+	    {
+		id = drv->cap_dev_idx;
+		make_global_index(i, &id);
+		break;
+	    } else if (id==PJMEDIA_VID_DEFAULT_RENDER_DEV && 
+		drv->rend_dev_idx >= 0) 
+	    {
+		id = drv->rend_dev_idx;
+		make_global_index(i, &id);
+		break;
+	    }
+	}
+
+	if (id < 0) {
+	    return PJMEDIA_EVID_NODEFDEV;
+	}
+    }
+
+    f_id = GET_FID(vid_subsys.dev_list[id]);
+    index = GET_INDEX(vid_subsys.dev_list[id]);
+
+    if (f_id < 0 || f_id >= (int)vid_subsys.drv_cnt)
+	return PJMEDIA_EVID_INVDEV;
+
+    if (index < 0 || index >= (int)vid_subsys.drv[f_id].dev_cnt)
+	return PJMEDIA_EVID_INVDEV;
+
+    *p_f = vid_subsys.drv[f_id].f;
+    *p_local_index = (unsigned)index;
+
+    return PJ_SUCCESS;
+
+}
+
+/* API: lookup device id */
+PJ_DEF(pj_status_t)
+pjmedia_vid_dev_get_local_index(pjmedia_vid_dev_index id,
+                                pjmedia_vid_dev_factory **p_f,
+                                unsigned *p_local_index)
+{
+    return lookup_dev(id, p_f, p_local_index);
+}
+
+/* API: from factory and local index, get global index */
+PJ_DEF(pj_status_t)
+pjmedia_vid_dev_get_global_index(const pjmedia_vid_dev_factory *f,
+                                 unsigned local_idx,
+                                 pjmedia_vid_dev_index *pid)
+{
+    PJ_ASSERT_RETURN(f->sys.drv_idx >= 0 && f->sys.drv_idx < MAX_DRIVERS,
+                     PJ_EINVALIDOP);
+    *pid = local_idx;
+    return make_global_index(f->sys.drv_idx, pid);
+}
+
+/* API: Get device information. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_get_info(pjmedia_vid_dev_index id,
+					     pjmedia_vid_dev_info *info)
+{
+    pjmedia_vid_dev_factory *f;
+    unsigned index;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(info, PJ_EINVAL);
+    PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+
+    if (id <= PJMEDIA_VID_INVALID_DEV)
+	return PJMEDIA_EVID_INVDEV;
+
+    status = lookup_dev(id, &f, &index);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = f->op->get_dev_info(f, index, info);
+
+    /* Make sure device ID is the real ID (not PJMEDIA_VID_DEFAULT_*_DEV) */
+    info->id = index;
+    make_global_index(f->sys.drv_idx, &info->id);
+
+    return status;
+}
+
+/* API: find device */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_lookup( const char *drv_name,
+					    const char *dev_name,
+					    pjmedia_vid_dev_index *id)
+{
+    pjmedia_vid_dev_factory *f = NULL;
+    unsigned drv_idx, dev_idx;
+
+    PJ_ASSERT_RETURN(drv_name && dev_name && id, PJ_EINVAL);
+    PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+
+    for (drv_idx=0; drv_idx<vid_subsys.drv_cnt; ++drv_idx) {
+	if (!pj_ansi_stricmp(drv_name, vid_subsys.drv[drv_idx].name))
+	{
+	    f = vid_subsys.drv[drv_idx].f;
+	    break;
+	}
+    }
+
+    if (!f)
+	return PJ_ENOTFOUND;
+
+    for (dev_idx=0; dev_idx<vid_subsys.drv[drv_idx].dev_cnt; ++dev_idx)
+    {
+	pjmedia_vid_dev_info info;
+	pj_status_t status;
+
+	status = f->op->get_dev_info(f, dev_idx, &info);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	if (!pj_ansi_stricmp(dev_name, info.name))
+	    break;
+    }
+
+    if (dev_idx==vid_subsys.drv[drv_idx].dev_cnt)
+	return PJ_ENOTFOUND;
+
+    *id = dev_idx;
+    make_global_index(drv_idx, id);
+
+    return PJ_SUCCESS;
+}
+
+/* API: Initialize the video device parameters with default values for the
+ * specified device.
+ */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_default_param(pj_pool_t *pool,
+                                                  pjmedia_vid_dev_index id,
+						  pjmedia_vid_dev_param *param)
+{
+    pjmedia_vid_dev_factory *f;
+    unsigned index;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(param, PJ_EINVAL);
+    PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+
+    if (id <= PJMEDIA_VID_INVALID_DEV)
+	return PJMEDIA_EVID_INVDEV;
+
+    status = lookup_dev(id, &f, &index);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = f->op->default_param(pool, f, index, param);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Normalize device IDs */
+    make_global_index(f->sys.drv_idx, &param->cap_id);
+    make_global_index(f->sys.drv_idx, &param->rend_id);
+
+    return PJ_SUCCESS;
+}
+
+/* API: Open video stream object using the specified parameters. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_create(
+					pjmedia_vid_dev_param *prm,
+					const pjmedia_vid_dev_cb *cb,
+					void *user_data,
+					pjmedia_vid_dev_stream **p_vid_strm)
+{
+    pjmedia_vid_dev_factory *cap_f=NULL, *rend_f=NULL, *f=NULL;
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(prm && prm->dir && p_vid_strm, PJ_EINVAL);
+    PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+    PJ_ASSERT_RETURN(prm->dir==PJMEDIA_DIR_CAPTURE ||
+		     prm->dir==PJMEDIA_DIR_RENDER ||
+		     prm->dir==PJMEDIA_DIR_CAPTURE_RENDER,
+		     PJ_EINVAL);
+
+    /* Normalize cap_id */
+    if (prm->dir & PJMEDIA_DIR_CAPTURE) {
+	unsigned index;
+
+	if (prm->cap_id < 0)
+	    prm->cap_id = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
+
+	status = lookup_dev(prm->cap_id, &cap_f, &index);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	prm->cap_id = index;
+	f = cap_f;
+    }
+
+    /* Normalize rend_id */
+    if (prm->dir & PJMEDIA_DIR_RENDER) {
+	unsigned index;
+
+	if (prm->rend_id < 0)
+	    prm->rend_id = PJMEDIA_VID_DEFAULT_RENDER_DEV;
+
+	status = lookup_dev(prm->rend_id, &rend_f, &index);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	prm->rend_id = index;
+	f = rend_f;
+    }
+
+    PJ_ASSERT_RETURN(f != NULL, PJ_EBUG);
+
+    /* For now, cap_id and rend_id must belong to the same factory */
+    PJ_ASSERT_RETURN((prm->dir != PJMEDIA_DIR_CAPTURE_RENDER) ||
+		     (cap_f == rend_f),
+		     PJMEDIA_EVID_INVDEV);
+
+    /* Create the stream */
+    status = f->op->create_stream(f, prm, cb,
+				  user_data, p_vid_strm);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Assign factory id to the stream */
+    (*p_vid_strm)->sys.drv_idx = f->sys.drv_idx;
+    return PJ_SUCCESS;
+}
+
+/* API: Get the running parameters for the specified video stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_param(
+					    pjmedia_vid_dev_stream *strm,
+					    pjmedia_vid_dev_param *param)
+{
+    pj_status_t status;
+
+    PJ_ASSERT_RETURN(strm && param, PJ_EINVAL);
+    PJ_ASSERT_RETURN(vid_subsys.pf, PJMEDIA_EVID_INIT);
+
+    status = strm->op->get_param(strm, param);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Normalize device id's */
+    make_global_index(strm->sys.drv_idx, &param->cap_id);
+    make_global_index(strm->sys.drv_idx, &param->rend_id);
+
+    return PJ_SUCCESS;
+}
+
+/* API: Get the value of a specific capability of the video stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_cap(
+					    pjmedia_vid_dev_stream *strm,
+					    pjmedia_vid_dev_cap cap,
+					    void *value)
+{
+    return strm->op->get_cap(strm, cap, value);
+}
+
+/* API: Set the value of a specific capability of the video stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_set_cap(
+					    pjmedia_vid_dev_stream *strm,
+					    pjmedia_vid_dev_cap cap,
+					    const void *value)
+{
+    return strm->op->set_cap(strm, cap, value);
+}
+
+/* API: Start the stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_start(pjmedia_vid_dev_stream *strm)
+{
+    pj_status_t status;
+
+    if (pjmedia_vid_dev_stream_is_running(strm))
+	return PJ_SUCCESS;
+
+    status = strm->op->start(strm);
+    if (status == PJ_SUCCESS)
+	strm->sys.is_running = PJ_TRUE;
+    return status;
+}
+
+/* API: has it been started? */
+PJ_DEF(pj_bool_t)
+pjmedia_vid_dev_stream_is_running(pjmedia_vid_dev_stream *strm)
+{
+    return strm->sys.is_running;
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_get_frame(
+					    pjmedia_vid_dev_stream *strm,
+					    pjmedia_frame *frame)
+{
+    pj_assert(strm->op->get_frame);
+    return strm->op->get_frame(strm, frame);
+}
+
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_put_frame(
+					    pjmedia_vid_dev_stream *strm,
+                                            const pjmedia_frame *frame)
+{
+    pj_assert(strm->op->put_frame);
+    return strm->op->put_frame(strm, frame);
+}
+
+/* API: Stop the stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_stop(pjmedia_vid_dev_stream *strm)
+{
+    strm->sys.is_running = PJ_FALSE;
+    return strm->op->stop(strm);
+}
+
+/* API: Destroy the stream. */
+PJ_DEF(pj_status_t) pjmedia_vid_dev_stream_destroy(
+						pjmedia_vid_dev_stream *strm)
+{
+    strm->sys.is_running = PJ_FALSE;
+    return strm->op->destroy(strm);
+}
+
+
+#endif /* PJMEDIA_HAS_VIDEO */