MGCP_Test: support multiple codecs

At the moment The RTP emulation and MGCP_Test only allow to specify one
codec and one set of RX/TX fixed payload octet strings to verify against.

This is quite limiting since it might be necessary to test against
different types and formats of payloads simultaneously in order to see
if osmo-mgw converts or forwards them correctly.

Let's extend this to support multiple codecs on MGCP/SDP level plus
support for multiple RTP payloads on RTP emulation level.

Related: OS#5461
Change-Id: I8422313fccad1bfcee52c933f643068bebdaf2d5
diff --git a/bts/BTS_Tests.ttcn b/bts/BTS_Tests.ttcn
index c55f962..34afaca 100644
--- a/bts/BTS_Tests.ttcn
+++ b/bts/BTS_Tests.ttcn
@@ -2637,7 +2637,7 @@
 
 	/* Pad the payload to conform the expected length */
 	payload := f_pad_oct(hdr & payload, payload_len, '00'O);
-	cfg.tx_fixed_payload := payload;
+	cfg.tx_payloads[0].fixed_payload := payload;
 	f_rtpem_configure(RTPEM_CTRL, cfg);
 
 	/* Step 2: bind the RTP emulation to the configured address */
diff --git a/hnodeb/HNBGW_ConnectionHandler.ttcn b/hnodeb/HNBGW_ConnectionHandler.ttcn
index 6ce2faf..e418527 100644
--- a/hnodeb/HNBGW_ConnectionHandler.ttcn
+++ b/hnodeb/HNBGW_ConnectionHandler.ttcn
@@ -259,7 +259,8 @@
 	var RtpemConfig cfg := c_RtpemDefaultCfg;
 	cfg.iuup_mode := true;
 	cfg.iuup_cfg.active_init := false;
-	cfg.tx_payload_type := 96;
+	cfg.tx_payloads[0].payload_type := 96;
+	cfg.rx_payloads[0].payload_type := 96;
 
 	vc_RTPEM := RTP_Emulation_CT.create(testcasename() & "-RTPEM") alive;
 	map(vc_RTPEM:RTP, system:RTP);
@@ -274,7 +275,7 @@
 
 	/* Pad the payload to conform the expected length */
 	payload := f_pad_oct(hdr & payload, payload_len, '00'O);
-	cfg.tx_fixed_payload := payload;
+	cfg.tx_payloads[0].fixed_payload := payload;
 	f_rtpem_configure(RTPEM_CTRL, cfg);
 
 	/* Bind the RTP emulation to the configured address */
diff --git a/library/RTP_Emulation.ttcn b/library/RTP_Emulation.ttcn
index eaff017..539ea45 100644
--- a/library/RTP_Emulation.ttcn
+++ b/library/RTP_Emulation.ttcn
@@ -153,24 +153,27 @@
 	num_pkts_rx_err_payload := 0
 }
 
+type record RtpemConfigPayload {
+	INT7b payload_type,
+	octetstring fixed_payload optional
+};
+
 type record RtpemConfig {
-	INT7b tx_payload_type,
 	integer tx_samplerate_hz,
 	integer tx_duration_ms,
 	BIT32_BO_LAST tx_ssrc,
-	octetstring tx_fixed_payload optional,
-	octetstring rx_fixed_payload optional,
+	record of RtpemConfigPayload tx_payloads,
+	record of RtpemConfigPayload rx_payloads,
 	boolean iuup_mode,
 	IuUP_Config iuup_cfg
 };
 
 const RtpemConfig c_RtpemDefaultCfg := {
-	tx_payload_type := 0,
 	tx_samplerate_hz := 8000,
 	tx_duration_ms := 20,
 	tx_ssrc := '11011110101011011011111011101111'B,
-	tx_fixed_payload := '01020304'O,
-	rx_fixed_payload := '01020304'O,
+	tx_payloads := {{0, '01020304'O}},
+	rx_payloads := {{0, '01020304'O}},
 	iuup_mode := false,
 	iuup_cfg := c_IuUP_Config_def
 }
@@ -342,7 +345,7 @@
 	data := payload
 }
 
-private function f_tx_rtp(octetstring payload, BIT1 marker := '0'B) runs on RTP_Emulation_CT {
+private function f_tx_rtp(octetstring payload, INT7b rtp_payload_type, BIT1 marker := '0'B) runs on RTP_Emulation_CT {
 	if (g_cfg.iuup_mode) {
 		payload := f_IuUP_Em_tx_encap(g_iuup_ent, payload);
 		if (lengthof(payload) == 0) {
@@ -350,7 +353,7 @@
 			return;
 		}
 	}
-	var PDU_RTP rtp := valueof(ts_RTP(g_cfg.tx_ssrc, g_cfg.tx_payload_type, g_tx_next_seq,
+	var PDU_RTP rtp := valueof(ts_RTP(g_cfg.tx_ssrc, rtp_payload_type, g_tx_next_seq,
 					  g_tx_next_ts, payload, marker));
 	RTP.send(t_RTP_Send(g_rtp_conn_id, RTP_messages_union:{rtp:=rtp}));
 	/* increment sequence + timestamp for next transmit */
@@ -358,6 +361,58 @@
 	g_tx_next_ts := g_tx_next_ts + (g_cfg.tx_samplerate_hz / (1000 / g_cfg.tx_duration_ms));
 }
 
+private function f_check_fixed_rx_payloads(INT7b rtp_payload_type, octetstring rtp_data) runs on RTP_Emulation_CT {
+	var boolean payload_type_match := false;
+
+	/* The API user has the option to define zero or multiple sets of rx_payloads. Each rx_payload set contains
+	   the payload type number of the expected payload and an optional fixed_payload, which resembles the actual
+	   payload.
+
+	   In case zero rx_payloads are defined nothing is verified and no errors are counted. This is a corner case
+	   and should be avoided since it would not yield any good test coverage.
+
+	   During verification the payload type has the highest priority. It must match before the optional fixed
+	   payload is checked. Since the fixed_payload is optional multiple error situations may apply:
+
+	   |  payload_type  | fixed_payload | result
+	   |     match      |    match      | full match           => no error counter is incremented
+	   |     match      |  not present  | counts as full match => no error counter is incremented
+	   |     match      |   mismatch    | payload type match   => only num_pkts_rx_err_payload is incremented
+	   |                |               |                         unless something of the above is detected later.
+	   |    mismatch    | (not checked) | no match             => num_pkts_rx_err_payload and num_pkts_rx_err_pt
+	   |                |               |                         are increment unless something of the above is
+	   |                |               |                         detected later.
+	*/
+
+	/* In case no rx payloads are defined any payload is accepted and no errors are counted. */
+	if (lengthof(g_cfg.rx_payloads) == 0) {
+		return;
+	}
+
+	/* Evaluate rtp_data and rtp_payload_type */
+	for (var integer i := 0; i < lengthof(g_cfg.rx_payloads); i := i + 1) {
+		if (rtp_payload_type == g_cfg.rx_payloads[i].payload_type) {
+			if (not ispresent(g_cfg.rx_payloads[i].fixed_payload)) {
+				/* full match */
+				return;
+			}
+			if (g_cfg.rx_payloads[i].fixed_payload == rtp_data) {
+				/* counts as full match */
+				return;
+			}
+
+			/* At least the payload type number did match
+			 * (but we still may see a full match later) */
+			payload_type_match := true;
+		}
+	}
+
+	g_stats_rtp.num_pkts_rx_err_payload := g_stats_rtp.num_pkts_rx_err_payload + 1;
+	if (not payload_type_match) {
+		g_stats_rtp.num_pkts_rx_err_pt := g_stats_rtp.num_pkts_rx_err_pt + 1;
+	}
+}
+
 function f_main() runs on RTP_Emulation_CT
 {
 	var Result res;
@@ -450,7 +505,7 @@
 			g_tx_connected := true;
 			/* Send any pending IuUP CTRL message whichwas delayed due to not being connected: */
 			if (isvalue(g_iuup_ent.pending_tx_pdu)) {
-				f_tx_rtp(''O);
+				f_tx_rtp(''O, g_cfg.tx_payloads[0].payload_type);
 			}
 			CTRL.reply(RTPEM_connect:{g_remote_host, g_remote_port});
 		}
@@ -517,7 +572,7 @@
 					g_stats_rtp.num_pkts_rx_err_disabled := g_stats_rtp.num_pkts_rx_err_disabled+1;
 				} else if (g_tx_connected and isvalue(g_iuup_ent.pending_tx_pdu)) {
 					/* IuUP Control packet was received and requires sending back something: */
-					f_tx_rtp(''O);
+					f_tx_rtp(''O, g_cfg.tx_payloads[0].payload_type);
 				}
 			} else {
 				g_stats_rtp.num_pkts_rx_err_disabled := g_stats_rtp.num_pkts_rx_err_disabled+1;
@@ -530,9 +585,6 @@
 		/* process received RTCP/RTP if receiver enabled */
 		[g_rx_enabled] RTP.receive(tr_rtp) -> value rx_rtp {
 			/* increment counters */
-			if (rx_rtp.msg.rtp.payload_type != g_cfg.tx_payload_type) {
-				g_stats_rtp.num_pkts_rx_err_pt := g_stats_rtp.num_pkts_rx_err_pt+1;
-			}
 			g_stats_rtp.num_pkts_rx := g_stats_rtp.num_pkts_rx+1;
 			g_stats_rtp.bytes_payload_rx := g_stats_rtp.bytes_payload_rx +
 								lengthof(rx_rtp.msg.rtp.data);
@@ -541,14 +593,15 @@
 				/* IuUP Control packet was received which may require sending back something: */
 				if (lengthof(rx_rtp.msg.rtp.data) == 0) {
 					if (g_tx_connected and isvalue(g_iuup_ent.pending_tx_pdu)) {
-						f_tx_rtp(''O);
+						f_tx_rtp(''O, g_cfg.tx_payloads[0].payload_type);
 					}
 					repeat;
 				}
 			}
-			if (ispresent(g_cfg.rx_fixed_payload) and rx_rtp.msg.rtp.data != g_cfg.rx_fixed_payload) {
-				g_stats_rtp.num_pkts_rx_err_payload := g_stats_rtp.num_pkts_rx_err_payload + 1;
-			}
+
+			/* Match the received payload against any of the predefined fixed rx payloads */
+			f_check_fixed_rx_payloads(rx_rtp.msg.rtp.payload_type, rx_rtp.msg.rtp.data);
+
 			if (DATA.checkstate("Connected")) {
 				DATA.send(rx_rtp.msg.rtp);
 			}
@@ -563,13 +616,14 @@
 
 		/* transmit if timer has expired */
 		[g_tx_connected] T_transmit.timeout {
+			var octetstring payload := g_cfg.tx_payloads[g_tx_next_seq mod lengthof(g_cfg.tx_payloads)].fixed_payload;
+			var INT7b rtp_payload_type := g_cfg.tx_payloads[g_tx_next_seq mod lengthof(g_cfg.tx_payloads)].payload_type;
 			/* send one RTP frame, re-start timer */
-			f_tx_rtp(g_cfg.tx_fixed_payload);
+			f_tx_rtp(payload, rtp_payload_type);
 			T_transmit.start;
 			/* update counters */
 			g_stats_rtp.num_pkts_tx := g_stats_rtp.num_pkts_tx+1;
-			g_stats_rtp.bytes_payload_tx := g_stats_rtp.bytes_payload_tx +
-								lengthof(g_cfg.tx_fixed_payload);
+			g_stats_rtp.bytes_payload_tx := g_stats_rtp.bytes_payload_tx + lengthof(payload);
 		}
 
 		/* connection refused */
diff --git a/mgw/MGCP_Test.ttcn b/mgw/MGCP_Test.ttcn
index a0c577a..c9e355b 100644
--- a/mgw/MGCP_Test.ttcn
+++ b/mgw/MGCP_Test.ttcn
@@ -341,15 +341,71 @@
 		MgcpOsmuxCID remote_cid optional,
 		OsmuxemConfig cfg optional
 	}
+	type record RtpCodecDescr {
+		uint7_t pt,
+		charstring codec,
+		charstring fmtp optional
+	}
 	type record RtpFlowData {
 		HostPort em,	/* emulation side */
 		HostPort mgw,	/* mgw side */
-		uint7_t pt,
-		charstring codec,
 		MgcpConnectionId mgcp_conn_id optional,
+		record of RtpCodecDescr codec_descr,
 		RtpemConfig rtp_cfg optional,
-		RtpOsmuxFlowData osmux,
-		charstring fmtp optional
+		RtpOsmuxFlowData osmux
+	}
+
+	/* To be used with f_flow_create/modify... functions */
+	function f_gen_sdp(RtpFlowData flow) runs on dummy_CT return SDP_Message {
+		var template SDP_Message sdp;
+		var SDP_fmt_list fmt_list := {};
+		var SDP_attribute_list attributes := {};
+
+		/* Add SDP RTMAP attributes (codec type, referenced by PT) */
+		for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
+			attributes := attributes & { valueof(ts_SDP_rtpmap(flow.codec_descr[i].pt,
+				      flow.codec_descr[i].codec)) };
+		}
+
+		/* Add SDP PTIME attribute, regardless of which codec, the packet intervall remains the same */
+		attributes := attributes & { valueof(ts_SDP_ptime(20)) };
+
+		/* Add SDP FMTP attributes (codec parameters for each codec, referenced by PT) */
+		for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
+			if (isvalue(flow.codec_descr[i].fmtp) and flow.codec_descr[i].fmtp != "") {
+				attributes := attributes & { valueof(ts_SDP_fmtp(flow.codec_descr[i].pt,
+							     flow.codec_descr[i].fmtp)) };
+			}
+		}
+
+		/* Final step: Generate SDP */
+		for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
+			fmt_list := fmt_list & {int2str(flow.codec_descr[i].pt)};
+		}
+		sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42", flow.em.portnr, fmt_list, attributes);
+
+		return valueof(sdp);
+	}
+
+	/* Generate a valid RTP emulation config from the payload type numbers configured in the flow description and
+	 * the the default configuration of the RTP emulation module. */
+	function f_gen_rtpem_config_from_flow(RtpFlowData flow) return RtpemConfig {
+		var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
+
+		for (var integer i := 0; i < lengthof(flow.codec_descr); i := i + 1) {
+			var RtpemConfigPayload tx_cfg_payload;
+			var RtpemConfigPayload rx_cfg_payload;
+
+			tx_cfg_payload.payload_type := flow.codec_descr[i].pt;
+			tx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.tx_payloads[0].fixed_payload;
+			rx_cfg_payload.payload_type := flow.codec_descr[i].pt;
+			rx_cfg_payload.fixed_payload := c_RtpemDefaultCfg.rx_payloads[0].fixed_payload;
+
+			rtp_cfg.tx_payloads := rtp_cfg.tx_payloads & {tx_cfg_payload};
+			rtp_cfg.rx_payloads := rtp_cfg.rx_payloads & {rx_cfg_payload};
+		}
+
+		return rtp_cfg;
 	}
 
 	/* Create an RTP flow (bidirectional, or receive-only) */
@@ -358,12 +414,6 @@
 	runs on dummy_CT {
 		var template MgcpCommand cmd;
 		var MgcpResponse resp;
-		var SDP_attribute_list attributes;
-
-		attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
-		if (isvalue(flow.fmtp) and flow.fmtp != "") {
-			attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
-		}
 
 		/* bind local RTP emulation socket */
 		f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
@@ -372,9 +422,7 @@
 		if (ispresent(flow.rtp_cfg)) {
 			f_rtpem_configure(pt, flow.rtp_cfg);
 		} else {
-			var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
-			rtp_cfg.tx_payload_type := flow.pt
-			f_rtpem_configure(pt, rtp_cfg);
+			f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow));
 		}
 
 		if (one_phase) {
@@ -384,8 +432,7 @@
 			 * one go. */
 
 			cmd := ts_CRCX(get_next_trans_id(), ep, mode, call_id);
-			cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
-					  flow.em.portnr, { int2str(flow.pt) }, attributes);
+			cmd.sdp := f_gen_sdp(flow);
 
 			resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK);
 			flow.mgcp_conn_id := extract_conn_id(resp);
@@ -416,17 +463,11 @@
 	runs on dummy_CT {
 		var template MgcpCommand cmd;
 		var MgcpResponse resp;
-		var SDP_attribute_list attributes;
 		var OsmuxTxHandle tx_hdl;
 		var OsmuxRxHandle rx_hdl;
 		var charstring cid_response;
 		var OsmuxCID cid_resp_parsed
 
-		attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
-		if (isvalue(flow.fmtp)) {
-			attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
-		}
-
 		/* bind local Osmux emulation socket */
 		f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
 
@@ -452,8 +493,7 @@
 				flow.osmux.local_cid_sent := true;
 			}
 			cmd := ts_CRCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.osmux.local_cid);
-			cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
-					  flow.em.portnr, { int2str(flow.pt) }, attributes);
+			cmd.sdp := f_gen_sdp(flow);
   			resp := mgcp_transceive_mgw(cmd, tr_CRCX_ACK_osmux);
 			flow.mgcp_conn_id := extract_conn_id(resp);
 			/* extract port number from response */
@@ -503,12 +543,6 @@
 	runs on dummy_CT {
 		var template MgcpCommand cmd;
 		var MgcpResponse resp;
-		var SDP_attribute_list attributes;
-
-		attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
-		if (isvalue(flow.fmtp)) {
-			attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
-		}
 
 		/* rebind local RTP emulation socket to the new address */
 		f_rtpem_bind(pt, flow.em.hostname, flow.em.portnr);
@@ -517,15 +551,12 @@
 		if (ispresent(flow.rtp_cfg)) {
 			f_rtpem_configure(pt, flow.rtp_cfg);
 		} else {
-			var RtpemConfig rtp_cfg := c_RtpemDefaultCfg;
-			rtp_cfg.tx_payload_type := flow.pt
-			f_rtpem_configure(pt, rtp_cfg);
+			f_rtpem_configure(pt, f_gen_rtpem_config_from_flow(flow));
 		}
 
 		/* connect MGW side RTP socket to the emulation-side RTP socket using SDP */
 		cmd := ts_MDCX(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id);
-		cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
-				  flow.em.portnr, { int2str(flow.pt) }, attributes);
+		cmd.sdp := f_gen_sdp(flow);
 		resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
 
 		/* extract MGW-side port number from response. (usually this
@@ -542,16 +573,10 @@
 	runs on dummy_CT {
 		var template MgcpCommand cmd;
 		var MgcpResponse resp;
-		var SDP_attribute_list attributes;
 		var OsmuxRxHandle rx_hdl;
 		var charstring cid_response;
 		var OsmuxCID cid_resp_parsed
 
-		attributes := { valueof(ts_SDP_rtpmap(flow.pt, flow.codec)), valueof(ts_SDP_ptime(20)) };
-		if (isvalue(flow.fmtp)) {
-			attributes := attributes & { valueof(ts_SDP_fmtp(flow.pt, flow.fmtp)) };
-		}
-
 		/* rebind local Osmux emulation socket to the new address */
 		f_osmuxem_bind(pt, flow.em.hostname, flow.em.portnr);
 
@@ -573,8 +598,7 @@
 
 		/* connect MGW side Osmux socket to the emulation-side Osmux socket using SDP */
 		cmd := ts_MDCX_osmux(get_next_trans_id(), ep, mode, call_id, flow.mgcp_conn_id, flow.osmux.local_cid);
-		cmd.sdp := ts_SDP(flow.em.hostname, flow.em.hostname, "23", "42",
-				  flow.em.portnr, { int2str(flow.pt) }, attributes);
+		cmd.sdp := f_gen_sdp(flow);
 		resp := mgcp_transceive_mgw(cmd, tr_MDCX_ACK);
 
 		/* extract MGW-side port number from response. (usually this
@@ -1375,10 +1399,11 @@
 		/* from us to MGW */
 		flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
 		flow[0].rtp_cfg := c_RtpemDefaultCfg
-		flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
-		flow[0].rtp_cfg.rx_fixed_payload := amr_payload;
-		flow[0].rtp_cfg.tx_fixed_payload := amr_payload;
-		flow[0].fmtp := fmtp;
+		flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
+		flow[0].rtp_cfg.rx_payloads[0].fixed_payload := amr_payload;
+		flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
+		flow[0].rtp_cfg.tx_payloads[0].fixed_payload := amr_payload;
+		flow[0].codec_descr[0].fmtp := fmtp;
 		/* bind local RTP emulation sockets */
 		flow[0].em.portnr := 10000;
 		f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
@@ -1483,10 +1508,11 @@
 		/* Create the first connection in receive only mode */
 		flow[0] := valueof(t_RtpFlow(local_ip_rtp, remote_ip_rtp, 112, "AMR/8000"));
 		flow[0].rtp_cfg := c_RtpemDefaultCfg
-		flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
+		flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
+		flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
 		/* 0014 is the ToC (CMR=AMR4.75) in front of AMR Payload in RTP Payload */
-		flow[0].rtp_cfg.rx_fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload);
-		flow[0].rtp_cfg.tx_fixed_payload := flow[0].rtp_cfg.rx_fixed_payload;
+		flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '0014'O & f_osmux_gen_expected_rx_rtp_payload(2 /* AMR_FT_2, 5.90 */, c_OsmuxemDefaultCfg.tx_fixed_payload);
+		flow[0].rtp_cfg.tx_payloads[0].fixed_payload := flow[0].rtp_cfg.rx_payloads[0].fixed_payload;
 		/* bind local RTP emulation sockets */
 		flow[0].em.portnr := 10000;
 		f_flow_create(RTPEM[0], ep, call_id, "recvonly", flow[0], true);
@@ -1780,8 +1806,10 @@
 			hostname := host_b,
 			portnr := omit
 		},
-		pt := pt,
-		codec := codec,
+		codec_descr := {{
+			pt := pt,
+			codec := codec
+		}},
 		osmux:= {
 			local_cid_sent := false,
 			local_cid := omit,
@@ -2259,20 +2287,22 @@
 		/* bind local RTP emulation sockets */
 		flow[0].em.portnr := 10000;
 		flow[0].rtp_cfg := c_RtpemDefaultCfg;
-		flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
-		flow[0].rtp_cfg.rx_fixed_payload := pl0
-		flow[0].rtp_cfg.tx_fixed_payload := pl0
-		flow[0].fmtp := fmtp0;
+		flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
+		flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
+		flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0;
+		flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0;
+		flow[0].codec_descr[0].fmtp := fmtp0;
 		f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
 
 		/* Connection #1 (Bidirectional) */
 		flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 111, "GSM-HR-08/8000"));
 		flow[1].em.portnr := 20000;
 		flow[1].rtp_cfg	:= c_RtpemDefaultCfg;
-		flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
-		flow[1].rtp_cfg.rx_fixed_payload := pl1
-		flow[1].rtp_cfg.tx_fixed_payload := pl1
-		flow[1].fmtp := fmtp1;
+		flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
+		flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
+		flow[1].rtp_cfg.rx_payloads[0].fixed_payload := pl1;
+		flow[1].rtp_cfg.tx_payloads[0].fixed_payload := pl1;
+		flow[1].codec_descr[0].fmtp := fmtp1;
 		f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
 
 		/* Send RTP packets to connection #0, receive on connection #1 */
@@ -2337,20 +2367,22 @@
 		/* bind local RTP emulation sockets */
 		flow[0].em.portnr := 10000;
 		flow[0].rtp_cfg := c_RtpemDefaultCfg;
-		flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
-		flow[0].rtp_cfg.rx_fixed_payload := pl0;
-		flow[0].rtp_cfg.tx_fixed_payload := pl0;
-		flow[0].fmtp := fmtp0;
+		flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
+		flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
+		flow[0].rtp_cfg.rx_payloads[0].fixed_payload := pl0;
+		flow[0].rtp_cfg.tx_payloads[0].fixed_payload := pl0;
+		flow[0].codec_descr[0].fmtp := fmtp0;
 		f_flow_create(RTPEM[0], ep, call_id, "sendrecv", flow[0]);
 
 		/* Connection #1 (Bidirectional) */
 		flow[1] := valueof(t_RtpFlow(mp_local_ipv4, mp_remote_ipv4, 112, "AMR/8000"));
 		flow[1].em.portnr := 20000;
 		flow[1].rtp_cfg	:= c_RtpemDefaultCfg;
-		flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
-		flow[1].rtp_cfg.rx_fixed_payload := pl1;
-		flow[1].rtp_cfg.tx_fixed_payload := pl1;
-		flow[1].fmtp := fmtp1;
+		flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
+		flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
+		flow[1].rtp_cfg.rx_payloads[0].fixed_payload := pl1;
+		flow[1].rtp_cfg.tx_payloads[0].fixed_payload := pl1;
+		flow[1].codec_descr[0].fmtp := fmtp1;
 		f_flow_create(RTPEM[1], ep, call_id, "sendrecv", flow[1]);
 
 		/* Send RTP packets to connection #0, receive on connection #1 */
@@ -2682,7 +2714,8 @@
 		flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
 		flow[0].em.portnr := 10000;
 		flow[0].rtp_cfg := c_RtpemDefaultCfg;
-		flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
+		flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
+		flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
 		flow[0].rtp_cfg.iuup_mode := true;
 		flow[0].rtp_cfg.iuup_cfg.active_init := true;
 		flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
@@ -2694,7 +2727,8 @@
 		flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 96, "VND.3GPP.IUFP/16000"));
 		flow[1].em.portnr := 20000;
 		flow[1].rtp_cfg := c_RtpemDefaultCfg;
-		flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
+		flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
+		flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
 		flow[1].rtp_cfg.iuup_mode := true;
 		flow[1].rtp_cfg.iuup_cfg.active_init := false;
 		f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
@@ -2816,9 +2850,11 @@
 		flow[0] := valueof(t_RtpFlow(local_ip_a, remote_ip_a, 96, "VND.3GPP.IUFP/16000"));
 		flow[0].em.portnr := 10000;
 		flow[0].rtp_cfg := c_RtpemDefaultCfg;
-		flow[0].rtp_cfg.tx_payload_type := flow[0].pt;
-		flow[0].rtp_cfg.tx_fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
-		flow[0].rtp_cfg.rx_fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O; /* flow[1].rtp_cfg.tx_fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
+		flow[0].rtp_cfg.tx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
+		flow[0].rtp_cfg.rx_payloads[0].payload_type := flow[0].codec_descr[0].pt;
+		flow[0].rtp_cfg.tx_payloads[0].fixed_payload := '4f28959ffeb80181f5c4e83d176c897b4a4e333298333419a493ca63ded6e0'O;
+		/* flow[1].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-BE-RTP->AMR-IUUP*/
+		flow[0].rtp_cfg.rx_payloads[0].fixed_payload := '08556d944c71a1a081e7ead204244480000ecd82b81118000097c4794e7740'O;
 		flow[0].rtp_cfg.iuup_mode := true;
 		flow[0].rtp_cfg.iuup_cfg.active_init := true;
 		flow[0].rtp_cfg.iuup_cfg.rab_flow_combs := rfcl_a;
@@ -2830,9 +2866,11 @@
 		flow[1] := valueof(t_RtpFlow(local_ip_b, remote_ip_b, 112, "AMR/8000"));
 		flow[1].em.portnr := 20000;
 		flow[1].rtp_cfg := c_RtpemDefaultCfg;
-		flow[1].rtp_cfg.tx_payload_type := flow[1].pt;
-		flow[1].rtp_cfg.tx_fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
-		flow[1].rtp_cfg.rx_fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O; /* flow[0].rtp_cfg.tx_fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
+		flow[1].rtp_cfg.tx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
+		flow[1].rtp_cfg.rx_payloads[0].payload_type := flow[1].codec_descr[0].pt;
+		flow[1].rtp_cfg.tx_payloads[0].fixed_payload := '0382155b65131c68682079fab4810911200003b360ae0446000025f11e539dd0'O;
+		/* flow[0].rtp_cfg.rx_payloads[0].fixed_payload converted AMR-IuUP->AMR-BE-RTP*/
+		flow[1].rtp_cfg.rx_payloads[0].fixed_payload := 'f3d3ca2567ffae00607d713a0f45db225ed2938ccca60ccd066924f298f7b5b8'O;
 		flow[1].rtp_cfg.iuup_mode := false;
 		f_flow_create(RTPEM[1], ep, call_id, "recvonly", flow[1], true);
 		f_rtpem_mode(RTPEM[1], RTPEM_MODE_RXONLY);