Esempio n. 1
0
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);
}
Esempio n. 2
0
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");
}
Esempio n. 3
0
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");
}
Esempio n. 4
0
/* 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;
}
Esempio n. 5
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);
}
Esempio n. 6
0
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;
}
Esempio n. 7
0
/* 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;
}
Esempio n. 8
0
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++;
	}
}
Esempio n. 9
0
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;
}