/* * Apply direction attribute in session to all media. */ static void apply_media_direction(pjmedia_sdp_session *sdp) { pjmedia_sdp_attr *dir_attr = NULL; unsigned i; const pj_str_t inactive = { "inactive", 8 }; const pj_str_t sendonly = { "sendonly", 8 }; const pj_str_t recvonly = { "recvonly", 8 }; const pj_str_t sendrecv = { "sendrecv", 8 }; /* Find direction attribute in session, don't need to find default * direction "sendrecv". */ for (i = 0; i < sdp->attr_count && !dir_attr; ++i) { if (!pj_strcmp(&sdp->attr[i]->name, &sendonly) || !pj_strcmp(&sdp->attr[i]->name, &recvonly) || !pj_strcmp(&sdp->attr[i]->name, &inactive)) { dir_attr = sdp->attr[i]; } } /* Found the direction attribute */ if (dir_attr) { /* Remove the direction attribute in session */ pjmedia_sdp_attr_remove(&sdp->attr_count, sdp->attr, dir_attr); /* Apply the direction attribute to all media, but not overriding it * if media already has direction attribute. */ for (i = 0; i < sdp->media_count; ++i) { pjmedia_sdp_media *m; unsigned j; /* Find direction attribute in this media */ m = sdp->media[i]; for (j = 0; j < m->attr_count; ++j) { if (!pj_strcmp(&m->attr[j]->name, &sendrecv) || !pj_strcmp(&m->attr[j]->name, &sendonly) || !pj_strcmp(&m->attr[j]->name, &recvonly) || !pj_strcmp(&m->attr[j]->name, &inactive)) { break; } } /* Not found, apply direction attribute from session */ if (j == m->attr_count) pjmedia_sdp_media_add_attr(m, dir_attr); } } }
pjmedia_sdp_media_remove_attr(pjmedia_sdp_media *m, pjmedia_sdp_attr *attr) { return pjmedia_sdp_attr_remove(&m->attr_count, m->attr, attr); }
PJ_DEF(pj_status_t) pjmedia_sdp_media_remove_attr(pjmedia_sdp_media *m, pjmedia_sdp_attr *attr) { return pjmedia_sdp_attr_remove(&m->attr_count, m->attr, attr); }
/* 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; }