Exemple #1
0
/**
 * The RFC 3550 Appendix A assumes there are multiple sources but
 * some of the supported endpoints (e.g. the nanoBTS) can only handle
 * one source and this code will patch packages to appear as if there
 * is only one source.
 * There is also no probation period for new sources. Every package
 * we receive will be seen as a switch in streams.
 */
void mgcp_patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
			  struct mgcp_rtp_end *rtp_end, struct sockaddr_in *addr,
			  char *data, int len)
{
	uint32_t arrival_time;
	int32_t transit, d;
	uint16_t seq, udelta;
	uint32_t timestamp, ssrc;
	struct rtp_hdr *rtp_hdr;
	int payload = rtp_end->codec.payload_type;

	if (len < sizeof(*rtp_hdr))
		return;

	rtp_hdr = (struct rtp_hdr *) data;
	seq = ntohs(rtp_hdr->sequence);
	timestamp = ntohl(rtp_hdr->timestamp);
	arrival_time = get_current_ts(rtp_end->codec.rate);
	ssrc = ntohl(rtp_hdr->ssrc);
	transit = arrival_time - timestamp;

	if (!state->initialized) {
		state->in_stream.last_seq = seq - 1;
		state->in_stream.ssrc = state->orig_ssrc = ssrc;
		state->in_stream.last_tsdelta = 0;
		state->base_seq = seq;
		state->initialized = 1;
		state->jitter = 0;
		state->transit = transit;
		state->packet_duration = mgcp_rtp_packet_duration(endp, rtp_end);
		state->out_stream = state->in_stream;
		state->out_stream.last_timestamp = timestamp;
		state->out_stream.ssrc = ssrc - 1; /* force output SSRC change */
		LOGP(DMGCP, LOGL_INFO,
			"Initializing stream on 0x%x SSRC: %u timestamp: %u "
			"pkt-duration: %d, from %s:%d in %d\n",
			ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
			state->seq_offset, state->packet_duration,
			inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
			endp->conn_mode);
		if (state->packet_duration == 0) {
			state->packet_duration = rtp_end->codec.rate * 20 / 1000;
			LOGP(DMGCP, LOGL_NOTICE,
			     "Fixed packet duration is not available on 0x%x, "
			     "using fixed 20ms instead: %d from %s:%d in %d\n",
			     ENDPOINT_NUMBER(endp), state->packet_duration,
			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
			     endp->conn_mode);
		}
	} else if (state->in_stream.ssrc != ssrc) {
		LOGP(DMGCP, LOGL_NOTICE,
			"The SSRC changed on 0x%x: %u -> %u  "
			"from %s:%d in %d\n",
			ENDPOINT_NUMBER(endp),
			state->in_stream.ssrc, rtp_hdr->ssrc,
			inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
			endp->conn_mode);

		state->in_stream.ssrc = ssrc;
		state->jitter = 0;
		state->transit = transit;
		if (rtp_end->force_constant_ssrc) {
			int16_t delta_seq;

			/* Always increment seqno by 1 */
			state->seq_offset =
				(state->out_stream.last_seq + 1) - seq;

			/* Estimate number of packets that would have been sent */
			delta_seq =
				(arrival_time - state->in_stream.last_arrival_time
				 + state->packet_duration/2) /
				state->packet_duration;

			adjust_rtp_timestamp_offset(endp, state, rtp_end, addr,
						    delta_seq, timestamp);

			state->patch_ssrc = 1;
			ssrc = state->orig_ssrc;
			if (rtp_end->force_constant_ssrc != -1)
				rtp_end->force_constant_ssrc -= 1;

			LOGP(DMGCP, LOGL_NOTICE,
			     "SSRC patching enabled on 0x%x SSRC: %u "
			     "SeqNo offset: %d, TS offset: %d "
			     "from %s:%d in %d\n",
			     ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
			     state->seq_offset, state->timestamp_offset,
			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
			     endp->conn_mode);
		}

		state->in_stream.last_tsdelta = 0;
	} else {
		/* Compute current per-packet timestamp delta */
		check_rtp_timestamp(endp, state, &state->in_stream, rtp_end, addr,
				    seq, timestamp, "input",
				    &state->in_stream.last_tsdelta);

		if (state->patch_ssrc)
			ssrc = state->orig_ssrc;
	}

	/* Save before patching */
	state->in_stream.last_timestamp = timestamp;
	state->in_stream.last_seq = seq;
	state->in_stream.last_arrival_time = arrival_time;

	if (rtp_end->force_aligned_timing &&
	    state->out_stream.ssrc == ssrc && state->packet_duration)
		/* Align the timestamp offset */
		align_rtp_timestamp_offset(endp, state, rtp_end, addr, timestamp);

	/* Store the updated SSRC back to the packet */
	if (state->patch_ssrc)
		rtp_hdr->ssrc = htonl(ssrc);

	/* Apply the offset and store it back to the packet.
	 * This won't change anything if the offset is 0, so the conditional is
	 * omitted. */
	seq += state->seq_offset;
	rtp_hdr->sequence = htons(seq);
	timestamp += state->timestamp_offset;
	rtp_hdr->timestamp = htonl(timestamp);

	/* Check again, whether the timestamps are still valid */
	if (state->out_stream.ssrc == ssrc)
		check_rtp_timestamp(endp, state, &state->out_stream, rtp_end,
				    addr, seq, timestamp, "output",
				    &state->out_stream.last_tsdelta);

	/*
	 * The below takes the shape of the validation from Appendix A. Check
	 * if there is something weird with the sequence number, otherwise check
	 * for a wrap around in the sequence number.
	 *
	 * Note that last_seq is used where the appendix mentions max_seq.
	 */
	udelta = seq - state->out_stream.last_seq;
	if (udelta < RTP_MAX_DROPOUT) {
		if (seq < state->out_stream.last_seq)
			state->cycles += RTP_SEQ_MOD;
	} else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
		LOGP(DMGCP, LOGL_NOTICE,
			"RTP seqno made a very large jump on 0x%x delta: %u\n",
			ENDPOINT_NUMBER(endp), udelta);
	}

	/*
	 * Calculate the jitter between the two packages. The TS should be
	 * taken closer to the read function. This was taken from the
	 * Appendix A of RFC 3550. Timestamp and arrival_time have a 1/rate
	 * resolution.
	 */
	d = transit - state->transit;
	state->transit = transit;
	if (d < 0)
		d = -d;
	state->jitter += d - ((state->jitter + 8) >> 4);

	/* Save output values */
	state->out_stream.last_seq = seq;
	state->out_stream.last_timestamp = timestamp;
	state->out_stream.ssrc = ssrc;

	if (payload < 0)
		return;

	rtp_hdr->payload_type = payload;
}
Exemple #2
0
/**
 * The RFC 3550 Appendix A assumes there are multiple sources but
 * some of the supported endpoints (e.g. the nanoBTS) can only handle
 * one source and this code will patch packages to appear as if there
 * is only one source.
 * There is also no probation period for new sources. Every package
 * we receive will be seen as a switch in streams.
 */
static void patch_and_count(struct mgcp_endpoint *endp, struct mgcp_rtp_state *state,
			    int payload, struct sockaddr_in *addr, char *data, int len)
{
	uint32_t arrival_time;
	int32_t transit, d;
	uint16_t seq, udelta;
	uint32_t timestamp;
	struct rtp_hdr *rtp_hdr;

	if (len < sizeof(*rtp_hdr))
		return;

	rtp_hdr = (struct rtp_hdr *) data;
	seq = ntohs(rtp_hdr->sequence);
	timestamp = ntohl(rtp_hdr->timestamp);
	arrival_time = get_current_ts();

	if (!state->initialized) {
		state->base_seq = seq;
		state->max_seq = seq - 1;
		state->ssrc = state->orig_ssrc = rtp_hdr->ssrc;
		state->initialized = 1;
		state->last_timestamp = timestamp;
		state->jitter = 0;
		state->transit = arrival_time - timestamp;
	} else if (state->ssrc != rtp_hdr->ssrc) {
		state->ssrc = rtp_hdr->ssrc;
		state->seq_offset = (state->max_seq + 1) - seq;
		state->timestamp_offset = state->last_timestamp - timestamp;
		state->patch = endp->allow_patch;
		LOGP(DMGCP, LOGL_NOTICE,
			"The SSRC changed on 0x%x SSRC: %u offset: %d from %s:%d in %d\n",
			ENDPOINT_NUMBER(endp), state->ssrc, state->seq_offset,
			inet_ntoa(addr->sin_addr), ntohs(addr->sin_port), endp->conn_mode);
	}

	/* apply the offset and store it back to the packet */
	if (state->patch) {
		seq += state->seq_offset;
		rtp_hdr->sequence = htons(seq);
		rtp_hdr->ssrc = state->orig_ssrc;

		timestamp += state->timestamp_offset;
		rtp_hdr->timestamp = htonl(timestamp);
	}

	/*
	 * The below takes the shape of the validation from Appendix A. Check
	 * if there is something weird with the sequence number, otherwise check
	 * for a wrap around in the sequence number.
	 */
	udelta = seq - state->max_seq;
	if (udelta < RTP_MAX_DROPOUT) {
		if (seq < state->max_seq)
			state->cycles += RTP_SEQ_MOD;
	} else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
		LOGP(DMGCP, LOGL_NOTICE,
			"RTP seqno made a very large jump on 0x%x delta: %u\n",
			ENDPOINT_NUMBER(endp), udelta);
	}

	/*
	 * calculate the jitter between the two packages. The TS should be
	 * taken closer to the read function. This was taken from the
	 * Appendix A of RFC 3550. The local timestamp has a usec resolution.
	 */
	transit = arrival_time - timestamp;
	d = transit - state->transit;
	state->transit = transit;
	if (d < 0)
		d = -d;
	state->jitter += d - ((state->jitter + 8) >> 4);


	state->max_seq = seq;
	state->last_timestamp = timestamp;

	if (payload < 0)
		return;

	rtp_hdr->payload_type = payload;
}