Загрузка данных
pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
pjsip_role_e role,
int security_level,
pj_pool_t *tmp_pool,
const pjmedia_sdp_session *rem_sdp,
int *sip_err_code,
pj_bool_t async,
pjsua_med_tp_state_cb cb)
{
const pj_str_t STR_AUDIO = { "audio", 5 };
const pj_str_t STR_VIDEO = { "video", 5 };
const pj_str_t STR_TEXT = { "text", 4 };
pjsua_call *call = &pjsua_var.calls[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);
unsigned mtotvidcnt = PJ_ARRAY_SIZE(mvididx);
pj_uint8_t mtxtidx[PJSUA_MAX_CALL_MEDIA];
unsigned mtxtcnt = PJ_ARRAY_SIZE(mtxtidx);
unsigned mtottxtcnt = PJ_ARRAY_SIZE(mtxtidx);
unsigned mi;
pj_bool_t pending_med_tp = PJ_FALSE;
pj_bool_t reinit = PJ_FALSE;
pj_status_t status;
PJ_UNUSED_ARG(role);
/*
* Note: this function may be called when the media already exists
* (e.g. in reinvites, updates, etc).
*/
if (pjsua_get_state() != PJSUA_STATE_RUNNING) {
if (sip_err_code)
*sip_err_code = PJSIP_SC_SERVICE_UNAVAILABLE;
return PJ_EBUSY;
}
if (async) {
pj_pool_t *tmppool = (call->inv? call->inv->pool_prov:
call->async_call.dlg->pool);
status = pj_mutex_create_simple(tmppool, NULL, &call->med_ch_mutex);
if (status != PJ_SUCCESS) {
if (sip_err_code)
*sip_err_code = PJSIP_SC_INTERNAL_SERVER_ERROR;
return status;
}
}
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();
/* Init provisional media state */
if (call->med_cnt == 0) {
/* New media session, just copy whole from call media state. */
pj_memcpy(call->media_prov, call->media, sizeof(call->media));
} else {
/* Clean up any unused transports. Note that when local SDP reoffer
* is rejected by remote, there may be any initialized transports that
* are not used by call media and currently there is no notification
* from PJSIP level regarding the reoffer rejection.
*/
pjsua_media_prov_clean_up(call_id);
/* Updating media session, copy from call media state. */
pj_memcpy(call->media_prov, call->media,
sizeof(call->media[0]) * call->med_cnt);
}
call->med_prov_cnt = call->med_cnt;
#if DISABLED_FOR_TICKET_1185
/* Return error if media transport has not been created yet
* (e.g. application is starting)
*/
for (i=0; i<call->med_cnt; ++i) {
if (call->media[i].tp == NULL) {
status = PJ_EBUSY;
goto on_error;
}
}
#endif
/* Get media count for each media type */
if (rem_sdp) {
/* We are sending answer, check media count for each media type
* from the remote SDP.
*/
sort_media(rem_sdp, &STR_AUDIO, acc->cfg.use_srtp,
maudidx, &maudcnt, &mtotaudcnt);
#if PJMEDIA_HAS_VIDEO
sort_media(rem_sdp, &STR_VIDEO, acc->cfg.use_srtp,
mvididx, &mvidcnt, &mtotvidcnt);
#else
mvidcnt = mtotvidcnt = 0;
PJ_UNUSED_ARG(STR_VIDEO);
#endif
sort_media(rem_sdp, &STR_TEXT, acc->cfg.use_srtp,
mtxtidx, &mtxtcnt, &mtottxtcnt);
if (maudcnt + mvidcnt + mtxtcnt == 0) {
/* Expecting media in the offer */
if (sip_err_code)
*sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
goto on_error;
}
/* Update media count only when remote add any media, this media count
* 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_prov_cnt < rem_sdp->media_count)
call->med_prov_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;
call->rem_txt_cnt = mtxtcnt;
} else {
/* If call is already established, adjust the existing call media list
* to media count setting in call setting, e.g: re-enable/disable/add
* media from existing media.
* Otherwise, apply media count from the call setting directly.
*/
if (reinit) {
pj_bool_t sort_check_tp;
/* Media sorting below will check transport, i.e: media without
* transport will have lower priority. If PJSUA_CALL_REINIT_MEDIA
* is set, we must not check transport.
*/
sort_check_tp = !(call->opt.flag & PJSUA_CALL_REINIT_MEDIA);
/* We are sending reoffer, check media count for each media type
* from the existing call media list.
*/
sort_media2(call->media_prov, sort_check_tp, call->med_prov_cnt,
PJMEDIA_TYPE_AUDIO, maudidx, &maudcnt, &mtotaudcnt);
/* No need to assert if there's no media. */
//pj_assert(maudcnt > 0);
sort_media2(call->media_prov, sort_check_tp, call->med_prov_cnt,
PJMEDIA_TYPE_VIDEO, mvididx, &mvidcnt, &mtotvidcnt);
sort_media2(call->media_prov, sort_check_tp, call->med_prov_cnt,
PJMEDIA_TYPE_TEXT, mtxtidx, &mtxtcnt, &mtottxtcnt);
/* 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.aud_cnt &&
mtotaudcnt < call->opt.aud_cnt)
{
for (mi = 0; mi < call->opt.aud_cnt - mtotaudcnt; ++mi)
maudidx[maudcnt++] = (pj_uint8_t)call->med_prov_cnt++;
mtotaudcnt = call->opt.aud_cnt;
}
maudcnt = call->opt.aud_cnt;
/* Check if we need to add new video */
if (mvidcnt < call->opt.vid_cnt &&
mtotvidcnt < call->opt.vid_cnt)
{
for (mi = 0; mi < call->opt.vid_cnt - mtotvidcnt; ++mi)
mvididx[mvidcnt++] = (pj_uint8_t)call->med_prov_cnt++;
mtotvidcnt = call->opt.vid_cnt;
}
mvidcnt = call->opt.vid_cnt;
/* Check if we need to add new text */
if (mtxtcnt < call->opt.txt_cnt &&
mtottxtcnt < call->opt.txt_cnt)
{
for (mi = 0; mi < call->opt.txt_cnt - mtottxtcnt; ++mi)
mtxtidx[mtxtcnt++] = (pj_uint8_t)call->med_prov_cnt++;
mtottxtcnt = call->opt.txt_cnt;
}
mtxtcnt = call->opt.txt_cnt;
/* In case of media reinit, 'med_prov_cnt' may be decreased
* because the new call->opt says so. As media count should
* never decrease, we should verify 'med_prov_cnt' to be
* at least equal to 'med_cnt' (see also #1987).
*/
if ((call->opt.flag & PJSUA_CALL_REINIT_MEDIA) &&
call->med_prov_cnt < call->med_cnt)
{
call->med_prov_cnt = call->med_cnt;
}
} else {
maudcnt = mtotaudcnt = call->opt.aud_cnt;
for (mi=0; mi<maudcnt; ++mi) {
maudidx[mi] = (pj_uint8_t)mi;
}
mvidcnt = mtotvidcnt = call->opt.vid_cnt;
for (mi=0; mi<mvidcnt; ++mi) {
mvididx[mi] = (pj_uint8_t)(maudcnt + mi);
}
mtxtcnt = mtottxtcnt = call->opt.txt_cnt;
for (mi=0; mi<mtxtcnt; ++mi) {
mtxtidx[mi] = (pj_uint8_t)(maudcnt + mvidcnt + mi);
}
call->med_prov_cnt = maudcnt + mvidcnt + mtxtcnt;
/* 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_prov_cnt++;
}
#if PJMEDIA_HAS_VIDEO
if (mtotvidcnt == 0) {
mtotvidcnt = 1;
mvididx[0] = (pj_uint8_t)call->med_prov_cnt++;
}
#endif
if (mtottxtcnt == 0) {
mtottxtcnt = 1;
mtxtidx[0] = (pj_uint8_t)call->med_prov_cnt++;
}
}
}
call->rem_offerer = PJ_FALSE;
}
if (call->med_prov_cnt == 0) {
/* Expecting at least one media */
if (sip_err_code)
*sip_err_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
status = PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE_HERE);
goto on_error;
}
if (async) {
call->med_ch_cb = cb;
}
if (rem_sdp && call->inv) {
call->async_call.rem_sdp =
pjmedia_sdp_session_clone(call->inv->pool_prov, rem_sdp);
} else {
call->async_call.rem_sdp = NULL;
}
call->async_call.pool_prov = tmp_pool;
/* Initialize each media line */
for (mi=0; mi < call->med_prov_cnt; ++mi) {
pjsua_call_media *call_med = &call->media_prov[mi];
pj_bool_t enabled = PJ_FALSE;
pjmedia_type media_type = PJMEDIA_TYPE_UNKNOWN;
if (pj_memchr(maudidx, mi, mtotaudcnt * sizeof(maudidx[0]))) {
media_type = PJMEDIA_TYPE_AUDIO;
if (call->opt.aud_cnt &&
pj_memchr(maudidx, mi, maudcnt * sizeof(maudidx[0])))
{
enabled = PJ_TRUE;
}
} else if (pj_memchr(mvididx, mi, mtotvidcnt * sizeof(mvididx[0]))) {
media_type = PJMEDIA_TYPE_VIDEO;
if (call->opt.vid_cnt &&
pj_memchr(mvididx, mi, mvidcnt * sizeof(mvididx[0])))
{
enabled = PJ_TRUE;
}
} else if (pj_memchr(mtxtidx, mi, mtottxtcnt * sizeof(mtxtidx[0]))) {
media_type = PJMEDIA_TYPE_TEXT;
if (call->opt.txt_cnt &&
pj_memchr(mtxtidx, mi, mtxtcnt * sizeof(mtxtidx[0])))
{
enabled = PJ_TRUE;
}
}
if (call->opt.flag & PJSUA_CALL_SET_MEDIA_DIR) {
call_med->def_dir = call->opt.media_dir[mi];
PJ_LOG(4,(THIS_FILE, "Call %d: setting media direction "
"#%d to %d.", call_id, mi,
call_med->def_dir));
} else if (!reinit || mi >= call->med_cnt) {
/* Initialize default media direction as bidirectional,
* for initial media or newly added media.
*/
call_med->def_dir = PJMEDIA_DIR_ENCODING_DECODING;
}
if (enabled) {
call_med->enable_rtcp_mux = acc->cfg.enable_rtcp_mux;
status = pjsua_call_media_init(call_med, media_type, rem_sdp,
&acc->cfg.rtp_cfg,
security_level, sip_err_code,
async,
(async? &media_channel_init_cb:
NULL));
if (status == PJ_EPENDING) {
pending_med_tp = PJ_TRUE;
} else if (status != PJ_SUCCESS) {
if (pending_med_tp) {
/* Save failure information. */
call_med->tp_ready = status;
pj_bzero(&call->med_ch_info, sizeof(call->med_ch_info));
call->med_ch_info.status = status;
call->med_ch_info.state = call_med->tp_st;
call->med_ch_info.med_idx = call_med->idx;
if (sip_err_code)
call->med_ch_info.sip_err_code = *sip_err_code;
/* We will return failure in the callback later. */
return PJ_EPENDING;
}
/* Revert back provisional media. */
pjsua_media_prov_revert(call_id);
goto on_error;
}
/* Find and save "a=mid". Currently this is for trickle ICE.
* Trickle ICE match media in SDP of SIP INFO by comparing this
* attribute, so remote SDP must be received first before remote
* SDP in SIP INFO can be processed.
*/
if (rem_sdp && call_med->rem_mid.slen == 0) {
const pjmedia_sdp_media *m = rem_sdp->media[mi];
pjmedia_sdp_attr *a;
a = pjmedia_sdp_media_find_attr2(m, "mid", NULL);
if (a)
call_med->rem_mid = a->value;
}
} else {
/* By convention, the media is disabled if transport is NULL
* or transport state is PJSUA_MED_TP_DISABLED.
*/
if (call_med->tp) {
// Don't close transport here, as SDP negotiation has not been
// done and stream may be still active. Once SDP negotiation
// is done (channel_update() invoked), this transport will be
// closed there.
//pjmedia_transport_close(call_med->tp);
//call_med->tp = NULL;
pj_assert(call_med->tp_st == PJSUA_MED_TP_INIT ||
call_med->tp_st == PJSUA_MED_TP_RUNNING);
pjsua_set_media_tp_state(call_med, PJSUA_MED_TP_DISABLED);
}
/* Put media type just for info if not yet defined */
if (call_med->type == PJMEDIA_TYPE_NONE)
call_med->type = media_type;
}
}
if (maudcnt > 0) {
call->audio_idx = maudidx[0];
PJ_LOG(4,(THIS_FILE, "Media index %d selected for audio call %d",
call->audio_idx, call->index));
} else {
call->audio_idx = -1;
}
if (pending_med_tp) {
/* We shouldn't use temporary pool anymore. */
call->async_call.pool_prov = NULL;
/* We have a pending media transport initialization. */
pj_log_pop_indent();
return PJ_EPENDING;
}
/* Media transport initialization completed immediately, so
* we don't need to call the callback.
*/
call->med_ch_cb = NULL;
status = media_channel_init_cb(call_id, NULL);
if (status != PJ_SUCCESS && sip_err_code) {
if (call->med_ch_info.sip_err_code)
*sip_err_code = call->med_ch_info.sip_err_code;
else
*sip_err_code = PJSIP_ERRNO_TO_SIP_STATUS(status);
}
pj_log_pop_indent();
return status;
on_error:
if (call->med_ch_mutex) {
pj_mutex_destroy(call->med_ch_mutex);
call->med_ch_mutex = NULL;
}
if (sip_err_code && *sip_err_code == 0)
*sip_err_code = PJSIP_ERRNO_TO_SIP_STATUS(status);
pj_log_pop_indent();
return status;
}