static void insert_candidates(struct sdp_chopper *chop, struct packet_stream *rtp, struct packet_stream *rtcp, struct sdp_ng_flags *flags, struct sdp_media *sdp_media) { const struct local_intf *ifa; struct call_media *media; struct ice_agent *ag; unsigned int type_pref, local_pref; enum ice_candidate_type cand_type; struct ice_candidate *cand; media = rtp->media; cand_type = ICT_HOST; if (flags->ice_force_relay) cand_type = ICT_RELAY; if (MEDIA_ISSET(media, PASSTHRU)) new_priority(sdp_media, cand_type, &type_pref, &local_pref); else { type_pref = ice_type_preference(cand_type); local_pref = -1; } ag = media->ice_agent; if (ag && AGENT_ISSET(ag, COMPLETED)) { ifa = rtp->selected_sfd->local_intf; insert_candidate(chop, rtp->selected_sfd, type_pref, ifa->unique_id, cand_type); if (rtcp) /* rtcp-mux only possible in answer */ insert_candidate(chop, rtcp->selected_sfd, type_pref, ifa->unique_id, cand_type); if (flags->opmode == OP_OFFER && AGENT_ISSET(ag, CONTROLLING)) { GQueue rc; GList *l; chopper_append_c(chop, "a=remote-candidates:"); ice_remote_candidates(&rc, ag); for (l = rc.head; l; l = l->next) { if (l != rc.head) chopper_append_c(chop, " "); cand = l->data; chopper_append_printf(chop, "%lu %s %u", cand->component_id, sockaddr_print_buf(&cand->endpoint.address), cand->endpoint.port); } chopper_append_c(chop, "\r\n"); g_queue_clear(&rc); } return; } insert_sfd_candidates(chop, rtp, type_pref, local_pref, cand_type); if (rtcp) /* rtcp-mux only possible in answer */ insert_sfd_candidates(chop, rtcp, type_pref, local_pref, cand_type); }
static void insert_crypto(struct call_media *media, struct sdp_chopper *chop) { char b64_buf[((SRTP_MAX_MASTER_KEY_LEN + SRTP_MAX_MASTER_SALT_LEN) / 3 + 1) * 4 + 4]; char *p; int state = 0, save = 0, i; struct crypto_params *cp = &media->sdes_out.params; unsigned long long ull; if (!cp->crypto_suite || !MEDIA_ISSET(media, SDES) || MEDIA_ISSET(media, PASSTHRU)) return; p = b64_buf; p += g_base64_encode_step((unsigned char *) cp->master_key, cp->crypto_suite->master_key_len, 0, p, &state, &save); p += g_base64_encode_step((unsigned char *) cp->master_salt, cp->crypto_suite->master_salt_len, 0, p, &state, &save); p += g_base64_encode_close(0, p, &state, &save); chopper_append_c(chop, "a=crypto:"); chopper_append_printf(chop, "%u ", media->sdes_out.tag); chopper_append_c(chop, cp->crypto_suite->name); chopper_append_c(chop, " inline:"); chopper_append_dup(chop, b64_buf, p - b64_buf); if (cp->mki_len) { ull = 0; for (i = 0; i < cp->mki_len && i < sizeof(ull); i++) ull |= cp->mki[cp->mki_len - i - 1] << (i * 8); chopper_append_printf(chop, "|%llu:%u", ull, cp->mki_len); } if (cp->session_params.unencrypted_srtp) chopper_append_c(chop, " UNENCRYPTED_SRTP"); if (cp->session_params.unencrypted_srtcp) chopper_append_c(chop, " UNENCRYPTED_SRTCP"); if (cp->session_params.unauthenticated_srtp) chopper_append_c(chop, " UNAUTHENTICATED_SRTP"); chopper_append_c(chop, "\r\n"); }
static void insert_dtls(struct call_media *media, struct sdp_chopper *chop) { char hexbuf[DTLS_MAX_DIGEST_LEN * 3 + 2]; unsigned char *p; char *o; int i; const struct dtls_hash_func *hf; const char *actpass; struct call *call = media->call; if (!call->dtls_cert || !MEDIA_ISSET(media, DTLS) || MEDIA_ISSET(media, PASSTHRU)) return; hf = call->dtls_cert->fingerprint.hash_func; assert(hf->num_bytes > 0); p = call->dtls_cert->fingerprint.digest; o = hexbuf; for (i = 0; i < hf->num_bytes; i++) o += sprintf(o, "%02X:", *p++); *(--o) = '\0'; actpass = "******"; if (MEDIA_ARESET2(media, SETUP_PASSIVE, SETUP_ACTIVE)) actpass = "******"; else if (MEDIA_ISSET(media, SETUP_PASSIVE)) actpass = "******"; else if (MEDIA_ISSET(media, SETUP_ACTIVE)) actpass = "******"; chopper_append_c(chop, "a=setup:"); chopper_append_c(chop, actpass); chopper_append_c(chop, "\r\na=fingerprint:"); chopper_append_c(chop, hf->name); chopper_append_c(chop, " "); chopper_append_dup(chop, hexbuf, o - hexbuf); chopper_append_c(chop, "\r\n"); }
/* called with call locked in W or R with ps->in_lock held */ int dtls(struct packet_stream *ps, const str *s, struct sockaddr_in6 *fsin) { struct dtls_connection *d; int ret; unsigned char buf[0x10000], ctrl[256]; struct msghdr mh; struct iovec iov; struct sockaddr_in6 sin; if (!ps || !ps->sfd) return 0; if (!MEDIA_ISSET(ps->media, DTLS)) return 0; d = &ps->sfd->dtls; if (s) __DBG("dtls packet input: len %u %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", s->len, (unsigned char) s->s[0], (unsigned char) s->s[1], (unsigned char) s->s[2], (unsigned char) s->s[3], (unsigned char) s->s[4], (unsigned char) s->s[5], (unsigned char) s->s[6], (unsigned char) s->s[7], (unsigned char) s->s[8], (unsigned char) s->s[9], (unsigned char) s->s[10], (unsigned char) s->s[11], (unsigned char) s->s[12], (unsigned char) s->s[13], (unsigned char) s->s[14], (unsigned char) s->s[15]); if (d->connected) return 0; if (!d->init || !d->ssl) return -1; if (s) { BIO_write(d->r_bio, s->s, s->len); /* we understand this as preference of DTLS over SDES */ MEDIA_CLEAR(ps->media, SDES); } ret = try_connect(d); if (ret == -1) { ilog(LOG_ERROR, "DTLS error on local port %hu", ps->sfd->fd.localport); /* fatal error */ dtls_connection_cleanup(d); return 0; } else if (ret == 1) { /* connected! */ if (dtls_setup_crypto(ps, d)) /* XXX ?? */ ; if (PS_ISSET(ps, RTP) && PS_ISSET(ps, RTCP) && ps->rtcp_sibling && MEDIA_ISSET(ps->media, RTCP_MUX)) { if (dtls_setup_crypto(ps->rtcp_sibling, d)) /* XXX ?? */ ; } } ret = BIO_ctrl_pending(d->w_bio); if (ret <= 0) return 0; if (ret > sizeof(buf)) { ilog(LOG_ERROR, "BIO buffer overflow"); (void) BIO_reset(d->w_bio); return 0; } ret = BIO_read(d->w_bio, buf, ret); if (ret <= 0) return 0; __DBG("dtls packet output: len %u %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", ret, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); if (!fsin) { ZERO(sin); sin.sin6_family = AF_INET6; sin.sin6_addr = ps->endpoint.ip46; sin.sin6_port = htons(ps->endpoint.port); fsin = &sin; } ZERO(mh); mh.msg_control = ctrl; mh.msg_controllen = sizeof(ctrl); mh.msg_name = fsin; mh.msg_namelen = sizeof(*fsin); mh.msg_iov = &iov; mh.msg_iovlen = 1; ZERO(iov); iov.iov_base = buf; iov.iov_len = ret; stream_msg_mh_src(ps, &mh); sendmsg(ps->sfd->fd.fd, &mh, 0); return 0; }
/* called with the call lock held in W, hence agent doesn't need to be locked */ void ice_update(struct ice_agent *ag, struct stream_params *sp) { GList *l, *k; struct ice_candidate *cand, *dup; struct call_media *media; struct call *call; int recalc = 0; unsigned int comps; struct packet_stream *components[MAX_COMPONENTS], *ps; GQueue *candidates; if (!ag) return; atomic64_set(&ag->last_activity, poller_now); media = ag->media; call = media->call; __role_change(ag, MEDIA_ISSET(media, ICE_CONTROLLING)); if (sp) { /* check for ICE restarts */ if (ag->ufrag[0].s && sp->ice_ufrag.s && str_cmp_str(&ag->ufrag[0], &sp->ice_ufrag)) __ice_restart(ag); else if (ag->pwd[0].s && sp->ice_pwd.s && str_cmp_str(&ag->pwd[0], &sp->ice_pwd)) __ice_restart(ag); else if (ag->local_interface != media->interface) __ice_restart(ag); /* update remote info */ if (sp->ice_ufrag.s) call_str_cpy(call, &ag->ufrag[0], &sp->ice_ufrag); if (sp->ice_pwd.s) call_str_cpy(call, &ag->pwd[0], &sp->ice_pwd); candidates = &sp->ice_candidates; } else /* this is a dummy update in case rtcp-mux has changed */ candidates = &ag->remote_candidates; /* get our component streams */ ZERO(components); comps = 0; for (l = media->streams.head; l; l = l->next) components[comps++] = l->data; if (comps == 2 && MEDIA_ISSET(media, RTCP_MUX)) components[1] = NULL; comps = 0; for (l = candidates->head; l; l = l->next) { if (ag->remote_candidates.length >= MAX_ICE_CANDIDATES) { ilog(LOG_WARNING, "Maxmimum number of ICE candidates exceeded"); break; } cand = l->data; /* skip invalid */ if (!cand->component_id || cand->component_id > G_N_ELEMENTS(components)) continue; ps = components[cand->component_id - 1]; if (ps) /* only count active components */ comps = MAX(comps, cand->component_id); dup = g_hash_table_lookup(ag->candidate_hash, cand); if (!sp && dup) /* this isn't a real update, so only check pairings */ goto pair; /* check for duplicates */ if (dup) { /* if this is peer reflexive, we've learned it through STUN. * otherwise it's simply one we've seen before. */ if (dup->type == ICT_PRFLX) { ilog(LOG_DEBUG, "Replacing previously learned prflx ICE candidate with " STR_FORMAT":%lu", STR_FMT(&cand->foundation), cand->component_id); } else { /* if the new one has higher priority then the old one, then we * update it, otherwise we just drop it */ if (cand->priority <= dup->priority) { ilog(LOG_DEBUG, "Dropping new ICE candidate "STR_FORMAT" in favour of " STR_FORMAT":%lu", STR_FMT(&cand->foundation), STR_FMT(&dup->foundation), cand->component_id); continue; } ilog(LOG_DEBUG, "Replacing known ICE candidate "STR_FORMAT" with higher " "priority " STR_FORMAT":%lu", STR_FMT(&dup->foundation), STR_FMT(&cand->foundation), cand->component_id); } /* priority and foundation may change */ g_hash_table_remove(ag->foundation_hash, dup); recalc += __copy_cand(call, dup, cand); } else { ilog(LOG_DEBUG, "Learning new ICE candidate "STR_FORMAT":%lu", STR_FMT(&cand->foundation), cand->component_id); dup = g_slice_alloc(sizeof(*dup)); __copy_cand(call, dup, cand); g_hash_table_insert(ag->candidate_hash, dup, dup); g_queue_push_tail(&ag->remote_candidates, dup); } g_hash_table_insert(ag->foundation_hash, dup, dup); pair: if (!ps) continue; for (k = ag->local_interface->list.head; k; k = k->next) { /* skip duplicates here also */ if (__pair_lookup(ag, dup, k->data)) continue; __pair_candidate(k->data, ag, dup, ps); } } if (comps) ag->active_components = comps; if (!ag->active_components) { /* determine components for tricke-ice case */ comps = 2; if (!components[1]) comps = 1; ag->active_components = comps; } /* if we're here, we can start our ICE checks */ if (recalc) __recalc_pair_prios(ag); else __all_pairs_list(ag); if (comps) __do_ice_checks(ag); else __agent_shutdown(ag); }
static int __ensure_codec_handler(struct media_player *mp, AVStream *avs) { if (mp->handler) return 0; // synthesise rtp payload type struct rtp_payload_type src_pt = { .payload_type = -1 }; // src_pt.codec_def = codec_find_by_av(avs->codec->codec_id); `codec` is deprecated src_pt.codec_def = codec_find_by_av(avs->CODECPAR->codec_id); if (!src_pt.codec_def) { ilog(LOG_ERR, "Attempting to play media from an unsupported file format/codec"); return -1; } src_pt.encoding = src_pt.codec_def->rtpname_str; src_pt.channels = avs->CODECPAR->channels; src_pt.clock_rate = avs->CODECPAR->sample_rate; codec_init_payload_type(&src_pt, mp->media); // find suitable output payload type struct rtp_payload_type *dst_pt; for (GList *l = mp->media->codecs_prefs_send.head; l; l = l->next) { dst_pt = l->data; if (dst_pt->codec_def && !dst_pt->codec_def->supplemental) goto found; } dst_pt = NULL; found: if (!dst_pt) { ilog(LOG_ERR, "No supported output codec found in SDP"); return -1; } ilog(LOG_DEBUG, "Output codec for media playback is " STR_FORMAT, STR_FMT(&dst_pt->encoding_with_params)); // if we played anything before, scale our sync TS according to the time // that has passed if (mp->sync_ts_tv.tv_sec) { long long ts_diff_us = timeval_diff(&rtpe_now, &mp->sync_ts_tv); mp->sync_ts += ts_diff_us * dst_pt->clock_rate / 1000000 / dst_pt->codec_def->clockrate_mult; } mp->handler = codec_handler_make_playback(&src_pt, dst_pt, mp->sync_ts); if (!mp->handler) return -1; mp->duration = avs->duration * 1000 * avs->time_base.num / avs->time_base.den; return 0; } // appropriate lock must be held static void media_player_read_packet(struct media_player *mp) { if (!mp->fmtctx) return; int ret = av_read_frame(mp->fmtctx, &mp->pkt); if (ret < 0) { if (ret == AVERROR_EOF) { ilog(LOG_DEBUG, "EOF reading from media stream"); return; } ilog(LOG_ERR, "Error while reading from media stream"); return; } if (!mp->fmtctx->streams) { ilog(LOG_ERR, "No AVStream present in format context"); goto out; } AVStream *avs = mp->fmtctx->streams[0]; if (!avs) { ilog(LOG_ERR, "No AVStream present in format context"); goto out; } if (__ensure_codec_handler(mp, avs)) goto out; // scale pts and duration according to sample rate long long duration_scaled = mp->pkt.duration * avs->CODECPAR->sample_rate * avs->time_base.num / avs->time_base.den; unsigned long long pts_scaled = mp->pkt.pts * avs->CODECPAR->sample_rate * avs->time_base.num / avs->time_base.den; long long us_dur = mp->pkt.duration * 1000000LL * avs->time_base.num / avs->time_base.den; ilog(LOG_DEBUG, "read media packet: pts %llu duration %lli (scaled %llu/%lli, %lli us), " "sample rate %i, time_base %i/%i", (unsigned long long) mp->pkt.pts, (long long) mp->pkt.duration, pts_scaled, duration_scaled, us_dur, avs->CODECPAR->sample_rate, avs->time_base.num, avs->time_base.den); // synthesise fake RTP header and media_packet context struct rtp_header rtp = { .timestamp = pts_scaled, // taken verbatim by handler_func_playback w/o byte swap .seq_num = htons(mp->seq), }; struct media_packet packet = { .tv = rtpe_now, .call = mp->call, .media = mp->media, .rtp = &rtp, .ssrc_out = mp->ssrc_out, }; str_init_len(&packet.raw, (char *) mp->pkt.data, mp->pkt.size); packet.payload = packet.raw; mp->handler->func(mp->handler, &packet); // as this is timing sensitive and we may have spent some time decoding, // update our global "now" timestamp gettimeofday(&rtpe_now, NULL); // keep track of RTP timestamps and real clock. look at the last packet we received // and update our sync TS. if (packet.packets_out.head) { struct codec_packet *p = packet.packets_out.head->data; if (p->rtp) { mp->sync_ts = ntohl(p->rtp->timestamp); mp->sync_ts_tv = p->to_send; } } media_packet_encrypt(mp->crypt_handler->out->rtp_crypt, mp->sink, &packet); mutex_lock(&mp->sink->out_lock); if (media_socket_dequeue(&packet, mp->sink)) ilog(LOG_ERR, "Error sending playback media to RTP sink"); mutex_unlock(&mp->sink->out_lock); timeval_add_usec(&mp->next_run, us_dur); timerthread_obj_schedule_abs(&mp->tt_obj, &mp->next_run); out: av_packet_unref(&mp->pkt); } // call->master_lock held in W static int media_player_play_init(struct media_player *mp) { media_player_shutdown(mp); // find call media suitable for playback struct call_media *media; for (GList *l = mp->ml->medias.head; l; l = l->next) { media = l->data; if (media->type_id != MT_AUDIO) continue; if (!MEDIA_ISSET(media, SEND)) continue; if (media->streams.length == 0) continue; goto found; } media = NULL; found: if (!media) { ilog(LOG_ERR, "No suitable SDP section for media playback"); return -1; } mp->media = media; mp->sink = media->streams.head->data; mp->crypt_handler = determine_handler(&transport_protocols[PROTO_RTP_AVP], media, 1); return 0; } // call->master_lock held in W static void media_player_play_start(struct media_player *mp) { // needed to have usable duration for some formats. ignore errors. avformat_find_stream_info(mp->fmtctx, NULL); mp->next_run = rtpe_now; // give ourselves a bit of a head start with decoding timeval_add_usec(&mp->next_run, -50000); media_player_read_packet(mp); } #endif // call->master_lock held in W int media_player_play_file(struct media_player *mp, const str *file) { #ifdef WITH_TRANSCODING if (media_player_play_init(mp)) return -1; char file_s[PATH_MAX]; snprintf(file_s, sizeof(file_s), STR_FORMAT, STR_FMT(file)); int ret = avformat_open_input(&mp->fmtctx, file_s, NULL, NULL); if (ret < 0) { ilog(LOG_ERR, "Failed to open media file for playback: %s", av_error(ret)); return -1; } media_player_play_start(mp); return 0; #else return -1; #endif } #ifdef WITH_TRANSCODING static int __mp_avio_read_wrap(void *opaque, uint8_t *buf, int buf_size) { struct media_player *mp = opaque; if (buf_size < 0) return AVERROR(EINVAL); if (buf_size == 0) return 0; if (!mp->read_pos.len) return AVERROR_EOF; int len = buf_size; if (len > mp->read_pos.len) len = mp->read_pos.len; memcpy(buf, mp->read_pos.s, len); str_shift(&mp->read_pos, len); return len; } static int __mp_avio_read(void *opaque, uint8_t *buf, int buf_size) { ilog(LOG_DEBUG, "__mp_avio_read(%i)", buf_size); int ret = __mp_avio_read_wrap(opaque, buf, buf_size); ilog(LOG_DEBUG, "__mp_avio_read(%i) = %i", buf_size, ret); return ret; }
/* called with call->master_lock held in W */ int sdp_replace(struct sdp_chopper *chop, GQueue *sessions, struct call_monologue *monologue, struct sdp_ng_flags *flags) { struct sdp_session *session; struct sdp_media *sdp_media; GList *l, *k, *m, *j; int media_index, sess_conn; struct call_media *call_media; struct packet_stream *ps, *ps_rtcp; m = monologue->medias.head; for (l = sessions->head; l; l = l->next) { session = l->data; if (!m) goto error; call_media = m->data; if (call_media->index != 1) goto error; j = call_media->streams.head; if (!j) goto error; ps = j->data; sess_conn = 0; if (flags->replace_sess_conn) sess_conn = 1; else { for (k = session->media_streams.head; k; k = k->next) { sdp_media = k->data; if (!sdp_media->connection.parsed) { sess_conn = 1; break; } } } if (session->origin.parsed && flags->replace_origin && !flags->ice_force_relay) { if (replace_network_address(chop, &session->origin.address, ps, flags)) goto error; } if (session->connection.parsed && sess_conn && !flags->ice_force_relay) { if (replace_network_address(chop, &session->connection.address, ps, flags)) goto error; } if (!MEDIA_ISSET(call_media, PASSTHRU)) { if (process_session_attributes(chop, &session->attributes, flags)) goto error; } media_index = 1; for (k = session->media_streams.head; k; k = k->next) { sdp_media = k->data; if (!m) goto error; call_media = m->data; if (call_media->index != media_index) goto error; j = call_media->streams.head; if (!j) goto error; ps = j->data; if (!flags->ice_force_relay) { if (replace_media_port(chop, sdp_media, ps)) goto error; if (replace_consecutive_port_count(chop, sdp_media, ps, j)) goto error; if (replace_transport_protocol(chop, sdp_media, call_media)) goto error; if (sdp_media->connection.parsed) { if (replace_network_address(chop, &sdp_media->connection.address, ps, flags)) goto error; } } if (process_media_attributes(chop, sdp_media, flags, call_media)) goto error; copy_up_to_end_of(chop, &sdp_media->s); ps_rtcp = NULL; if (ps->rtcp_sibling) { ps_rtcp = ps->rtcp_sibling; j = j->next; if (!j) goto error; assert(j->data == ps_rtcp); } if (!sdp_media->port_num || !ps->sfd) goto next; if (MEDIA_ARESET2(call_media, SEND, RECV)) chopper_append_c(chop, "a=sendrecv\r\n"); else if (MEDIA_ISSET(call_media, SEND)) chopper_append_c(chop, "a=sendonly\r\n"); else if (MEDIA_ISSET(call_media, RECV)) chopper_append_c(chop, "a=recvonly\r\n"); else chopper_append_c(chop, "a=inactive\r\n"); if (call_media->protocol && call_media->protocol->rtp) { if (MEDIA_ISSET(call_media, RTCP_MUX) && flags->opmode == OP_ANSWER) { chopper_append_c(chop, "a=rtcp:"); chopper_append_printf(chop, "%hu", ps->sfd->fd.localport); chopper_append_c(chop, "\r\na=rtcp-mux\r\n"); ps_rtcp = NULL; } else if (ps_rtcp && !flags->ice_force_relay) { chopper_append_c(chop, "a=rtcp:"); chopper_append_printf(chop, "%hu", ps_rtcp->sfd->fd.localport); if (!MEDIA_ISSET(call_media, RTCP_MUX)) chopper_append_c(chop, "\r\n"); else chopper_append_c(chop, "\r\na=rtcp-mux\r\n"); } } else ps_rtcp = NULL; insert_crypto(call_media, chop); insert_dtls(call_media, chop); if (MEDIA_ISSET(call_media, ICE) && call_media->ice_agent) { chopper_append_c(chop, "a=ice-ufrag:"); chopper_append_str(chop, &call_media->ice_agent->ufrag[1]); chopper_append_c(chop, "\r\na=ice-pwd:"); chopper_append_str(chop, &call_media->ice_agent->pwd[1]); chopper_append_c(chop, "\r\n"); } if (!flags->ice_remove) insert_candidates(chop, ps, ps_rtcp, flags, sdp_media); next: media_index++; m = m->next; } } copy_remainder(chop); return 0; error: ilog(LOG_ERROR, "Error rewriting SDP"); return -1; }
static void insert_candidates(struct sdp_chopper *chop, struct packet_stream *rtp, struct packet_stream *rtcp, struct sdp_ng_flags *flags, struct sdp_media *sdp_media) { GList *l; struct interface_address *ifa; unsigned int pref; struct call_media *media; struct local_interface *lif; struct ice_agent *ag; unsigned int type_pref, local_pref; enum ice_candidate_type cand_type; struct ice_candidate *cand; media = rtp->media; cand_type = ICT_HOST; if (flags->ice_force_relay) cand_type = ICT_RELAY; if (MEDIA_ISSET(media, PASSTHRU)) new_priority(sdp_media, cand_type, &type_pref, &local_pref); else { type_pref = ice_type_preference(cand_type); local_pref = -1; } ag = media->ice_agent; lif = ag ? ag->local_interface : media->interface; if (ag && AGENT_ISSET(ag, COMPLETED)) { ifa = g_atomic_pointer_get(&media->local_address); insert_candidate(chop, rtp, 1, type_pref, ifa->preference, cand_type, ifa); if (rtcp) /* rtcp-mux only possible in answer */ insert_candidate(chop, rtcp, 2, type_pref, ifa->preference, cand_type, ifa); if (flags->opmode == OP_OFFER && AGENT_ISSET(ag, CONTROLLING)) { GQueue rc; GList *l; chopper_append_c(chop, "a=remote-candidates:"); ice_remote_candidates(&rc, ag); for (l = rc.head; l; l = l->next) { if (l != rc.head) chopper_append_c(chop, " "); cand = l->data; chopper_append_printf(chop, "%lu %s %u", cand->component_id, smart_ntop_buf(&cand->endpoint.ip46), cand->endpoint.port); } chopper_append_c(chop, "\r\n"); g_queue_clear(&rc); } return; } for (l = lif->list.head; l; l = l->next) { ifa = l->data; pref = (local_pref == -1) ? ifa->preference : local_pref; insert_candidate(chop, rtp, 1, type_pref, pref, cand_type, ifa); if (rtcp) /* rtcp-mux only possible in answer */ insert_candidate(chop, rtcp, 2, type_pref, pref, cand_type, ifa); if (local_pref != -1) local_pref++; } }
static int process_media_attributes(struct sdp_chopper *chop, struct sdp_media *sdp, struct sdp_ng_flags *flags, struct call_media *media) { GList *l; struct sdp_attributes *attrs = &sdp->attributes; struct sdp_attribute *attr, *a; for (l = attrs->list.head; l; l = l->next) { attr = l->data; switch (attr->attr) { case ATTR_ICE: case ATTR_ICE_UFRAG: case ATTR_ICE_PWD: case ATTR_ICE_OPTIONS: case ATTR_ICE_LITE: if (MEDIA_ISSET(media, PASSTHRU)) break; if (!flags->ice_remove && !flags->ice_force) break; goto strip; case ATTR_CANDIDATE: if (MEDIA_ISSET(media, PASSTHRU)) break; if (flags->ice_force_relay) { if ((attr->u.candidate.type_str.len == 5) && (strncasecmp(attr->u.candidate.type_str.s, "relay", 5) == 0)) goto strip; else break; } if (!flags->ice_remove && !flags->ice_force) break; goto strip; case ATTR_RTCP: case ATTR_RTCP_MUX: if (flags->ice_force_relay) break; case ATTR_INACTIVE: case ATTR_SENDONLY: case ATTR_RECVONLY: case ATTR_SENDRECV: goto strip; case ATTR_EXTMAP: case ATTR_CRYPTO: case ATTR_FINGERPRINT: case ATTR_SETUP: if (MEDIA_ISSET(media, PASSTHRU)) break; goto strip; case ATTR_MID: if (MEDIA_ISSET(media, PASSTHRU)) break; a = attr_get_by_id(&sdp->session->attributes, ATTR_GROUP); if (a && a->u.group.semantics == GROUP_BUNDLE) goto strip; break; default: break; } continue; strip: if (copy_up_to(chop, &attr->full_line)) return -1; if (skip_over(chop, &attr->full_line)) return -1; } return 0; }