Re #1312: Support for refreshing video device list
Add pjmedia-videodev API: pjmedia_vid_dev_refresh() and its implementation for Windows and Linux.



git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/2.0-dev@3592 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjmedia/include/pjmedia-videodev/videodev.h b/pjmedia/include/pjmedia-videodev/videodev.h
index 42f25f9..ef3a779 100644
--- a/pjmedia/include/pjmedia-videodev/videodev.h
+++ b/pjmedia/include/pjmedia-videodev/videodev.h
@@ -445,6 +445,20 @@
 
 
 /**
+ * Refresh the list of video devices installed in the system. This function
+ * will only refresh the list of videoo device so all active video streams will
+ * be unaffected. After refreshing the device list, application MUST make sure
+ * to update all index references to video devices (i.e. all variables of type
+ * pjmedia_vid_dev_index) before calling any function that accepts video device
+ * index as its parameter.
+ *
+ * @return		PJ_SUCCESS on successful operation or the appropriate
+ *			error code.
+ */
+PJ_DECL(pj_status_t) pjmedia_vid_dev_refresh(void);
+
+
+/**
  * Get the number of video devices installed in the system.
  *
  * @return          The number of video devices installed in the system.
diff --git a/pjmedia/include/pjmedia-videodev/videodev_imp.h b/pjmedia/include/pjmedia-videodev/videodev_imp.h
index 254044b..4b32d13 100644
--- a/pjmedia/include/pjmedia-videodev/videodev_imp.h
+++ b/pjmedia/include/pjmedia-videodev/videodev_imp.h
@@ -91,6 +91,13 @@
 				 void *user_data,
 				 pjmedia_vid_dev_stream **p_vid_strm);
 
+    /**
+     * Refresh the list of video devices installed in the system.
+     *
+     * @param f		The video device factory.
+     */
+    pj_status_t (*refresh)(pjmedia_vid_dev_factory *f);
+
 } pjmedia_vid_dev_factory_op;
 
 
diff --git a/pjmedia/src/pjmedia-videodev/colorbar_dev.c b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
index 8c2e41e..7866e39 100644
--- a/pjmedia/src/pjmedia-videodev/colorbar_dev.c
+++ b/pjmedia/src/pjmedia-videodev/colorbar_dev.c
@@ -99,6 +99,7 @@
 /* Prototypes */
 static pj_status_t cbar_factory_init(pjmedia_vid_dev_factory *f);
 static pj_status_t cbar_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t cbar_factory_refresh(pjmedia_vid_dev_factory *f); 
 static unsigned    cbar_factory_get_dev_count(pjmedia_vid_dev_factory *f);
 static pj_status_t cbar_factory_get_dev_info(pjmedia_vid_dev_factory *f,
 					     unsigned index,
@@ -136,7 +137,8 @@
     &cbar_factory_get_dev_count,
     &cbar_factory_get_dev_info,
     &cbar_factory_default_param,
-    &cbar_factory_create_stream
+    &cbar_factory_create_stream,
+    &cbar_factory_refresh
 };
 
 static pjmedia_vid_dev_stream_op stream_op =
@@ -228,6 +230,13 @@
     return PJ_SUCCESS;
 }
 
+/* API: refresh the list of devices */
+static pj_status_t cbar_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+    PJ_UNUSED_ARG(f);
+    return PJ_SUCCESS;
+}
+
 /* API: get number of devices */
 static unsigned cbar_factory_get_dev_count(pjmedia_vid_dev_factory *f)
 {
diff --git a/pjmedia/src/pjmedia-videodev/dshow_dev.c b/pjmedia/src/pjmedia-videodev/dshow_dev.c
index ff31b3f..5e316ff 100644
--- a/pjmedia/src/pjmedia-videodev/dshow_dev.c
+++ b/pjmedia/src/pjmedia-videodev/dshow_dev.c
@@ -82,6 +82,7 @@
 {
     pjmedia_vid_dev_factory	 base;
     pj_pool_t			*pool;
+    pj_pool_t			*dev_pool;
     pj_pool_factory		*pf;
 
     unsigned			 dev_count;
@@ -123,6 +124,7 @@
 /* Prototypes */
 static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f);
 static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f);
 static unsigned    dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f);
 static pj_status_t dshow_factory_get_dev_info(pjmedia_vid_dev_factory *f,
 					      unsigned index,
@@ -160,7 +162,8 @@
     &dshow_factory_get_dev_count,
     &dshow_factory_get_dev_info,
     &dshow_factory_default_param,
-    &dshow_factory_create_stream
+    &dshow_factory_create_stream,
+    &dshow_factory_refresh
 };
 
 static pjmedia_vid_dev_stream_op stream_op =
@@ -199,6 +202,31 @@
 /* API: init factory */
 static pj_status_t dshow_factory_init(pjmedia_vid_dev_factory *f)
 {
+    CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+    return dshow_factory_refresh(f);
+}
+
+/* API: destroy factory */
+static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+    struct dshow_factory *df = (struct dshow_factory*)f;
+    pj_pool_t *pool = df->pool;
+
+    df->pool = NULL;
+    if (df->dev_pool)
+        pj_pool_release(df->dev_pool);
+    if (pool)
+        pj_pool_release(pool);
+
+    CoUninitialize();
+
+    return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t dshow_factory_refresh(pjmedia_vid_dev_factory *f)
+{
     struct dshow_factory *df = (struct dshow_factory*)f;
     struct dshow_dev_info *ddi;
     int dev_count = 0;
@@ -209,9 +237,13 @@
     HRESULT hr;
     ULONG fetched;
 
-    df->dev_count = 0;
+    if (df->dev_pool) {
+        pj_pool_release(df->dev_pool);
+        df->dev_pool = NULL;
+    }
 
-    CoInitializeEx(NULL, COINIT_MULTITHREADED);
+    df->dev_count = 0;
+    df->dev_pool = pj_pool_create(df->pf, "dshow video", 500, 500, NULL);
 
     hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL,
                           CLSCTX_INPROC_SERVER, &IID_ICreateDevEnum,
@@ -233,7 +265,7 @@
     /* Add renderer device */
     dev_count += 1;
     df->dev_info = (struct dshow_dev_info*)
- 		   pj_pool_calloc(df->pool, dev_count,
+ 		   pj_pool_calloc(df->dev_pool, dev_count,
  				  sizeof(struct dshow_dev_info));
 
     if (dev_count > 1) {
@@ -304,7 +336,7 @@
         ddi = &df->dev_info[c];
         ddi->info.fmt_cnt = sizeof(dshow_fmts)/sizeof(dshow_fmts[0]);
         ddi->info.fmt = (pjmedia_format*)
-                        pj_pool_calloc(df->pool, ddi->info.fmt_cnt,
+                        pj_pool_calloc(df->dev_pool, ddi->info.fmt_cnt,
                                        sizeof(pjmedia_format));
 
         for (i = 0; i < ddi->info.fmt_cnt; i++) {
@@ -321,7 +353,7 @@
 //    TODO:
 //    ddi->info.caps = PJMEDIA_VID_DEV_CAP_OUTPUT_WINDOW;
 
-    PJ_LOG(4, (THIS_FILE, "DShow initialized, found %d devices:", 
+    PJ_LOG(4, (THIS_FILE, "DShow has %d devices:", 
 	       df->dev_count));
     for (c = 0; c < df->dev_count; ++c) {
 	PJ_LOG(4, (THIS_FILE, " dev_id %d: %s (%s)", 
@@ -334,20 +366,6 @@
     return PJ_SUCCESS;
 }
 
-/* API: destroy factory */
-static pj_status_t dshow_factory_destroy(pjmedia_vid_dev_factory *f)
-{
-    struct dshow_factory *df = (struct dshow_factory*)f;
-    pj_pool_t *pool = df->pool;
-
-    df->pool = NULL;
-    pj_pool_release(pool);
-
-    CoUninitialize();
-
-    return PJ_SUCCESS;
-}
-
 /* API: get number of devices */
 static unsigned dshow_factory_get_dev_count(pjmedia_vid_dev_factory *f)
 {
diff --git a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
index a2017da..2016548 100644
--- a/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
+++ b/pjmedia/src/pjmedia-videodev/ffmpeg_dev.c
@@ -60,6 +60,7 @@
     pjmedia_vid_dev_factory	 base;
     pj_pool_factory		*pf;
     pj_pool_t                   *pool;
+    pj_pool_t                   *dev_pool;
     unsigned                     dev_count;
     ffmpeg_dev_info              dev_info[MAX_DEV_CNT];
 } ffmpeg_factory;
@@ -78,6 +79,7 @@
 /* Prototypes */
 static pj_status_t ffmpeg_factory_init(pjmedia_vid_dev_factory *f);
 static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f);
 static unsigned    ffmpeg_factory_get_dev_count(pjmedia_vid_dev_factory *f);
 static pj_status_t ffmpeg_factory_get_dev_info(pjmedia_vid_dev_factory *f,
 					       unsigned index,
@@ -115,7 +117,8 @@
     &ffmpeg_factory_get_dev_count,
     &ffmpeg_factory_get_dev_info,
     &ffmpeg_factory_default_param,
-    &ffmpeg_factory_create_stream
+    &ffmpeg_factory_create_stream,
+    &ffmpeg_factory_refresh
 };
 
 static pjmedia_vid_dev_stream_op stream_op =
@@ -219,6 +222,28 @@
 /* API: init factory */
 static pj_status_t ffmpeg_factory_init(pjmedia_vid_dev_factory *f)
 {
+    return ffmpeg_factory_refresh(f);
+}
+
+/* API: destroy factory */
+static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f)
+{
+    ffmpeg_factory *ff = (ffmpeg_factory*)f;
+    pj_pool_t *pool = ff->pool;
+
+    ff->dev_count = 0;
+    ff->pool = NULL;
+    if (ff->dev_pool)
+        pj_pool_release(ff->dev_pool);
+    if (pool)
+        pj_pool_release(pool);
+
+    return PJ_SUCCESS;
+}
+
+/* API: refresh the list of devices */
+static pj_status_t ffmpeg_factory_refresh(pjmedia_vid_dev_factory *f)
+{
     ffmpeg_factory *ff = (ffmpeg_factory*)f;
     AVInputFormat *p;
     ffmpeg_dev_info *info;
@@ -226,8 +251,15 @@
     av_log_set_callback(&print_ffmpeg_log);
     av_log_set_level(AV_LOG_DEBUG);
 
+    if (ff->dev_pool) {
+        pj_pool_release(ff->dev_pool);
+        ff->dev_pool = NULL;
+    }
+
     /* TODO: this should enumerate devices, now it enumerates host APIs */
     ff->dev_count = 0;
+    ff->dev_pool = pj_pool_create(ff->pf, "ffmpeg_cap_dev", 500, 500, NULL);
+
     p = av_iformat_next(NULL);
     while (p) {
         if (p->flags & AVFMT_NOFILE) {
@@ -254,7 +286,7 @@
             info->base.caps = PJMEDIA_VID_DEV_CAP_FORMAT;
             info->base.fmt_cnt = 1;
             info->base.fmt = (pjmedia_format*)
- 		             pj_pool_calloc(ff->pool, info->base.fmt_cnt,
+ 		             pj_pool_calloc(ff->dev_pool, info->base.fmt_cnt,
  				            sizeof(pjmedia_format));
             for (i = 0; i < info->base.fmt_cnt; ++i) {
                 pjmedia_format *fmt = &info->base.fmt[i];
@@ -270,16 +302,6 @@
     return PJ_SUCCESS;
 }
 
-/* API: destroy factory */
-static pj_status_t ffmpeg_factory_destroy(pjmedia_vid_dev_factory *f)
-{
-    ffmpeg_factory *ff = (ffmpeg_factory*)f;
-
-    ff->dev_count = 0;
-
-    return PJ_SUCCESS;
-}
-
 /* API: get number of devices */
 static unsigned ffmpeg_factory_get_dev_count(pjmedia_vid_dev_factory *f)
 {
diff --git a/pjmedia/src/pjmedia-videodev/sdl_dev.c b/pjmedia/src/pjmedia-videodev/sdl_dev.c
index 8c5720b..9a7e5fd 100644
--- a/pjmedia/src/pjmedia-videodev/sdl_dev.c
+++ b/pjmedia/src/pjmedia-videodev/sdl_dev.c
@@ -185,6 +185,7 @@
 /* Prototypes */
 static pj_status_t sdl_factory_init(pjmedia_vid_dev_factory *f);
 static pj_status_t sdl_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t sdl_factory_refresh(pjmedia_vid_dev_factory *f);
 static unsigned    sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f);
 static pj_status_t sdl_factory_get_dev_info(pjmedia_vid_dev_factory *f,
 					    unsigned index,
@@ -226,7 +227,8 @@
     &sdl_factory_get_dev_count,
     &sdl_factory_get_dev_info,
     &sdl_factory_default_param,
-    &sdl_factory_create_stream
+    &sdl_factory_create_stream,
+    &sdl_factory_refresh
 };
 
 static pjmedia_vid_dev_stream_op stream_op =
@@ -356,6 +358,13 @@
     return PJ_SUCCESS;
 }
 
+/* API: refresh the list of devices */
+static pj_status_t sdl_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+    PJ_UNUSED_ARG(f);
+    return PJ_SUCCESS;
+}
+
 /* API: get number of devices */
 static unsigned sdl_factory_get_dev_count(pjmedia_vid_dev_factory *f)
 {
diff --git a/pjmedia/src/pjmedia-videodev/v4l2_dev.c b/pjmedia/src/pjmedia-videodev/v4l2_dev.c
index b5909c3..281560d 100644
--- a/pjmedia/src/pjmedia-videodev/v4l2_dev.c
+++ b/pjmedia/src/pjmedia-videodev/v4l2_dev.c
@@ -81,6 +81,7 @@
 {
     pjmedia_vid_dev_factory	 base;
     pj_pool_t			*pool;
+    pj_pool_t			*dev_pool;
     pj_pool_factory		*pf;
 
     unsigned			 dev_count;
@@ -119,6 +120,7 @@
 /* Prototypes */
 static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f);
 static pj_status_t vid4lin_factory_destroy(pjmedia_vid_dev_factory *f);
+static pj_status_t vid4lin_factory_refresh(pjmedia_vid_dev_factory *f);
 static unsigned    vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f);
 static pj_status_t vid4lin_factory_get_dev_info(pjmedia_vid_dev_factory *f,
 					        unsigned index,
@@ -155,7 +157,8 @@
     &vid4lin_factory_get_dev_count,
     &vid4lin_factory_get_dev_info,
     &vid4lin_factory_default_param,
-    &vid4lin_factory_create_stream
+    &vid4lin_factory_create_stream,
+    &vid4lin_factory_refresh
 };
 
 static pjmedia_vid_dev_stream_op stream_op =
@@ -212,14 +215,20 @@
     unsigned i, old_count;
     pj_status_t status;
 
+    if (f->dev_pool) {
+        pj_pool_release(f->dev_pool);
+        f->dev_pool = NULL;
+    }
+
     pj_bzero(vdi, sizeof(vdi));
     old_count = f->dev_count;
     f->dev_count = 0;
+    f->dev_pool = pj_pool_create(f->pf, DRIVER_NAME, 500, 500, NULL);
 
     for (i=0; i<V4L2_MAX_DEVS && f->dev_count < V4L2_MAX_DEVS; ++i) {
 	int fd;
 	vid4lin_dev_info *pdi;
-	pj_pool_t *pool = f->pool;
+	pj_pool_t *pool = f->dev_pool;
 	pj_uint32_t fmt_cap[8];
 	int j, fmt_cnt=0;
 
@@ -312,7 +321,7 @@
 
     if (f->dev_count > old_count || f->dev_info == NULL) {
 	f->dev_info = (vid4lin_dev_info*)
-		      pj_pool_calloc(f->pool,
+		      pj_pool_calloc(f->dev_pool,
 				     f->dev_count,
 				     sizeof(vid4lin_dev_info));
     }
@@ -325,17 +334,7 @@
 /* API: init factory */
 static pj_status_t vid4lin_factory_init(pjmedia_vid_dev_factory *f)
 {
-    vid4lin_factory *cf = (vid4lin_factory*)f;
-    pj_status_t status;
-
-    status = v4l2_scan_devs(cf);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    PJ_LOG(4, (THIS_FILE, "Video4Linux2 initialized with %d devices",
-	       cf->dev_count));
-
-    return PJ_SUCCESS;
+    return vid4lin_factory_refresh(f);
 }
 
 /* API: destroy factory */
@@ -344,6 +343,8 @@
     vid4lin_factory *cf = (vid4lin_factory*)f;
     pj_pool_t *pool = cf->pool;
 
+    if (cf->dev_pool)
+        pj_pool_release(cf->dev_pool);
     if (cf->pool) {
 	cf->pool = NULL;
 	pj_pool_release(pool);
@@ -352,6 +353,22 @@
     return PJ_SUCCESS;
 }
 
+/* API: refresh the list of devices */
+static pj_status_t vid4lin_factory_refresh(pjmedia_vid_dev_factory *f)
+{
+    vid4lin_factory *cf = (vid4lin_factory*)f;
+    pj_status_t status;
+
+    status = v4l2_scan_devs(cf);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    PJ_LOG(4, (THIS_FILE, "Video4Linux2 has %d devices",
+	       cf->dev_count));
+
+    return PJ_SUCCESS;
+}
+
 /* API: get number of devices */
 static unsigned vid4lin_factory_get_dev_count(pjmedia_vid_dev_factory *f)
 {
diff --git a/pjmedia/src/pjmedia-videodev/videodev.c b/pjmedia/src/pjmedia-videodev/videodev.c
index 4741a40..2d65e02 100644
--- a/pjmedia/src/pjmedia-videodev/videodev.c
+++ b/pjmedia/src/pjmedia-videodev/videodev.c
@@ -210,23 +210,27 @@
 }
 
 /* Internal: init driver */
-static pj_status_t init_driver(unsigned drv_idx)
+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;
 
-    /* Create the factory */
-    f = (*drv->create)(vid_subsys.pf);
-    if (!f)
-	return PJ_EUNKNOWN;
+    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;
+        /* 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 */
@@ -356,7 +360,7 @@
 
     /* Initialize each factory and build the device ID list */
     for (i=0; i<vid_subsys.drv_cnt; ++i) {
-	status = init_driver(i);
+	status = init_driver(i, PJ_FALSE);
 	if (status != PJ_SUCCESS) {
 	    deinit_driver(i);
 	    continue;
@@ -376,7 +380,7 @@
 	return PJMEDIA_EVID_INIT;
 
     vid_subsys.drv[vid_subsys.drv_cnt].create = adf;
-    status = init_driver(vid_subsys.drv_cnt);
+    status = init_driver(vid_subsys.drv_cnt, PJ_FALSE);
     if (status == PJ_SUCCESS) {
 	vid_subsys.drv_cnt++;
     } else {
@@ -442,6 +446,27 @@
     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)
 {