From fb7ffe37b9ca728f0d3f6bf57b937e114d2f9161 Mon Sep 17 00:00:00 2001 From: Nanang Izzuddin Date: Thu, 1 Dec 2011 09:06:14 +0000 Subject: Re #1419, implement media count manipulation per call basis: - moved the media count setting from account setting to call setting - introduced pjsua_call_setting, to be used by pjsua_call_make_call() and some new APIs: pjsua_call_answer2(), pjsua_call_reinvite2(), pjsua_call_update2() git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3891 74dad513-b988-da41-8d7b-12977e46ad98 --- pjsip/include/pjsua-lib/pjsua.h | 147 +++++++++++++++++---- pjsip/include/pjsua-lib/pjsua_internal.h | 8 +- pjsip/src/pjsua-lib/pjsua_acc.c | 19 --- pjsip/src/pjsua-lib/pjsua_call.c | 216 ++++++++++++++++++++++++++++--- pjsip/src/pjsua-lib/pjsua_core.c | 1 - pjsip/src/pjsua-lib/pjsua_media.c | 211 +++++++++++++++++++----------- pjsip/src/pjsua-lib/pjsua_vid.c | 12 +- 7 files changed, 476 insertions(+), 138 deletions(-) (limited to 'pjsip') diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index b45f69f7..31fbfca1 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -2696,24 +2696,6 @@ typedef struct pjsua_acc_config */ pj_str_t ka_data; - /** - * Maximum number of simultaneous active audio streams to be allowed - * for calls on this account. Setting this to zero will disable audio - * in calls on this account. - * - * Default: 1 - */ - unsigned max_audio_cnt; - - /** - * Maximum number of simultaneous active video streams to be allowed - * for calls on this account. Setting this to zero will disable video - * in calls on this account, regardless of other video settings. - * - * Default: 1 - */ - unsigned max_video_cnt; - /** * Specify whether incoming video should be shown to screen by default. * This applies to incoming call (INVITE), incoming re-INVITE, and @@ -3388,6 +3370,37 @@ typedef enum pjsua_call_media_status } pjsua_call_media_status; +/** + * Call settings. + */ +typedef struct pjsua_call_setting +{ + /** + * Bitmask of pjsua_call_flag constants. + * + * Default: 0 + */ + unsigned flag; + + /** + * Number of simultaneous active audio streams for this call. Setting + * this to zero will disable audio in this call. + * + * Default: 1 + */ + unsigned audio_cnt; + + /** + * Number of simultaneous active video streams for this call. Setting + * this to zero will disable video in this call. + * + * Default: 1 (if video feature is enabled, otherwise it is zero) + */ + unsigned video_cnt; + +} pjsua_call_setting; + + /** * This structure describes the information and current status of a call. */ @@ -3417,6 +3430,9 @@ typedef struct pjsua_call_info /** Dialog Call-ID string. */ pj_str_t call_id; + /** Call setting */ + pjsua_call_setting setting; + /** Call state */ pjsip_inv_state state; @@ -3490,6 +3506,15 @@ typedef struct pjsua_call_info /** Total call duration, including set-up time */ pj_time_val total_duration; + /** Flag if remote was SDP offerer */ + pj_bool_t rem_offerer; + + /** Number of audio streams offered by remote */ + unsigned rem_audio_cnt; + + /** Number of video streams offered by remote */ + unsigned rem_video_cnt; + /** Internal */ struct { char local_info[128]; @@ -3525,7 +3550,14 @@ typedef enum pjsua_call_flag * session with the new Contact and to inform this new Contact to the * remote peer with the outgoing re-INVITE or UPDATE */ - PJSUA_CALL_UPDATE_CONTACT = 2 + PJSUA_CALL_UPDATE_CONTACT = 2, + + /** + * Include SDP "m=" line with port set to zero for each disabled media + * (i.e when aud_cnt or vid_cnt is set to zero). This flag is only valid + * for #pjsua_call_make_call(). + */ + PJSUA_CALL_INCLUDE_DISABLED_MEDIA = 4 } pjsua_call_flag; @@ -3578,8 +3610,7 @@ typedef enum pjsua_call_vid_strm_op * Add a new video stream. This will add a new m=video line to * the media, regardless of whether existing video is/are present * or not. This will cause re-INVITE or UPDATE to be sent to remote - * party. The number of maximum active video streams in a call is - * still limited by \a max_video_cnt setting in pjsua_acc_config. + * party. */ PJSUA_CALL_VID_STRM_ADD, @@ -3666,6 +3697,14 @@ typedef struct pjsua_call_vid_strm_op_param } pjsua_call_vid_strm_op_param; +/** + * Initialize call settings. + * + * @param opt The call setting to be initialized. + */ +PJ_DECL(void) pjsua_call_setting_default(pjsua_call_setting *opt); + + /** * Initialize video stream operation param with default values. * @@ -3709,7 +3748,8 @@ PJ_DECL(pj_status_t) pjsua_enum_calls(pjsua_call_id ids[], * @param acc_id The account to be used. * @param dst_uri URI to be put in the To header (normally is the same * as the target URI). - * @param options Options (must be zero at the moment). + * @param opt Optional call setting. This should be initialized + * using #pjsua_call_setting_default(). * @param user_data Arbitrary user data to be attached to the call, and * can be retrieved later. * @param msg_data Optional headers etc to be added to outgoing INVITE @@ -3720,7 +3760,7 @@ PJ_DECL(pj_status_t) pjsua_enum_calls(pjsua_call_id ids[], */ PJ_DECL(pj_status_t) pjsua_call_make_call(pjsua_acc_id acc_id, const pj_str_t *dst_uri, - unsigned options, + const pjsua_call_setting *opt, void *user_data, const pjsua_msg_data *msg_data, pjsua_call_id *p_call_id); @@ -3862,6 +3902,29 @@ PJ_DECL(pj_status_t) pjsua_call_answer(pjsua_call_id call_id, const pj_str_t *reason, const pjsua_msg_data *msg_data); + +/** + * Send response to incoming INVITE request. Depending on the status + * code specified as parameter, this function may send provisional + * response, establish the call, or terminate the call. + * + * @param call_id Incoming call identification. + * @param opt Optional call setting. + * @param code Status code, (100-699). + * @param reason Optional reason phrase. If NULL, default text + * will be used. + * @param msg_data Optional list of headers etc to be added to outgoing + * response message. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsua_call_answer2(pjsua_call_id call_id, + const pjsua_call_setting *opt, + unsigned code, + const pj_str_t *reason, + const pjsua_msg_data *msg_data); + + /** * Hangup call by using method that is appropriate according to the * call state. This function is different than answering the call with @@ -3949,6 +4012,25 @@ PJ_DECL(pj_status_t) pjsua_call_reinvite(pjsua_call_id call_id, unsigned options, const pjsua_msg_data *msg_data); + +/** + * Send re-INVITE to release hold. + * The final status of the request itself will be reported on the + * \a on_call_media_state() callback, which inform the application that + * the media state of the call has changed. + * + * @param call_id Call identification. + * @param opt Optional call setting. + * @param msg_data Optional message components to be sent with + * the request. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsua_call_reinvite2(pjsua_call_id call_id, + const pjsua_call_setting *opt, + const pjsua_msg_data *msg_data); + + /** * Send UPDATE request. * @@ -3963,6 +4045,22 @@ PJ_DECL(pj_status_t) pjsua_call_update(pjsua_call_id call_id, unsigned options, const pjsua_msg_data *msg_data); + +/** + * Send UPDATE request. + * + * @param call_id Call identification. + * @param opt Optional call setting. + * @param msg_data Optional message components to be sent with + * the request. + * + * @return PJ_SUCCESS on success, or the appropriate error code. + */ +PJ_DECL(pj_status_t) pjsua_call_update2(pjsua_call_id call_id, + const pjsua_call_setting *opt, + const pjsua_msg_data *msg_data); + + /** * Initiate call transfer to the specified address. This function will send * REFER request to instruct remote call party to initiate a new INVITE @@ -4106,7 +4204,8 @@ PJ_DECL(pj_status_t) pjsua_call_dump(pjsua_call_id call_id, /** * Get the media stream index of the default video stream in the call. * Typically this will just retrieve the stream index of the first - * activated video stream in the call. + * activated video stream in the call. If none is active, it will return + * the first inactive video stream. * * @param call_id Call identification. * diff --git a/pjsip/include/pjsua-lib/pjsua_internal.h b/pjsip/include/pjsua-lib/pjsua_internal.h index db566ed5..31d303f0 100644 --- a/pjsip/include/pjsua-lib/pjsua_internal.h +++ b/pjsip/include/pjsua-lib/pjsua_internal.h @@ -120,6 +120,7 @@ typedef struct call_answer struct pjsua_call { unsigned index; /**< Index in pjsua array. */ + pjsua_call_setting opt; /**< Call setting. */ pjsip_inv_session *inv; /**< The invite session. */ void *user_data; /**< User/application data. */ pjsip_status_code last_code; /**< Last status code seen. */ @@ -162,7 +163,6 @@ struct pjsua_call pj_bool_t med_ch_deinit;/**< Media channel de-init-ed? */ union { struct { - unsigned options; /**< Outgoing call options. */ pjsua_msg_data *msg_data;/**< Headers for outgoing INVITE. */ } out_call; struct { @@ -171,6 +171,12 @@ struct pjsua_call } call_var; } async_call; /**< Temporary storage for async outgoing/incoming call. */ + + pj_bool_t rem_offerer; /**< Was remote SDP offerer? */ + unsigned rem_aud_cnt; /**< No of active audio in last remote + offer. */ + unsigned rem_vid_cnt; /**< No of active video in last remote + offer. */ }; diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 24df6e01..b8f0dd8e 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -383,14 +383,6 @@ PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg, /* Must have a transport */ PJ_ASSERT_RETURN(pjsua_var.tpdata[0].data.ptr != NULL, PJ_EINVALIDOP); - /* Verify media count */ -#if !defined(PJMEDIA_HAS_VIDEO) || (PJMEDIA_HAS_VIDEO == 0) - /* Enable PJMEDIA_HAS_VIDEO in your config_site.h! */ - PJ_ASSERT_RETURN(cfg->max_video_cnt == 0, PJ_EINVAL); -#endif - PJ_ASSERT_RETURN(cfg->max_audio_cnt + cfg->max_video_cnt <= - PJSUA_MAX_CALL_MEDIA, PJ_ETOOMANY); - PJ_LOG(4,(THIS_FILE, "Adding account: id=%.*s", (int)cfg->id.slen, cfg->id.ptr)); pj_log_push_indent(); @@ -674,13 +666,6 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc), PJ_EINVAL); - /* Verify media count */ -#if !defined(PJMEDIA_HAS_VIDEO) || (PJMEDIA_HAS_VIDEO == 0) - PJ_ASSERT_RETURN(cfg->max_video_cnt == 0, PJ_EINVAL); -#endif - PJ_ASSERT_RETURN(cfg->max_audio_cnt + cfg->max_video_cnt <= - PJSUA_MAX_CALL_MEDIA, PJ_ETOOMANY); - PJ_LOG(4,(THIS_FILE, "Modifying accunt %d", acc_id)); pj_log_push_indent(); @@ -1064,10 +1049,6 @@ PJ_DEF(pj_status_t) pjsua_acc_modify( pjsua_acc_id acc_id, } } - /* Max number of audio and video stream in a call */ - acc->cfg.max_audio_cnt = cfg->max_audio_cnt; - acc->cfg.max_video_cnt = cfg->max_video_cnt; - /* Video settings */ acc->cfg.vid_in_auto_show = cfg->vid_in_auto_show; acc->cfg.vid_out_auto_transmit = cfg->vid_out_auto_transmit; diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 1e337dd6..cec75935 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -116,6 +116,7 @@ static void reset_call(pjsua_call_id id) call_med->idx = i; call_med->tp_auto_del = PJ_TRUE; } + pjsua_call_setting_default(&call->opt); } @@ -341,7 +342,7 @@ on_make_call_med_tp_complete(pjsua_call_id call_id, pjsua_call *call = &pjsua_var.calls[call_id]; pjsua_acc *acc = &pjsua_var.acc[call->acc_id]; pjsip_dialog *dlg = call->async_call.dlg; - unsigned options = call->async_call.call_var.out_call.options; + unsigned options = 0; pjsip_tx_data *tdata; pj_status_t status = (info? info->status: PJ_SUCCESS); @@ -486,15 +487,80 @@ on_error: } +/* + * Initialize call settings based on account ID. + */ +PJ_DEF(void) pjsua_call_setting_default(pjsua_call_setting *opt) +{ + pj_assert(opt); + + pj_bzero(opt, sizeof(*opt)); + opt->flag = PJSUA_CALL_INCLUDE_DISABLED_MEDIA; + opt->audio_cnt = 1; + +#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0) + opt->video_cnt = 1; + //{ + // unsigned i; + // for (i = 0; i < PJ_ARRAY_SIZE(opt->vid_cap_dev); ++i) + // opt->vid_cap_dev[i] = PJMEDIA_VID_DEFAULT_CAPTURE_DEV; + //} +#endif +} + +static pj_status_t apply_call_setting(pjsua_call *call, + const pjsua_call_setting *opt, + const pjmedia_sdp_session *rem_sdp) +{ + pj_assert(call); + + if (!opt) + return PJ_SUCCESS; + +#if !PJMEDIA_HAS_VIDEO + pj_assert(opt->video_cnt == 0); +#endif + + /* If call is established, reinit media channel */ + if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) { + pjsua_call_setting old_opt; + pj_status_t status; + + old_opt = call->opt; + call->opt = *opt; + + /* Reinit media channel when media count is changed */ + if (opt->audio_cnt != old_opt.audio_cnt || + opt->video_cnt != old_opt.video_cnt) + { + pjsip_role_e role = rem_sdp? PJSIP_ROLE_UAS : PJSIP_ROLE_UAC; + status = pjsua_media_channel_init(call->index, role, + call->secure_level, + call->inv->pool_prov, + rem_sdp, NULL, + PJ_FALSE, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error re-initializing media channel", + status); + return status; + } + } + } else { + call->opt = *opt; + } + + return PJ_SUCCESS; +} + /* * Make outgoing call to the specified URI using the specified account. */ -PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id, - const pj_str_t *dest_uri, - unsigned options, - void *user_data, - const pjsua_msg_data *msg_data, - pjsua_call_id *p_call_id) +PJ_DEF(pj_status_t) pjsua_call_make_call(pjsua_acc_id acc_id, + const pj_str_t *dest_uri, + const pjsua_call_setting *opt, + void *user_data, + const pjsua_msg_data *msg_data, + pjsua_call_id *p_call_id) { pj_pool_t *tmp_pool = NULL; pjsip_dialog *dlg = NULL; @@ -555,6 +621,13 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id, call->acc_id = acc_id; call->call_hold_type = acc->cfg.call_hold_type; + /* Apply call setting */ + status = apply_call_setting(call, opt, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Failed to apply call setting", status); + goto on_error; + } + /* Create temporary pool */ tmp_pool = pjsua_pool_create("tmpcall10", 512, 256); @@ -622,7 +695,6 @@ PJ_DEF(pj_status_t) pjsua_call_make_call( pjsua_acc_id acc_id, /* Store variables required for the callback after the async * media transport creation is completed. */ - call->async_call.call_var.out_call.options = options; if (msg_data) { call->async_call.call_var.out_call.msg_data = pjsua_msg_data_clone( dlg->pool, msg_data); @@ -1465,6 +1537,9 @@ PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id, pj_strncpy(&info->call_id, &dlg->call_id->id, sizeof(info->buf_.call_id)); + /* call setting */ + pj_memcpy(&info->setting, &call->opt, sizeof(call->opt)); + /* state, state_text */ info->state = (call->inv? call->inv->state: PJSIP_INV_STATE_DISCONNECTED); info->state_text = pj_str((char*)pjsip_inv_state_name(info->state)); @@ -1486,6 +1561,13 @@ PJ_DEF(pj_status_t) pjsua_call_get_info( pjsua_call_id call_id, sizeof(info->buf_.last_status_text)); } + /* Audio & video count offered by remote */ + info->rem_offerer = call->rem_offerer; + if (call->rem_offerer) { + info->rem_audio_cnt = call->rem_aud_cnt; + info->rem_video_cnt = call->rem_vid_cnt; + } + /* Build array of media status and dir */ info->media_cnt = 0; for (mi=0; mi < call->med_cnt && @@ -1759,6 +1841,19 @@ PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id, unsigned code, const pj_str_t *reason, const pjsua_msg_data *msg_data) +{ + return pjsua_call_answer2(call_id, NULL, code, reason, msg_data); +} + + +/* + * Send response to incoming INVITE request. + */ +PJ_DEF(pj_status_t) pjsua_call_answer2(pjsua_call_id call_id, + const pjsua_call_setting *opt, + unsigned code, + const pj_str_t *reason, + const pjsua_msg_data *msg_data) { pjsua_call *call; pjsip_dialog *dlg = NULL; @@ -1775,6 +1870,13 @@ PJ_DEF(pj_status_t) pjsua_call_answer( pjsua_call_id call_id, if (status != PJ_SUCCESS) goto on_return; + /* Apply call setting */ + status = apply_call_setting(call, opt, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Failed to apply call setting", status); + goto on_return; + } + PJSUA_LOCK(); /* If media transport creation is not yet completed, we will answer * the call in the media transport creation callback instead. @@ -2015,6 +2117,32 @@ on_return: PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id, unsigned options, const pjsua_msg_data *msg_data) +{ + pjsua_call *call; + pjsip_dialog *dlg = NULL; + pj_status_t status; + + status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg); + if (status != PJ_SUCCESS) + goto on_return; + + if (options != call->opt.flag) + call->opt.flag = options; + + status = pjsua_call_reinvite2(call_id, NULL, msg_data); + +on_return: + if (dlg) pjsip_dlg_dec_lock(dlg); + return status; +} + + +/* + * Send re-INVITE (to release hold). + */ +PJ_DEF(pj_status_t) pjsua_call_reinvite2(pjsua_call_id call_id, + const pjsua_call_setting *opt, + const pjsua_msg_data *msg_data) { pjmedia_sdp_session *sdp; pj_str_t *new_contact = NULL; @@ -2030,7 +2158,7 @@ PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id, PJ_LOG(4,(THIS_FILE, "Sending re-INVITE on call %d", call_id)); pj_log_push_indent(); - status = acquire_call("pjsua_call_reinvite()", call_id, &call, &dlg); + status = acquire_call("pjsua_call_reinvite2()", call_id, &call, &dlg); if (status != PJ_SUCCESS) goto on_return; @@ -2040,8 +2168,14 @@ PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id, goto on_return; } + status = apply_call_setting(call, opt, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Failed to apply call setting", status); + goto on_return; + } + /* Create SDP */ - if (call->local_hold && (options & PJSUA_CALL_UNHOLD)==0) { + if (call->local_hold && (call->opt.flag & PJSUA_CALL_UNHOLD)==0) { status = create_sdp_of_call_hold(call, &sdp); } else { status = pjsua_media_channel_create_sdp(call->index, @@ -2055,7 +2189,7 @@ PJ_DEF(pj_status_t) pjsua_call_reinvite( pjsua_call_id call_id, goto on_return; } - if ((options & PJSUA_CALL_UPDATE_CONTACT) & + if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) & pjsua_acc_is_valid(call->acc_id)) { new_contact = &pjsua_var.acc[call->acc_id].contact; @@ -2091,6 +2225,32 @@ on_return: PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id, unsigned options, const pjsua_msg_data *msg_data) +{ + pjsua_call *call; + pjsip_dialog *dlg = NULL; + pj_status_t status; + + status = acquire_call("pjsua_call_update()", call_id, &call, &dlg); + if (status != PJ_SUCCESS) + goto on_return; + + if (options != call->opt.flag) + call->opt.flag = options; + + status = pjsua_call_update2(call_id, NULL, msg_data); + +on_return: + if (dlg) pjsip_dlg_dec_lock(dlg); + return status; +} + + +/* + * Send UPDATE request. + */ +PJ_DEF(pj_status_t) pjsua_call_update2(pjsua_call_id call_id, + const pjsua_call_setting *opt, + const pjsua_msg_data *msg_data) { pjmedia_sdp_session *sdp; pj_str_t *new_contact = NULL; @@ -2099,18 +2259,22 @@ PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id, pjsip_dialog *dlg = NULL; pj_status_t status; - PJ_UNUSED_ARG(options); - PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls, PJ_EINVAL); PJ_LOG(4,(THIS_FILE, "Sending UPDATE on call %d", call_id)); pj_log_push_indent(); - status = acquire_call("pjsua_call_update()", call_id, &call, &dlg); + status = acquire_call("pjsua_call_update2()", call_id, &call, &dlg); if (status != PJ_SUCCESS) goto on_return; + status = apply_call_setting(call, opt, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Failed to apply call setting", status); + goto on_return; + } + /* Create SDP */ status = pjsua_media_channel_create_sdp(call->index, call->inv->pool_prov, @@ -2121,7 +2285,7 @@ PJ_DEF(pj_status_t) pjsua_call_update( pjsua_call_id call_id, goto on_return; } - if ((options & PJSUA_CALL_UPDATE_CONTACT) & + if ((call->opt.flag & PJSUA_CALL_UPDATE_CONTACT) & pjsua_acc_is_valid(call->acc_id)) { new_contact = &pjsua_var.acc[call->acc_id].contact; @@ -3356,6 +3520,7 @@ static void pjsua_call_on_rx_offer(pjsip_inv_session *inv, pjsua_call *call; pjmedia_sdp_session *answer; unsigned i; + int vid_idx; pj_status_t status; PJSUA_LOCK(); @@ -3367,6 +3532,27 @@ static void pjsua_call_on_rx_offer(pjsip_inv_session *inv, call->index)); pj_log_push_indent(); +#if 0 && PJMEDIA_HAS_VIDEO + /* If current session has no video, let's just stay with no video. + * If application want to enable video, it must send re-INVITE + * with video. + */ + vid_idx = pjsua_call_get_vid_stream_idx(call->index); + if (vid_idx == -1 || call->media[vid_idx].dir == PJMEDIA_DIR_NONE) + call->opt.video_cnt = 0; +#endif + + /* Re-init media for the new remote offer before creating SDP */ + status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS, + call->secure_level, + call->inv->pool_prov, + offer, NULL, + PJ_FALSE, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Error re-initializing media channel", status); + goto on_return; + } + status = pjsua_media_channel_create_sdp(call->index, call->inv->pool_prov, offer, &answer, NULL); diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 6a80380c..fc47121d 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -216,7 +216,6 @@ PJ_DEF(void) pjsua_acc_config_default(pjsua_acc_config *cfg) cfg->timer_setting = pjsua_var.ua_cfg.timer_setting; cfg->ka_interval = 15; cfg->ka_data = pj_str("\r\n"); - cfg->max_audio_cnt = 1; cfg->vid_cap_dev = PJMEDIA_VID_DEFAULT_CAPTURE_DEV; cfg->vid_rend_dev = PJMEDIA_VID_DEFAULT_RENDER_DEV; pjsua_transport_config_default(&cfg->rtp_cfg); diff --git a/pjsip/src/pjsua-lib/pjsua_media.c b/pjsip/src/pjsua-lib/pjsua_media.c index 8fe57a11..138f4808 100644 --- a/pjsip/src/pjsua-lib/pjsua_media.c +++ b/pjsip/src/pjsua-lib/pjsua_media.c @@ -1157,15 +1157,18 @@ static void sort_media(const pjmedia_sdp_session *sdp, const pj_str_t *type, pjmedia_srtp_use use_srtp, pj_uint8_t midx[], - unsigned *p_count) + unsigned *p_count, + unsigned *p_total_count) { unsigned i; unsigned count = 0; int score[PJSUA_MAX_CALL_MEDIA]; pj_assert(*p_count >= PJSUA_MAX_CALL_MEDIA); + pj_assert(*p_total_count >= PJSUA_MAX_CALL_MEDIA); *p_count = 0; + *p_total_count = 0; for (i=0; i= 0) { - midx[*p_count] = (pj_uint8_t)best; + midx[i] = (pj_uint8_t)best; + if (score[best] >= 0) (*p_count)++; - } + if (score[best] > -22000) + (*p_total_count)++; score[best] = -22000; @@ -1588,11 +1592,13 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, pjsua_acc *acc = &pjsua_var.acc[call->acc_id]; pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA]; unsigned maudcnt = PJ_ARRAY_SIZE(maudidx); + unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx); pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA]; unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx); - pjmedia_type media_types[PJSUA_MAX_CALL_MEDIA]; + unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx); unsigned mi; pj_bool_t pending_med_tp = PJ_FALSE; + pj_bool_t reinit = PJ_FALSE; pj_status_t status; PJ_UNUSED_ARG(role); @@ -1614,7 +1620,12 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, return status; } - PJ_LOG(4,(THIS_FILE, "Call %d: initializing media..", call_id)); + if (call->inv && call->inv->state == PJSIP_INV_STATE_CONFIRMED) + reinit = PJ_TRUE; + + PJ_LOG(4,(THIS_FILE, "Call %d: %sinitializing media..", + call_id, (reinit?"re-":"") )); + pj_log_push_indent(); #if DISABLED_FOR_TICKET_1185 @@ -1629,13 +1640,10 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, } #endif + /* Get media count for each media type */ if (rem_sdp) { sort_media(rem_sdp, &STR_AUDIO, acc->cfg.use_srtp, - maudidx, &maudcnt); - // Don't apply media count limitation until SDP negotiation is done. - //if (maudcnt > acc->cfg.max_audio_cnt) - // maudcnt = acc->cfg.max_audio_cnt; - + maudidx, &maudcnt, &mtotaudcnt); if (maudcnt==0) { /* Expecting audio in the offer */ if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE; @@ -1646,35 +1654,98 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, #if PJMEDIA_HAS_VIDEO sort_media(rem_sdp, &STR_VIDEO, acc->cfg.use_srtp, - mvididx, &mvidcnt); - // Don't apply media count limitation until SDP negotiation is done. - //if (mvidcnt > acc->cfg.max_video_cnt) - //mvidcnt = acc->cfg.max_video_cnt; + mvididx, &mvidcnt, &mtotvidcnt); #else - mvidcnt = 0; + mvidcnt = mtotvidcnt = 0; + PJ_UNUSED_ARG(STR_VIDEO); #endif /* Update media count only when remote add any media, this media count - * must never decrease. + * must never decrease. Also note that we shouldn't apply the media + * count setting (of the call setting) before the SDP negotiation. */ if (call->med_cnt < rem_sdp->media_count) call->med_cnt = PJ_MIN(rem_sdp->media_count, PJSUA_MAX_CALL_MEDIA); + call->rem_offerer = PJ_TRUE; + call->rem_aud_cnt = maudcnt; + call->rem_vid_cnt = mvidcnt; + } else { - maudcnt = acc->cfg.max_audio_cnt; - for (mi=0; miinv->neg, &sdp); + pj_assert(status == PJ_SUCCESS); + + sort_media(sdp, &STR_AUDIO, acc->cfg.use_srtp, + maudidx, &maudcnt, &mtotaudcnt); + pj_assert(maudcnt > 0); + + sort_media(sdp, &STR_VIDEO, acc->cfg.use_srtp, + mvididx, &mvidcnt, &mtotvidcnt); + + /* Call setting may add or remove media. Adding media is done by + * enabling any disabled/port-zeroed media first, then adding new + * media whenever needed. Removing media is done by disabling + * media with the lowest 'quality'. + */ + + /* Check if we need to add new audio */ + if (maudcnt < call->opt.audio_cnt && + mtotaudcnt < call->opt.audio_cnt) + { + for (mi = 0; mi < call->opt.audio_cnt - mtotaudcnt; ++mi) + maudidx[maudcnt++] = (pj_uint8_t)call->med_cnt++; + + mtotaudcnt = call->opt.audio_cnt; + } + maudcnt = call->opt.audio_cnt; + + /* Check if we need to add new video */ + if (mvidcnt < call->opt.video_cnt && + mtotvidcnt < call->opt.video_cnt) + { + for (mi = 0; mi < call->opt.video_cnt - mtotvidcnt; ++mi) + mvididx[mvidcnt++] = (pj_uint8_t)call->med_cnt++; + + mtotvidcnt = call->opt.video_cnt; + } + mvidcnt = call->opt.video_cnt; + + } else { + + maudcnt = mtotaudcnt = call->opt.audio_cnt; + for (mi=0; miopt.video_cnt; + for (mi=0; mimed_cnt = maudcnt + mvidcnt; + + /* Need to publish supported media? */ + if (call->opt.flag & PJSUA_CALL_INCLUDE_DISABLED_MEDIA) { + if (mtotaudcnt == 0) { + mtotaudcnt = 1; + maudidx[0] = (pj_uint8_t)call->med_cnt++; + } #if PJMEDIA_HAS_VIDEO - mvidcnt = acc->cfg.max_video_cnt; - for (mi=0; mimed_cnt++; + } +#endif + } } -#else - mvidcnt = 0; -#endif - call->med_cnt = maudcnt + mvidcnt; + + call->rem_offerer = PJ_FALSE; } if (call->med_cnt == 0) { @@ -1702,28 +1773,22 @@ pj_status_t pjsua_media_channel_init(pjsua_call_id call_id, for (mi=0; mi < call->med_cnt; ++mi) { pjsua_call_media *call_med = &call->media[mi]; pj_bool_t enabled = PJ_FALSE; - pjmedia_type media_type = PJMEDIA_TYPE_NONE; - - if (rem_sdp) { - if (mi >= rem_sdp->media_count) { - /* Media has been removed in remote re-offer */ - media_type = call_med->type; - } else if (!pj_stricmp(&rem_sdp->media[mi]->desc.media, &STR_AUDIO)) { - media_type = PJMEDIA_TYPE_AUDIO; - if (pj_memchr(maudidx, mi, maudcnt * sizeof(maudidx[0]))) { - enabled = PJ_TRUE; - } + pjmedia_type media_type = PJMEDIA_TYPE_UNKNOWN; + + if (pj_memchr(maudidx, mi, mtotaudcnt * sizeof(maudidx[0]))) { + media_type = PJMEDIA_TYPE_AUDIO; + if (call->opt.audio_cnt && + pj_memchr(maudidx, mi, maudcnt * sizeof(maudidx[0]))) + { + enabled = PJ_TRUE; } - else if (!pj_stricmp(&rem_sdp->media[mi]->desc.media, &STR_VIDEO)) { - media_type = PJMEDIA_TYPE_VIDEO; - if (pj_memchr(mvididx, mi, mvidcnt * sizeof(mvididx[0]))) { - enabled = PJ_TRUE; - } + } else if (pj_memchr(mvididx, mi, mtotvidcnt * sizeof(mvididx[0]))) { + media_type = PJMEDIA_TYPE_VIDEO; + if (call->opt.video_cnt && + pj_memchr(mvididx, mi, mvidcnt * sizeof(mvididx[0]))) + { + enabled = PJ_TRUE; } - - } else { - enabled = PJ_TRUE; - media_type = media_types[mi]; } if (enabled) { @@ -1807,6 +1872,12 @@ on_error: return status; } + +/* Create SDP based on the current media channel. Note that, this function + * will not modify the media channel, so when receiving new offer or + * updating media count (via call setting), media channel must be reinit'd + * (using pjsua_media_channel_init()) first before calling this function. + */ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, pj_pool_t *pool, const pjmedia_sdp_session *rem_sdp, @@ -1824,6 +1895,8 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, if (pjsua_get_state() != PJSUA_STATE_RUNNING) return PJ_EBUSY; +#if 0 + // This function should not really change the media channel. if (rem_sdp) { /* If this is a re-offer, let's re-initialize media as remote may * add or remove media @@ -1836,24 +1909,6 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, if (status != PJ_SUCCESS) return status; } - -#if 0 - pjsua_acc *acc = &pjsua_var.acc[call->acc_id]; - pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA]; - unsigned maudcnt = PJ_ARRAY_SIZE(maudidx); - - sort_media(rem_sdp, &STR_AUDIO, acc->cfg.use_srtp, - maudidx, &maudcnt); - - if (maudcnt==0) { - /* Expecting audio in the offer */ - if (sip_err_code) *sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE; - pjsua_media_channel_deinit(call_id); - return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE); - } - - call->audio_idx = maudidx[0]; -#endif } else { /* Audio is first in our offer, by convention */ // The audio_idx should not be changed here, as this function may be @@ -1861,6 +1916,7 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, // can be anywhere. //call->audio_idx = 0; } +#endif #if 0 // Since r3512, old-style hold should have got transport, created by @@ -2084,6 +2140,8 @@ pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, } #endif + call->rem_offerer = (rem_sdp != NULL); + *p_sdp = sdp; return PJ_SUCCESS; } @@ -2463,8 +2521,10 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, const pj_str_t STR_VIDEO = { "video", 5 }; pj_uint8_t maudidx[PJSUA_MAX_CALL_MEDIA]; unsigned maudcnt = PJ_ARRAY_SIZE(maudidx); + unsigned mtotaudcnt = PJ_ARRAY_SIZE(maudidx); pj_uint8_t mvididx[PJSUA_MAX_CALL_MEDIA]; unsigned mvidcnt = PJ_ARRAY_SIZE(mvididx); + unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx); pj_bool_t need_renego_sdp = PJ_FALSE; if (pjsua_get_state() != PJSUA_STATE_RUNNING) @@ -2484,22 +2544,27 @@ pj_status_t pjsua_media_channel_update(pjsua_call_id call_id, /* Reset audio_idx first */ call->audio_idx = -1; - /* Apply maximum audio/video count of the account */ + /* Sort audio/video based on "quality" */ sort_media(local_sdp, &STR_AUDIO, acc->cfg.use_srtp, - maudidx, &maudcnt); + maudidx, &maudcnt, &mtotaudcnt); #if PJMEDIA_HAS_VIDEO sort_media(local_sdp, &STR_VIDEO, acc->cfg.use_srtp, - mvididx, &mvidcnt); + mvididx, &mvidcnt, &mtotvidcnt); #else PJ_UNUSED_ARG(STR_VIDEO); mvidcnt = 0; #endif - if (maudcnt > acc->cfg.max_audio_cnt || mvidcnt > acc->cfg.max_video_cnt) + + /* Applying media count limitation. Note that in generating SDP answer, + * no media count limitation applied, as we didn't know yet which media + * would pass the SDP negotiation. + */ + if (maudcnt > call->opt.audio_cnt || mvidcnt > call->opt.video_cnt) { pjmedia_sdp_session *local_sdp2; - maudcnt = PJ_MIN(maudcnt, acc->cfg.max_audio_cnt); - mvidcnt = PJ_MIN(mvidcnt, acc->cfg.max_video_cnt); + maudcnt = PJ_MIN(maudcnt, call->opt.audio_cnt); + mvidcnt = PJ_MIN(mvidcnt, call->opt.video_cnt); local_sdp2 = pjmedia_sdp_session_clone(tmp_pool, local_sdp); for (mi=0; mi < local_sdp2->media_count; ++mi) { diff --git a/pjsip/src/pjsua-lib/pjsua_vid.c b/pjsip/src/pjsua-lib/pjsua_vid.c index 38b77f22..81b943e1 100644 --- a/pjsip/src/pjsua-lib/pjsua_vid.c +++ b/pjsip/src/pjsua-lib/pjsua_vid.c @@ -1312,6 +1312,10 @@ PJ_DEF(pj_status_t) pjsua_vid_win_set_show( pjsua_vid_win_id wid, return PJ_EINVAL; } + /* Make sure that renderer gets started before shown up */ + if (show && !pjmedia_vid_port_is_running(w->vp_rend)) + status = pjmedia_vid_port_start(w->vp_rend); + hide = !show; status = pjmedia_vid_dev_stream_set_cap(s, PJMEDIA_VID_DEV_CAP_OUTPUT_HIDE, &hide); @@ -1540,17 +1544,12 @@ static pj_status_t call_add_video(pjsua_call *call, pjmedia_sdp_session *sdp; pjmedia_sdp_media *sdp_m; pjmedia_transport_info tpinfo; - unsigned active_cnt; pj_status_t status; /* Verify media slot availability */ if (call->med_cnt == PJSUA_MAX_CALL_MEDIA) return PJ_ETOOMANY; - call_get_vid_strm_info(call, NULL, NULL, &active_cnt, NULL); - if (active_cnt == acc_cfg->max_video_cnt) - return PJ_ETOOMANY; - /* Get active local SDP and clone it */ status = pjmedia_sdp_neg_get_active_local(call->inv->neg, ¤t_sdp); if (status != PJ_SUCCESS) @@ -1617,6 +1616,8 @@ static pj_status_t call_add_video(pjsua_call *call, if (status != PJ_SUCCESS) goto on_error; + call->opt.video_cnt++; + return PJ_SUCCESS; on_error: @@ -1758,6 +1759,7 @@ on_error: /* Deactivate the stream */ pjmedia_sdp_media_deactivate(pool, sdp->media[med_idx]); + call->opt.video_cnt--; } status = call_reoffer_sdp(call->index, sdp); -- cgit v1.2.3