blob: bdca75da52e1b02df20f785cec5db3cb5c8439c1 [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 *
Pau Espin Pedrol08005d72020-09-08 13:16:14 +02007 * for all of the above, we want to be able to
Harald Welte067d66e2017-12-13 17:24:03 +01008 * * 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 Welte34b5a952019-05-27 11:54:11 +020017/* (C) 2017-2018 Harald Welte <laforge@gnumonks.org>
18 * (C) 2018-2019 sysmocom - s.f.m.c. GmbH
19 * All rights reserved.
20 *
21 * Released under the terms of GNU General Public License, Version 2 or
22 * (at your option) any later version.
23 *
24 * SPDX-License-Identifier: GPL-2.0-or-later
25 */
26
27
Harald Weltea4ca4462018-02-09 00:17:14 +010028/* Ideas:
29
30* each component consists of transmitter and receiver
31* transmitters and receivers can be operated as tuple?
32* high-level operation
33** set-up config at transmitter + receiver
34** transmit sequence of payloads
35** verify receiption of those payloads
36* can operate full-duplex/bi-directional as needed
37
38* transmitter
39** trigger transmission of n number of packets
40** transmit them at normal ptime interval
41** payload size configurable
42** payload contents PRBS or the like
43
44* receiver
45** count number of related packets at receiver
46** check received payload type
47** check received timestamp increments
48** check received seq_nr increments
49** (optionally) check for SSRC
50** (optionally) check for payload size
51** (optionally) check for payload contents
52
53* later
54** how to test transcoding?
55** how to test pure play-out endpoints (rx only)?
56** how to test "Rx from wrong IP/port" scenarios?
57** how to test RTCP?
58** maybe keep ports un-connected to show wrong src -lrt
59
60*/
61
62
63
64
Harald Welte067d66e2017-12-13 17:24:03 +010065import from General_Types all;
66import from Osmocom_Types all;
67import from IPL4asp_Types all;
68import from RTP_Types all;
69import from RTP_CodecPort all;
70import from RTP_CodecPort_CtrlFunct all;
71
Harald Welte80981642017-12-24 23:59:47 +010072import from IuUP_Types all;
73import from IuUP_Emulation all;
74
Harald Welte067d66e2017-12-13 17:24:03 +010075type component RTP_Emulation_CT {
76 /* down-facing ports for RTP and RTCP codec ports on top of IPL4asp */
77 port RTP_CODEC_PT RTP;
78 var integer g_rtp_conn_id := -1;
79 port RTP_CODEC_PT RTCP;
80 var integer g_rtcp_conn_id := -1;
81
82 /* user-facing port for controlling the binding */
83 port RTPEM_CTRL_PT CTRL;
Vadim Yanitskiyed5129f2021-07-04 03:42:31 +020084 /* user-facing port for sniffing RTP frames */
85 port RTPEM_DATA_PT DATA;
Harald Welte067d66e2017-12-13 17:24:03 +010086
87 /* configurable by user, should be fixed */
Harald Welte80981642017-12-24 23:59:47 +010088 var RtpemConfig g_cfg := c_RtpemDefaultCfg;
Harald Welte067d66e2017-12-13 17:24:03 +010089
Harald Weltecb8b4272018-03-28 19:57:58 +020090 /* statistics */
91 var RtpemStats g_stats_rtp := c_RtpemStatsReset;
92 var RtpemStats g_stats_rtcp := c_RtpemStatsReset;
93
Harald Welte067d66e2017-12-13 17:24:03 +010094 var HostName g_remote_host;
95 var PortNumber g_remote_port;
96 var HostName g_local_host;
97 var PortNumber g_local_port;
98
99 /* state variables, change over time */
100 var boolean g_rx_enabled := false;
Pau Espin Pedrole38bfe02019-05-17 18:40:43 +0200101 var boolean g_tx_connected := false; /* Set to true after connect() */
Harald Welte067d66e2017-12-13 17:24:03 +0100102 var LIN2_BO_LAST g_tx_next_seq := 0;
103 var uint32_t g_tx_next_ts := 0;
104
105 var INT7b g_rx_payload_type := 0;
106 var LIN2_BO_LAST g_rx_last_seq;
107 var uint32_t g_rx_last_ts;
Harald Welte80981642017-12-24 23:59:47 +0100108
109 var IuUP_Entity g_iuup_ent; // := valueof(t_IuUP_Entity(1));
Oliver Smith216019f2019-06-26 12:21:38 +0200110
111 var boolean g_conn_refuse_expect := false;
112 var boolean g_conn_refuse_received := false;
Harald Welte067d66e2017-12-13 17:24:03 +0100113}
114
115type enumerated RtpemMode {
116 RTPEM_MODE_NONE,
117 RTPEM_MODE_TXONLY,
118 RTPEM_MODE_RXONLY,
119 RTPEM_MODE_BIDIR
120};
121
Harald Weltecb8b4272018-03-28 19:57:58 +0200122type record RtpemStats {
123 /* number of packets transmitted */
124 integer num_pkts_tx,
125 /* number of RTP payload bytes transmitted */
126 integer bytes_payload_tx,
127
128 /* number of packets received */
129 integer num_pkts_rx,
130 /* number of RTP payload bytes received */
131 integer bytes_payload_rx,
132 /* number of packets received out-of-sequence */
133 integer num_pkts_rx_err_seq,
134 /* number of packets received wrong timestamp */
135 integer num_pkts_rx_err_ts,
Philipp Maierc290d722018-07-24 18:51:36 +0200136 /* number of packets received wrong payload type */
137 integer num_pkts_rx_err_pt,
Harald Weltecb8b4272018-03-28 19:57:58 +0200138 /* number of packets received during Rx disable */
Philipp Maiera071ee42019-02-21 16:17:32 +0100139 integer num_pkts_rx_err_disabled,
140 /* number of packets received with mismatching payload */
141 integer num_pkts_rx_err_payload
Harald Weltecb8b4272018-03-28 19:57:58 +0200142}
143
144const RtpemStats c_RtpemStatsReset := {
145 num_pkts_tx := 0,
146 bytes_payload_tx := 0,
147 num_pkts_rx := 0,
148 bytes_payload_rx := 0,
149 num_pkts_rx_err_seq := 0,
150 num_pkts_rx_err_ts := 0,
Philipp Maierc290d722018-07-24 18:51:36 +0200151 num_pkts_rx_err_pt := 0,
Philipp Maiera071ee42019-02-21 16:17:32 +0100152 num_pkts_rx_err_disabled := 0,
153 num_pkts_rx_err_payload := 0
Harald Weltecb8b4272018-03-28 19:57:58 +0200154}
155
Harald Welte3f6f48f2017-12-24 21:48:33 +0100156type record RtpemConfig {
157 INT7b tx_payload_type,
158 integer tx_samplerate_hz,
159 integer tx_duration_ms,
160 BIT32_BO_LAST tx_ssrc,
Harald Welte80981642017-12-24 23:59:47 +0100161 octetstring tx_fixed_payload optional,
Philipp Maiera071ee42019-02-21 16:17:32 +0100162 octetstring rx_fixed_payload optional,
Harald Welte80981642017-12-24 23:59:47 +0100163 boolean iuup_mode,
164 boolean iuup_tx_init
Harald Welte3f6f48f2017-12-24 21:48:33 +0100165};
166
Harald Welte80981642017-12-24 23:59:47 +0100167const RtpemConfig c_RtpemDefaultCfg := {
Harald Welte3f6f48f2017-12-24 21:48:33 +0100168 tx_payload_type := 0,
169 tx_samplerate_hz := 8000,
170 tx_duration_ms := 20,
171 tx_ssrc := '11011110101011011011111011101111'B,
Harald Welte80981642017-12-24 23:59:47 +0100172 tx_fixed_payload := '01020304'O,
Philipp Maiera071ee42019-02-21 16:17:32 +0100173 rx_fixed_payload := '01020304'O,
Harald Welte80981642017-12-24 23:59:47 +0100174 iuup_mode := false,
175 iuup_tx_init := true
Harald Welte3f6f48f2017-12-24 21:48:33 +0100176}
177
Harald Welte067d66e2017-12-13 17:24:03 +0100178signature RTPEM_bind(in HostName local_host, inout PortNumber local_port);
179signature RTPEM_connect(in HostName remote_host, in PortNumber remote_port);
180signature RTPEM_mode(in RtpemMode mode);
Harald Welte3f6f48f2017-12-24 21:48:33 +0100181signature RTPEM_configure(in RtpemConfig cfg);
Harald Weltecb8b4272018-03-28 19:57:58 +0200182signature RTPEM_stats_get(out RtpemStats stats, in boolean rtcp);
Oliver Smith216019f2019-06-26 12:21:38 +0200183signature RTPEM_conn_refuse_expect(in boolean expect);
184signature RTPEM_conn_refuse_received(out boolean received);
Harald Welte067d66e2017-12-13 17:24:03 +0100185
186type port RTPEM_CTRL_PT procedure {
Oliver Smith216019f2019-06-26 12:21:38 +0200187 inout RTPEM_bind, RTPEM_connect, RTPEM_mode, RTPEM_configure, RTPEM_stats_get, RTPEM_conn_refuse_expect,
188 RTPEM_conn_refuse_received;
Harald Welte067d66e2017-12-13 17:24:03 +0100189} with { extension "internal" };
190
Vadim Yanitskiyed5129f2021-07-04 03:42:31 +0200191type port RTPEM_DATA_PT message {
192 inout PDU_RTP, PDU_RTCP;
193} with { extension "internal" };
194
Harald Welte1af40332018-03-29 08:50:33 +0200195function f_rtpem_bind(RTPEM_CTRL_PT pt, in HostName local_host, inout PortNumber local_port) {
196 pt.call(RTPEM_bind:{local_host, local_port}) {
197 [] pt.getreply(RTPEM_bind:{local_host, ?}) -> param (local_port) {};
198 }
199}
200function f_rtpem_connect(RTPEM_CTRL_PT pt, in HostName remote_host, in PortNumber remote_port) {
201 pt.call(RTPEM_connect:{remote_host, remote_port}) {
202 [] pt.getreply(RTPEM_connect:{remote_host, remote_port}) {};
203 }
204}
205function f_rtpem_mode(RTPEM_CTRL_PT pt, in RtpemMode mode) {
206 pt.call(RTPEM_mode:{mode}) {
207 [] pt.getreply(RTPEM_mode:{mode}) {};
208 }
209}
210function f_rtpem_configure(RTPEM_CTRL_PT pt, in RtpemConfig cfg) {
211 pt.call(RTPEM_configure:{cfg}) {
212 [] pt.getreply(RTPEM_configure:{cfg}) {};
213 }
214}
215function f_rtpem_stats_get(RTPEM_CTRL_PT pt, boolean rtcp := false) return RtpemStats {
216 var RtpemStats stats;
217 pt.call(RTPEM_stats_get:{-, rtcp}) {
218 [] pt.getreply(RTPEM_stats_get:{?, rtcp}) -> param(stats) {};
219 }
220 return stats;
221}
222
Philipp Maier2321ef92018-06-27 17:52:04 +0200223function f_rtpem_stats_compare_value(integer a, integer b, integer tolerance := 0) return boolean {
224 var integer temp;
Harald Welte5c49ba42018-03-29 08:51:20 +0200225
Philipp Maier2321ef92018-06-27 17:52:04 +0200226 temp := (a - b)
227 if (temp < 0) {
228 temp := -temp;
229 }
230
231 if (temp > tolerance) {
Harald Welte5c49ba42018-03-29 08:51:20 +0200232 return false;
233 }
Philipp Maier2321ef92018-06-27 17:52:04 +0200234
Harald Welte5c49ba42018-03-29 08:51:20 +0200235 return true;
236}
Harald Welte1af40332018-03-29 08:50:33 +0200237
Philipp Maier2321ef92018-06-27 17:52:04 +0200238/* Cross-compare two rtpem-statistics. The transmission statistics on the a side
239 * must match the reception statistics on the other side and vice versa. The
240 * user may also supply a tolerance value (number of packets) when deviations
241 * are acceptable */
242function f_rtpem_stats_compare(RtpemStats a, RtpemStats b, integer tolerance := 0) return boolean {
243 var integer plen;
244
245 log("stats A: ", a);
246 log("stats B: ", b);
247 log("tolerance: ", tolerance, " packets");
248
249 if (f_rtpem_stats_compare_value(a.num_pkts_tx, b.num_pkts_rx, tolerance) == false) {
250 return false;
251 }
252
253 if (f_rtpem_stats_compare_value(a.num_pkts_rx, b.num_pkts_tx, tolerance) == false) {
254 return false;
255 }
256
257 if(a.num_pkts_tx > 0) {
258 plen := a.bytes_payload_tx / a.num_pkts_tx;
259 } else {
260 plen := 0;
261 }
262
263 if (f_rtpem_stats_compare_value(a.bytes_payload_tx, b.bytes_payload_rx, tolerance * plen) == false) {
264 return false;
265 }
266
267 if (f_rtpem_stats_compare_value(a.bytes_payload_rx, b.bytes_payload_tx, tolerance * plen) == false) {
268 return false;
269 }
270
271 return true;
272}
Harald Welte1af40332018-03-29 08:50:33 +0200273
Philipp Maier36291392018-07-25 09:40:44 +0200274/* Check the statistics for general signs of errors. This is a basic general
275 * check that will fit most situations and is intended to be executed by
276 * the testcases as as needed. */
277function f_rtpem_stats_err_check(RtpemStats s) {
278 log("stats: ", s);
279
280 /* Check if there was some activity at either on the RX or on the
281 * TX side, but complete silence would indicate some problem */
282 if (s.num_pkts_tx < 1 and s.num_pkts_rx < 1) {
283 setverdict(fail, "no RTP packet activity detected (packets)");
284 mtc.stop;
285 }
286 if (s.bytes_payload_tx < 1 and s.bytes_payload_rx < 1) {
287 setverdict(fail, "no RTP packet activity detected (bytes)");
288 mtc.stop;
289 }
290
291 /* Check error counters */
292 if (s.num_pkts_rx_err_seq != 0) {
293 setverdict(fail, "RTP packet sequence number errors occurred");
294 mtc.stop;
295 }
296 if (s.num_pkts_rx_err_ts != 0) {
297 setverdict(fail, "RTP packet timestamp errors occurred");
298 mtc.stop;
299 }
300 if (s.num_pkts_rx_err_pt != 0) {
301 setverdict(fail, "RTP packet payload type errors occurred");
302 mtc.stop;
303 }
304 if (s.num_pkts_rx_err_disabled != 0) {
305 setverdict(fail, "RTP packets received while RX was disabled");
306 mtc.stop;
307 }
Philipp Maiera071ee42019-02-21 16:17:32 +0100308 if (s.num_pkts_rx_err_payload != 0) {
309 setverdict(fail, "RTP packets with mismatching payload received");
310 mtc.stop;
311 }
Philipp Maier36291392018-07-25 09:40:44 +0200312}
313
Oliver Smith216019f2019-06-26 12:21:38 +0200314function f_rtpem_conn_refuse_expect(RTPEM_CTRL_PT pt) {
315 pt.call(RTPEM_conn_refuse_expect:{true}) {
316 [] pt.getreply(RTPEM_conn_refuse_expect:{true}) {};
317 }
318}
319
320function f_rtpem_conn_refuse_verify(RTPEM_CTRL_PT pt) {
321 pt.call(RTPEM_conn_refuse_received:{?}) {
322 [] pt.getreply(RTPEM_conn_refuse_received:{true}) {};
323 [] pt.getreply(RTPEM_conn_refuse_received:{false}) {
324 setverdict(fail, "Expected to receive connection refused");
325 };
326 }
327}
328
Harald Welte067d66e2017-12-13 17:24:03 +0100329template PDU_RTP ts_RTP(BIT32_BO_LAST ssrc, INT7b pt, LIN2_BO_LAST seq, uint32_t ts,
330 octetstring payload, BIT1 marker := '0'B) := {
331 version := 2,
332 padding_ind := '0'B,
333 extension_ind := '0'B,
334 CSRC_count := 0,
335 marker_bit := marker,
336 payload_type := pt,
337 sequence_number := seq,
Harald Welte8a4d3952017-12-24 21:30:23 +0100338 time_stamp := int2bit(ts, 32),
Harald Welte067d66e2017-12-13 17:24:03 +0100339 SSRC_id := ssrc,
340 CSRCs := omit,
341 ext_header := omit,
342 data := payload
343}
344
345private function f_tx_rtp(octetstring payload, BIT1 marker := '0'B) runs on RTP_Emulation_CT {
Harald Welte80981642017-12-24 23:59:47 +0100346 if (g_cfg.iuup_mode) {
347 payload := f_IuUP_Em_tx_encap(g_iuup_ent, payload);
Pau Espin Pedrolb204a4e2021-12-24 14:18:39 +0100348 if (lengthof(payload) == 0) {
349 /* Nothing to transmit, waiting for INIT-ACK */
350 return;
351 }
Harald Welte80981642017-12-24 23:59:47 +0100352 }
Harald Welte3f6f48f2017-12-24 21:48:33 +0100353 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 +0100354 g_tx_next_ts, payload, marker));
355 RTP.send(t_RTP_Send(g_rtp_conn_id, RTP_messages_union:{rtp:=rtp}));
356 /* increment sequence + timestamp for next transmit */
357 g_tx_next_seq := g_tx_next_seq + 1;
Harald Welte3f6f48f2017-12-24 21:48:33 +0100358 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 +0100359}
360
361function f_main() runs on RTP_Emulation_CT
362{
363 var Result res;
Harald Weltecb8b4272018-03-28 19:57:58 +0200364 var boolean is_rtcp
Harald Welte067d66e2017-12-13 17:24:03 +0100365
Harald Welte3f6f48f2017-12-24 21:48:33 +0100366 timer T_transmit := int2float(g_cfg.tx_duration_ms)/1000.0;
Harald Welte067d66e2017-12-13 17:24:03 +0100367 var RTP_RecvFrom rx_rtp;
Harald Welte3f6f48f2017-12-24 21:48:33 +0100368 var RtpemConfig cfg;
Harald Welte067d66e2017-12-13 17:24:03 +0100369 var template RTP_RecvFrom tr := {
370 connId := ?,
371 remName := ?,
372 remPort := ?,
373 locName := ?,
374 locPort := ?,
375 msg := ?
376 };
377 var template RTP_RecvFrom tr_rtp := tr;
378 var template RTP_RecvFrom tr_rtcp := tr;
Harald Welte067d66e2017-12-13 17:24:03 +0100379 tr_rtp.msg := { rtp := ? };
Harald Welte067d66e2017-12-13 17:24:03 +0100380 tr_rtcp.msg := { rtcp := ? };
381
Oliver Smith216019f2019-06-26 12:21:38 +0200382 var template ASP_Event tr_conn_refuse := {result := { errorCode := ERROR_SOCKET,
383 connId := ?,
384 os_error_code := 111,
385 os_error_text := ? /* "Connection refused" */}};
386
Harald Welte80981642017-12-24 23:59:47 +0100387 g_iuup_ent := valueof(t_IuUP_Entity(g_cfg.iuup_tx_init));
388
Harald Welte067d66e2017-12-13 17:24:03 +0100389 while (true) {
390 alt {
391 /* control procedures (calls) from the user */
392 [] CTRL.getcall(RTPEM_bind:{?,?}) -> param(g_local_host, g_local_port) {
393 if (g_local_port rem 2 == 1) {
394 //CTRL.raise(RTPEM_bind, "Local Port is not an even port number!");
395 log("Local Port is not an even port number!");
396 continue;
397 }
Pau Espin Pedrole38bfe02019-05-17 18:40:43 +0200398
399 g_tx_connected := false; /* will set it back to true upon next connect() call */
Pau Espin Pedrol08005d72020-09-08 13:16:14 +0200400
401 if (g_rtp_conn_id != -1) {
402 res := RTP_CodecPort_CtrlFunct.f_IPL4_close(RTP, g_rtp_conn_id, {udp := {}});
403 g_rtp_conn_id := -1;
404 }
Harald Welte067d66e2017-12-13 17:24:03 +0100405 res := RTP_CodecPort_CtrlFunct.f_IPL4_listen(RTP, g_local_host,
406 g_local_port, {udp:={}});
Harald Welte9220f632018-05-23 20:27:02 +0200407 if (not ispresent(res.connId)) {
408 setverdict(fail, "Could not listen on RTP socket, check your configuration");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200409 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200410 }
Harald Welte067d66e2017-12-13 17:24:03 +0100411 g_rtp_conn_id := res.connId;
Harald Welte9774e7a2018-03-29 08:49:38 +0200412 tr_rtp.connId := g_rtp_conn_id;
Pau Espin Pedrol08005d72020-09-08 13:16:14 +0200413
414 if (g_rtcp_conn_id != -1) {
415 res := RTP_CodecPort_CtrlFunct.f_IPL4_close(RTCP, g_rtcp_conn_id, {udp := {}});
416 g_rtcp_conn_id := -1;
417 }
Harald Welte98eb1bf2018-04-02 18:18:44 +0200418 res := RTP_CodecPort_CtrlFunct.f_IPL4_listen(RTCP, g_local_host,
Harald Welte067d66e2017-12-13 17:24:03 +0100419 g_local_port+1, {udp:={}});
Harald Welte9220f632018-05-23 20:27:02 +0200420 if (not ispresent(res.connId)) {
421 setverdict(fail, "Could not listen on RTCP socket, check your configuration");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200422 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200423 }
Harald Welte067d66e2017-12-13 17:24:03 +0100424 g_rtcp_conn_id := res.connId;
Harald Welte9774e7a2018-03-29 08:49:38 +0200425 tr_rtcp.connId := g_rtcp_conn_id;
Harald Welte067d66e2017-12-13 17:24:03 +0100426 CTRL.reply(RTPEM_bind:{g_local_host, g_local_port});
427 }
428 [] CTRL.getcall(RTPEM_connect:{?,?}) -> param (g_remote_host, g_remote_port) {
429 if (g_remote_port rem 2 == 1) {
430 //CTRL.raise(RTPEM_connect, "Remote Port is not an even number!");
431 log("Remote Port is not an even number!");
432 continue;
433 }
434 res := RTP_CodecPort_CtrlFunct.f_IPL4_connect(RTP, g_remote_host,
435 g_remote_port,
436 g_local_host, g_local_port,
437 g_rtp_conn_id, {udp:={}});
Harald Welte9220f632018-05-23 20:27:02 +0200438 if (not ispresent(res.connId)) {
439 setverdict(fail, "Could not connect to RTP socket, check your configuration");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200440 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200441 }
Harald Welte067d66e2017-12-13 17:24:03 +0100442 res := RTP_CodecPort_CtrlFunct.f_IPL4_connect(RTCP, g_remote_host,
443 g_remote_port+1,
444 g_local_host, g_local_port+1,
445 g_rtcp_conn_id, {udp:={}});
Harald Welte9220f632018-05-23 20:27:02 +0200446 if (not ispresent(res.connId)) {
447 setverdict(fail, "Could not connect to RTCP socket, check your configuration");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200448 mtc.stop;
Harald Welte9220f632018-05-23 20:27:02 +0200449 }
Pau Espin Pedrole38bfe02019-05-17 18:40:43 +0200450 g_tx_connected := true;
Harald Welte067d66e2017-12-13 17:24:03 +0100451 CTRL.reply(RTPEM_connect:{g_remote_host, g_remote_port});
452 }
453 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_NONE}) {
454 T_transmit.stop;
455 g_rx_enabled := false;
Harald Welte80981642017-12-24 23:59:47 +0100456 CTRL.reply(RTPEM_mode:{RTPEM_MODE_NONE});
Harald Welte067d66e2017-12-13 17:24:03 +0100457 }
458 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_TXONLY}) {
459 /* start transmit timer */
460 T_transmit.start;
461 g_rx_enabled := false;
Harald Welte80981642017-12-24 23:59:47 +0100462 CTRL.reply(RTPEM_mode:{RTPEM_MODE_TXONLY});
Harald Welte067d66e2017-12-13 17:24:03 +0100463 }
464 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_RXONLY}) {
465
466 T_transmit.stop;
467 if (g_rx_enabled == false) {
468 /* flush queues */
469 RTP.clear;
470 RTCP.clear;
471 g_rx_enabled := true;
472 }
Harald Welte80981642017-12-24 23:59:47 +0100473 CTRL.reply(RTPEM_mode:{RTPEM_MODE_RXONLY});
Harald Welte067d66e2017-12-13 17:24:03 +0100474 }
475 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_BIDIR}) {
476 T_transmit.start;
477 if (g_rx_enabled == false) {
478 /* flush queues */
479 RTP.clear;
480 RTCP.clear;
481 g_rx_enabled := true;
482 }
Harald Welte80981642017-12-24 23:59:47 +0100483 CTRL.reply(RTPEM_mode:{RTPEM_MODE_BIDIR});
Harald Welte067d66e2017-12-13 17:24:03 +0100484 }
Harald Welte3f6f48f2017-12-24 21:48:33 +0100485 [] CTRL.getcall(RTPEM_configure:{?}) -> param (cfg) {
486 g_cfg := cfg;
Harald Welte80981642017-12-24 23:59:47 +0100487 g_iuup_ent.cfg.active_init := g_cfg.iuup_tx_init;
488 CTRL.reply(RTPEM_configure:{cfg});
Harald Welte3f6f48f2017-12-24 21:48:33 +0100489 }
Harald Weltecb8b4272018-03-28 19:57:58 +0200490 [] CTRL.getcall(RTPEM_stats_get:{?, ?}) -> param (is_rtcp) {
491 if (is_rtcp) {
492 CTRL.reply(RTPEM_stats_get:{g_stats_rtcp, is_rtcp});
493 } else {
494 CTRL.reply(RTPEM_stats_get:{g_stats_rtp, is_rtcp});
495 }
496 }
Oliver Smith216019f2019-06-26 12:21:38 +0200497 [] CTRL.getcall(RTPEM_conn_refuse_expect:{?}) -> param(g_conn_refuse_expect) {
498 CTRL.reply(RTPEM_conn_refuse_expect:{g_conn_refuse_expect});
499 }
500 [] CTRL.getcall(RTPEM_conn_refuse_received:{?}) {
501 CTRL.reply(RTPEM_conn_refuse_received:{g_conn_refuse_received});
502 }
Harald Weltecb8b4272018-03-28 19:57:58 +0200503
504
505 /* simply ignore any RTTP/RTP if receiver not enabled */
Vadim Yanitskiy8c978ec2021-07-04 03:13:36 +0200506 [not g_rx_enabled] RTP.receive(tr_rtp) {
Harald Weltecb8b4272018-03-28 19:57:58 +0200507 g_stats_rtp.num_pkts_rx_err_disabled := g_stats_rtp.num_pkts_rx_err_disabled+1;
508 }
Vadim Yanitskiy8c978ec2021-07-04 03:13:36 +0200509 [not g_rx_enabled] RTCP.receive(tr_rtcp) {
Harald Weltecb8b4272018-03-28 19:57:58 +0200510 g_stats_rtcp.num_pkts_rx_err_disabled := g_stats_rtcp.num_pkts_rx_err_disabled+1;
511 }
Harald Welte067d66e2017-12-13 17:24:03 +0100512
513 /* process received RTCP/RTP if receiver enabled */
514 [g_rx_enabled] RTP.receive(tr_rtp) -> value rx_rtp {
Harald Weltecb8b4272018-03-28 19:57:58 +0200515 /* increment counters */
Philipp Maierc290d722018-07-24 18:51:36 +0200516 if (rx_rtp.msg.rtp.payload_type != g_cfg.tx_payload_type) {
517 g_stats_rtp.num_pkts_rx_err_pt := g_stats_rtp.num_pkts_rx_err_pt+1;
518 }
Harald Weltecb8b4272018-03-28 19:57:58 +0200519 g_stats_rtp.num_pkts_rx := g_stats_rtp.num_pkts_rx+1;
520 g_stats_rtp.bytes_payload_rx := g_stats_rtp.bytes_payload_rx +
521 lengthof(rx_rtp.msg.rtp.data);
Philipp Maiera071ee42019-02-21 16:17:32 +0100522 if (ispresent(g_cfg.rx_fixed_payload) and rx_rtp.msg.rtp.data != g_cfg.rx_fixed_payload) {
523 g_stats_rtp.num_pkts_rx_err_payload := g_stats_rtp.num_pkts_rx_err_payload + 1;
524 }
Harald Welte80981642017-12-24 23:59:47 +0100525 if (g_cfg.iuup_mode) {
526 rx_rtp.msg.rtp.data := f_IuUP_Em_rx_decaps(g_iuup_ent, rx_rtp.msg.rtp.data);
527 }
Vadim Yanitskiyed5129f2021-07-04 03:42:31 +0200528 if (DATA.checkstate("Connected")) {
529 DATA.send(rx_rtp.msg.rtp);
530 }
Harald Welte067d66e2017-12-13 17:24:03 +0100531 }
532 [g_rx_enabled] RTCP.receive(tr_rtcp) -> value rx_rtp {
Harald Welte8d3ea0e2018-03-29 08:50:18 +0200533 //log("RX RTCP: ", rx_rtp);
Harald Weltecb8b4272018-03-28 19:57:58 +0200534 g_stats_rtcp.num_pkts_rx := g_stats_rtcp.num_pkts_rx+1;
Vadim Yanitskiyed5129f2021-07-04 03:42:31 +0200535 if (DATA.checkstate("Connected")) {
536 DATA.send(rx_rtp.msg.rtcp);
537 }
Harald Welte067d66e2017-12-13 17:24:03 +0100538 }
539
540 /* transmit if timer has expired */
Pau Espin Pedrole38bfe02019-05-17 18:40:43 +0200541 [g_tx_connected] T_transmit.timeout {
Harald Welte067d66e2017-12-13 17:24:03 +0100542 /* send one RTP frame, re-start timer */
Harald Welte3f6f48f2017-12-24 21:48:33 +0100543 f_tx_rtp(g_cfg.tx_fixed_payload);
Harald Welte067d66e2017-12-13 17:24:03 +0100544 T_transmit.start;
Harald Weltecb8b4272018-03-28 19:57:58 +0200545 /* update counters */
546 g_stats_rtp.num_pkts_tx := g_stats_rtp.num_pkts_tx+1;
547 g_stats_rtp.bytes_payload_tx := g_stats_rtp.bytes_payload_tx +
548 lengthof(g_cfg.tx_fixed_payload);
Harald Welte067d66e2017-12-13 17:24:03 +0100549 }
550
Oliver Smith216019f2019-06-26 12:21:38 +0200551 /* connection refused */
552 [g_conn_refuse_expect] RTP.receive(tr_conn_refuse) {
553 log("Connection refused (expected)");
554 g_conn_refuse_received := true;
555 }
556 [not g_conn_refuse_expect] RTP.receive(tr_conn_refuse) {
557 setverdict(fail, "Connection refused (unexpected)");
558 mtc.stop;
559 }
560
Harald Welte067d66e2017-12-13 17:24:03 +0100561 /* fail on any unexpected messages */
562 [] RTP.receive {
563 setverdict(fail, "Received unexpected type from RTP");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200564 mtc.stop;
Harald Welte067d66e2017-12-13 17:24:03 +0100565 }
566 [] RTCP.receive {
567 setverdict(fail, "Received unexpected type from RTCP");
Daniel Willmanne4ff5372018-07-05 17:35:03 +0200568 mtc.stop;
Harald Welte067d66e2017-12-13 17:24:03 +0100569 }
570 }
571 }
572}
573
574
575}