void get_and_register_SDP_to_cloud(struct ice_trans_s* icetrans, ice_option_t opt, char *usrid) { static char buffer[2048]; int len; if (icetrans->icest == NULL) { PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); return; } PJ_LOG(4, (__FUNCTION__, "General info")); PJ_LOG(4, (__FUNCTION__,"---------------")); PJ_LOG(4, (__FUNCTION__,"Component count : %d\n", opt.comp_cnt)); PJ_LOG(4, (__FUNCTION__,"Status : ")); if (pj_ice_strans_sess_is_complete(icetrans->icest)) puts("negotiation complete"); else if (pj_ice_strans_sess_is_running(icetrans->icest)) puts("negotiation is in progress"); else if (pj_ice_strans_has_sess(icetrans->icest)) puts("session ready"); else puts("session not created"); if (!pj_ice_strans_has_sess(icetrans->icest)) { puts("Create the session first to see more info"); return; } printf("Negotiated comp_cnt: %d\n", pj_ice_strans_get_running_comp_cnt(icetrans->icest)); printf("Role : %s\n", pj_ice_strans_get_role(icetrans->icest)==PJ_ICE_SESS_ROLE_CONTROLLED ? "controlled" : "controlling"); len = extract_sdp_to_xml(icetrans, buffer, 2048, opt, usrid); if (len < 0) err_exit("not enough buffer to show ICE status", -len, icetrans); // Register this local SDP to cloud char full_url[256]; //printf("[DEBUG] %s, %d \n", __FUNCTION__, __LINE__ ); strcpy(full_url, gUrl); // plus URL strcpy(&full_url[strlen(full_url)], "/peer/registerPeer"); // plus API http_post_request(full_url, buffer); PJ_LOG(4, (__FUNCTION__,"Local SDP (paste this to remote host):\n" "--------------------------------------\n" "%s\n", buffer)); }
/* * Show information contained in the ICE stream transport. This is * invoked from the menu. */ static void icedemo_show_ice(void) { static char buffer[1000]; int len; if (icedemo.icest == NULL) { PJ_LOG(1,(THIS_FILE, "Error: No ICE instance, create it first")); return; } puts("General info"); puts("---------------"); printf("Component count : %d\n", icedemo.opt.comp_cnt); printf("Status : "); if (pj_ice_strans_sess_is_complete(icedemo.icest)) puts("negotiation complete"); else if (pj_ice_strans_sess_is_running(icedemo.icest)) puts("negotiation is in progress"); else if (pj_ice_strans_has_sess(icedemo.icest)) puts("session ready"); else puts("session not created"); if (!pj_ice_strans_has_sess(icedemo.icest)) { puts("Create the session first to see more info"); return; } printf("Negotiated comp_cnt: %d\n", pj_ice_strans_get_running_comp_cnt(icedemo.icest)); printf("Role : %s\n", pj_ice_strans_get_role(icedemo.icest)==PJ_ICE_SESS_ROLE_CONTROLLED ? "controlled" : "controlling"); len = encode_session(buffer, sizeof(buffer)); if (len < 0) err_exit("not enough buffer to show ICE status", -len); puts(""); printf("Local SDP (paste this to remote host):\n" "--------------------------------------\n" "%s\n", buffer); puts(""); puts("Remote info:\n" "----------------------"); if (icedemo.rem.cand_cnt==0) { puts("No remote info yet"); } else { unsigned i; printf("Remote ufrag : %s\n", icedemo.rem.ufrag); printf("Remote password : %s\n", icedemo.rem.pwd); printf("Remote cand. cnt. : %d\n", icedemo.rem.cand_cnt); for (i=0; i<icedemo.rem.cand_cnt; ++i) { len = print_cand(buffer, sizeof(buffer), &icedemo.rem.cand[i]); if (len < 0) err_exit("not enough buffer to show ICE status", -len); printf(" %s", buffer); } } }
/* Encode ICE information in SDP */ static pj_status_t encode_session_in_sdp(struct transport_ice *tp_ice, pj_pool_t *sdp_pool, pjmedia_sdp_session *sdp_local, unsigned media_index, unsigned comp_cnt, pj_bool_t restart_session) { enum { ATTR_BUF_LEN = 160, /* Max len of a=candidate attr */ RATTR_BUF_LEN= 160 /* Max len of a=remote-candidates attr */ }; pjmedia_sdp_media *m = sdp_local->media[media_index]; pj_str_t local_ufrag, local_pwd; pjmedia_sdp_attr *attr; pj_status_t status; /* Must have a session */ PJ_ASSERT_RETURN(pj_ice_strans_has_sess(tp_ice->ice_st), PJ_EBUG); /* Get ufrag and pwd from current session */ pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, &local_ufrag, &local_pwd, NULL, NULL); /* The listing of candidates depends on whether ICE has completed * or not. When ICE has completed: * * 9.1.2.2: Existing Media Streams with ICE Completed * The agent MUST include a candidate attributes for candidates * matching the default destination for each component of the * media stream, and MUST NOT include any other candidates. * * When ICE has not completed, we shall include all candidates. * * Except when we have detected that remote is offering to restart * the session, in this case we will answer with full ICE SDP and * new ufrag/pwd pair. */ if (!restart_session && pj_ice_strans_sess_is_complete(tp_ice->ice_st)) { const pj_ice_sess_check *check; char *attr_buf; pjmedia_sdp_conn *conn; pjmedia_sdp_attr *a_rtcp; pj_str_t rem_cand; unsigned comp; /* Encode ice-ufrag attribute */ attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, &local_ufrag); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); /* Encode ice-pwd attribute */ attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, &local_pwd); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); /* Prepare buffer */ attr_buf = (char*) pj_pool_alloc(sdp_pool, ATTR_BUF_LEN); rem_cand.ptr = (char*) pj_pool_alloc(sdp_pool, RATTR_BUF_LEN); rem_cand.slen = 0; /* 9.1.2.2: Existing Media Streams with ICE Completed * The default destination for media (i.e., the values of * the IP addresses and ports in the m and c line used for * that media stream) MUST be the local candidate from the * highest priority nominated pair in the valid list for each * component. */ check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, 1); if (check == NULL) { pj_assert(!"Shouldn't happen"); return PJ_EBUG; } /* Override connection line address and media port number */ conn = m->conn; if (conn == NULL) conn = sdp_local->conn; conn->addr.ptr = (char*) pj_pool_alloc(sdp_pool, PJ_INET6_ADDRSTRLEN); pj_sockaddr_print(&check->lcand->addr, conn->addr.ptr, PJ_INET6_ADDRSTRLEN, 0); conn->addr.slen = pj_ansi_strlen(conn->addr.ptr); m->desc.port = pj_sockaddr_get_port(&check->lcand->addr); /* Override address RTCP attribute if it's present */ if (comp_cnt == 2 && (check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, COMP_RTCP)) != NULL && (a_rtcp = pjmedia_sdp_attr_find(m->attr_count, m->attr, &STR_RTCP, 0)) != NULL) { pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a_rtcp); a_rtcp = pjmedia_sdp_attr_create_rtcp(sdp_pool, &check->lcand->addr); if (a_rtcp) pjmedia_sdp_attr_add(&m->attr_count, m->attr, a_rtcp); } /* Encode only candidates matching the default destination * for each component */ for (comp=0; comp < comp_cnt; ++comp) { int len; pj_str_t value; /* Get valid pair for this component */ check = pj_ice_strans_get_valid_pair(tp_ice->ice_st, comp+1); if (check == NULL) continue; /* Print and add local candidate in the pair */ value.ptr = attr_buf; value.slen = print_sdp_cand_attr(attr_buf, ATTR_BUF_LEN, check->lcand); if (value.slen < 0) { pj_assert(!"Not enough attr_buf to print candidate"); return PJ_EBUG; } attr = pjmedia_sdp_attr_create(sdp_pool, STR_CANDIDATE.ptr, &value); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); /* Append to a=remote-candidates attribute */ if (pj_ice_strans_get_role(tp_ice->ice_st) == PJ_ICE_SESS_ROLE_CONTROLLING) { char rem_addr[PJ_INET6_ADDRSTRLEN]; pj_sockaddr_print(&check->rcand->addr, rem_addr, sizeof(rem_addr), 0); len = pj_ansi_snprintf( rem_cand.ptr + rem_cand.slen, RATTR_BUF_LEN - rem_cand.slen, "%s%u %s %u", (rem_cand.slen==0? "" : " "), comp+1, rem_addr, pj_sockaddr_get_port(&check->rcand->addr) ); if (len < 1 || len >= RATTR_BUF_LEN) { pj_assert(!"Not enough buffer to print " "remote-candidates"); return PJ_EBUG; } rem_cand.slen += len; } } /* 9.1.2.2: Existing Media Streams with ICE Completed * In addition, if the agent is controlling, it MUST include * the a=remote-candidates attribute for each media stream * whose check list is in the Completed state. The attribute * contains the remote candidates from the highest priority * nominated pair in the valid list for each component of that * media stream. */ if (pj_ice_strans_get_role(tp_ice->ice_st) == PJ_ICE_SESS_ROLE_CONTROLLING) { attr = pjmedia_sdp_attr_create(sdp_pool, STR_REM_CAND.ptr, &rem_cand); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } } else if (pj_ice_strans_has_sess(tp_ice->ice_st)) { /* Encode all candidates to SDP media */ char *attr_buf; unsigned comp; /* If ICE is not restarted, encode current ICE ufrag/pwd. * Otherwise generate new one. */ if (!restart_session) { attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, &local_ufrag); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, &local_pwd); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } else { pj_str_t str; str.slen = PJ_ICE_UFRAG_LEN; str.ptr = (char*) pj_pool_alloc(sdp_pool, str.slen); pj_create_random_string(str.ptr, str.slen); attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_UFRAG.ptr, &str); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); str.ptr = (char*) pj_pool_alloc(sdp_pool, str.slen); pj_create_random_string(str.ptr, str.slen); attr = pjmedia_sdp_attr_create(sdp_pool, STR_ICE_PWD.ptr, &str); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } /* Create buffer to encode candidates as SDP attribute */ attr_buf = (char*) pj_pool_alloc(sdp_pool, ATTR_BUF_LEN); for (comp=0; comp < comp_cnt; ++comp) { unsigned cand_cnt; pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND]; unsigned i; cand_cnt = PJ_ARRAY_SIZE(cand); status = pj_ice_strans_enum_cands(tp_ice->ice_st, comp+1, &cand_cnt, cand); if (status != PJ_SUCCESS) return status; for (i=0; i<cand_cnt; ++i) { pj_str_t value; value.slen = print_sdp_cand_attr(attr_buf, ATTR_BUF_LEN, &cand[i]); if (value.slen < 0) { pj_assert(!"Not enough attr_buf to print candidate"); return PJ_EBUG; } value.ptr = attr_buf; attr = pjmedia_sdp_attr_create(sdp_pool, STR_CANDIDATE.ptr, &value); pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr); } } } else { /* ICE has failed, application should have terminated this call */ } /* Removing a=rtcp line when there is only one component. */ if (comp_cnt == 1) { attr = pjmedia_sdp_attr_find(m->attr_count, m->attr, &STR_RTCP, NULL); if (attr) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, attr); } return PJ_SUCCESS; }
/* Verify incoming offer */ static pj_status_t verify_ice_sdp(struct transport_ice *tp_ice, pj_pool_t *tmp_pool, const pjmedia_sdp_session *rem_sdp, unsigned media_index, pj_ice_sess_role current_ice_role, struct sdp_state *sdp_state) { const pjmedia_sdp_media *rem_m; const pjmedia_sdp_attr *ufrag_attr, *pwd_attr; const pjmedia_sdp_conn *rem_conn; pj_bool_t comp1_found=PJ_FALSE, comp2_found=PJ_FALSE, has_rtcp=PJ_FALSE; pj_sockaddr rem_conn_addr, rtcp_addr; unsigned i; pj_status_t status; rem_m = rem_sdp->media[media_index]; /* Get the "ice-ufrag" and "ice-pwd" attributes */ get_ice_attr(rem_sdp, rem_m, &ufrag_attr, &pwd_attr); /* If "ice-ufrag" or "ice-pwd" are not found, disable ICE */ if (ufrag_attr==NULL || pwd_attr==NULL) { sdp_state->match_comp_cnt = 0; return PJ_SUCCESS; } /* Verify that default target for each component matches one of the * candidate for the component. Otherwise stop ICE with ICE ice_mismatch * error. */ /* Component 1 is the c= line */ rem_conn = rem_m->conn; if (rem_conn == NULL) rem_conn = rem_sdp->conn; if (!rem_conn) return PJMEDIA_SDP_EMISSINGCONN; /* Verify address family matches */ if ((tp_ice->af==pj_AF_INET() && pj_strcmp(&rem_conn->addr_type, &STR_IP4)!=0) || (tp_ice->af==pj_AF_INET6() && pj_strcmp(&rem_conn->addr_type, &STR_IP6)!=0)) { return PJMEDIA_SDP_ETPORTNOTEQUAL; } /* Assign remote connection address */ status = pj_sockaddr_init(tp_ice->af, &rem_conn_addr, &rem_conn->addr, (pj_uint16_t)rem_m->desc.port); if (status != PJ_SUCCESS) return status; if (tp_ice->comp_cnt > 1) { const pjmedia_sdp_attr *attr; /* Get default RTCP candidate from a=rtcp line, if present, otherwise * calculate default RTCP candidate from default RTP target. */ attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, &STR_RTCP, NULL); has_rtcp = (attr != NULL); if (attr) { pjmedia_sdp_rtcp_attr rtcp_attr; status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp_attr); if (status != PJ_SUCCESS) { /* Error parsing a=rtcp attribute */ return status; } if (rtcp_attr.addr.slen) { /* Verify address family matches */ if ((tp_ice->af==pj_AF_INET() && pj_strcmp(&rtcp_attr.addr_type, &STR_IP4)!=0) || (tp_ice->af==pj_AF_INET6() && pj_strcmp(&rtcp_attr.addr_type, &STR_IP6)!=0)) { return PJMEDIA_SDP_ETPORTNOTEQUAL; } /* Assign RTCP address */ status = pj_sockaddr_init(tp_ice->af, &rtcp_addr, &rtcp_attr.addr, (pj_uint16_t)rtcp_attr.port); if (status != PJ_SUCCESS) { return PJMEDIA_SDP_EINRTCP; } } else { /* Assign RTCP address */ status = pj_sockaddr_init(tp_ice->af, &rtcp_addr, NULL, (pj_uint16_t)rtcp_attr.port); if (status != PJ_SUCCESS) { return PJMEDIA_SDP_EINRTCP; } pj_sockaddr_copy_addr(&rtcp_addr, &rem_conn_addr); } } else { unsigned rtcp_port; rtcp_port = pj_sockaddr_get_port(&rem_conn_addr) + 1; pj_sockaddr_cp(&rtcp_addr, &rem_conn_addr); pj_sockaddr_set_port(&rtcp_addr, (pj_uint16_t)rtcp_port); } } /* Find the default addresses in a=candidate attributes. */ for (i=0; i<rem_m->attr_count; ++i) { pj_ice_sess_cand cand; if (pj_strcmp(&rem_m->attr[i]->name, &STR_CANDIDATE)!=0) continue; status = parse_cand(tp_ice->base.name, tmp_pool, &rem_m->attr[i]->value, &cand); if (status != PJ_SUCCESS) { PJ_LOG(4,(tp_ice->base.name, "Error in parsing SDP candidate attribute '%.*s', " "candidate is ignored", (int)rem_m->attr[i]->value.slen, rem_m->attr[i]->value.ptr)); continue; } if (!comp1_found && cand.comp_id==COMP_RTP && pj_sockaddr_cmp(&rem_conn_addr, &cand.addr)==0) { comp1_found = PJ_TRUE; } else if (!comp2_found && cand.comp_id==COMP_RTCP && pj_sockaddr_cmp(&rtcp_addr, &cand.addr)==0) { comp2_found = PJ_TRUE; } if (cand.comp_id == COMP_RTCP) has_rtcp = PJ_TRUE; if (comp1_found && (comp2_found || tp_ice->comp_cnt==1)) break; } /* Check matched component count and ice_mismatch */ if (comp1_found && (tp_ice->comp_cnt==1 || !has_rtcp)) { sdp_state->match_comp_cnt = 1; sdp_state->ice_mismatch = PJ_FALSE; } else if (comp1_found && comp2_found) { sdp_state->match_comp_cnt = 2; sdp_state->ice_mismatch = PJ_FALSE; } else { sdp_state->match_comp_cnt = (tp_ice->comp_cnt > 1 && has_rtcp)? 2 : 1; sdp_state->ice_mismatch = PJ_TRUE; } /* Detect remote restarting session */ if (pj_ice_strans_has_sess(tp_ice->ice_st) && (pj_ice_strans_sess_is_running(tp_ice->ice_st) || pj_ice_strans_sess_is_complete(tp_ice->ice_st))) { pj_str_t rem_run_ufrag, rem_run_pwd; pj_ice_strans_get_ufrag_pwd(tp_ice->ice_st, NULL, NULL, &rem_run_ufrag, &rem_run_pwd); if (pj_strcmp(&ufrag_attr->value, &rem_run_ufrag) || pj_strcmp(&pwd_attr->value, &rem_run_pwd)) { /* Remote offers to restart ICE */ sdp_state->ice_restart = PJ_TRUE; } else { sdp_state->ice_restart = PJ_FALSE; } } else { sdp_state->ice_restart = PJ_FALSE; } /* Detect our role */ if (current_ice_role==PJ_ICE_SESS_ROLE_CONTROLLING) { sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING; } else { if (pjmedia_sdp_attr_find(rem_sdp->attr_count, rem_sdp->attr, &STR_ICE_LITE, NULL) != NULL) { /* Remote is ICE Lite */ sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLING; } else { sdp_state->local_role = PJ_ICE_SESS_ROLE_CONTROLLED; } } PJ_LOG(4,(tp_ice->base.name, "Processing SDP: support ICE=%u, common comp_cnt=%u, " "ice_mismatch=%u, ice_restart=%u, local_role=%s", (sdp_state->match_comp_cnt != 0), sdp_state->match_comp_cnt, sdp_state->ice_mismatch, sdp_state->ice_restart, pj_ice_sess_role_name(sdp_state->local_role))); return PJ_SUCCESS; }
/* * Start ICE checks when both offer and answer have been negotiated * by SDP negotiator. */ static pj_status_t transport_media_start(pjmedia_transport *tp, pj_pool_t *tmp_pool, const pjmedia_sdp_session *sdp_local, const pjmedia_sdp_session *rem_sdp, unsigned media_index) { struct transport_ice *tp_ice = (struct transport_ice*)tp; pjmedia_sdp_media *rem_m; enum oa_role current_oa_role; pj_bool_t initial_oa; pj_status_t status; PJ_ASSERT_RETURN(tp && tmp_pool && rem_sdp, PJ_EINVAL); PJ_ASSERT_RETURN(media_index < rem_sdp->media_count, PJ_EINVAL); rem_m = rem_sdp->media[media_index]; initial_oa = tp_ice->initial_sdp; current_oa_role = tp_ice->oa_role; /* SDP has been negotiated */ tp_ice->initial_sdp = PJ_FALSE; tp_ice->oa_role = ROLE_NONE; /* Nothing to do if we don't have ICE session */ if (pj_ice_strans_has_sess(tp_ice->ice_st) == PJ_FALSE) { return PJ_SUCCESS; } /* Processing depends on the offer/answer role */ if (current_oa_role == ROLE_OFFERER) { /* * We are offerer. So this will be the first time we see the * remote's SDP. */ struct sdp_state answer_state; /* Verify the answer */ status = verify_ice_sdp(tp_ice, tmp_pool, rem_sdp, media_index, PJ_ICE_SESS_ROLE_CONTROLLING, &answer_state); if (status != PJ_SUCCESS) { /* Something wrong in the SDP answer */ set_no_ice(tp_ice, "Invalid remote SDP answer", status); return status; } /* Does it have ICE? */ if (answer_state.match_comp_cnt == 0) { /* Remote doesn't support ICE */ set_no_ice(tp_ice, "Remote answer doesn't support ICE", PJ_SUCCESS); return PJ_SUCCESS; } /* Check if remote has reported ice-mismatch */ if (pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, &STR_ICE_MISMATCH, NULL) != NULL) { /* Remote has reported ice-mismatch */ set_no_ice(tp_ice, "Remote answer contains 'ice-mismatch' attribute", PJ_SUCCESS); return PJ_SUCCESS; } /* Check if remote has indicated a restart */ if (answer_state.ice_restart) { PJ_LOG(2,(tp_ice->base.name, "Warning: remote has signalled ICE restart in SDP " "answer which is disallowed. Remote ICE negotiation" " may fail.")); } /* Check if the answer itself is mismatched */ if (answer_state.ice_mismatch) { /* This happens either when a B2BUA modified remote answer but * strangely didn't modify our offer, or remote is not capable * of detecting mismatch in our offer (it didn't put * 'ice-mismatch' attribute in the answer). */ PJ_LOG(2,(tp_ice->base.name, "Warning: remote answer mismatch, but it does not " "reject our offer with 'ice-mismatch'. ICE negotiation " "may fail")); } /* Do nothing if ICE is complete or running */ if (pj_ice_strans_sess_is_running(tp_ice->ice_st)) { PJ_LOG(4,(tp_ice->base.name, "Ignored offer/answer because ICE is running")); return PJ_SUCCESS; } if (pj_ice_strans_sess_is_complete(tp_ice->ice_st)) { PJ_LOG(4,(tp_ice->base.name, "ICE session unchanged")); return PJ_SUCCESS; } /* Start ICE */ } else { /* * We are answerer. We've seen and negotiated remote's SDP * before, and the result is in "rem_offer_state". */ const pjmedia_sdp_attr *ufrag_attr, *pwd_attr; /* Check for ICE in remote offer */ if (tp_ice->rem_offer_state.match_comp_cnt == 0) { /* No ICE attribute present */ set_no_ice(tp_ice, "Remote no longer offers ICE", PJ_SUCCESS); return PJ_SUCCESS; } /* Check for ICE ice_mismatch condition in the offer */ if (tp_ice->rem_offer_state.ice_mismatch) { set_no_ice(tp_ice, "Remote offer mismatch: ", PJNATH_EICEMISMATCH); return PJ_SUCCESS; } /* If ICE is complete and remote doesn't request restart, * then leave the session as is. */ if (!initial_oa && tp_ice->rem_offer_state.ice_restart == PJ_FALSE) { /* Remote has not requested ICE restart, so session is * unchanged. */ PJ_LOG(4,(tp_ice->base.name, "ICE session unchanged")); return PJ_SUCCESS; } /* Either remote has requested ICE restart or this is our * first answer. */ /* Stop ICE */ if (!initial_oa) { set_no_ice(tp_ice, "restarting by remote request..", PJ_SUCCESS); /* We have put new ICE ufrag and pwd in the answer. Now * create a new ICE session with that ufrag/pwd pair. */ get_ice_attr(sdp_local, sdp_local->media[media_index], &ufrag_attr, &pwd_attr); status = pj_ice_strans_init_ice(tp_ice->ice_st, tp_ice->rem_offer_state.local_role, &ufrag_attr->value, &pwd_attr->value); if (status != PJ_SUCCESS) { PJ_LOG(1,(tp_ice->base.name, "ICE re-initialization failed (status=%d)!", status)); return status; } } /* start ICE */ } /* Now start ICE */ status = start_ice(tp_ice, tmp_pool, rem_sdp, media_index); if (status != PJ_SUCCESS) { PJ_LOG(1,(tp_ice->base.name, "ICE restart failed (status=%d)!", status)); return status; } /* Done */ tp_ice->use_ice = PJ_TRUE; return PJ_SUCCESS; }