Merge remote-tracking branch 'jerlbeck/features/rtp-header-patching'
diff --git a/openbsc/include/openbsc/mgcp.h b/openbsc/include/openbsc/mgcp.h
index 4e22e0f..8ab52ce 100644
--- a/openbsc/include/openbsc/mgcp.h
+++ b/openbsc/include/openbsc/mgcp.h
@@ -118,6 +118,10 @@
 
 	int omit_rtcp;
 
+	/* RTP patching */
+	int force_constant_ssrc; /* 0: don't, 1: once */
+	int force_constant_timing;
+
 	/* spec handling */
 	int force_realloc;
 
diff --git a/openbsc/include/openbsc/mgcp_internal.h b/openbsc/include/openbsc/mgcp_internal.h
index 0b52c1c..d59c5d7 100644
--- a/openbsc/include/openbsc/mgcp_internal.h
+++ b/openbsc/include/openbsc/mgcp_internal.h
@@ -50,7 +50,7 @@
 
 struct mgcp_rtp_state {
 	int initialized;
-	int patch;
+	int patch_ssrc;
 
 	uint32_t orig_ssrc;
 
@@ -59,6 +59,7 @@
 	int cycles;
 
 	int32_t  timestamp_offset;
+	uint32_t packet_duration;
 	uint32_t jitter;
 	int32_t transit;
 
@@ -81,8 +82,13 @@
 	uint32_t frame_duration_num;
 	uint32_t frame_duration_den;
 	int  frames_per_packet;
+	uint32_t packet_duration_ms;
 	char *fmtp_extra;
 
+	/* RTP patching */
+	int force_constant_ssrc; /* -1: always, 0: don't, 1: once */
+	int force_constant_timing;
+
 	/*
 	 * Each end has a socket...
 	 */
@@ -142,9 +148,6 @@
 	struct mgcp_rtp_state net_state;
 	struct mgcp_rtp_state bts_state;
 
-	/* SSRC/seq/ts patching for loop */
-	int allow_patch;
-
 	/* fields for re-transmission */
 	char *last_trans;
 	char *last_response;
@@ -176,6 +179,11 @@
 struct mgcp_trunk_config *mgcp_trunk_alloc(struct mgcp_config *cfg, int index);
 struct mgcp_trunk_config *mgcp_trunk_num(struct mgcp_config *cfg, int index);
 
+void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
+			 struct mgcp_rtp_end *rtp);
+uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
+				  struct mgcp_rtp_end *rtp);
+
 void mgcp_state_calc_loss(struct mgcp_rtp_state *s, struct mgcp_rtp_end *,
 			uint32_t *expected, int *loss);
 uint32_t mgcp_state_calc_jitter(struct mgcp_rtp_state *);
diff --git a/openbsc/src/libmgcp/mgcp_network.c b/openbsc/src/libmgcp/mgcp_network.c
index 75d39c1..bf205d1 100644
--- a/openbsc/src/libmgcp/mgcp_network.c
+++ b/openbsc/src/libmgcp/mgcp_network.c
@@ -216,7 +216,7 @@
 	uint32_t arrival_time;
 	int32_t transit, d;
 	uint16_t seq, udelta;
-	uint32_t timestamp;
+	uint32_t timestamp, ssrc;
 	struct rtp_hdr *rtp_hdr;
 	int payload = rtp_end->payload_type;
 
@@ -227,41 +227,68 @@
 	seq = ntohs(rtp_hdr->sequence);
 	timestamp = ntohl(rtp_hdr->timestamp);
 	arrival_time = get_current_ts();
+	ssrc = ntohl(rtp_hdr->ssrc);
 
 	if (!state->initialized) {
 		state->in_stream.last_seq = seq - 1;
-		state->in_stream.ssrc = state->orig_ssrc = rtp_hdr->ssrc;
+		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 = arrival_time - timestamp;
+		state->packet_duration = mgcp_rtp_packet_duration(endp, rtp_end);
 		state->out_stream = state->in_stream;
-	} else if (state->in_stream.ssrc != rtp_hdr->ssrc) {
-		int32_t tsdelta = state->out_stream.last_tsdelta;
-		if (tsdelta == 0) {
-			tsdelta = rtp_end->rate * rtp_end->frames_per_packet *
-				rtp_end->frame_duration_num /
-				rtp_end->frame_duration_den;
-			LOGP(DMGCP, LOGL_NOTICE,
-			     "Computed timestamp delta %d based on "
-			     "rate %d, num frames %d, frame duration %d/%d\n",
-			     tsdelta, rtp_end->rate, rtp_end->frames_per_packet,
-			     rtp_end->frame_duration_num,
-			     rtp_end->frame_duration_den);
-		}
-		state->in_stream.ssrc = rtp_hdr->ssrc;
-		state->seq_offset = (state->out_stream.last_seq + 1) - seq;
-		state->timestamp_offset =
-			(state->out_stream.last_timestamp + tsdelta) - timestamp;
-		state->patch = endp->allow_patch;
-		LOGP(DMGCP, LOGL_NOTICE,
-			"The SSRC changed on 0x%x SSRC: %u offset: %d tsdelta: %d "
-			"from %s:%d in %d\n",
+		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, tsdelta,
+			state->seq_offset, 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;
+		if (rtp_end->force_constant_ssrc) {
+			int32_t tsdelta = state->out_stream.last_tsdelta;
+			if (tsdelta == 0) {
+				tsdelta = state->packet_duration;
+				LOGP(DMGCP, LOGL_NOTICE,
+				     "Timestamp delta is not available on 0x%x, "
+				     "using packet duration instead: %d "
+				     "from %s:%d in %d\n",
+				     ENDPOINT_NUMBER(endp), tsdelta,
+				     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+				     endp->conn_mode);
+			}
+			state->seq_offset =
+				(state->out_stream.last_seq + 1) - seq;
+			state->timestamp_offset =
+				(state->out_stream.last_timestamp + tsdelta) -
+				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 "
+			     "offset: %d tsdelta: %d "
+			     "from %s:%d in %d\n",
+			     ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
+			     state->seq_offset, tsdelta,
+			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+			     endp->conn_mode);
+		}
 
 		state->in_stream.last_tsdelta = 0;
 	} else {
@@ -269,26 +296,52 @@
 		check_rtp_timestamp(endp, &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;
 
-	/* 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;
+	if (rtp_end->force_constant_timing &&
+	    state->out_stream.ssrc == ssrc && state->packet_duration) {
+		int delta_seq = seq + state->seq_offset - state->out_stream.last_seq;
+		int timestamp_offset =
+			state->out_stream.last_timestamp - timestamp +
+			delta_seq * state->packet_duration;
+		if (state->timestamp_offset != timestamp_offset) {
+			state->timestamp_offset = timestamp_offset;
 
-		timestamp += state->timestamp_offset;
-		rtp_hdr->timestamp = htonl(timestamp);
+			LOGP(DMGCP, LOGL_NOTICE,
+			     "Timestamp patching enabled on 0x%x SSRC: %u "
+			     "SeqNo delta: %d, TS offset: %d, "
+			     "from %s:%d in %d\n",
+			     ENDPOINT_NUMBER(endp), state->in_stream.ssrc,
+			     delta_seq, state->timestamp_offset,
+			     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port),
+			     endp->conn_mode);
+		}
 	}
 
+	/* 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 */
-	check_rtp_timestamp(endp, &state->out_stream, rtp_end, addr,
-			    seq, timestamp, "output",
-			    &state->out_stream.last_tsdelta);
+	if (state->out_stream.ssrc == ssrc)
+		check_rtp_timestamp(endp, &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
@@ -322,7 +375,7 @@
 	/* Save output values */
 	state->out_stream.last_seq = seq;
 	state->out_stream.last_timestamp = timestamp;
-	state->out_stream.ssrc = rtp_hdr->ssrc;
+	state->out_stream.ssrc = ssrc;
 
 	if (payload < 0)
 		return;
diff --git a/openbsc/src/libmgcp/mgcp_protocol.c b/openbsc/src/libmgcp/mgcp_protocol.c
index a0b905d..ab94164 100644
--- a/openbsc/src/libmgcp/mgcp_protocol.c
+++ b/openbsc/src/libmgcp/mgcp_protocol.c
@@ -75,7 +75,7 @@
 /* Assume audio frame length of 20ms */
 #define DEFAULT_RTP_AUDIO_FRAME_DUR_NUM 20
 #define DEFAULT_RTP_AUDIO_FRAME_DUR_DEN 1000
-#define DEFAULT_RTP_AUDIO_FRAMES_PER_PACKET 1
+#define DEFAULT_RTP_AUDIO_PACKET_DURATION_MS 20
 #define DEFAULT_RTP_AUDIO_DEFAULT_RATE  8000
 
 static void mgcp_rtp_end_reset(struct mgcp_rtp_end *end);
@@ -614,6 +614,40 @@
 	return found_media;
 }
 
+void mgcp_rtp_end_config(struct mgcp_endpoint *endp, int expect_ssrc_change,
+			 struct mgcp_rtp_end *rtp)
+{
+	struct mgcp_trunk_config *tcfg = endp->tcfg;
+
+	int patch_ssrc = expect_ssrc_change && tcfg->force_constant_ssrc;
+
+	rtp->force_constant_timing = tcfg->force_constant_timing;
+	rtp->force_constant_ssrc = patch_ssrc ? 1 : 0;
+
+	LOGP(DMGCP, LOGL_DEBUG,
+	     "Configuring RTP endpoint: local port %d%s%s\n",
+	     ntohs(rtp->rtp_port),
+	     rtp->force_constant_timing ? ", force constant timing" : "",
+	     rtp->force_constant_ssrc ? ", force constant ssrc" : "");
+}
+
+uint32_t mgcp_rtp_packet_duration(struct mgcp_endpoint *endp,
+				  struct mgcp_rtp_end *rtp)
+{
+	int f = 0;
+
+	/* Get the number of frames per channel and packet */
+	if (rtp->frames_per_packet)
+		f = rtp->frames_per_packet;
+	else if (rtp->packet_duration_ms && rtp->frame_duration_num) {
+		int den = 1000 * rtp->frame_duration_num;
+		f = (rtp->packet_duration_ms * rtp->frame_duration_den + den/2)
+			/ den;
+	}
+
+	return rtp->rate * f * rtp->frame_duration_num / rtp->frame_duration_den;
+}
+
 static struct msgb *handle_create_con(struct mgcp_parse_data *p)
 {
 	struct mgcp_trunk_config *tcfg;
@@ -688,6 +722,8 @@
 
 	/* initialize */
 	endp->net_end.rtp_port = endp->net_end.rtcp_port = endp->bts_end.rtp_port = endp->bts_end.rtcp_port = 0;
+	mgcp_rtp_end_config(endp, 0, &endp->net_end);
+	mgcp_rtp_end_config(endp, 0, &endp->bts_end);
 
 	/* set to zero until we get the info */
 	memset(&endp->net_end.addr, 0, sizeof(endp->net_end.addr));
@@ -825,6 +861,9 @@
 		}
 	}
 
+	mgcp_rtp_end_config(endp, 1, &endp->net_end);
+	mgcp_rtp_end_config(endp, 1, &endp->bts_end);
+
 	/* modify */
 	LOGP(DMGCP, LOGL_DEBUG, "Modified endpoint on: 0x%x Server: %s:%u\n",
 		ENDPOINT_NUMBER(endp), inet_ntoa(endp->net_end.addr), ntohs(endp->net_end.rtp_port));
@@ -1064,8 +1103,9 @@
 	/* Set default values */
 	end->frame_duration_num = DEFAULT_RTP_AUDIO_FRAME_DUR_NUM;
 	end->frame_duration_den = DEFAULT_RTP_AUDIO_FRAME_DUR_DEN;
-	end->frames_per_packet  = DEFAULT_RTP_AUDIO_FRAMES_PER_PACKET;
-	end->rate = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
+	end->frames_per_packet  = 0; /* unknown */
+	end->packet_duration_ms = DEFAULT_RTP_AUDIO_PACKET_DURATION_MS;
+	end->rate               = DEFAULT_RTP_AUDIO_DEFAULT_RATE;
 }
 
 static void mgcp_rtp_end_init(struct mgcp_rtp_end *end)
@@ -1121,7 +1161,6 @@
 	memset(&endp->bts_state, 0, sizeof(endp->bts_state));
 
 	endp->conn_mode = endp->orig_mode = MGCP_CONN_NONE;
-	endp->allow_patch = 0;
 
 	memset(&endp->taps, 0, sizeof(endp->taps));
 }
diff --git a/openbsc/src/libmgcp/mgcp_vty.c b/openbsc/src/libmgcp/mgcp_vty.c
index 5aeb393..e48b050 100644
--- a/openbsc/src/libmgcp/mgcp_vty.c
+++ b/openbsc/src/libmgcp/mgcp_vty.c
@@ -31,6 +31,7 @@
 #include <string.h>
 
 #define RTCP_OMIT_STR "Drop RTCP packets in both directions\n"
+#define RTP_PATCH_STR "Modify RTP packet header in both directions\n"
 
 static struct mgcp_config *g_cfg = NULL;
 
@@ -88,6 +89,13 @@
 		vty_out(vty, "  rtcp-omit%s", VTY_NEWLINE);
 	else
 		vty_out(vty, "  no rtcp-omit%s", VTY_NEWLINE);
+	if (g_cfg->trunk.force_constant_ssrc || g_cfg->trunk.force_constant_timing) {
+		vty_out(vty, "  %srtp-patch ssrc%s",
+			g_cfg->trunk.force_constant_ssrc ? "" : "no ", VTY_NEWLINE);
+		vty_out(vty, "  %srtp-patch timestamp%s",
+			g_cfg->trunk.force_constant_timing ? "" : "no ", VTY_NEWLINE);
+	} else
+		vty_out(vty, "  no rtp-patch%s", VTY_NEWLINE);
 	if (g_cfg->trunk.audio_payload != -1)
 		vty_out(vty, "  sdp audio-payload number %d%s",
 			g_cfg->trunk.audio_payload, VTY_NEWLINE);
@@ -421,6 +429,61 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_mgcp_patch_rtp_ssrc,
+      cfg_mgcp_patch_rtp_ssrc_cmd,
+      "rtp-patch ssrc",
+      RTP_PATCH_STR
+      "Force a fixed SSRC\n"
+      )
+{
+	g_cfg->trunk.force_constant_ssrc = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_patch_rtp_ssrc,
+      cfg_mgcp_no_patch_rtp_ssrc_cmd,
+      "no rtp-patch ssrc",
+      NO_STR RTP_PATCH_STR
+      "Force a fixed SSRC\n"
+      )
+{
+	g_cfg->trunk.force_constant_ssrc = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_patch_rtp_ts,
+      cfg_mgcp_patch_rtp_ts_cmd,
+      "rtp-patch timestamp",
+      RTP_PATCH_STR
+      "Adjust RTP timestamp\n"
+      )
+{
+	g_cfg->trunk.force_constant_timing = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_patch_rtp_ts,
+      cfg_mgcp_no_patch_rtp_ts_cmd,
+      "no rtp-patch timestamp",
+      NO_STR RTP_PATCH_STR
+      "Adjust RTP timestamp\n"
+      )
+{
+	g_cfg->trunk.force_constant_timing = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_mgcp_no_patch_rtp,
+      cfg_mgcp_no_patch_rtp_cmd,
+      "no rtp-patch",
+      NO_STR RTP_PATCH_STR)
+{
+	g_cfg->trunk.force_constant_ssrc = 0;
+	g_cfg->trunk.force_constant_timing = 0;
+	return CMD_SUCCESS;
+}
+
+
 #define CALL_AGENT_STR "Callagent information\n"
 DEFUN(cfg_mgcp_agent_addr,
       cfg_mgcp_agent_addr_cmd,
@@ -511,6 +574,13 @@
 			vty_out(vty, "  rtcp-omit%s", VTY_NEWLINE);
 		else
 			vty_out(vty, "  no rtcp-omit%s", VTY_NEWLINE);
+		if (trunk->force_constant_ssrc || trunk->force_constant_timing) {
+			vty_out(vty, "  %srtp-patch ssrc%s",
+				trunk->force_constant_ssrc ? "" : "no ", VTY_NEWLINE);
+			vty_out(vty, "  %srtp-patch timestamp%s",
+				trunk->force_constant_timing ? "" : "no ", VTY_NEWLINE);
+		} else
+			vty_out(vty, "  no rtp-patch%s", VTY_NEWLINE);
 		if (trunk->audio_fmtp_extra)
 			vty_out(vty, "   sdp audio fmtp-extra %s%s",
 				trunk->audio_fmtp_extra, VTY_NEWLINE);
@@ -599,6 +669,66 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_trunk_patch_rtp_ssrc,
+      cfg_trunk_patch_rtp_ssrc_cmd,
+      "rtp-patch ssrc",
+      RTP_PATCH_STR
+      "Force a fixed SSRC\n"
+      )
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->force_constant_ssrc = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_no_patch_rtp_ssrc,
+      cfg_trunk_no_patch_rtp_ssrc_cmd,
+      "no rtp-patch ssrc",
+      NO_STR RTP_PATCH_STR
+      "Force a fixed SSRC\n"
+      )
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->force_constant_ssrc = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_patch_rtp_ts,
+      cfg_trunk_patch_rtp_ts_cmd,
+      "rtp-patch timestamp",
+      RTP_PATCH_STR
+      "Adjust RTP timestamp\n"
+      )
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->force_constant_timing = 1;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_no_patch_rtp_ts,
+      cfg_trunk_no_patch_rtp_ts_cmd,
+      "no rtp-patch timestamp",
+      NO_STR RTP_PATCH_STR
+      "Adjust RTP timestamp\n"
+      )
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->force_constant_timing = 0;
+	return CMD_SUCCESS;
+}
+
+DEFUN(cfg_trunk_no_patch_rtp,
+      cfg_trunk_no_patch_rtp_cmd,
+      "no rtp-patch",
+      NO_STR RTP_PATCH_STR)
+{
+	struct mgcp_trunk_config *trunk = vty->index;
+	trunk->force_constant_ssrc = 0;
+	trunk->force_constant_timing = 0;
+	return CMD_SUCCESS;
+}
+
+
 DEFUN(loop_endp,
       loop_endp_cmd,
       "loop-endpoint <0-64> NAME (0|1)",
@@ -636,7 +766,10 @@
 		endp->conn_mode = MGCP_CONN_LOOPBACK;
 	else
 		endp->conn_mode = endp->orig_mode;
-	endp->allow_patch = 1;
+
+	/* Handle it like a MDCX, switch on SSRC patching if enabled */
+	mgcp_rtp_end_config(endp, 1, &endp->bts_end);
+	mgcp_rtp_end_config(endp, 1, &endp->net_end);
 
 	return CMD_SUCCESS;
 }
@@ -827,6 +960,11 @@
 	install_element(MGCP_NODE, &cfg_mgcp_number_endp_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_omit_rtcp_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_no_omit_rtcp_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ssrc_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ssrc_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_patch_rtp_ts_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_ts_cmd);
+	install_element(MGCP_NODE, &cfg_mgcp_no_patch_rtp_cmd);
 	install_element(MGCP_NODE, &cfg_mgcp_sdp_fmtp_extra_cmd);
 
 	install_element(MGCP_NODE, &cfg_mgcp_trunk_cmd);
@@ -839,6 +977,11 @@
 	install_element(TRUNK_NODE, &cfg_trunk_loop_cmd);
 	install_element(TRUNK_NODE, &cfg_trunk_omit_rtcp_cmd);
 	install_element(TRUNK_NODE, &cfg_trunk_no_omit_rtcp_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ssrc_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ssrc_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_patch_rtp_ts_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_ts_cmd);
+	install_element(TRUNK_NODE, &cfg_trunk_no_patch_rtp_cmd);
 	install_element(TRUNK_NODE, &cfg_trunk_sdp_fmtp_extra_cmd);
 
 	return 0;
diff --git a/openbsc/tests/mgcp/mgcp_test.c b/openbsc/tests/mgcp/mgcp_test.c
index c58f52d..e5b8bbd 100644
--- a/openbsc/tests/mgcp/mgcp_test.c
+++ b/openbsc/tests/mgcp/mgcp_test.c
@@ -510,13 +510,33 @@
 	/* RTP: SeqNo=14, TS=35008 */
 	{0.280000, 20, "\x80\x62\x00\x0E\x00\x00\x88\xC0\x10\x20\x30\x40"
 		       "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
+	/* Non 20ms RTP timestamp (delta = 120): */
+	/* RTP: SeqNo=15, TS=35128 */
+	{0.300000, 20, "\x80\x62\x00\x0F\x00\x00\x89\x38\x10\x20\x30\x40"
+		       "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
+	/* RTP: SeqNo=16, TS=35288 */
+	{0.320000, 20, "\x80\x62\x00\x10\x00\x00\x89\xD8\x10\x20\x30\x40"
+		       "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
+	/* RTP: SeqNo=17, TS=35448 */
+	{0.340000, 20, "\x80\x62\x00\x11\x00\x00\x8A\x78\x10\x20\x30\x40"
+		       "\x01\x23\x45\x67\x8A\xAB\xCD\xEF"},
+	/* SeqNo increment by 2, RTP timestamp delta = 320: */
+	/* RTP: SeqNo=19, TS=35768 */
+	{0.360000, 20, "\x80\x62\x00\x13\x00\x00\x8B\xB8\x10\x20\x30\x40"
+		       "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
+	/* RTP: SeqNo=20, TS=35928 */
+	{0.380000, 20, "\x80\x62\x00\x14\x00\x00\x8C\x58\x10\x20\x30\x40"
+		       "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
+	/* RTP: SeqNo=21, TS=36088 */
+	{0.380000, 20, "\x80\x62\x00\x14\x00\x00\x8C\xF8\x10\x20\x30\x40"
+		       "\x01\x23\x45\x67\x89\xAB\xCD\xEF"},
 };
 
 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);
 
-static void test_packet_error_detection(void)
+static void test_packet_error_detection(int patch_ssrc, int patch_ts)
 {
 	int i;
 
@@ -526,8 +546,11 @@
 	struct mgcp_rtp_end *rtp = &endp.net_end;
 	struct sockaddr_in addr = {0};
 	char buffer[4096];
+	uint32_t last_ssrc = 0;
 
-	printf("Testing packet error detection.\n");
+	printf("Testing packet error detection%s%s.\n",
+	       patch_ssrc ? ", patch SSRC" : "",
+	       patch_ts ? ", patch timestamps" : "");
 
 	memset(&trunk, 0, sizeof(trunk));
 	memset(&endp, 0, sizeof(endp));
@@ -535,6 +558,9 @@
 
 	trunk.number_endpoints = 1;
 	trunk.endpoints = &endp;
+	trunk.force_constant_ssrc = patch_ssrc;
+	trunk.force_constant_timing = patch_ts;
+
 	endp.tcfg = &trunk;
 
 	/* This doesn't free endp but resets/frees all fields of the structure
@@ -545,7 +571,6 @@
 	mgcp_free_endp(&endp);
 
 	rtp->payload_type = 98;
-	endp.allow_patch = 1;
 
 	for (i = 0; i < ARRAY_SIZE(test_rtp_packets1); ++i) {
 		struct rtp_packet_info *info = test_rtp_packets1 + i;
@@ -554,9 +579,17 @@
 		OSMO_ASSERT(info->len >= 0);
 		memmove(buffer, info->data, info->len);
 
+		mgcp_rtp_end_config(&endp, 1, rtp);
+
 		mgcp_patch_and_count(&endp, &state, rtp, &addr,
 				     buffer, info->len);
 
+		if (state.out_stream.ssrc != last_ssrc) {
+			printf("Output SSRC changed to %08x\n",
+			       state.out_stream.ssrc);
+			last_ssrc = state.out_stream.ssrc;
+		}
+
 		printf("TS: %d, dTS: %d, TS Errs: in %d, out %d\n",
 		       state.out_stream.last_timestamp,
 		       state.out_stream.last_tsdelta,
@@ -575,7 +608,10 @@
 	test_packet_loss_calc();
 	test_rqnt_cb();
 	test_mgcp_stats();
-	test_packet_error_detection();
+	test_packet_error_detection(1, 0);
+	test_packet_error_detection(0, 0);
+	test_packet_error_detection(0, 1);
+	test_packet_error_detection(1, 1);
 
 	printf("Done\n");
 	return EXIT_SUCCESS;
diff --git a/openbsc/tests/mgcp/mgcp_test.ok b/openbsc/tests/mgcp/mgcp_test.ok
index 3bfd78b..3beeb59 100644
--- a/openbsc/tests/mgcp/mgcp_test.ok
+++ b/openbsc/tests/mgcp/mgcp_test.ok
@@ -42,7 +42,8 @@
 Testing stat parsing
 Parsing result: 0
 Parsing result: 0
-Testing packet error detection.
+Testing packet error detection, patch SSRC.
+Output SSRC changed to 11223344
 TS: 0, dTS: 0, TS Errs: in 0, out 0
 TS: 160, dTS: 160, TS Errs: in 0, out 0
 TS: 320, dTS: 160, TS Errs: in 0, out 0
@@ -58,4 +59,81 @@
 TS: 1880, dTS: 160, TS Errs: in 5, out 5
 TS: 2040, dTS: 160, TS Errs: in 5, out 5
 TS: 2200, dTS: 160, TS Errs: in 5, out 5
+TS: 2320, dTS: 120, TS Errs: in 6, out 6
+TS: 2480, dTS: 160, TS Errs: in 7, out 7
+TS: 2640, dTS: 160, TS Errs: in 7, out 7
+TS: 2960, dTS: 160, TS Errs: in 7, out 7
+TS: 3120, dTS: 160, TS Errs: in 7, out 7
+TS: 3280, dTS: 160, TS Errs: in 8, out 8
+Testing packet error detection.
+Output SSRC changed to 11223344
+TS: 0, dTS: 0, TS Errs: in 0, out 0
+TS: 160, dTS: 160, TS Errs: in 0, out 0
+TS: 320, dTS: 160, TS Errs: in 0, out 0
+TS: 320, dTS: 160, TS Errs: in 1, out 1
+TS: 480, dTS: 160, TS Errs: in 1, out 1
+TS: 640, dTS: 160, TS Errs: in 1, out 1
+TS: 960, dTS: 320, TS Errs: in 2, out 2
+TS: 1120, dTS: 160, TS Errs: in 3, out 3
+TS: 1280, dTS: 160, TS Errs: in 3, out 3
+TS: 1400, dTS: 120, TS Errs: in 4, out 4
+TS: 1560, dTS: 160, TS Errs: in 5, out 5
+TS: 1720, dTS: 160, TS Errs: in 5, out 5
+Output SSRC changed to 10203040
+TS: 34688, dTS: 160, TS Errs: in 5, out 5
+TS: 34848, dTS: 160, TS Errs: in 5, out 5
+TS: 35008, dTS: 160, TS Errs: in 5, out 5
+TS: 35128, dTS: 120, TS Errs: in 6, out 6
+TS: 35288, dTS: 160, TS Errs: in 7, out 7
+TS: 35448, dTS: 160, TS Errs: in 7, out 7
+TS: 35768, dTS: 160, TS Errs: in 7, out 7
+TS: 35928, dTS: 160, TS Errs: in 7, out 7
+TS: 36088, dTS: 160, TS Errs: in 8, out 8
+Testing packet error detection, patch timestamps.
+Output SSRC changed to 11223344
+TS: 0, dTS: 0, TS Errs: in 0, out 0
+TS: 160, dTS: 160, TS Errs: in 0, out 0
+TS: 320, dTS: 160, TS Errs: in 0, out 0
+TS: 480, dTS: 160, TS Errs: in 1, out 0
+TS: 640, dTS: 160, TS Errs: in 1, out 0
+TS: 800, dTS: 160, TS Errs: in 1, out 0
+TS: 960, dTS: 160, TS Errs: in 2, out 0
+TS: 1120, dTS: 160, TS Errs: in 3, out 0
+TS: 1280, dTS: 160, TS Errs: in 3, out 0
+TS: 1440, dTS: 160, TS Errs: in 4, out 0
+TS: 1600, dTS: 160, TS Errs: in 5, out 0
+TS: 1760, dTS: 160, TS Errs: in 5, out 0
+Output SSRC changed to 10203040
+TS: 34728, dTS: 160, TS Errs: in 5, out 0
+TS: 34888, dTS: 160, TS Errs: in 5, out 0
+TS: 35048, dTS: 160, TS Errs: in 5, out 0
+TS: 35208, dTS: 160, TS Errs: in 6, out 0
+TS: 35368, dTS: 160, TS Errs: in 7, out 0
+TS: 35528, dTS: 160, TS Errs: in 7, out 0
+TS: 35848, dTS: 160, TS Errs: in 7, out 0
+TS: 36008, dTS: 160, TS Errs: in 7, out 0
+TS: 36008, dTS: 160, TS Errs: in 8, out 0
+Testing packet error detection, patch SSRC, patch timestamps.
+Output SSRC changed to 11223344
+TS: 0, dTS: 0, TS Errs: in 0, out 0
+TS: 160, dTS: 160, TS Errs: in 0, out 0
+TS: 320, dTS: 160, TS Errs: in 0, out 0
+TS: 480, dTS: 160, TS Errs: in 1, out 0
+TS: 640, dTS: 160, TS Errs: in 1, out 0
+TS: 800, dTS: 160, TS Errs: in 1, out 0
+TS: 960, dTS: 160, TS Errs: in 2, out 0
+TS: 1120, dTS: 160, TS Errs: in 3, out 0
+TS: 1280, dTS: 160, TS Errs: in 3, out 0
+TS: 1440, dTS: 160, TS Errs: in 4, out 0
+TS: 1600, dTS: 160, TS Errs: in 5, out 0
+TS: 1760, dTS: 160, TS Errs: in 5, out 0
+TS: 1920, dTS: 160, TS Errs: in 5, out 0
+TS: 2080, dTS: 160, TS Errs: in 5, out 0
+TS: 2240, dTS: 160, TS Errs: in 5, out 0
+TS: 2400, dTS: 160, TS Errs: in 6, out 0
+TS: 2560, dTS: 160, TS Errs: in 7, out 0
+TS: 2720, dTS: 160, TS Errs: in 7, out 0
+TS: 3040, dTS: 160, TS Errs: in 7, out 0
+TS: 3200, dTS: 160, TS Errs: in 7, out 0
+TS: 3200, dTS: 160, TS Errs: in 8, out 0
 Done