/** * 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; }
/** * 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; }