blob: e4a30294abd709c1f371a6eeb89e42cd86adb253 [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 */
35 var INT7b g_tx_payload_type := 0;
36 var integer g_tx_samplerate_hz := 8000;
37 var integer g_tx_duration_ms := 20;
38 var BIT32_BO_LAST g_tx_ssrc := hex2bit('DEADBEEF'H);
39
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;
53}
54
55type enumerated RtpemMode {
56 RTPEM_MODE_NONE,
57 RTPEM_MODE_TXONLY,
58 RTPEM_MODE_RXONLY,
59 RTPEM_MODE_BIDIR
60};
61
62signature RTPEM_bind(in HostName local_host, inout PortNumber local_port);
63signature RTPEM_connect(in HostName remote_host, in PortNumber remote_port);
64signature RTPEM_mode(in RtpemMode mode);
65
66type port RTPEM_CTRL_PT procedure {
67 inout RTPEM_bind, RTPEM_connect, RTPEM_mode;
68} with { extension "internal" };
69
70template PDU_RTP ts_RTP(BIT32_BO_LAST ssrc, INT7b pt, LIN2_BO_LAST seq, uint32_t ts,
71 octetstring payload, BIT1 marker := '0'B) := {
72 version := 2,
73 padding_ind := '0'B,
74 extension_ind := '0'B,
75 CSRC_count := 0,
76 marker_bit := marker,
77 payload_type := pt,
78 sequence_number := seq,
Harald Welte8a4d3952017-12-24 21:30:23 +010079 time_stamp := int2bit(ts, 32),
Harald Welte067d66e2017-12-13 17:24:03 +010080 SSRC_id := ssrc,
81 CSRCs := omit,
82 ext_header := omit,
83 data := payload
84}
85
86private function f_tx_rtp(octetstring payload, BIT1 marker := '0'B) runs on RTP_Emulation_CT {
87 var PDU_RTP rtp := valueof(ts_RTP(g_tx_ssrc, g_tx_payload_type, g_tx_next_seq,
88 g_tx_next_ts, payload, marker));
89 RTP.send(t_RTP_Send(g_rtp_conn_id, RTP_messages_union:{rtp:=rtp}));
90 /* increment sequence + timestamp for next transmit */
91 g_tx_next_seq := g_tx_next_seq + 1;
92 g_tx_next_ts := g_tx_next_ts + (g_tx_samplerate_hz mod (1000 mod g_tx_duration_ms));
93}
94
95function f_main() runs on RTP_Emulation_CT
96{
97 var Result res;
98
99 timer T_transmit := 1000.0/int2float(g_tx_duration_ms);
100 var RTP_RecvFrom rx_rtp;
101 var template RTP_RecvFrom tr := {
102 connId := ?,
103 remName := ?,
104 remPort := ?,
105 locName := ?,
106 locPort := ?,
107 msg := ?
108 };
109 var template RTP_RecvFrom tr_rtp := tr;
110 var template RTP_RecvFrom tr_rtcp := tr;
111 tr_rtp.connId := g_rtp_conn_id;
112 tr_rtp.msg := { rtp := ? };
113 tr_rtp.connId := g_rtcp_conn_id;
114 tr_rtcp.msg := { rtcp := ? };
115
116 while (true) {
117 alt {
118 /* control procedures (calls) from the user */
119 [] CTRL.getcall(RTPEM_bind:{?,?}) -> param(g_local_host, g_local_port) {
120 if (g_local_port rem 2 == 1) {
121 //CTRL.raise(RTPEM_bind, "Local Port is not an even port number!");
122 log("Local Port is not an even port number!");
123 continue;
124 }
125 res := RTP_CodecPort_CtrlFunct.f_IPL4_listen(RTP, g_local_host,
126 g_local_port, {udp:={}});
127 g_rtp_conn_id := res.connId;
128 res := RTP_CodecPort_CtrlFunct.f_IPL4_listen(RTP, g_local_host,
129 g_local_port+1, {udp:={}});
130 g_rtcp_conn_id := res.connId;
131 CTRL.reply(RTPEM_bind:{g_local_host, g_local_port});
132 }
133 [] CTRL.getcall(RTPEM_connect:{?,?}) -> param (g_remote_host, g_remote_port) {
134 if (g_remote_port rem 2 == 1) {
135 //CTRL.raise(RTPEM_connect, "Remote Port is not an even number!");
136 log("Remote Port is not an even number!");
137 continue;
138 }
139 res := RTP_CodecPort_CtrlFunct.f_IPL4_connect(RTP, g_remote_host,
140 g_remote_port,
141 g_local_host, g_local_port,
142 g_rtp_conn_id, {udp:={}});
143 res := RTP_CodecPort_CtrlFunct.f_IPL4_connect(RTCP, g_remote_host,
144 g_remote_port+1,
145 g_local_host, g_local_port+1,
146 g_rtcp_conn_id, {udp:={}});
147 CTRL.reply(RTPEM_connect:{g_remote_host, g_remote_port});
148 }
149 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_NONE}) {
150 T_transmit.stop;
151 g_rx_enabled := false;
152 }
153 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_TXONLY}) {
154 /* start transmit timer */
155 T_transmit.start;
156 g_rx_enabled := false;
157 }
158 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_RXONLY}) {
159
160 T_transmit.stop;
161 if (g_rx_enabled == false) {
162 /* flush queues */
163 RTP.clear;
164 RTCP.clear;
165 g_rx_enabled := true;
166 }
167 }
168 [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_BIDIR}) {
169 T_transmit.start;
170 if (g_rx_enabled == false) {
171 /* flush queues */
172 RTP.clear;
173 RTCP.clear;
174 g_rx_enabled := true;
175 }
176 }
177
178 /* simply ignore any RTTP/RTCP if receiver not enabled */
179 [g_rx_enabled==false] RTP.receive(tr_rtp) { }
180 [g_rx_enabled==false] RTCP.receive(tr_rtp) { }
181
182 /* process received RTCP/RTP if receiver enabled */
183 [g_rx_enabled] RTP.receive(tr_rtp) -> value rx_rtp {
184 log("RX RTP: ", rx_rtp);
185 }
186 [g_rx_enabled] RTCP.receive(tr_rtcp) -> value rx_rtp {
187 log("RX RTCP: ", rx_rtp);
188 }
189
190 /* transmit if timer has expired */
191 [] T_transmit.timeout {
192 /* send one RTP frame, re-start timer */
193 f_tx_rtp('01020304'O);
194 T_transmit.start;
195 }
196
197 /* fail on any unexpected messages */
198 [] RTP.receive {
199 setverdict(fail, "Received unexpected type from RTP");
200 }
201 [] RTCP.receive {
202 setverdict(fail, "Received unexpected type from RTCP");
203 }
204 }
205 }
206}
207
208
209}