blob: e55890e9b7b16c831104c70a66ff292f09b7f29f [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
24type component RTP_Emulation_CT {
25 /* down-facing ports for RTP and RTCP codec ports on top of IPL4asp */
26 port RTP_CODEC_PT RTP;
27 var integer g_rtp_conn_id := -1;
28 port RTP_CODEC_PT RTCP;
29 var integer g_rtcp_conn_id := -1;
30
31 /* user-facing port for controlling the binding */
32 port RTPEM_CTRL_PT CTRL;
33
34 /* configurable by user, should be fixed */
Harald Welte3f6f48f2017-12-24 21:48:33 +010035 var RtpemConfig g_cfg := c_default_cfg;
Harald Welte067d66e2017-12-13 17:24:03 +010036
37 var HostName g_remote_host;
38 var PortNumber g_remote_port;
39 var HostName g_local_host;
40 var PortNumber g_local_port;
41
42 /* state variables, change over time */
43 var boolean g_rx_enabled := false;
44 var LIN2_BO_LAST g_tx_next_seq := 0;
45 var uint32_t g_tx_next_ts := 0;
46
47 var INT7b g_rx_payload_type := 0;
48 var LIN2_BO_LAST g_rx_last_seq;
49 var uint32_t g_rx_last_ts;
50}
51
52type enumerated RtpemMode {
53 RTPEM_MODE_NONE,
54 RTPEM_MODE_TXONLY,
55 RTPEM_MODE_RXONLY,
56 RTPEM_MODE_BIDIR
57};
58
Harald Welte3f6f48f2017-12-24 21:48:33 +010059type record RtpemConfig {
60 INT7b tx_payload_type,
61 integer tx_samplerate_hz,
62 integer tx_duration_ms,
63 BIT32_BO_LAST tx_ssrc,
64 octetstring tx_fixed_payload optional
65};
66
67const RtpemConfig c_default_cfg := {
68 tx_payload_type := 0,
69 tx_samplerate_hz := 8000,
70 tx_duration_ms := 20,
71 tx_ssrc := '11011110101011011011111011101111'B,
72 tx_fixed_payload := '01020304'O
73}
74
Harald Welte067d66e2017-12-13 17:24:03 +010075signature RTPEM_bind(in HostName local_host, inout PortNumber local_port);
76signature RTPEM_connect(in HostName remote_host, in PortNumber remote_port);
77signature RTPEM_mode(in RtpemMode mode);
Harald Welte3f6f48f2017-12-24 21:48:33 +010078signature RTPEM_configure(in RtpemConfig cfg);
Harald Welte067d66e2017-12-13 17:24:03 +010079
80type port RTPEM_CTRL_PT procedure {
Harald Welte3f6f48f2017-12-24 21:48:33 +010081 inout RTPEM_bind, RTPEM_connect, RTPEM_mode, RTPEM_configure;
Harald Welte067d66e2017-12-13 17:24:03 +010082} with { extension "internal" };
83
84template PDU_RTP ts_RTP(BIT32_BO_LAST ssrc, INT7b pt, LIN2_BO_LAST seq, uint32_t ts,
85 octetstring payload, BIT1 marker := '0'B) := {
86 version := 2,
87 padding_ind := '0'B,
88 extension_ind := '0'B,
89 CSRC_count := 0,
90 marker_bit := marker,
91 payload_type := pt,
92 sequence_number := seq,
Harald Welte8a4d3952017-12-24 21:30:23 +010093 time_stamp := int2bit(ts, 32),
Harald Welte067d66e2017-12-13 17:24:03 +010094 SSRC_id := ssrc,
95 CSRCs := omit,
96 ext_header := omit,
97 data := payload
98}
99
100private function f_tx_rtp(octetstring payload, BIT1 marker := '0'B) runs on RTP_Emulation_CT {
Harald Welte3f6f48f2017-12-24 21:48:33 +0100101 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 +0100102 g_tx_next_ts, payload, marker));
103 RTP.send(t_RTP_Send(g_rtp_conn_id, RTP_messages_union:{rtp:=rtp}));
104 /* increment sequence + timestamp for next transmit */
105 g_tx_next_seq := g_tx_next_seq + 1;
Harald Welte3f6f48f2017-12-24 21:48:33 +0100106 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 +0100107}
108
109function f_main() runs on RTP_Emulation_CT
110{
111 var Result res;
112
Harald Welte3f6f48f2017-12-24 21:48:33 +0100113 timer T_transmit := int2float(g_cfg.tx_duration_ms)/1000.0;
Harald Welte067d66e2017-12-13 17:24:03 +0100114 var RTP_RecvFrom rx_rtp;
Harald Welte3f6f48f2017-12-24 21:48:33 +0100115 var RtpemConfig cfg;
Harald Welte067d66e2017-12-13 17:24:03 +0100116 var template RTP_RecvFrom tr := {
117 connId := ?,
118 remName := ?,
119 remPort := ?,
120 locName := ?,
121 locPort := ?,
122 msg := ?
123 };
124 var template RTP_RecvFrom tr_rtp := tr;
125 var template RTP_RecvFrom tr_rtcp := tr;
126 tr_rtp.connId := g_rtp_conn_id;
127 tr_rtp.msg := { rtp := ? };
128 tr_rtp.connId := g_rtcp_conn_id;
129 tr_rtcp.msg := { rtcp := ? };
130
131 while (true) {
132 alt {
133 /* control procedures (calls) from the user */
134 [] CTRL.getcall(RTPEM_bind:{?,?}) -> param(g_local_host, g_local_port) {
135 if (g_local_port rem 2 == 1) {
136 //CTRL.raise(RTPEM_bind, "Local Port is not an even port number!");
137 log("Local Port is not an even port number!");
138 continue;
139 }
140 res := RTP_CodecPort_CtrlFunct.f_IPL4_listen(RTP, g_local_host,
141 g_local_port, {udp:={}});
142 g_rtp_conn_id := res.connId;
143 res := RTP_CodecPort_CtrlFunct.f_IPL4_listen(RTP, g_local_host,
144 g_local_port+1, {udp:={}});
145 g_rtcp_conn_id := res.connId;
146 CTRL.reply(RTPEM_bind:{g_local_host, g_local_port});
147 }
148 [] CTRL.getcall(RTPEM_connect:{?,?}) -> param (g_remote_host, g_remote_port) {
149 if (g_remote_port rem 2 == 1) {
150 //CTRL.raise(RTPEM_connect, "Remote Port is not an even number!");
151 log("Remote Port is not an even number!");
152 continue;
153 }
154 res := RTP_CodecPort_CtrlFunct.f_IPL4_connect(RTP, g_remote_host,
155 g_remote_port,
156 g_local_host, g_local_port,
157 g_rtp_conn_id, {udp:={}});
158 res := RTP_CodecPort_CtrlFunct.f_IPL4_connect(RTCP, g_remote_host,
159 g_remote_port+1,
160 g_local_host, g_local_port+1,
161 g_rtcp_conn_id, {udp:={}});
162 CTRL.reply(RTPEM_connect:{g_remote_host, g_remote_port});
163 }
164 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_NONE}) {
165 T_transmit.stop;
166 g_rx_enabled := false;
167 }
168 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_TXONLY}) {
169 /* start transmit timer */
170 T_transmit.start;
171 g_rx_enabled := false;
172 }
173 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_RXONLY}) {
174
175 T_transmit.stop;
176 if (g_rx_enabled == false) {
177 /* flush queues */
178 RTP.clear;
179 RTCP.clear;
180 g_rx_enabled := true;
181 }
182 }
183 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_BIDIR}) {
184 T_transmit.start;
185 if (g_rx_enabled == false) {
186 /* flush queues */
187 RTP.clear;
188 RTCP.clear;
189 g_rx_enabled := true;
190 }
191 }
Harald Welte3f6f48f2017-12-24 21:48:33 +0100192 [] CTRL.getcall(RTPEM_configure:{?}) -> param (cfg) {
193 g_cfg := cfg;
194 }
Harald Welte067d66e2017-12-13 17:24:03 +0100195
196 /* simply ignore any RTTP/RTCP if receiver not enabled */
197 [g_rx_enabled==false] RTP.receive(tr_rtp) { }
198 [g_rx_enabled==false] RTCP.receive(tr_rtp) { }
199
200 /* process received RTCP/RTP if receiver enabled */
201 [g_rx_enabled] RTP.receive(tr_rtp) -> value rx_rtp {
202 log("RX RTP: ", rx_rtp);
203 }
204 [g_rx_enabled] RTCP.receive(tr_rtcp) -> value rx_rtp {
205 log("RX RTCP: ", rx_rtp);
206 }
207
208 /* transmit if timer has expired */
209 [] T_transmit.timeout {
210 /* send one RTP frame, re-start timer */
Harald Welte3f6f48f2017-12-24 21:48:33 +0100211 f_tx_rtp(g_cfg.tx_fixed_payload);
Harald Welte067d66e2017-12-13 17:24:03 +0100212 T_transmit.start;
213 }
214
215 /* fail on any unexpected messages */
216 [] RTP.receive {
217 setverdict(fail, "Received unexpected type from RTP");
218 }
219 [] RTCP.receive {
220 setverdict(fail, "Received unexpected type from RTCP");
221 }
222 }
223 }
224}
225
226
227}