/* (C) 2022 by sysmocom - s.f.m.c. GmbH * * Author: Pau Espin Pedrol * * SPDX-License-Identifier: GPL-2.0+ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static uint16_t rtp_next_seq; static uint16_t rtp_next_ts; void *tall_ctx; #define TIME_RTP_PKT_MS 20 #define BATCH_FACTOR 6 /* ----------------------------- */ /* Logging related stuff */ #define INT2IDX(x) (-1*(x)-1) struct log_info_cat jibuf_test_cat[] = { [INT2IDX(DLMUX)] = { .name = "DLMUX", .description = "Osmocom Osmux", .enabled = 1, .loglevel = LOGL_DEBUG, }, }; const struct log_info log_info = { .filter_fn = NULL, .cat = jibuf_test_cat, .num_cat = ARRAY_SIZE(jibuf_test_cat), }; /* ----------------------------- */ static void rtp_init(uint16_t seq, uint16_t ts) { rtp_next_seq = seq; rtp_next_ts = ts; } static struct msgb *rtp_new(uint16_t seq, uint8_t timestamp, uint8_t marker) { struct msgb *msg; struct rtp_hdr *rtph; msg = msgb_alloc(1500, "rtp"); if (!msg) exit(EXIT_FAILURE); msgb_put(msg, sizeof(struct rtp_hdr)); rtph = (struct rtp_hdr *)msg->data; rtph->version = RTP_VERSION; rtph->marker = marker; rtph->sequence = htons(seq); rtph->timestamp = htons(timestamp); rtph->ssrc = 0x6789; return msg; } static struct msgb *rtp_next(void) { rtp_next_seq++; rtp_next_ts += TIME_RTP_PKT_MS; return rtp_new(rtp_next_seq, rtp_next_ts, 0); } static struct amr_hdr *rtp_append_amr(struct msgb *msg, uint8_t ft) { struct amr_hdr *amrh; struct rtp_hdr *rtph = (struct rtp_hdr *)msg->data; msgb_put(msg, sizeof(struct amr_hdr)); amrh = (struct amr_hdr *)rtph->data; amrh->cmr = 0; amrh->q = 1; amrh->f = 0; amrh->ft = ft; msgb_put(msg, osmo_amr_bytes(amrh->ft)); return amrh; } static void sigalarm_handler(int foo) { printf("FAIL: test did not run successfully\n"); exit(EXIT_FAILURE); } #define clock_debug(fmt, args...) \ do { \ struct timespec ts; \ struct timeval tv; \ osmo_clock_gettime(CLOCK_MONOTONIC, &ts); \ osmo_gettimeofday(&tv, NULL); \ fprintf(stdout, "sys={%lu.%06lu}, mono={%lu.%06lu}: " fmt "\n", \ tv.tv_sec, tv.tv_usec, ts.tv_sec, ts.tv_nsec/1000, ##args); \ } while (0) static void clock_override_enable(bool enable) { osmo_gettimeofday_override = enable; osmo_clock_override_enable(CLOCK_MONOTONIC, enable); } static void clock_override_set(long sec, long usec) { struct timespec *mono; osmo_gettimeofday_override_time.tv_sec = sec; osmo_gettimeofday_override_time.tv_usec = usec; mono = osmo_clock_override_gettimespec(CLOCK_MONOTONIC); mono->tv_sec = sec; mono->tv_nsec = usec*1000; clock_debug("clock_override_set"); } static void clock_override_add_debug(long sec, long usec, bool dbg) { osmo_gettimeofday_override_add(sec, usec); osmo_clock_override_add(CLOCK_MONOTONIC, sec, usec*1000); if (dbg) clock_debug("clock_override_add"); } #define clock_override_add(sec, usec) clock_override_add_debug(sec, usec, true) static void test_amr_ft_change_middle_batch_osmux_deliver_cb(struct msgb *batch_msg, void *data) { struct osmux_hdr *osmuxh; char buf[2048]; int n = 0; bool *osmux_transmitted = (bool *)data; osmux_snprintf(buf, sizeof(buf), batch_msg); clock_debug("OSMUX message (len=%d): %s\n", batch_msg->len, buf); /* We expect 3 batches: */ while ((osmuxh = osmux_xfrm_output_pull(batch_msg))) { n++; OSMO_ASSERT(osmuxh->ft == OSMUX_FT_VOICE_AMR); OSMO_ASSERT(osmuxh->rtp_m == 0); OSMO_ASSERT(osmuxh->amr_cmr == 0); OSMO_ASSERT(osmuxh->amr_q == 1); switch (n) { case 1: OSMO_ASSERT(osmuxh->seq == 0); OSMO_ASSERT(osmuxh->ctr == 1); OSMO_ASSERT(osmuxh->amr_ft == AMR_FT_2); break; case 2: OSMO_ASSERT(osmuxh->seq == 1); OSMO_ASSERT(osmuxh->ctr == 0); OSMO_ASSERT(osmuxh->amr_ft == AMR_FT_6); break; case 3: OSMO_ASSERT(osmuxh->seq == 2); OSMO_ASSERT(osmuxh->ctr == 0); OSMO_ASSERT(osmuxh->amr_ft == AMR_FT_1); break; } } OSMO_ASSERT(n == 3); msgb_free(batch_msg); *osmux_transmitted = true; } /* Test if an RTP pkt with changed AMR FT passed to osmux_input is properly * processed: The current batch ends and a new batch with a new osmux header is * appeneded to the generated packet. */ static void test_amr_ft_change_middle_batch(void) { struct msgb *msg; int rc; const uint8_t cid = 30; bool osmux_transmitted = false; struct osmux_in_handle *h_input; printf("===%s===\n", __func__); clock_override_enable(true); clock_override_set(0, 0); rtp_init(0, 0); h_input = osmux_xfrm_input_alloc(tall_ctx); osmux_xfrm_input_set_initial_seqnum(h_input, 0); osmux_xfrm_input_set_batch_factor(h_input, 4); osmux_xfrm_input_set_deliver_cb(h_input, test_amr_ft_change_middle_batch_osmux_deliver_cb, &osmux_transmitted); osmux_xfrm_input_open_circuit(h_input, cid, false); /* First RTP frame at t=0 */ msg = rtp_next(); rtp_append_amr(msg, AMR_FT_2); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); /* Second RTP frame at t=20 */ clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_2); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); /* Third RTP frame at t=40, AMR FT changes: */ clock_debug("Submit RTP with 1st AMR FT change"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_6); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); /* Forth RTP frame at t=60, AMR FT changes again: */ clock_debug("Submit RTP with 2nd AMR FT change"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_1); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); /* t=80, osmux batch is scheduled to be transmitted: */ clock_override_add(0, TIME_RTP_PKT_MS*1000); clock_debug("Osmux frame should now be transmitted"); osmo_select_main(0); OSMO_ASSERT(osmux_transmitted == true); clock_debug("Closing circuit"); osmux_xfrm_input_close_circuit(h_input, cid); talloc_free(h_input); } static void test_last_amr_cmr_f_q_used_osmux_deliver_cb(struct msgb *batch_msg, void *data) { struct osmux_hdr *osmuxh; char buf[2048]; bool *osmux_transmitted = (bool *)data; osmux_snprintf(buf, sizeof(buf), batch_msg); clock_debug("OSMUX message (len=%d): %s\n", batch_msg->len, buf); /* We expect 1 batch: */ osmuxh = osmux_xfrm_output_pull(batch_msg); OSMO_ASSERT(osmuxh); OSMO_ASSERT(osmuxh->ft == OSMUX_FT_VOICE_AMR); /* Check CMR and Q values are the ones from the last message: */ OSMO_ASSERT(osmuxh->amr_f == 0); OSMO_ASSERT(osmuxh->amr_q == 0); OSMO_ASSERT(osmuxh->amr_cmr == 2); osmuxh = osmux_xfrm_output_pull(batch_msg); OSMO_ASSERT(osmuxh == NULL); msgb_free(batch_msg); *osmux_transmitted = true; } /* Test that fields CMR, F and Q of the last RTP packet in the batch are the * ones set in the osmux batch header. */ static void test_last_amr_cmr_f_q_used(void) { struct msgb *msg; int rc; const uint8_t cid = 32; bool osmux_transmitted = false; struct amr_hdr *amrh; struct osmux_in_handle *h_input; printf("===%s===\n", __func__); clock_override_enable(true); clock_override_set(0, 0); rtp_init(0, 0); h_input = osmux_xfrm_input_alloc(tall_ctx); osmux_xfrm_input_set_initial_seqnum(h_input, 0); osmux_xfrm_input_set_batch_factor(h_input, 3); osmux_xfrm_input_set_deliver_cb(h_input, test_last_amr_cmr_f_q_used_osmux_deliver_cb, &osmux_transmitted); osmux_xfrm_input_open_circuit(h_input, cid, false); /* First RTP frame at t=0 */ msg = rtp_next(); amrh = rtp_append_amr(msg, AMR_FT_2); amrh->f = 1; amrh->q = 1; amrh->cmr = 0; rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); /* Second RTP frame at t=20, CMR changes 0->1 */ clock_debug("Submit 2nd RTP packet, CMR changes"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); amrh = rtp_append_amr(msg, AMR_FT_2); amrh->f = 1; amrh->q = 1; amrh->cmr = 1; rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); /* Third RTP frame at t=40, q changes 1->0, CMR changes 1->2: */ clock_debug("Submit 3rd RTP packet with Q and CMR changes"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); amrh = rtp_append_amr(msg, AMR_FT_2); amrh->f = 0; amrh->q = 0; amrh->cmr = 2; rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); /* t=60, osmux batch is scheduled to be transmitted: */ clock_override_add(0, TIME_RTP_PKT_MS*1000); clock_debug("Osmux frame should now be transmitted"); osmo_select_main(0); OSMO_ASSERT(osmux_transmitted == true); clock_debug("Closing circuit"); osmux_xfrm_input_close_circuit(h_input, cid); talloc_free(h_input); } static void test_initial_osmux_seqnum_osmux_deliver_cb(struct msgb *batch_msg, void *data) { struct osmux_hdr *osmuxh; char buf[2048]; bool *osmux_transmitted = (bool *)data; osmux_snprintf(buf, sizeof(buf), batch_msg); clock_debug("OSMUX message (len=%d): %s\n", batch_msg->len, buf); /* We expect 1 batch: */ osmuxh = osmux_xfrm_output_pull(batch_msg); OSMO_ASSERT(osmuxh); /* Check seqnum is the one configured beforehand: */ OSMO_ASSERT(osmuxh->seq == 123); osmuxh = osmux_xfrm_output_pull(batch_msg); OSMO_ASSERT(osmuxh == NULL); msgb_free(batch_msg); *osmux_transmitted = true; } /* Test that the first transmitted osmux header is set according to what has been configured. */ static void test_initial_osmux_seqnum(void) { struct msgb *msg; int rc; const uint8_t cid = 33; bool osmux_transmitted = false; struct amr_hdr *amrh; struct osmux_in_handle *h_input; printf("===%s===\n", __func__); clock_override_enable(true); clock_override_set(0, 0); rtp_init(0, 0); h_input = osmux_xfrm_input_alloc(tall_ctx); osmux_xfrm_input_set_initial_seqnum(h_input, 123); osmux_xfrm_input_set_batch_factor(h_input, 1); osmux_xfrm_input_set_deliver_cb(h_input, test_initial_osmux_seqnum_osmux_deliver_cb, &osmux_transmitted); osmux_xfrm_input_open_circuit(h_input, cid, false); /* First RTP frame at t=0 */ msg = rtp_next(); amrh = rtp_append_amr(msg, AMR_FT_2); amrh->f = 1; amrh->q = 1; amrh->cmr = 0; rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); /* t=20, osmux batch is scheduled to be transmitted: */ clock_debug("Submit 2nd RTP packet, CMR changes"); clock_override_add(0, TIME_RTP_PKT_MS*1000); osmo_select_main(0); OSMO_ASSERT(osmux_transmitted == true); clock_debug("Closing circuit"); osmux_xfrm_input_close_circuit(h_input, cid); talloc_free(h_input); } static void test_rtp_dup_osmux_deliver_cb(struct msgb *batch_msg, void *data) { struct osmux_hdr *osmuxh; char buf[2048]; uint8_t *osmux_pl; bool *osmux_transmitted = (bool *)data; osmux_snprintf(buf, sizeof(buf), batch_msg); clock_debug("OSMUX message (len=%d): %s\n", batch_msg->len, buf); /* We expect 1 batch: */ osmuxh = osmux_xfrm_output_pull(batch_msg); OSMO_ASSERT(osmuxh); /* Check seqnum is the one configured beforehand: */ OSMO_ASSERT(osmuxh->seq == 123); osmux_pl = (uint8_t *)osmuxh + sizeof(*osmuxh); OSMO_ASSERT(osmux_pl[0] == 0x12); osmuxh = osmux_xfrm_output_pull(batch_msg); OSMO_ASSERT(osmuxh == NULL); msgb_free(batch_msg); *osmux_transmitted = true; } /* Test user pushes duplicated RTP (dup seqnum) to osmux: */ static void test_rtp_dup(void) { struct msgb *msg, *msg_dup; struct amr_hdr *amrh; int rc; const uint8_t cid = 33; bool osmux_transmitted = false; struct osmux_in_handle *h_input; printf("===%s===\n", __func__); clock_override_enable(true); clock_override_set(0, 0); rtp_init(0, 0); h_input = osmux_xfrm_input_alloc(tall_ctx); osmux_xfrm_input_set_initial_seqnum(h_input, 123); osmux_xfrm_input_set_batch_factor(h_input, 2); osmux_xfrm_input_set_deliver_cb(h_input, test_rtp_dup_osmux_deliver_cb, &osmux_transmitted); osmux_xfrm_input_open_circuit(h_input, cid, false); /* First RTP frame at t=0 */ msg = rtp_next(); msg_dup = msgb_copy(msg, "dup"); rtp_append_amr(msg, AMR_FT_2); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); clock_debug("Submit 2nd RTP packet, seqnum dup"); clock_override_add(0, TIME_RTP_PKT_MS*1000); amrh = rtp_append_amr(msg_dup, AMR_FT_2); amrh->data[0] = 0x12; /* Change AMR payload to check it is updated. */ rc = osmux_xfrm_input(h_input, msg_dup, cid); OSMO_ASSERT(rc == 0); OSMO_ASSERT(osmux_transmitted == false); /* t=60, osmux batch is scheduled to be transmitted: */ clock_debug("Submit 3rd RTP packet, triggers osmux batch"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_2); osmo_select_main(0); OSMO_ASSERT(osmux_transmitted == true); clock_debug("Closing circuit"); osmux_xfrm_input_close_circuit(h_input, cid); talloc_free(h_input); } /* Test some scenario where one RTP packet is lost */ static void test_rtp_pkt_gap_osmux_deliver_cb(struct msgb *batch_msg, void *data) { struct osmux_hdr *osmuxh; char buf[2048]; bool *osmux_transmitted = (bool *)data; osmux_snprintf(buf, sizeof(buf), batch_msg); clock_debug("OSMUX message (len=%d): %s\n", batch_msg->len, buf); /* We expect 1 batch: */ osmuxh = osmux_xfrm_output_pull(batch_msg); OSMO_ASSERT(osmuxh); /* Check seqnum is the one configured beforehand: */ OSMO_ASSERT(osmuxh->seq == 123); /* Check four AMR payloads appear: */ OSMO_ASSERT(osmuxh->ctr == 3); osmuxh = osmux_xfrm_output_pull(batch_msg); OSMO_ASSERT(osmuxh == NULL); msgb_free(batch_msg); *osmux_transmitted = true; } static void test_rtp_pkt_gap(uint16_t rtp_start_seqnum) { struct msgb *msg; int rc; const uint8_t cid = 33; bool osmux_transmitted = false; struct osmux_in_handle *h_input; printf("===%s(%" PRIu16 ")===\n", __func__, rtp_start_seqnum); clock_override_enable(true); clock_override_set(0, 0); rtp_init(rtp_start_seqnum, 18000); h_input = osmux_xfrm_input_alloc(tall_ctx); osmux_xfrm_input_set_initial_seqnum(h_input, 123); osmux_xfrm_input_set_batch_factor(h_input, 4); osmux_xfrm_input_set_deliver_cb(h_input, test_rtp_pkt_gap_osmux_deliver_cb, &osmux_transmitted); osmux_xfrm_input_open_circuit(h_input, cid, false); /* First RTP frame at t=0 */ clock_debug("1st RTP packet is received"); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_2); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); clock_debug("2nd RTP packet is lost"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); msgb_free(msg); clock_debug("3rd RTP packet is received"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_2); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); clock_debug("4th RTP packet is received"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_2); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); /* t=80, osmux batch is scheduled to be transmitted: */ clock_debug("osmux batch transmitted"); clock_override_add(0, TIME_RTP_PKT_MS*1000); osmo_select_main(0); OSMO_ASSERT(osmux_transmitted == true); clock_debug("Closing circuit"); osmux_xfrm_input_close_circuit(h_input, cid); talloc_free(h_input); } /* Test some scenario where some RTP packets (>batch_factor) are lost */ static void test_rtp_pkt_gap_bigger_than_batch_factor_osmux_deliver_cb(struct msgb *batch_msg, void *data) { struct osmux_hdr *osmuxh; char buf[2048]; unsigned *osmux_transmitted = (unsigned *)data; (*osmux_transmitted)++; osmux_snprintf(buf, sizeof(buf), batch_msg); clock_debug("OSMUX message %u (len=%d): %s\n", *osmux_transmitted, batch_msg->len, buf); /* We expect 1 batch: */ osmuxh = osmux_xfrm_output_pull(batch_msg); OSMO_ASSERT(osmuxh); /* Check seqnum is the one configured beforehand: */ OSMO_ASSERT(osmuxh->seq == (123 + *osmux_transmitted - 1)); /* Check four AMR payloads appear: */ OSMO_ASSERT(osmuxh->ctr == 3); /* Second osmux frame: is it expected to have the Marker bit set, since * there was an incoming RTP jump between this resulting osmux frame and * the previous one. */ if (*osmux_transmitted == 2) OSMO_ASSERT(osmuxh->rtp_m == 1); osmuxh = osmux_xfrm_output_pull(batch_msg); OSMO_ASSERT(osmuxh == NULL); msgb_free(batch_msg); } static void test_rtp_pkt_gap_bigger_than_batch_factor(uint16_t rtp_start_seqnum) { struct msgb *msg; int rc; const uint8_t cid = 33; unsigned osmux_transmitted = 0; struct osmux_in_handle *h_input; int i; printf("===%s(%" PRIu16 ")===\n", __func__, rtp_start_seqnum); clock_override_enable(true); clock_override_set(0, 0); rtp_init(rtp_start_seqnum, 18000); h_input = osmux_xfrm_input_alloc(tall_ctx); osmux_xfrm_input_set_initial_seqnum(h_input, 123); osmux_xfrm_input_set_batch_factor(h_input, 4); osmux_xfrm_input_set_deliver_cb(h_input, test_rtp_pkt_gap_bigger_than_batch_factor_osmux_deliver_cb, &osmux_transmitted); osmux_xfrm_input_open_circuit(h_input, cid, false); /* First RTP frame at t=0 */ clock_debug("1st RTP packet is received"); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_2); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); clock_debug("10 packets are lost"); for (i = 0; i < 10; i++) { clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); msgb_free(msg); } clock_debug("12th RTP packet is received"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_2); /* The recreation of lost pkts fills the batch and hence triggers transmission of osmux frame: */ rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 1); /* We call deliver as requested by API: */ osmux_xfrm_input_deliver(h_input); OSMO_ASSERT(osmux_transmitted == 1); /* We try to feed the RTP packet again as per API. This time 0 is * received, pkt is stored in osmux layer. This is because we don't * recreate entire batches with fake data. Instead, Osmux should use the * Marker bit to announce the peer there was a (potentially huge) jump * in seqnums. */ rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); /* We continue filling the current batch so it gets transmitted: */ clock_debug("12th+1 RTP packet is received"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_2); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); clock_debug("12th+2 RTP packet is received"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_2); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); clock_debug("12th+3 RTP packet is received"); clock_override_add(0, TIME_RTP_PKT_MS*1000); msg = rtp_next(); rtp_append_amr(msg, AMR_FT_2); rc = osmux_xfrm_input(h_input, msg, cid); OSMO_ASSERT(rc == 0); clock_debug("osmux batch transmitted"); clock_override_add(0, TIME_RTP_PKT_MS*1000); osmo_select_main(0); OSMO_ASSERT(osmux_transmitted == 2); clock_debug("Closing circuit"); osmux_xfrm_input_close_circuit(h_input, cid); talloc_free(h_input); } int main(int argc, char **argv) { if (signal(SIGALRM, sigalarm_handler) == SIG_ERR) { perror("signal"); exit(EXIT_FAILURE); } tall_ctx = talloc_named_const(NULL, 1, "Root context"); msgb_talloc_ctx_init(tall_ctx, 0); osmo_init_logging2(tall_ctx, &log_info); log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE); log_set_log_level(osmo_stderr_target, LOGL_DEBUG); log_set_category_filter(osmo_stderr_target, DLMUX, 1, LOGL_DEBUG); alarm(10); test_amr_ft_change_middle_batch(); test_last_amr_cmr_f_q_used(); test_initial_osmux_seqnum(); test_rtp_dup(); test_rtp_pkt_gap(60); /* Test several wraparound scenarios: */ test_rtp_pkt_gap(65533); test_rtp_pkt_gap(65534); test_rtp_pkt_gap(65535); test_rtp_pkt_gap_bigger_than_batch_factor(60); /* Test several wraparound scenarios: */ test_rtp_pkt_gap_bigger_than_batch_factor(65533); test_rtp_pkt_gap_bigger_than_batch_factor(65534); test_rtp_pkt_gap_bigger_than_batch_factor(65535); fprintf(stdout, "OK: Test passed\n"); return EXIT_SUCCESS; }