/* Start ICE negotiation on the endpoint, based on parameter from * the other endpoint. */ static pj_status_t start_ice(struct ice_ept *ept, const struct ice_ept *remote) { pj_ice_sess_cand rcand[32]; unsigned i, rcand_cnt = 0; pj_status_t status; /* Enum remote candidates */ for (i=0; i<remote->cfg.comp_cnt; ++i) { unsigned cnt = PJ_ARRAY_SIZE(rcand) - rcand_cnt; status = pj_ice_strans_enum_cands(remote->ice, i+1, &cnt, rcand+rcand_cnt); if (status != PJ_SUCCESS) { app_perror(INDENT "err: pj_ice_strans_enum_cands()", status); return status; } rcand_cnt += cnt; } status = pj_ice_strans_start_ice(ept->ice, &remote->ufrag, &remote->pass, rcand_cnt, rcand); if (status != PJ_SUCCESS) { app_perror(INDENT "err: pj_ice_strans_start_ice()", status); return status; } return PJ_SUCCESS; }
static int extract_sdp_to_xml(struct ice_trans_s* icetrans,char buffer[], unsigned maxlen, ice_option_t opt, char *usrid) { char *p = buffer; unsigned comp; int printed; pj_str_t local_ufrag, local_pwd; pj_status_t status; //Me: add PRINT(" <registerPeer>"); PRINT("<device> <uniqueId>%s</uniqueId> </device>", usrid); /* Write "dummy" SDP v=, o=, s=, and t= lines */ // Me: comment //PRINT("v=0\no=- 3414953978 3414953978 IN IP4 localhost\ns=ice\nt=0 0\n"); /* Get ufrag and pwd from current session */ pj_ice_strans_get_ufrag_pwd(icetrans->icest, &local_ufrag, &local_pwd, NULL, NULL); /* Write the a=ice-ufrag and a=ice-pwd attributes */ // Me: comment //PRINT("a=ice-ufrag:%.*s\na=ice-pwd:%.*s\n", // (int)local_ufrag.slen, // local_ufrag.ptr, // (int)local_pwd.slen, // local_pwd.ptr); PRINT("<ufrag>%.*s</ufrag> <pwd>%.*s</pwd>", (int)local_ufrag.slen, local_ufrag.ptr, (int)local_pwd.slen, local_pwd.ptr); PRINT(" <candidateList>"); /* Write each component */ for (comp=0; comp<opt.comp_cnt; ++comp) { unsigned j, cand_cnt; pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND]; char ipaddr[PJ_INET6_ADDRSTRLEN]; /* Get default candidate for the component */ status = pj_ice_strans_get_def_cand(icetrans->icest, comp+1, &cand[0]); if (status != PJ_SUCCESS) return -status; /* Write the default address */ if (comp==0) { /* For component 1, default address is in m= and c= lines */ // Me: comment // PRINT("m=audio %d RTP/AVP 0\n" // "c=IN IP4 %s\n", // (int)pj_sockaddr_get_port(&cand[0].addr), // pj_sockaddr_print(&cand[0].addr, ipaddr, // sizeof(ipaddr), 0)); PRINT("<comp_1> <port>%d</port>" "<ip>%s</ip> </comp_1>", (int)pj_sockaddr_get_port(&cand[0].addr), pj_sockaddr_print(&cand[0].addr, ipaddr, sizeof(ipaddr), 0)); } else if (comp==1) { /* For component 2, default address is in a=rtcp line */ //Me: comment //PRINT("a=rtcp:%d IN IP4 %s\n", // (int)pj_sockaddr_get_port(&cand[0].addr), // pj_sockaddr_print(&cand[0].addr, ipaddr, // sizeof(ipaddr), 0)); } else { /* For other components, we'll just invent this.. */ // Me: comment //PRINT("a=Xice-defcand:%d IN IP4 %s\n", // (int)pj_sockaddr_get_port(&cand[0].addr), // pj_sockaddr_print(&cand[0].addr, ipaddr, // sizeof(ipaddr), 0)); } /* Enumerate all candidates for this component */ cand_cnt = PJ_ARRAY_SIZE(cand); status = pj_ice_strans_enum_cands(icetrans->icest, comp+1, &cand_cnt, cand); if (status != PJ_SUCCESS) return -status; /* And encode the candidates as SDP */ //const int buffer_xml_len = 2048; //char buffer_xml[buffer_xml_len]; for (j=0; j<cand_cnt; ++j) { printed = print_cand_to_xml(p, maxlen - (unsigned)(p-buffer), &cand[j]); if (printed < 0) return -PJ_ETOOSMALL; // printed = print_cand_to_xml(p, maxlen - (unsigned)(p-buffer), &cand[j]); // if (printed < 0) // return -PJ_ETOOSMALL; p += printed; } } PRINT("</candidateList>"); PRINT(" </registerPeer>"); if (p == buffer+maxlen) return -PJ_ETOOSMALL; *p = '\0'; //printf("DEBUGGGGGG: %s \n", (p - buffer); return (int)(p - buffer); }
int krx_ice_start_session(krx_ice* k) { pj_status_t r; if(!k) { return - 1; } if(!k->ice_st) { return -2; } if(pj_ice_strans_has_sess(k->ice_st)) { printf("Error: ice already has a session.\n"); return -3; } r = pj_ice_strans_init_ice(k->ice_st, PJ_ICE_SESS_ROLE_CONTROLLED, NULL, NULL); if(r != PJ_SUCCESS) { printf("Error: cannot initialize an ice session.\n"); return -4; } /* this is where we can create an sdp */ char sdp_buf[8096] = { 0 } ; sprintf(sdp_buf, "v=0\n" "o=- 123456789 34234324 IN IP4 localhost\n" /* - [identifier] [session version] IN IP4 localhost */ "s=krx_ice\n" /* software */ "t=0 0\n" /* start, ending time */ "a=ice-ufrag:%s\n" "a=ice-pwd:%s\n" , k->ice_ufrag, k->ice_pwd ); /* write each component */ for(int i = 0; i < k->ncomp; ++i) { pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND] = { 0 } ; char ipaddr[PJ_INET6_ADDRSTRLEN] = { 0 } ; /* get default candidate for component, note that compoments start numbering from 1, not zero. */ r = pj_ice_strans_get_def_cand(k->ice_st, 1, &cand[0]); if(r != PJ_SUCCESS) { printf("Error: cannot retrieve default candidate for component: %d\n", i+1); continue; } if(i == 0) { int offset = strlen(sdp_buf); sprintf(sdp_buf + offset, "m=video %d RTP/SAVPF 120\n" "c=IN IP4 %s\n" , (int)pj_sockaddr_get_port(&cand[0].addr), pj_sockaddr_print(&cand[0].addr, ipaddr, sizeof(ipaddr), 0) ); /* print all candidates */ unsigned num_cands = PJ_ARRAY_SIZE(cand); printf("Found number of candidates: %d\n", num_cands); // (ice_st && ice_st->ice && comp_id && comp_id <= ice_st->comp_cnt && count && cand), printf("ice: %p\n", k->ice_st); r = pj_ice_strans_enum_cands(k->ice_st, i + 1, &num_cands, cand); if(r != PJ_SUCCESS) { printf("Error: cannot retrieve candidates.\n"); exit(1); } #if 1 for(int j = 0; j < num_cands; ++j) { int offset = strlen(sdp_buf); char* start_addr = sdp_buf + offset; krx_ice_candidate_to_string(sdp_buf, sizeof(sdp_buf)-offset, &cand[j]); char* end_addr = sdp_buf + strlen(sdp_buf); printf("--------\n%s\n--------------\n", sdp_buf); } offset = strlen(sdp_buf); char* start_addr = sdp_buf + offset; krx_ice_candidate_to_string(sdp_buf + offset, sizeof(sdp_buf)-offset, &cand[1]); char* end_addr = sdp_buf + strlen(sdp_buf); #endif } } printf("SDP: %s\n", sdp_buf); r = pj_ice_strans_init_ice(k->ice_st, PJ_ICE_SESS_ROLE_CONTROLLED, NULL, NULL); CHECK_PJ_STATUS(r, "Error: cannot init ice session.\n", -4); return 0; }
/* * Encode ICE information in SDP. */ static int encode_session(char buffer[], unsigned maxlen) { char *p = buffer; unsigned comp; int printed; pj_str_t local_ufrag, local_pwd; pj_status_t status; /* Write "dummy" SDP v=, o=, s=, and t= lines */ PRINT("v=0\no=- 3414953978 3414953978 IN IP4 localhost\ns=ice\nt=0 0\n", 0, 0, 0, 0, 0, 0); /* Get ufrag and pwd from current session */ pj_ice_strans_get_ufrag_pwd(icedemo.icest, &local_ufrag, &local_pwd, NULL, NULL); /* Write the a=ice-ufrag and a=ice-pwd attributes */ PRINT("a=ice-ufrag:%.*s\na=ice-pwd:%.*s\n", (int)local_ufrag.slen, local_ufrag.ptr, (int)local_pwd.slen, local_pwd.ptr, 0, 0); /* Write each component */ for (comp=0; comp<icedemo.opt.comp_cnt; ++comp) { unsigned j, cand_cnt; pj_ice_sess_cand cand[PJ_ICE_ST_MAX_CAND]; char ipaddr[PJ_INET6_ADDRSTRLEN]; /* Get default candidate for the component */ status = pj_ice_strans_get_def_cand(icedemo.icest, comp+1, &cand[0]); if (status != PJ_SUCCESS) return -status; /* Write the default address */ if (comp==0) { /* For component 1, default address is in m= and c= lines */ PRINT("m=audio %d RTP/AVP 0\n" "c=IN IP4 %s\n", (int)pj_sockaddr_get_port(&cand[0].addr), pj_sockaddr_print(&cand[0].addr, ipaddr, sizeof(ipaddr), 0), 0, 0, 0, 0); } else if (comp==1) { /* For component 2, default address is in a=rtcp line */ PRINT("a=rtcp:%d IN IP4 %s\n", (int)pj_sockaddr_get_port(&cand[0].addr), pj_sockaddr_print(&cand[0].addr, ipaddr, sizeof(ipaddr), 0), 0, 0, 0, 0); } else { /* For other components, we'll just invent this.. */ PRINT("a=Xice-defcand:%d IN IP4 %s\n", (int)pj_sockaddr_get_port(&cand[0].addr), pj_sockaddr_print(&cand[0].addr, ipaddr, sizeof(ipaddr), 0), 0, 0, 0, 0); } /* Enumerate all candidates for this component */ status = pj_ice_strans_enum_cands(icedemo.icest, comp+1, &cand_cnt, cand); if (status != PJ_SUCCESS) return -status; /* And encode the candidates as SDP */ for (j=0; j<cand_cnt; ++j) { printed = print_cand(p, maxlen - (p-buffer), &cand[j]); if (printed < 0) return -PJ_ETOOSMALL; p += printed; } } if (p == buffer+maxlen) return -PJ_ETOOSMALL; *p = '\0'; return p - 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; }