aboutsummaryrefslogtreecommitdiffstats
path: root/library/RTP_Emulation.ttcn
diff options
context:
space:
mode:
Diffstat (limited to 'library/RTP_Emulation.ttcn')
-rw-r--r--library/RTP_Emulation.ttcn204
1 files changed, 167 insertions, 37 deletions
diff --git a/library/RTP_Emulation.ttcn b/library/RTP_Emulation.ttcn
index 900a196a..90952148 100644
--- a/library/RTP_Emulation.ttcn
+++ b/library/RTP_Emulation.ttcn
@@ -81,6 +81,8 @@ type component RTP_Emulation_CT {
/* user-facing port for controlling the binding */
port RTPEM_CTRL_PT CTRL;
+ /* user-facing port for sniffing RTP frames */
+ port RTPEM_DATA_PT DATA;
/* configurable by user, should be fixed */
var RtpemConfig g_cfg := c_RtpemDefaultCfg;
@@ -95,6 +97,7 @@ type component RTP_Emulation_CT {
var PortNumber g_local_port;
/* state variables, change over time */
+ var boolean g_loopback := false;
var boolean g_rx_enabled := false;
var boolean g_tx_connected := false; /* Set to true after connect() */
var LIN2_BO_LAST g_tx_next_seq := 0;
@@ -114,7 +117,8 @@ type enumerated RtpemMode {
RTPEM_MODE_NONE,
RTPEM_MODE_TXONLY,
RTPEM_MODE_RXONLY,
- RTPEM_MODE_BIDIR
+ RTPEM_MODE_BIDIR,
+ RTPEM_MODE_LOOPBACK
};
type record RtpemStats {
@@ -151,26 +155,29 @@ const RtpemStats c_RtpemStatsReset := {
num_pkts_rx_err_payload := 0
}
+type record RtpemConfigPayload {
+ INT7b payload_type,
+ octetstring fixed_payload optional
+};
+
type record RtpemConfig {
- INT7b tx_payload_type,
integer tx_samplerate_hz,
integer tx_duration_ms,
BIT32_BO_LAST tx_ssrc,
- octetstring tx_fixed_payload optional,
- octetstring rx_fixed_payload optional,
+ record of RtpemConfigPayload tx_payloads,
+ record of RtpemConfigPayload rx_payloads,
boolean iuup_mode,
- boolean iuup_tx_init
+ IuUP_Config iuup_cfg
};
const RtpemConfig c_RtpemDefaultCfg := {
- tx_payload_type := 0,
tx_samplerate_hz := 8000,
tx_duration_ms := 20,
tx_ssrc := '11011110101011011011111011101111'B,
- tx_fixed_payload := '01020304'O,
- rx_fixed_payload := '01020304'O,
+ tx_payloads := {{0, '01020304'O}},
+ rx_payloads := {{0, '01020304'O}},
iuup_mode := false,
- iuup_tx_init := true
+ iuup_cfg := c_IuUP_Config_def
}
signature RTPEM_bind(in HostName local_host, inout PortNumber local_port);
@@ -186,6 +193,10 @@ type port RTPEM_CTRL_PT procedure {
RTPEM_conn_refuse_received;
} with { extension "internal" };
+type port RTPEM_DATA_PT message {
+ inout PDU_RTP, PDU_RTCP;
+} with { extension "internal" };
+
function f_rtpem_bind(RTPEM_CTRL_PT pt, in HostName local_host, inout PortNumber local_port) {
pt.call(RTPEM_bind:{local_host, local_port}) {
[] pt.getreply(RTPEM_bind:{local_host, ?}) -> param (local_port) {};
@@ -269,38 +280,49 @@ function f_rtpem_stats_compare(RtpemStats a, RtpemStats b, integer tolerance :=
* check that will fit most situations and is intended to be executed by
* the testcases as as needed. */
function f_rtpem_stats_err_check(RtpemStats s) {
+ var boolean do_stop := false;
log("stats: ", s);
/* Check if there was some activity at either on the RX or on the
* TX side, but complete silence would indicate some problem */
if (s.num_pkts_tx < 1 and s.num_pkts_rx < 1) {
setverdict(fail, "no RTP packet activity detected (packets)");
- mtc.stop;
+ do_stop := true;
}
if (s.bytes_payload_tx < 1 and s.bytes_payload_rx < 1) {
setverdict(fail, "no RTP packet activity detected (bytes)");
- mtc.stop;
+ do_stop := true;
}
/* Check error counters */
if (s.num_pkts_rx_err_seq != 0) {
- setverdict(fail, "RTP packet sequence number errors occurred");
- mtc.stop;
+ setverdict(fail, log2str(s.num_pkts_rx_err_seq, " RTP packet sequence number errors occurred"));
+ do_stop := true;
}
if (s.num_pkts_rx_err_ts != 0) {
- setverdict(fail, "RTP packet timestamp errors occurred");
- mtc.stop;
+ setverdict(fail, log2str(s.num_pkts_rx_err_ts, " RTP packet timestamp errors occurred"));
+ do_stop := true;
}
if (s.num_pkts_rx_err_pt != 0) {
- setverdict(fail, "RTP packet payload type errors occurred");
- mtc.stop;
+ setverdict(fail, log2str(s.num_pkts_rx_err_pt, " RTP packet payload type errors occurred"));
+ do_stop := true;
}
if (s.num_pkts_rx_err_disabled != 0) {
- setverdict(fail, "RTP packets received while RX was disabled");
- mtc.stop;
+ setverdict(fail, log2str(s.num_pkts_rx_err_disabled, " RTP packets received while RX was disabled"));
+ do_stop := true;
}
if (s.num_pkts_rx_err_payload != 0) {
- setverdict(fail, "RTP packets with mismatching payload received");
+ setverdict(fail, log2str(s.num_pkts_rx_err_payload, " RTP packets with mismatching payload received"));
+ do_stop := true;
+ }
+
+ if (do_stop) {
+ if (self == mtc) {
+ /* Properly stop all ports before disconnecting them. This avoids
+ * running into the dynamic testcase error due to messages arriving on
+ * unconnected ports. */
+ all component.stop;
+ }
mtc.stop;
}
}
@@ -336,11 +358,15 @@ template PDU_RTP ts_RTP(BIT32_BO_LAST ssrc, INT7b pt, LIN2_BO_LAST seq, uint32_t
data := payload
}
-private function f_tx_rtp(octetstring payload, BIT1 marker := '0'B) runs on RTP_Emulation_CT {
+private function f_tx_rtp(octetstring payload, INT7b rtp_payload_type, BIT1 marker := '0'B) runs on RTP_Emulation_CT {
if (g_cfg.iuup_mode) {
payload := f_IuUP_Em_tx_encap(g_iuup_ent, payload);
+ if (lengthof(payload) == 0) {
+ /* Nothing to transmit, waiting for INIT-ACK */
+ return;
+ }
}
- var PDU_RTP rtp := valueof(ts_RTP(g_cfg.tx_ssrc, g_cfg.tx_payload_type, g_tx_next_seq,
+ var PDU_RTP rtp := valueof(ts_RTP(g_cfg.tx_ssrc, rtp_payload_type, g_tx_next_seq,
g_tx_next_ts, payload, marker));
RTP.send(t_RTP_Send(g_rtp_conn_id, RTP_messages_union:{rtp:=rtp}));
/* increment sequence + timestamp for next transmit */
@@ -348,6 +374,58 @@ private function f_tx_rtp(octetstring payload, BIT1 marker := '0'B) runs on RTP_
g_tx_next_ts := g_tx_next_ts + (g_cfg.tx_samplerate_hz / (1000 / g_cfg.tx_duration_ms));
}
+private function f_check_fixed_rx_payloads(INT7b rtp_payload_type, octetstring rtp_data) runs on RTP_Emulation_CT {
+ var boolean payload_type_match := false;
+
+ /* The API user has the option to define zero or multiple sets of rx_payloads. Each rx_payload set contains
+ the payload type number of the expected payload and an optional fixed_payload, which resembles the actual
+ payload.
+
+ In case zero rx_payloads are defined nothing is verified and no errors are counted. This is a corner case
+ and should be avoided since it would not yield any good test coverage.
+
+ During verification the payload type has the highest priority. It must match before the optional fixed
+ payload is checked. Since the fixed_payload is optional multiple error situations may apply:
+
+ | payload_type | fixed_payload | result
+ | match | match | full match => no error counter is incremented
+ | match | not present | counts as full match => no error counter is incremented
+ | match | mismatch | payload type match => only num_pkts_rx_err_payload is incremented
+ | | | unless something of the above is detected later.
+ | mismatch | (not checked) | no match => num_pkts_rx_err_payload and num_pkts_rx_err_pt
+ | | | are increment unless something of the above is
+ | | | detected later.
+ */
+
+ /* In case no rx payloads are defined any payload is accepted and no errors are counted. */
+ if (lengthof(g_cfg.rx_payloads) == 0) {
+ return;
+ }
+
+ /* Evaluate rtp_data and rtp_payload_type */
+ for (var integer i := 0; i < lengthof(g_cfg.rx_payloads); i := i + 1) {
+ if (rtp_payload_type == g_cfg.rx_payloads[i].payload_type) {
+ if (not ispresent(g_cfg.rx_payloads[i].fixed_payload)) {
+ /* full match */
+ return;
+ }
+ if (g_cfg.rx_payloads[i].fixed_payload == rtp_data) {
+ /* counts as full match */
+ return;
+ }
+
+ /* At least the payload type number did match
+ * (but we still may see a full match later) */
+ payload_type_match := true;
+ }
+ }
+
+ g_stats_rtp.num_pkts_rx_err_payload := g_stats_rtp.num_pkts_rx_err_payload + 1;
+ if (not payload_type_match) {
+ g_stats_rtp.num_pkts_rx_err_pt := g_stats_rtp.num_pkts_rx_err_pt + 1;
+ }
+}
+
function f_main() runs on RTP_Emulation_CT
{
var Result res;
@@ -374,7 +452,7 @@ function f_main() runs on RTP_Emulation_CT
os_error_code := 111,
os_error_text := ? /* "Connection refused" */}};
- g_iuup_ent := valueof(t_IuUP_Entity(g_cfg.iuup_tx_init));
+ g_iuup_ent := valueof(t_IuUP_Entity(g_cfg.iuup_cfg));
while (true) {
alt {
@@ -438,17 +516,23 @@ function f_main() runs on RTP_Emulation_CT
mtc.stop;
}
g_tx_connected := true;
+ /* Send any pending IuUP CTRL message whichwas delayed due to not being connected: */
+ if (isvalue(g_iuup_ent.pending_tx_pdu)) {
+ f_tx_rtp(''O, g_cfg.tx_payloads[0].payload_type);
+ }
CTRL.reply(RTPEM_connect:{g_remote_host, g_remote_port});
}
[] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_NONE}) {
T_transmit.stop;
g_rx_enabled := false;
+ g_loopback := false;
CTRL.reply(RTPEM_mode:{RTPEM_MODE_NONE});
}
[] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_TXONLY}) {
/* start transmit timer */
T_transmit.start;
g_rx_enabled := false;
+ g_loopback := false;
CTRL.reply(RTPEM_mode:{RTPEM_MODE_TXONLY});
}
[] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_RXONLY}) {
@@ -460,6 +544,7 @@ function f_main() runs on RTP_Emulation_CT
RTCP.clear;
g_rx_enabled := true;
}
+ g_loopback := false;
CTRL.reply(RTPEM_mode:{RTPEM_MODE_RXONLY});
}
[] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_BIDIR}) {
@@ -470,11 +555,23 @@ function f_main() runs on RTP_Emulation_CT
RTCP.clear;
g_rx_enabled := true;
}
+ g_loopback := false;
CTRL.reply(RTPEM_mode:{RTPEM_MODE_BIDIR});
}
+ [] CTRL.getcall(RTPEM_mode:{RTPEM_MODE_LOOPBACK}) {
+ T_transmit.stop;
+ if (g_rx_enabled == false) {
+ /* flush queues */
+ RTP.clear;
+ RTCP.clear;
+ g_rx_enabled := true;
+ }
+ g_loopback := true;
+ CTRL.reply(RTPEM_mode:{RTPEM_MODE_LOOPBACK});
+ }
[] CTRL.getcall(RTPEM_configure:{?}) -> param (cfg) {
g_cfg := cfg;
- g_iuup_ent.cfg.active_init := g_cfg.iuup_tx_init;
+ g_iuup_ent.cfg := g_cfg.iuup_cfg;
CTRL.reply(RTPEM_configure:{cfg});
}
[] CTRL.getcall(RTPEM_stats_get:{?, ?}) -> param (is_rtcp) {
@@ -492,44 +589,77 @@ function f_main() runs on RTP_Emulation_CT
}
- /* simply ignore any RTTP/RTP if receiver not enabled */
- [g_rx_enabled==false] RTP.receive(tr_rtp) {
- g_stats_rtp.num_pkts_rx_err_disabled := g_stats_rtp.num_pkts_rx_err_disabled+1;
+ /* simply ignore any RTCP/RTP if receiver not enabled */
+ [not g_rx_enabled] RTP.receive(tr_rtp) -> value rx_rtp {
+ /* In IuUP we need to decode possible Control packets, such as INIT-ACK: */
+ if (g_cfg.iuup_mode) {
+ /* In IuUP we need to decode possible Control packets, such as INIT-ACK: */
+ rx_rtp.msg.rtp.data := f_IuUP_Em_rx_decaps(g_iuup_ent, rx_rtp.msg.rtp.data);
+ if (lengthof(rx_rtp.msg.rtp.data) != 0) {
+ /* Unexpected RTP payload (user data) arrived: */
+ g_stats_rtp.num_pkts_rx_err_disabled := g_stats_rtp.num_pkts_rx_err_disabled+1;
+ } else if (g_tx_connected and isvalue(g_iuup_ent.pending_tx_pdu)) {
+ /* IuUP Control packet was received and requires sending back something: */
+ f_tx_rtp(''O, g_cfg.tx_payloads[0].payload_type);
+ }
+ } else {
+ g_stats_rtp.num_pkts_rx_err_disabled := g_stats_rtp.num_pkts_rx_err_disabled+1;
}
- [g_rx_enabled==false] RTCP.receive(tr_rtcp) {
+ }
+ [not g_rx_enabled] RTCP.receive(tr_rtcp) {
g_stats_rtcp.num_pkts_rx_err_disabled := g_stats_rtcp.num_pkts_rx_err_disabled+1;
}
/* process received RTCP/RTP if receiver enabled */
[g_rx_enabled] RTP.receive(tr_rtp) -> value rx_rtp {
/* increment counters */
- if (rx_rtp.msg.rtp.payload_type != g_cfg.tx_payload_type) {
- g_stats_rtp.num_pkts_rx_err_pt := g_stats_rtp.num_pkts_rx_err_pt+1;
- }
g_stats_rtp.num_pkts_rx := g_stats_rtp.num_pkts_rx+1;
g_stats_rtp.bytes_payload_rx := g_stats_rtp.bytes_payload_rx +
lengthof(rx_rtp.msg.rtp.data);
- if (ispresent(g_cfg.rx_fixed_payload) and rx_rtp.msg.rtp.data != g_cfg.rx_fixed_payload) {
- g_stats_rtp.num_pkts_rx_err_payload := g_stats_rtp.num_pkts_rx_err_payload + 1;
- }
if (g_cfg.iuup_mode) {
rx_rtp.msg.rtp.data := f_IuUP_Em_rx_decaps(g_iuup_ent, rx_rtp.msg.rtp.data);
+ /* IuUP Control packet was received which may require sending back something: */
+ if (lengthof(rx_rtp.msg.rtp.data) == 0) {
+ if (g_tx_connected and isvalue(g_iuup_ent.pending_tx_pdu)) {
+ f_tx_rtp(''O, g_cfg.tx_payloads[0].payload_type);
+ }
+ repeat;
+ }
+ }
+
+ if (g_loopback) {
+ f_tx_rtp(rx_rtp.msg.rtp.data, rx_rtp.msg.rtp.payload_type);
+ /* update counters */
+ g_stats_rtp.num_pkts_tx := g_stats_rtp.num_pkts_tx + 1;
+ g_stats_rtp.bytes_payload_tx := g_stats_rtp.bytes_payload_tx
+ + lengthof(rx_rtp.msg.rtp.data);
+ } else {
+ /* Match the received payload against any of the predefined fixed rx payloads */
+ f_check_fixed_rx_payloads(rx_rtp.msg.rtp.payload_type, rx_rtp.msg.rtp.data);
+ }
+
+ if (DATA.checkstate("Connected")) {
+ DATA.send(rx_rtp.msg.rtp);
}
}
[g_rx_enabled] RTCP.receive(tr_rtcp) -> value rx_rtp {
//log("RX RTCP: ", rx_rtp);
g_stats_rtcp.num_pkts_rx := g_stats_rtcp.num_pkts_rx+1;
+ if (DATA.checkstate("Connected")) {
+ DATA.send(rx_rtp.msg.rtcp);
+ }
}
/* transmit if timer has expired */
[g_tx_connected] T_transmit.timeout {
+ var octetstring payload := g_cfg.tx_payloads[g_tx_next_seq mod lengthof(g_cfg.tx_payloads)].fixed_payload;
+ var INT7b rtp_payload_type := g_cfg.tx_payloads[g_tx_next_seq mod lengthof(g_cfg.tx_payloads)].payload_type;
/* send one RTP frame, re-start timer */
- f_tx_rtp(g_cfg.tx_fixed_payload);
+ f_tx_rtp(payload, rtp_payload_type);
T_transmit.start;
/* update counters */
g_stats_rtp.num_pkts_tx := g_stats_rtp.num_pkts_tx+1;
- g_stats_rtp.bytes_payload_tx := g_stats_rtp.bytes_payload_tx +
- lengthof(g_cfg.tx_fixed_payload);
+ g_stats_rtp.bytes_payload_tx := g_stats_rtp.bytes_payload_tx + lengthof(payload);
}
/* connection refused */