Re #1201: Added pjsua API for video devices and codecs management.



git-svn-id: https://svn.pjsip.org/repos/pjproject/branches/projects/2.0-dev@3471 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 304b8cb..436c5f5 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -634,6 +634,10 @@
     pjsua_var.cap_dev = PJMEDIA_AUD_DEFAULT_CAPTURE_DEV;
     pjsua_var.play_dev = PJMEDIA_AUD_DEFAULT_PLAYBACK_DEV;
 
+    /* Set default video device ID */
+    pjsua_var.vcap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
+    pjsua_var.vrdr_dev = PJMEDIA_VID_DEFAULT_RENDER_DEV;
+
     /* Init caching pool. */
     pj_caching_pool_init(&pjsua_var.cp, NULL, 0);
 
diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c
index fec7d0a..160b775 100644
--- a/pjsip/src/pjsua-lib/pjsua_media.c
+++ b/pjsip/src/pjsua-lib/pjsua_media.c
@@ -58,6 +58,11 @@
     pj_stun_auth_cred_dup(pool, &dst->turn_auth_cred, &src->turn_auth_cred);
 }
 
+
+PJ_DECL(pj_status_t)
+pjmedia_libswscale_converter_init(pjmedia_converter_mgr *mgr,
+				  pj_pool_t *pool);
+
 /**
  * Init media subsystems.
  */
@@ -314,6 +319,15 @@
     }
 #endif
 
+#if PJMEDIA_HAS_VIDEO && PJMEDIA_HAS_LIBSWSCALE && PJMEDIA_HAS_LIBAVUTIL
+    status = pjmedia_libswscale_converter_init(NULL, pjsua_var.pool);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error initializing libswscale converter",
+		     status);
+	return status;
+    }
+#endif
+
     /* Save additional conference bridge parameters for future
      * reference.
      */
@@ -1836,7 +1850,10 @@
 		pjmedia_stream_destroy(strm);
 		call_med->strm.a.stream = NULL;
 	    }
-	} else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
+	}
+
+#if PJMEDIA_HAS_VIDEO
+	else if (call_med->type == PJMEDIA_TYPE_VIDEO) {
 	    pjmedia_vid_stream *strm = call_med->strm.v.stream;
 
 	    if (strm) {
@@ -1872,6 +1889,7 @@
 		call_med->strm.v.stream = NULL;
 	    }
 	}
+#endif
 
 	PJ_LOG(4,(THIS_FILE, "Media session call%02d:%d is destroyed",
 			     call_id, mi));
@@ -2133,6 +2151,9 @@
     return PJ_SUCCESS;
 }
 
+
+#if PJMEDIA_HAS_VIDEO
+
 static pj_status_t video_channel_update(pjsua_call_media *call_med,
                                         pj_pool_t *tmp_pool,
 				        const pjmedia_sdp_session *local_sdp,
@@ -2211,6 +2232,34 @@
 	si->use_ka = pjsua_var.acc[call->acc_id].cfg.use_stream_ka;
 #endif
 
+	/* Try to get shared format ID between the capture device and 
+	 * the encoder to avoid format conversion in the capture device.
+	 */
+	if (si->dir & PJMEDIA_DIR_ENCODING) {
+	    pjmedia_vid_dev_info dev_info;
+	    pjmedia_vid_codec_info *codec_info = &si->codec_info;
+	    unsigned i, j;
+
+	    status = pjmedia_vid_dev_get_info(pjsua_var.vcap_dev, &dev_info);
+	    if (status != PJ_SUCCESS)
+		return status;
+
+	    /* Find matched format ID */
+	    for (i = 0; i < codec_info->dec_fmt_id_cnt; ++i) {
+		for (j = 0; j < dev_info.fmt_cnt; ++j) {
+		    if (codec_info->dec_fmt_id[i] == 
+			(pjmedia_format_id)dev_info.fmt[j].id)
+		    {
+			/* Apply the matched format ID to the codec */
+			si->codec_param->dec_fmt.id = codec_info->dec_fmt_id[i];
+			/* Force outer loop to break */
+			i = codec_info->dec_fmt_id_cnt;
+			break;
+		    }
+		}
+	    }
+	}
+
 	/* Create session based on session info. */
 	status = pjmedia_vid_stream_create(pjsua_var.med_endpt, NULL, si,
 					   call_med->tp, NULL,
@@ -2234,7 +2283,7 @@
 		return status;
 
 	    status = pjmedia_vid_dev_default_param(
-				tmp_pool, PJMEDIA_VID_DEFAULT_RENDER_DEV,
+				tmp_pool, pjsua_var.vrdr_dev,
 				&vport_param.vidparam);
 	    if (status != PJ_SUCCESS)
 		return status;
@@ -2274,14 +2323,13 @@
 		return status;
 
 	    status = pjmedia_vid_dev_default_param(
-				tmp_pool, PJMEDIA_VID_DEFAULT_CAPTURE_DEV,
+				tmp_pool, pjsua_var.vcap_dev,
 				&vport_param.vidparam);
 	    if (status != PJ_SUCCESS)
 		return status;
 
 	    pjmedia_format_copy(&vport_param.vidparam.fmt,
 				&media_port->info.fmt);
-
 	    vport_param.vidparam.dir = PJMEDIA_DIR_CAPTURE;
 	    vport_param.active = PJ_TRUE;
 
@@ -2352,6 +2400,8 @@
     return PJ_SUCCESS;
 }
 
+#endif
+
 
 pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
 				       const pjmedia_sdp_session *local_sdp,
@@ -2394,10 +2444,12 @@
 		call->audio_idx = mi;
 	    }
 	    break;
+#if PJMEDIA_HAS_VIDEO
 	case PJMEDIA_TYPE_VIDEO:
 	    status = video_channel_update(call_med, tmp_pool,
 	                                  local_sdp, remote_sdp);
 	    break;
+#endif
 	default:
 	    break;
 	}
@@ -3771,7 +3823,7 @@
 	return status;
 
     if (count != 1)
-	return PJ_ENOTFOUND;
+	return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
 
     status = pjmedia_codec_mgr_get_default_param( codec_mgr, info, param);
     return status;
@@ -3808,3 +3860,213 @@
     status = pjmedia_codec_mgr_set_default_param(codec_mgr, info[0], param);
     return status;
 }
+
+
+#if PJMEDIA_HAS_VIDEO
+
+/*****************************************************************************
+ * Video codecs.
+ */
+
+/*
+ * Enum all supported video codecs in the system.
+ */
+PJ_DEF(pj_status_t) pjsua_vid_enum_codecs( pjsua_codec_info id[],
+					   unsigned *p_count )
+{
+    pjmedia_vid_codec_info info[32];
+    unsigned i, j, count, prio[32];
+    pj_status_t status;
+
+    count = PJ_ARRAY_SIZE(info);
+    status = pjmedia_vid_codec_mgr_enum_codecs(NULL, &count, info, prio);
+    if (status != PJ_SUCCESS) {
+	*p_count = 0;
+	return status;
+    }
+
+    for (i=0, j=0; i<count && j<*p_count; ++i) {
+	if (info[i].has_rtp_pack) {
+	    pjmedia_vid_codec_info_to_id(&info[i], id[j].buf_, sizeof(id[j].buf_));
+	    id[j].codec_id = pj_str(id[j].buf_);
+	    id[j].priority = (pj_uint8_t) prio[i];
+	    ++j;
+	}
+    }
+
+    *p_count = j;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Change video codec priority.
+ */
+PJ_DEF(pj_status_t) pjsua_vid_codec_set_priority( const pj_str_t *codec_id,
+						  pj_uint8_t priority )
+{
+    const pj_str_t all = { NULL, 0 };
+
+    if (codec_id->slen==1 && *codec_id->ptr=='*')
+	codec_id = &all;
+
+    return pjmedia_vid_codec_mgr_set_codec_priority(NULL, codec_id,
+						    priority);
+}
+
+
+/*
+ * Get video codec parameters.
+ */
+PJ_DEF(pj_status_t) pjsua_vid_codec_get_param(
+					const pj_str_t *codec_id,
+					pjmedia_vid_codec_param *param)
+{
+    const pj_str_t all = { NULL, 0 };
+    const pjmedia_vid_codec_info *info;
+    unsigned count = 1;
+    pj_status_t status;
+
+    if (codec_id->slen==1 && *codec_id->ptr=='*')
+	codec_id = &all;
+
+    status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
+						     &count, &info, NULL);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    if (count != 1)
+	return (count > 1? PJ_ETOOMANY : PJ_ENOTFOUND);
+
+    status = pjmedia_vid_codec_mgr_get_default_param(NULL, info, param);
+    return status;
+}
+
+
+/*
+ * Set video codec parameters.
+ */
+PJ_DEF(pj_status_t) pjsua_vid_codec_set_param( 
+					const pj_str_t *codec_id,
+					const pjmedia_vid_codec_param *param)
+{
+    const pjmedia_vid_codec_info *info[2];
+    unsigned count = 2;
+    pj_status_t status;
+
+    status = pjmedia_vid_codec_mgr_find_codecs_by_id(NULL, codec_id,
+						     &count, info, NULL);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    /* Codec ID should be specific */
+    if (count > 1) {
+	pj_assert(!"Codec ID is not specific");
+	return PJ_ETOOMANY;
+    }
+
+    status = pjmedia_vid_codec_mgr_set_default_param(NULL, pjsua_var.pool,
+						     info[0], param);
+    return status;
+}
+
+
+/*****************************************************************************
+ * Video devices.
+ */
+
+/*
+ * Enum all video devices installed in the system.
+ */
+PJ_DEF(pj_status_t) pjsua_vid_enum_devs(pjmedia_vid_dev_info info[],
+					unsigned *count)
+{
+    unsigned i, dev_count;
+
+    dev_count = pjmedia_vid_dev_count();
+    
+    if (dev_count > *count) dev_count = *count;
+
+    for (i=0; i<dev_count; ++i) {
+	pj_status_t status;
+
+	status = pjmedia_vid_dev_get_info(i, &info[i]);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    *count = dev_count;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get currently active video devices.
+ */
+PJ_DEF(pj_status_t) pjsua_vid_get_dev(int *capture_dev, int *render_dev)
+{
+    if (capture_dev)
+	*capture_dev = pjsua_var.vcap_dev;
+    if (render_dev)
+	*render_dev = pjsua_var.vrdr_dev;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Select video device for the next video sessions.
+ */
+PJ_DEF(pj_status_t) pjsua_vid_set_dev(int capture_dev, int render_dev)
+{
+    pjmedia_vid_dev_info info;
+    pj_status_t status;
+
+    if (capture_dev < 0)
+	capture_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV;
+    if (render_dev < 0)
+	render_dev = PJMEDIA_VID_DEFAULT_RENDER_DEV;
+
+    status = pjmedia_vid_dev_get_info(capture_dev, &info);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    status = pjmedia_vid_dev_get_info(render_dev, &info);
+    if (status != PJ_SUCCESS)
+	return status;
+
+    pjsua_var.vcap_dev = capture_dev;
+    pjsua_var.vrdr_dev = render_dev;
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Configure video device setting to the video device being used.
+ */
+PJ_DECL(pj_status_t) pjsua_vid_set_setting(pjmedia_vid_dev_cap cap,
+					   const void *pval,
+					   pj_bool_t keep)
+{
+    PJ_UNUSED_ARG(cap);
+    PJ_UNUSED_ARG(pval);
+    PJ_UNUSED_ARG(keep);
+    return PJ_ENOTSUP;
+}
+
+
+/*
+ * Retrieve a video device setting.
+ */
+PJ_DECL(pj_status_t) pjsua_vid_get_setting(pjmedia_aud_dev_cap cap,
+					   void *pval)
+{
+    PJ_UNUSED_ARG(cap);
+    PJ_UNUSED_ARG(pval);
+    return PJ_ENOTSUP;
+}
+
+#endif /* PJMEDIA_HAS_VIDEO */