blob: f9525106d11bed1d56d9679da0985113140bccf8 [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
17import from General_Types all;
18import from Osmocom_Types all;
19import from IPL4asp_Types all;
20import from RTP_Types all;
21import from RTP_CodecPort all;
22import from RTP_CodecPort_CtrlFunct all;
23
Harald Welte80981642017-12-24 23:59:47 +010024import from IuUP_Types all;
25import from IuUP_Emulation all;
26
Harald Welte067d66e2017-12-13 17:24:03 +010027type component RTP_Emulation_CT {
28 /* down-facing ports for RTP and RTCP codec ports on top of IPL4asp */
29 port RTP_CODEC_PT RTP;
30 var integer g_rtp_conn_id := -1;
31 port RTP_CODEC_PT RTCP;
32 var integer g_rtcp_conn_id := -1;
33
34 /* user-facing port for controlling the binding */
35 port RTPEM_CTRL_PT CTRL;
36
37 /* configurable by user, should be fixed */
Harald Welte80981642017-12-24 23:59:47 +010038 var RtpemConfig g_cfg := c_RtpemDefaultCfg;
Harald Welte067d66e2017-12-13 17:24:03 +010039
40 var HostName g_remote_host;
41 var PortNumber g_remote_port;
42 var HostName g_local_host;
43 var PortNumber g_local_port;
44
45 /* state variables, change over time */
46 var boolean g_rx_enabled := false;
47 var LIN2_BO_LAST g_tx_next_seq := 0;
48 var uint32_t g_tx_next_ts := 0;
49
50 var INT7b g_rx_payload_type := 0;
51 var LIN2_BO_LAST g_rx_last_seq;
52 var uint32_t g_rx_last_ts;
Harald Welte80981642017-12-24 23:59:47 +010053
54 var IuUP_Entity g_iuup_ent; // := valueof(t_IuUP_Entity(1));
Harald Welte067d66e2017-12-13 17:24:03 +010055}
56
57type enumerated RtpemMode {
58 RTPEM_MODE_NONE,
59 RTPEM_MODE_TXONLY,
60 RTPEM_MODE_RXONLY,
61 RTPEM_MODE_BIDIR
62};
63
Harald Welte3f6f48f2017-12-24 21:48:33 +010064type record RtpemConfig {
65 INT7b tx_payload_type,
66 integer tx_samplerate_hz,
67 integer tx_duration_ms,
68 BIT32_BO_LAST tx_ssrc,
Harald Welte80981642017-12-24 23:59:47 +010069 octetstring tx_fixed_payload optional,
70 boolean iuup_mode,
71 boolean iuup_tx_init
Harald Welte3f6f48f2017-12-24 21:48:33 +010072};
73
Harald Welte80981642017-12-24 23:59:47 +010074const RtpemConfig c_RtpemDefaultCfg := {
Harald Welte3f6f48f2017-12-24 21:48:33 +010075 tx_payload_type := 0,
76 tx_samplerate_hz := 8000,
77 tx_duration_ms := 20,
78 tx_ssrc := '11011110101011011011111011101111'B,
Harald Welte80981642017-12-24 23:59:47 +010079 tx_fixed_payload := '01020304'O,
80 iuup_mode := false,
81 iuup_tx_init := true
Harald Welte3f6f48f2017-12-24 21:48:33 +010082}
83
Harald Welte067d66e2017-12-13 17:24:03 +010084signature RTPEM_bind(in HostName local_host, inout PortNumber local_port);
85signature RTPEM_connect(in HostName remote_host, in PortNumber remote_port);
86signature RTPEM_mode(in RtpemMode mode);
Harald Welte3f6f48f2017-12-24 21:48:33 +010087signature RTPEM_configure(in RtpemConfig cfg);
Harald Welte067d66e2017-12-13 17:24:03 +010088
89type port RTPEM_CTRL_PT procedure {
Harald Welte3f6f48f2017-12-24 21:48:33 +010090 inout RTPEM_bind, RTPEM_connect, RTPEM_mode, RTPEM_configure;
Harald Welte067d66e2017-12-13 17:24:03 +010091} with { extension "internal" };
92
93template PDU_RTP ts_RTP(BIT32_BO_LAST ssrc, INT7b pt, LIN2_BO_LAST seq, uint32_t ts,
94 octetstring payload, BIT1 marker := '0'B) := {
95 version := 2,
96 padding_ind := '0'B,
97 extension_ind := '0'B,
98 CSRC_count := 0,
99 marker_bit := marker,
100 payload_type := pt,
101 sequence_number := seq,
Harald Welte8a4d3952017-12-24 21:30:23 +0100102 time_stamp := int2bit(ts, 32),
Harald Welte067d66e2017-12-13 17:24:03 +0100103 SSRC_id := ssrc,
104 CSRCs := omit,
105 ext_header := omit,
106 data := payload
107}
108
109private function f_tx_rtp(octetstring payload, BIT1 marker := '0'B) runs on RTP_Emulation_CT {
Harald Welte80981642017-12-24 23:59:47 +0100110 if (g_cfg.iuup_mode) {
111 payload := f_IuUP_Em_tx_encap(g_iuup_ent, payload);
112 }
Harald Welte3f6f48f2017-12-24 21:48:33 +0100113 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 +0100114 g_tx_next_ts, payload, marker));
115 RTP.send(t_RTP_Send(g_rtp_conn_id, RTP_messages_union:{rtp:=rtp}));
116 /* increment sequence + timestamp for next transmit */
117 g_tx_next_seq := g_tx_next_seq + 1;
Harald Welte3f6f48f2017-12-24 21:48:33 +0100118 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 +0100119}
120
121function f_main() runs on RTP_Emulation_CT
122{
123 var Result res;
124
Harald Welte3f6f48f2017-12-24 21:48:33 +0100125 timer T_transmit := int2float(g_cfg.tx_duration_ms)/1000.0;
Harald Welte067d66e2017-12-13 17:24:03 +0100126 var RTP_RecvFrom rx_rtp;
Harald Welte3f6f48f2017-12-24 21:48:33 +0100127 var RtpemConfig cfg;
Harald Welte067d66e2017-12-13 17:24:03 +0100128 var template RTP_RecvFrom tr := {
129 connId := ?,
130 remName := ?,
131 remPort := ?,
132 locName := ?,
133 locPort := ?,
134 msg := ?
135 };
136 var template RTP_RecvFrom tr_rtp := tr;
137 var template RTP_RecvFrom tr_rtcp := tr;
138 tr_rtp.connId := g_rtp_conn_id;
139 tr_rtp.msg := { rtp := ? };
140 tr_rtp.connId := g_rtcp_conn_id;
141 tr_rtcp.msg := { rtcp := ? };
142
Harald Welte80981642017-12-24 23:59:47 +0100143 g_iuup_ent := valueof(t_IuUP_Entity(g_cfg.iuup_tx_init));
144
Harald Welte067d66e2017-12-13 17:24:03 +0100145 while (true) {
146 alt {
147 /* control procedures (calls) from the user */
148 [] CTRL.getcall(RTPEM_bind:{?,?}) -> param(g_local_host, g_local_port) {
149 if (g_local_port rem 2 == 1) {
150 //CTRL.raise(RTPEM_bind, "Local Port is not an even port number!");
151 log("Local Port is not an even port number!");
152 continue;
153 }
154 res := RTP_CodecPort_CtrlFunct.f_IPL4_listen(RTP, g_local_host,
155 g_local_port, {udp:={}});
156 g_rtp_conn_id := res.connId;
157 res := RTP_CodecPort_CtrlFunct.f_IPL4_listen(RTP, g_local_host,
158 g_local_port+1, {udp:={}});
159 g_rtcp_conn_id := res.connId;
160 CTRL.reply(RTPEM_bind:{g_local_host, g_local_port});
161 }
162 [] CTRL.getcall(RTPEM_connect:{?,?}) -> param (g_remote_host, g_remote_port) {
163 if (g_remote_port rem 2 == 1) {
164 //CTRL.raise(RTPEM_connect, "Remote Port is not an even number!");
165 log("Remote Port is not an even number!");
166 continue;
167 }
168 res := RTP_CodecPort_CtrlFunct.f_IPL4_connect(RTP, g_remote_host,
169 g_remote_port,
170 g_local_host, g_local_port,
171 g_rtp_conn_id, {udp:={}});
172 res := RTP_CodecPort_CtrlFunct.f_IPL4_connect(RTCP, g_remote_host,
173 g_remote_port+1,
174 g_local_host, g_local_port+1,
175 g_rtcp_conn_id, {udp:={}});
176 CTRL.reply(RTPEM_connect:{g_remote_host, g_remote_port});
177 }
178 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_NONE}) {
179 T_transmit.stop;
180 g_rx_enabled := false;
Harald Welte80981642017-12-24 23:59:47 +0100181 CTRL.reply(RTPEM_mode:{RTPEM_MODE_NONE});
Harald Welte067d66e2017-12-13 17:24:03 +0100182 }
183 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_TXONLY}) {
184 /* start transmit timer */
185 T_transmit.start;
186 g_rx_enabled := false;
Harald Welte80981642017-12-24 23:59:47 +0100187 CTRL.reply(RTPEM_mode:{RTPEM_MODE_TXONLY});
Harald Welte067d66e2017-12-13 17:24:03 +0100188 }
189 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_RXONLY}) {
190
191 T_transmit.stop;
192 if (g_rx_enabled == false) {
193 /* flush queues */
194 RTP.clear;
195 RTCP.clear;
196 g_rx_enabled := true;
197 }
Harald Welte80981642017-12-24 23:59:47 +0100198 CTRL.reply(RTPEM_mode:{RTPEM_MODE_RXONLY});
Harald Welte067d66e2017-12-13 17:24:03 +0100199 }
200 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_BIDIR}) {
201 T_transmit.start;
202 if (g_rx_enabled == false) {
203 /* flush queues */
204 RTP.clear;
205 RTCP.clear;
206 g_rx_enabled := true;
207 }
Harald Welte80981642017-12-24 23:59:47 +0100208 CTRL.reply(RTPEM_mode:{RTPEM_MODE_BIDIR});
Harald Welte067d66e2017-12-13 17:24:03 +0100209 }
Harald Welte3f6f48f2017-12-24 21:48:33 +0100210 [] CTRL.getcall(RTPEM_configure:{?}) -> param (cfg) {
211 g_cfg := cfg;
Harald Welte80981642017-12-24 23:59:47 +0100212 g_iuup_ent.cfg.active_init := g_cfg.iuup_tx_init;
213 CTRL.reply(RTPEM_configure:{cfg});
Harald Welte3f6f48f2017-12-24 21:48:33 +0100214 }
Harald Welte067d66e2017-12-13 17:24:03 +0100215
216 /* simply ignore any RTTP/RTCP if receiver not enabled */
217 [g_rx_enabled==false] RTP.receive(tr_rtp) { }
218 [g_rx_enabled==false] RTCP.receive(tr_rtp) { }
219
220 /* process received RTCP/RTP if receiver enabled */
221 [g_rx_enabled] RTP.receive(tr_rtp) -> value rx_rtp {
222 log("RX RTP: ", rx_rtp);
Harald Welte80981642017-12-24 23:59:47 +0100223 if (g_cfg.iuup_mode) {
224 rx_rtp.msg.rtp.data := f_IuUP_Em_rx_decaps(g_iuup_ent, rx_rtp.msg.rtp.data);
225 }
Harald Welte067d66e2017-12-13 17:24:03 +0100226 }
227 [g_rx_enabled] RTCP.receive(tr_rtcp) -> value rx_rtp {
228 log("RX RTCP: ", rx_rtp);
229 }
230
231 /* transmit if timer has expired */
232 [] T_transmit.timeout {
233 /* send one RTP frame, re-start timer */
Harald Welte3f6f48f2017-12-24 21:48:33 +0100234 f_tx_rtp(g_cfg.tx_fixed_payload);
Harald Welte067d66e2017-12-13 17:24:03 +0100235 T_transmit.start;
236 }
237
238 /* fail on any unexpected messages */
239 [] RTP.receive {
240 setverdict(fail, "Received unexpected type from RTP");
241 }
242 [] RTCP.receive {
243 setverdict(fail, "Received unexpected type from RTCP");
244 }
245 }
246 }
247}
248
249
250}