blob: 8d741fba331f6a432620e773b3013618b76af5e5 [file] [log] [blame]
Harald Welte067d66e2017-12-13 17:24:03 +01001module RTP_Emulation {
2
3/* Functionalities that we want this module to imeplement:
4 * * act as a RTP source that generates a RTP Stream
5 * * act asaa RTP sink that consumes a RTP Stream
6 *
7 * for all of the above, we want to be able to
8 * * specify the payload type
9 * * specify the interval / sample rate
10 * * create drop-outs in the stream
11 * * detect reordered or lost frames
12 * * validate if the size of the frames matches epectations
13 * * play back real audio (at least some tones?)
14 * * enable/disable generation/verification of RTCP
15 */
16
Harald Weltea4ca4462018-02-09 00:17:14 +010017/* Ideas:
18
19* each component consists of transmitter and receiver
20* transmitters and receivers can be operated as tuple?
21* high-level operation
22** set-up config at transmitter + receiver
23** transmit sequence of payloads
24** verify receiption of those payloads
25* can operate full-duplex/bi-directional as needed
26
27* transmitter
28** trigger transmission of n number of packets
29** transmit them at normal ptime interval
30** payload size configurable
31** payload contents PRBS or the like
32
33* receiver
34** count number of related packets at receiver
35** check received payload type
36** check received timestamp increments
37** check received seq_nr increments
38** (optionally) check for SSRC
39** (optionally) check for payload size
40** (optionally) check for payload contents
41
42* later
43** how to test transcoding?
44** how to test pure play-out endpoints (rx only)?
45** how to test "Rx from wrong IP/port" scenarios?
46** how to test RTCP?
47** maybe keep ports un-connected to show wrong src -lrt
48
49*/
50
51
52
53
Harald Welte067d66e2017-12-13 17:24:03 +010054import from General_Types all;
55import from Osmocom_Types all;
56import from IPL4asp_Types all;
57import from RTP_Types all;
58import from RTP_CodecPort all;
59import from RTP_CodecPort_CtrlFunct all;
60
Harald Welte80981642017-12-24 23:59:47 +010061import from IuUP_Types all;
62import from IuUP_Emulation all;
63
Harald Welte067d66e2017-12-13 17:24:03 +010064type component RTP_Emulation_CT {
65 /* down-facing ports for RTP and RTCP codec ports on top of IPL4asp */
66 port RTP_CODEC_PT RTP;
67 var integer g_rtp_conn_id := -1;
68 port RTP_CODEC_PT RTCP;
69 var integer g_rtcp_conn_id := -1;
70
71 /* user-facing port for controlling the binding */
72 port RTPEM_CTRL_PT CTRL;
73
74 /* configurable by user, should be fixed */
Harald Welte80981642017-12-24 23:59:47 +010075 var RtpemConfig g_cfg := c_RtpemDefaultCfg;
Harald Welte067d66e2017-12-13 17:24:03 +010076
77 var HostName g_remote_host;
78 var PortNumber g_remote_port;
79 var HostName g_local_host;
80 var PortNumber g_local_port;
81
82 /* state variables, change over time */
83 var boolean g_rx_enabled := false;
84 var LIN2_BO_LAST g_tx_next_seq := 0;
85 var uint32_t g_tx_next_ts := 0;
86
87 var INT7b g_rx_payload_type := 0;
88 var LIN2_BO_LAST g_rx_last_seq;
89 var uint32_t g_rx_last_ts;
Harald Welte80981642017-12-24 23:59:47 +010090
91 var IuUP_Entity g_iuup_ent; // := valueof(t_IuUP_Entity(1));
Harald Welte067d66e2017-12-13 17:24:03 +010092}
93
94type enumerated RtpemMode {
95 RTPEM_MODE_NONE,
96 RTPEM_MODE_TXONLY,
97 RTPEM_MODE_RXONLY,
98 RTPEM_MODE_BIDIR
99};
100
Harald Welte3f6f48f2017-12-24 21:48:33 +0100101type record RtpemConfig {
102 INT7b tx_payload_type,
103 integer tx_samplerate_hz,
104 integer tx_duration_ms,
105 BIT32_BO_LAST tx_ssrc,
Harald Welte80981642017-12-24 23:59:47 +0100106 octetstring tx_fixed_payload optional,
107 boolean iuup_mode,
108 boolean iuup_tx_init
Harald Welte3f6f48f2017-12-24 21:48:33 +0100109};
110
Harald Welte80981642017-12-24 23:59:47 +0100111const RtpemConfig c_RtpemDefaultCfg := {
Harald Welte3f6f48f2017-12-24 21:48:33 +0100112 tx_payload_type := 0,
113 tx_samplerate_hz := 8000,
114 tx_duration_ms := 20,
115 tx_ssrc := '11011110101011011011111011101111'B,
Harald Welte80981642017-12-24 23:59:47 +0100116 tx_fixed_payload := '01020304'O,
117 iuup_mode := false,
118 iuup_tx_init := true
Harald Welte3f6f48f2017-12-24 21:48:33 +0100119}
120
Harald Welte067d66e2017-12-13 17:24:03 +0100121signature RTPEM_bind(in HostName local_host, inout PortNumber local_port);
122signature RTPEM_connect(in HostName remote_host, in PortNumber remote_port);
123signature RTPEM_mode(in RtpemMode mode);
Harald Welte3f6f48f2017-12-24 21:48:33 +0100124signature RTPEM_configure(in RtpemConfig cfg);
Harald Welte067d66e2017-12-13 17:24:03 +0100125
126type port RTPEM_CTRL_PT procedure {
Harald Welte3f6f48f2017-12-24 21:48:33 +0100127 inout RTPEM_bind, RTPEM_connect, RTPEM_mode, RTPEM_configure;
Harald Welte067d66e2017-12-13 17:24:03 +0100128} with { extension "internal" };
129
130template PDU_RTP ts_RTP(BIT32_BO_LAST ssrc, INT7b pt, LIN2_BO_LAST seq, uint32_t ts,
131 octetstring payload, BIT1 marker := '0'B) := {
132 version := 2,
133 padding_ind := '0'B,
134 extension_ind := '0'B,
135 CSRC_count := 0,
136 marker_bit := marker,
137 payload_type := pt,
138 sequence_number := seq,
Harald Welte8a4d3952017-12-24 21:30:23 +0100139 time_stamp := int2bit(ts, 32),
Harald Welte067d66e2017-12-13 17:24:03 +0100140 SSRC_id := ssrc,
141 CSRCs := omit,
142 ext_header := omit,
143 data := payload
144}
145
146private function f_tx_rtp(octetstring payload, BIT1 marker := '0'B) runs on RTP_Emulation_CT {
Harald Welte80981642017-12-24 23:59:47 +0100147 if (g_cfg.iuup_mode) {
148 payload := f_IuUP_Em_tx_encap(g_iuup_ent, payload);
149 }
Harald Welte3f6f48f2017-12-24 21:48:33 +0100150 var PDU_RTP rtp := valueof(ts_RTP(g_cfg.tx_ssrc, g_cfg.tx_payload_type, g_tx_next_seq,
Harald Welte067d66e2017-12-13 17:24:03 +0100151 g_tx_next_ts, payload, marker));
152 RTP.send(t_RTP_Send(g_rtp_conn_id, RTP_messages_union:{rtp:=rtp}));
153 /* increment sequence + timestamp for next transmit */
154 g_tx_next_seq := g_tx_next_seq + 1;
Harald Welte3f6f48f2017-12-24 21:48:33 +0100155 g_tx_next_ts := g_tx_next_ts + (g_cfg.tx_samplerate_hz / (1000 / g_cfg.tx_duration_ms));
Harald Welte067d66e2017-12-13 17:24:03 +0100156}
157
158function f_main() runs on RTP_Emulation_CT
159{
160 var Result res;
161
Harald Welte3f6f48f2017-12-24 21:48:33 +0100162 timer T_transmit := int2float(g_cfg.tx_duration_ms)/1000.0;
Harald Welte067d66e2017-12-13 17:24:03 +0100163 var RTP_RecvFrom rx_rtp;
Harald Welte3f6f48f2017-12-24 21:48:33 +0100164 var RtpemConfig cfg;
Harald Welte067d66e2017-12-13 17:24:03 +0100165 var template RTP_RecvFrom tr := {
166 connId := ?,
167 remName := ?,
168 remPort := ?,
169 locName := ?,
170 locPort := ?,
171 msg := ?
172 };
173 var template RTP_RecvFrom tr_rtp := tr;
174 var template RTP_RecvFrom tr_rtcp := tr;
175 tr_rtp.connId := g_rtp_conn_id;
176 tr_rtp.msg := { rtp := ? };
177 tr_rtp.connId := g_rtcp_conn_id;
178 tr_rtcp.msg := { rtcp := ? };
179
Harald Welte80981642017-12-24 23:59:47 +0100180 g_iuup_ent := valueof(t_IuUP_Entity(g_cfg.iuup_tx_init));
181
Harald Welte067d66e2017-12-13 17:24:03 +0100182 while (true) {
183 alt {
184 /* control procedures (calls) from the user */
185 [] CTRL.getcall(RTPEM_bind:{?,?}) -> param(g_local_host, g_local_port) {
186 if (g_local_port rem 2 == 1) {
187 //CTRL.raise(RTPEM_bind, "Local Port is not an even port number!");
188 log("Local Port is not an even port number!");
189 continue;
190 }
191 res := RTP_CodecPort_CtrlFunct.f_IPL4_listen(RTP, g_local_host,
192 g_local_port, {udp:={}});
193 g_rtp_conn_id := res.connId;
194 res := RTP_CodecPort_CtrlFunct.f_IPL4_listen(RTP, g_local_host,
195 g_local_port+1, {udp:={}});
196 g_rtcp_conn_id := res.connId;
197 CTRL.reply(RTPEM_bind:{g_local_host, g_local_port});
198 }
199 [] CTRL.getcall(RTPEM_connect:{?,?}) -> param (g_remote_host, g_remote_port) {
200 if (g_remote_port rem 2 == 1) {
201 //CTRL.raise(RTPEM_connect, "Remote Port is not an even number!");
202 log("Remote Port is not an even number!");
203 continue;
204 }
205 res := RTP_CodecPort_CtrlFunct.f_IPL4_connect(RTP, g_remote_host,
206 g_remote_port,
207 g_local_host, g_local_port,
208 g_rtp_conn_id, {udp:={}});
209 res := RTP_CodecPort_CtrlFunct.f_IPL4_connect(RTCP, g_remote_host,
210 g_remote_port+1,
211 g_local_host, g_local_port+1,
212 g_rtcp_conn_id, {udp:={}});
213 CTRL.reply(RTPEM_connect:{g_remote_host, g_remote_port});
214 }
215 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_NONE}) {
216 T_transmit.stop;
217 g_rx_enabled := false;
Harald Welte80981642017-12-24 23:59:47 +0100218 CTRL.reply(RTPEM_mode:{RTPEM_MODE_NONE});
Harald Welte067d66e2017-12-13 17:24:03 +0100219 }
220 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_TXONLY}) {
221 /* start transmit timer */
222 T_transmit.start;
223 g_rx_enabled := false;
Harald Welte80981642017-12-24 23:59:47 +0100224 CTRL.reply(RTPEM_mode:{RTPEM_MODE_TXONLY});
Harald Welte067d66e2017-12-13 17:24:03 +0100225 }
226 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_RXONLY}) {
227
228 T_transmit.stop;
229 if (g_rx_enabled == false) {
230 /* flush queues */
231 RTP.clear;
232 RTCP.clear;
233 g_rx_enabled := true;
234 }
Harald Welte80981642017-12-24 23:59:47 +0100235 CTRL.reply(RTPEM_mode:{RTPEM_MODE_RXONLY});
Harald Welte067d66e2017-12-13 17:24:03 +0100236 }
237 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_BIDIR}) {
238 T_transmit.start;
239 if (g_rx_enabled == false) {
240 /* flush queues */
241 RTP.clear;
242 RTCP.clear;
243 g_rx_enabled := true;
244 }
Harald Welte80981642017-12-24 23:59:47 +0100245 CTRL.reply(RTPEM_mode:{RTPEM_MODE_BIDIR});
Harald Welte067d66e2017-12-13 17:24:03 +0100246 }
Harald Welte3f6f48f2017-12-24 21:48:33 +0100247 [] CTRL.getcall(RTPEM_configure:{?}) -> param (cfg) {
248 g_cfg := cfg;
Harald Welte80981642017-12-24 23:59:47 +0100249 g_iuup_ent.cfg.active_init := g_cfg.iuup_tx_init;
250 CTRL.reply(RTPEM_configure:{cfg});
Harald Welte3f6f48f2017-12-24 21:48:33 +0100251 }
Harald Welte067d66e2017-12-13 17:24:03 +0100252
253 /* simply ignore any RTTP/RTCP if receiver not enabled */
254 [g_rx_enabled==false] RTP.receive(tr_rtp) { }
255 [g_rx_enabled==false] RTCP.receive(tr_rtp) { }
256
257 /* process received RTCP/RTP if receiver enabled */
258 [g_rx_enabled] RTP.receive(tr_rtp) -> value rx_rtp {
259 log("RX RTP: ", rx_rtp);
Harald Welte80981642017-12-24 23:59:47 +0100260 if (g_cfg.iuup_mode) {
261 rx_rtp.msg.rtp.data := f_IuUP_Em_rx_decaps(g_iuup_ent, rx_rtp.msg.rtp.data);
262 }
Harald Welte067d66e2017-12-13 17:24:03 +0100263 }
264 [g_rx_enabled] RTCP.receive(tr_rtcp) -> value rx_rtp {
265 log("RX RTCP: ", rx_rtp);
266 }
267
268 /* transmit if timer has expired */
269 [] T_transmit.timeout {
270 /* send one RTP frame, re-start timer */
Harald Welte3f6f48f2017-12-24 21:48:33 +0100271 f_tx_rtp(g_cfg.tx_fixed_payload);
Harald Welte067d66e2017-12-13 17:24:03 +0100272 T_transmit.start;
273 }
274
275 /* fail on any unexpected messages */
276 [] RTP.receive {
277 setverdict(fail, "Received unexpected type from RTP");
278 }
279 [] RTCP.receive {
280 setverdict(fail, "Received unexpected type from RTCP");
281 }
282 }
283 }
284}
285
286
287}