637 lines
17 KiB
C
637 lines
17 KiB
C
/* (C) 2017 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
|
*
|
|
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <unistd.h>
|
|
#include <stdint.h>
|
|
#include <inttypes.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
#include <arpa/inet.h>
|
|
#include <sys/time.h>
|
|
#include <getopt.h>
|
|
|
|
#include <osmocom/core/select.h>
|
|
#include <osmocom/core/application.h>
|
|
#include <osmocom/core/logging.h>
|
|
#include <osmocom/core/msgb.h>
|
|
#include <osmocom/core/linuxlist.h>
|
|
#include <osmocom/netif/jibuf.h>
|
|
#include <osmocom/netif/rtp.h>
|
|
#include <osmocom/netif/osmux.h>
|
|
|
|
#include "osmo-pcap-test/osmo_pcap.h"
|
|
|
|
|
|
struct checkpoint {
|
|
struct timeval ts;
|
|
int transit;
|
|
double jitter;
|
|
uint32_t timestamp;
|
|
uint16_t seq;
|
|
};
|
|
|
|
struct rtp_pkt_info {
|
|
struct osmo_timer_list timer;
|
|
struct timeval tx_prev_time;
|
|
struct timeval tx_time;
|
|
uint32_t tx_delay;
|
|
int32_t skew_us;
|
|
struct checkpoint prequeue;
|
|
struct checkpoint postqueue;
|
|
};
|
|
|
|
struct rtp_pkt_info_cb {
|
|
struct rtp_pkt_info *data;
|
|
};
|
|
|
|
/* Option parameters to the program */
|
|
static bool opt_test_rand;
|
|
static bool opt_debug_human;
|
|
static bool opt_debug_table;
|
|
static bool opt_osmux;
|
|
static bool opt_skew;
|
|
static char* opt_pcap_file;
|
|
uint32_t opt_buffer_min = 60;
|
|
uint32_t opt_buffer_max = 500;
|
|
/* ----------------------------- */
|
|
|
|
/* Logging related stuff */
|
|
#define INT2IDX(x) (-1*(x)-1)
|
|
struct log_info_cat jibuf_test_cat[] = {
|
|
[INT2IDX(DLJIBUF)] = {
|
|
.name = "DLJIBUF",
|
|
.description = "Osmocom Jitter Buffer",
|
|
.enabled = 1, .loglevel = LOGL_DEBUG,
|
|
},
|
|
};
|
|
const struct log_info jibuf_test_log_info = {
|
|
.filter_fn = NULL,
|
|
.cat = jibuf_test_cat,
|
|
.num_cat = ARRAY_SIZE(jibuf_test_cat),
|
|
};
|
|
/* ----------------------------- */
|
|
|
|
/* Used for test random: */
|
|
#define SAMPLES_PER_PKT 160
|
|
#define RTP_FREQ_MS 20
|
|
#define RTP_PKTS_PER_SEC (1000/RTP_FREQ_MS)
|
|
#define NET_DELAY_MS 300
|
|
#define GENERATED_JITTER_MS 160
|
|
#define NUM_PACKETS_TO_SEND 1000
|
|
|
|
/* RTP packet with AMR payload */
|
|
static uint8_t rtp_pkt[] = {
|
|
0x80, 0x62, 0x3f, 0xcc, 0x00, 0x01, 0xa7, 0x6f, /* RTP */
|
|
0x07, 0x09, 0x00, 0x62, 0x20, 0x14, 0xff, 0xd4, /* AMR */
|
|
0xf9, 0xff, 0xfb, 0xe7, 0xeb, 0xf9, 0x9f, 0xf8,
|
|
0xf2, 0x26, 0x33, 0x65, 0x54,
|
|
};
|
|
|
|
static struct osmo_jibuf *jb;
|
|
static uint16_t rtp_first_seq;
|
|
static uint16_t rtp_next_seq;
|
|
static uint32_t rtp_next_ts;
|
|
static struct timeval tx_prev_time;
|
|
static uint32_t packets_sent;
|
|
static uint32_t packets_received;
|
|
static uint32_t packets_dropped;
|
|
static uint32_t packets_too_much_jitter;
|
|
/* ----------------------------- */
|
|
|
|
/* Used for test pcap: */
|
|
static struct osmo_pcap osmo_pcap;
|
|
static bool pcap_finished;
|
|
static struct osmux_out_handle *pcap_osmux_h;
|
|
/* ----------------------------- */
|
|
|
|
static void sigalarm_handler(int foo)
|
|
{
|
|
printf("FAIL: test did not run successfully\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
struct rtp_pkt_info *msgb_get_pinfo(struct msgb* msg)
|
|
{
|
|
struct rtp_pkt_info_cb *cb = (struct rtp_pkt_info_cb *)&((msg)->cb[0]);
|
|
return cb->data;
|
|
}
|
|
|
|
static uint32_t timeval2ms(const struct timeval *ts)
|
|
{
|
|
return ts->tv_sec * 1000 + ts->tv_usec / 1000;
|
|
}
|
|
|
|
bool pkt_is_syncpoint(struct msgb* msg, uint16_t prev_seq, uint32_t prev_timestamp)
|
|
{
|
|
struct rtp_hdr *rtph = osmo_rtp_get_hdr(msg);
|
|
|
|
uint16_t current_seq = ntohs(rtph->sequence);
|
|
uint32_t current_tx_ts = ntohl(rtph->timestamp);
|
|
bool insync = (current_tx_ts - prev_timestamp) == (current_seq - prev_seq)*SAMPLES_PER_PKT;
|
|
return !insync || rtph->marker;
|
|
}
|
|
|
|
int32_t calc_rel_transmit_time(uint32_t tx_0, uint32_t tx_f, uint32_t rx_0, uint32_t rx_f, bool tx_is_samples, bool pre)
|
|
{
|
|
int32_t rxdiff, txdiff, res;
|
|
rxdiff = (rx_f - rx_0);
|
|
txdiff = (tx_f - tx_0);
|
|
if(tx_is_samples)
|
|
txdiff = txdiff * RTP_FREQ_MS/SAMPLES_PER_PKT;
|
|
res = rxdiff - txdiff;
|
|
//fprintf(stderr, "%s: (%u - %u) - (%u - %u) = %d - %d (%d) = %d\n", (pre ? "pre" : "post"), rx_f, rx_0, tx_f, tx_0, rxdiff, txdiff, (tx_f - tx_0), res);
|
|
return res;
|
|
}
|
|
|
|
void trace_pkt(struct msgb *msg, char* info) {
|
|
struct timeval now, total_delay;
|
|
struct rtp_hdr *rtph = osmo_rtp_get_hdr(msg);
|
|
struct rtp_pkt_info *pinfo = msgb_get_pinfo(msg);
|
|
|
|
gettimeofday(&now, NULL);
|
|
timersub(&now, &pinfo->tx_time, &total_delay);
|
|
|
|
if (opt_debug_human) {
|
|
uint32_t total_delay_ms = timeval2ms(&total_delay);
|
|
LOGP(DLJIBUF, LOGL_DEBUG, "%s: seq=%"PRIu16" ts=%"PRIu32" (%ld.%06ld) tx_delay=%"PRIu32 \
|
|
" end_delay=%"PRIu32" pre_trans=%d pre_jitter=%f post_trans=%d post_jitter=%f skew=%"PRId32"\n",
|
|
info, ntohs(rtph->sequence), ntohl(rtph->timestamp),
|
|
pinfo->tx_time.tv_sec, pinfo->tx_time.tv_usec,
|
|
pinfo->tx_delay, total_delay_ms,
|
|
pinfo->prequeue.transit, pinfo->prequeue.jitter,
|
|
pinfo->postqueue.transit, pinfo->postqueue.jitter, pinfo->skew_us);
|
|
|
|
if (pinfo->prequeue.jitter < pinfo->postqueue.jitter)
|
|
LOGP(DLJIBUF, LOGL_ERROR, "JITTER HIGHER THAN REF: seq=%"PRIu16" ts=%"PRIu32 \
|
|
" (%ld.%06ld) tx_delay=%"PRIu32" end_delay=%"PRIu32 \
|
|
" pre_trans=%d pre_jitter=%f post_trans=%d post_jitter=%f dropped=%"PRIu32 \
|
|
" buffer=%"PRIu32"\n",
|
|
ntohs(rtph->sequence), ntohl(rtph->timestamp),
|
|
pinfo->tx_time.tv_sec, pinfo->tx_time.tv_usec,
|
|
pinfo->tx_delay, total_delay_ms,
|
|
pinfo->prequeue.transit, pinfo->prequeue.jitter,
|
|
pinfo->postqueue.transit, pinfo->postqueue.jitter,
|
|
packets_dropped, jb->threshold_delay);
|
|
}
|
|
|
|
if (opt_debug_table) {
|
|
/* Used as input for gplot: "gnuplot -p jitter.plt -"" */
|
|
uint32_t tx_time_ms = timeval2ms(&pinfo->tx_time);
|
|
uint32_t prequeue_time_ms = timeval2ms(&pinfo->prequeue.ts);
|
|
uint32_t postqueue_time_ms = timeval2ms(&pinfo->postqueue.ts);
|
|
fprintf(stderr, "%"PRIu16"\t%"PRIu32"\t%"PRIu32"\t%"PRIu32"\t%d\t%d\t%f\t%f\t%"PRIu32"\t%"PRIu32"\t%f\n",
|
|
ntohs(rtph->sequence), tx_time_ms,
|
|
prequeue_time_ms, postqueue_time_ms,
|
|
pinfo->prequeue.transit, pinfo->postqueue.transit,
|
|
pinfo->prequeue.jitter, pinfo->postqueue.jitter,
|
|
packets_dropped, jb->threshold_delay, (double)jb->skew_us/1000.0);
|
|
}
|
|
}
|
|
|
|
void pkt_add_result(struct msgb *msg, bool dropped)
|
|
{
|
|
struct rtp_pkt_info *pinfo = msgb_get_pinfo(msg);
|
|
|
|
if (dropped) {
|
|
packets_dropped++;
|
|
trace_pkt(msg,"dropped");
|
|
} else {
|
|
packets_received++;
|
|
trace_pkt(msg,"received");
|
|
|
|
if (pinfo->prequeue.jitter < pinfo->postqueue.jitter)
|
|
packets_too_much_jitter++;
|
|
}
|
|
}
|
|
|
|
void dequeue_cb(struct msgb *msg, void *data)
|
|
{
|
|
static struct checkpoint postqueue_prev;
|
|
static bool postqueue_started = false;
|
|
|
|
bool tx_is_samples;
|
|
struct rtp_hdr *rtph = osmo_rtp_get_hdr(msg);
|
|
struct rtp_pkt_info *pinfo = msgb_get_pinfo(msg);
|
|
|
|
uint32_t tx1, tx0, rx0, rx1;
|
|
|
|
gettimeofday(&pinfo->postqueue.ts, NULL);
|
|
pinfo->postqueue.timestamp = htonl(rtph->timestamp);
|
|
|
|
/* If pkt->marker -> init of talkspurt, there may be missing packets before,
|
|
* better to start calculating the jitter from here */
|
|
if (postqueue_started && !pkt_is_syncpoint(msg, postqueue_prev.seq, postqueue_prev.timestamp)) {
|
|
/* In random test mode we now the sender time, so we get real
|
|
* jitter results using it */
|
|
if(opt_test_rand) {
|
|
tx0 = timeval2ms(&pinfo->tx_prev_time);
|
|
tx1 = timeval2ms(&pinfo->tx_time);
|
|
tx_is_samples = false;
|
|
} else {
|
|
tx0 = postqueue_prev.timestamp;
|
|
tx1 = pinfo->postqueue.timestamp;
|
|
tx_is_samples = true;
|
|
}
|
|
rx0 = timeval2ms(&postqueue_prev.ts);
|
|
rx1 = timeval2ms(&pinfo->postqueue.ts);
|
|
pinfo->postqueue.transit = calc_rel_transmit_time(tx0, tx1, rx0, rx1, tx_is_samples, 0);
|
|
|
|
uint32_t abs_transit = pinfo->postqueue.transit *
|
|
( pinfo->postqueue.transit >= 0 ? 1 : -1 );
|
|
|
|
pinfo->postqueue.jitter = postqueue_prev.jitter +
|
|
((double)abs_transit - postqueue_prev.jitter)/16.0;
|
|
} else {
|
|
postqueue_started = true;
|
|
pinfo->postqueue.transit = 0;
|
|
pinfo->postqueue.jitter = 0;
|
|
}
|
|
|
|
postqueue_prev = pinfo->postqueue;
|
|
postqueue_prev.seq = htons(rtph->sequence);
|
|
|
|
pkt_add_result(msg, false);
|
|
|
|
osmo_timer_del(&pinfo->timer);
|
|
msgb_free(msg);
|
|
}
|
|
|
|
void pkt_arrived_cb(void *data)
|
|
{
|
|
static struct checkpoint prequeue_prev;
|
|
static bool prequeue_started = false;
|
|
|
|
bool tx_is_samples;
|
|
struct msgb *msg = (struct msgb*) data;
|
|
struct rtp_hdr *rtph = osmo_rtp_get_hdr(msg);
|
|
struct rtp_pkt_info *pinfo = msgb_get_pinfo(msg);
|
|
|
|
|
|
uint32_t tx1, tx0, rx0, rx1;
|
|
|
|
gettimeofday(&pinfo->prequeue.ts, NULL);
|
|
pinfo->prequeue.timestamp = htonl(rtph->timestamp);
|
|
|
|
/* If pkt->marker -> init of talkspurt, there may be missing packets before,
|
|
* better to start calculating the jitter from here */
|
|
if (prequeue_started && !pkt_is_syncpoint(msg, prequeue_prev.seq, prequeue_prev.timestamp)) {
|
|
/* In random test mode we now the sender time, so we get real
|
|
* jitter results using it */
|
|
if(opt_test_rand) {
|
|
tx0 = timeval2ms(&pinfo->tx_prev_time);
|
|
tx1 = timeval2ms(&pinfo->tx_time);
|
|
tx_is_samples = false;
|
|
} else {
|
|
tx0 = prequeue_prev.timestamp;
|
|
tx1 = pinfo->prequeue.timestamp;
|
|
tx_is_samples = true;
|
|
}
|
|
rx0 = timeval2ms(&prequeue_prev.ts);
|
|
rx1 = timeval2ms(&pinfo->prequeue.ts);
|
|
pinfo->prequeue.transit = calc_rel_transmit_time(tx0, tx1, rx0, rx1, tx_is_samples, 1);
|
|
|
|
uint32_t abs_transit = pinfo->prequeue.transit *
|
|
( pinfo->prequeue.transit >= 0 ? 1 : -1 );
|
|
|
|
pinfo->prequeue.jitter = prequeue_prev.jitter +
|
|
((double)abs_transit - prequeue_prev.jitter)/16.0;
|
|
} else {
|
|
prequeue_started = true;
|
|
pinfo->prequeue.transit = 0;
|
|
pinfo->prequeue.jitter = 0;
|
|
}
|
|
|
|
prequeue_prev = pinfo->prequeue;
|
|
prequeue_prev.seq = htons(rtph->sequence);
|
|
|
|
int n = osmo_jibuf_enqueue(jb, msg);
|
|
|
|
/* skew has been updated with this new packet. We pick the updated one
|
|
* as it's the one applied to this packet. */
|
|
pinfo->skew_us = jb->skew_us;
|
|
|
|
if (n<0) {
|
|
pkt_add_result(msg, true);
|
|
osmo_timer_del(&pinfo->timer);
|
|
msgb_free(msg);
|
|
}
|
|
}
|
|
|
|
struct rtp_pkt_info * msgb_allocate_pinfo(struct msgb *msg)
|
|
{
|
|
struct rtp_pkt_info_cb *cb = (struct rtp_pkt_info_cb *)&((msg)->cb[0]);
|
|
cb->data = (struct rtp_pkt_info *) talloc_zero(msg, struct rtp_pkt_info);
|
|
return cb->data;
|
|
}
|
|
|
|
void rand_send_rtp_packet()
|
|
{
|
|
|
|
struct rtp_pkt_info *pinfo;
|
|
struct rtp_hdr *rtph;
|
|
struct msgb *msg;
|
|
|
|
/* Set fake prev_time for 1st packet. Otherwise transit calculations for first
|
|
* packet can be really weird if they not arrive in order */
|
|
if (rtp_next_seq == rtp_first_seq) {
|
|
struct timeval now, time_rate = { .tv_sec = 0, .tv_usec = RTP_FREQ_MS * 1000};
|
|
gettimeofday(&now, NULL);
|
|
timersub(&now, &time_rate, &tx_prev_time);
|
|
}
|
|
|
|
|
|
msg = msgb_alloc(1500, "test");
|
|
if (!msg)
|
|
exit(EXIT_FAILURE);
|
|
|
|
memcpy(msg->data, rtp_pkt, sizeof(rtp_pkt));
|
|
msgb_put(msg, sizeof(rtp_pkt));
|
|
|
|
rtph = osmo_rtp_get_hdr(msg);
|
|
|
|
rtph->sequence = htons(rtp_next_seq);
|
|
rtp_next_seq++;
|
|
|
|
rtph->timestamp = htonl(rtp_next_ts);
|
|
rtp_next_ts += SAMPLES_PER_PKT;
|
|
|
|
pinfo = msgb_allocate_pinfo(msg);
|
|
|
|
gettimeofday(&pinfo->tx_time, NULL);
|
|
pinfo->tx_prev_time = tx_prev_time;
|
|
memset(&pinfo->timer, 0, sizeof(struct osmo_timer_list));
|
|
pinfo->timer.cb = pkt_arrived_cb;
|
|
pinfo->timer.data = msg;
|
|
pinfo->tx_delay = NET_DELAY_MS + (random() % (GENERATED_JITTER_MS));
|
|
|
|
tx_prev_time = pinfo->tx_time;
|
|
|
|
/* TODO: add a random() to lose/drop packets */
|
|
|
|
osmo_timer_schedule(&pinfo->timer, 0, pinfo->tx_delay * 1000);
|
|
}
|
|
|
|
void rand_generate_pkt_cb(void *data)
|
|
{
|
|
static struct osmo_timer_list enqueue_timer = {.cb = rand_generate_pkt_cb, .data = NULL};
|
|
static struct timeval last_generated;
|
|
|
|
struct timeval time_rate = { .tv_sec = 0, .tv_usec = RTP_FREQ_MS * 1000};
|
|
struct timeval sched_ts;
|
|
|
|
if (!packets_sent)
|
|
gettimeofday(&last_generated, NULL);
|
|
|
|
rand_send_rtp_packet();
|
|
packets_sent++;
|
|
|
|
timeradd(&last_generated, &time_rate, &sched_ts);
|
|
last_generated = sched_ts;
|
|
if (packets_sent < NUM_PACKETS_TO_SEND) {
|
|
enqueue_timer.timeout = sched_ts;
|
|
osmo_timer_add(&enqueue_timer);
|
|
}
|
|
}
|
|
|
|
|
|
static int pcap_generate_pkt_cb(struct msgb *msg)
|
|
{
|
|
struct rtp_pkt_info *pinfo;
|
|
/* Set fake prev_time for 1st packet. Otherwise transit calculations for first
|
|
* packet can be really weird if they not arrive in order */
|
|
if (!packets_sent) {
|
|
struct timeval now, time_rate = { .tv_sec = 0, .tv_usec = RTP_FREQ_MS * 1000};
|
|
gettimeofday(&now, NULL);
|
|
timersub(&now, &time_rate, &tx_prev_time);
|
|
}
|
|
|
|
pinfo = msgb_allocate_pinfo(msg);
|
|
gettimeofday(&pinfo->tx_time, NULL);
|
|
pinfo->tx_prev_time = tx_prev_time;
|
|
|
|
tx_prev_time = pinfo->tx_time;
|
|
packets_sent++;
|
|
pkt_arrived_cb(msg);
|
|
return 0;
|
|
}
|
|
|
|
void glue_cb(struct msgb *msg, void *data)
|
|
{
|
|
pcap_generate_pkt_cb(msg);
|
|
}
|
|
|
|
int pcap_read_osmux(struct msgb *msg)
|
|
{
|
|
struct osmux_hdr *osmuxh;
|
|
|
|
/* This code below belongs to the osmux receiver */
|
|
while((osmuxh = osmux_xfrm_output_pull(msg)) != NULL)
|
|
osmux_xfrm_output_sched(pcap_osmux_h, osmuxh);
|
|
msgb_free(msg);
|
|
return 0;
|
|
}
|
|
|
|
void pcap_pkt_timer_cb(void *data)
|
|
{
|
|
int (*mycb)(struct msgb *msgb);
|
|
if(opt_osmux)
|
|
mycb = pcap_read_osmux;
|
|
else
|
|
mycb = pcap_generate_pkt_cb;
|
|
|
|
if (osmo_pcap_test_run(&osmo_pcap, IPPROTO_UDP, mycb) < 0) {
|
|
osmo_pcap_stats_printf();
|
|
osmo_pcap_test_close(osmo_pcap.h);
|
|
pcap_finished=true;
|
|
}
|
|
}
|
|
|
|
void rand_test_check()
|
|
{
|
|
uint32_t drop_threshold = NUM_PACKETS_TO_SEND * 5 / 100;
|
|
if (packets_dropped > drop_threshold) {
|
|
fprintf(stdout, "Too many dropped packets (%"PRIu32" > %"PRIu32")\n",
|
|
packets_dropped, drop_threshold);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
uint32_t jitter_high_threshold = NUM_PACKETS_TO_SEND * 8 / 100;
|
|
if (packets_too_much_jitter > jitter_high_threshold) {
|
|
fprintf(stdout, "Too many packets with higher jitter (%"PRIu32" > %"PRIu32")\n",
|
|
packets_too_much_jitter, jitter_high_threshold);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
void rand_test()
|
|
{
|
|
srandom(time(NULL));
|
|
rtp_first_seq = (uint16_t) random();
|
|
rtp_next_seq = rtp_first_seq;
|
|
rtp_next_ts = (uint32_t) random();
|
|
jb = osmo_jibuf_alloc(NULL);
|
|
|
|
osmo_jibuf_set_min_delay(jb, GENERATED_JITTER_MS - RTP_FREQ_MS);
|
|
osmo_jibuf_set_max_delay(jb, GENERATED_JITTER_MS + RTP_FREQ_MS*2);
|
|
osmo_jibuf_enable_skew_compensation(jb, opt_skew);
|
|
osmo_jibuf_set_dequeue_cb(jb, dequeue_cb, NULL);
|
|
|
|
/* first run */
|
|
rand_generate_pkt_cb(NULL);
|
|
|
|
/* If the test takes longer than twice the time needed to generate the packets
|
|
plus 10 seconds, abort it */
|
|
alarm(NUM_PACKETS_TO_SEND*20/1000 +10);
|
|
|
|
while((packets_received + packets_dropped) < NUM_PACKETS_TO_SEND)
|
|
osmo_select_main(0);
|
|
|
|
osmo_jibuf_delete(jb);
|
|
|
|
rand_test_check();
|
|
}
|
|
|
|
void pcap_test_check() {
|
|
|
|
}
|
|
|
|
void pcap_test() {
|
|
osmo_pcap_init();
|
|
|
|
osmo_pcap.h = osmo_pcap_test_open(opt_pcap_file);
|
|
if (osmo_pcap.h == NULL)
|
|
exit(EXIT_FAILURE);
|
|
|
|
osmo_pcap.timer.cb = pcap_pkt_timer_cb;
|
|
|
|
if(opt_osmux) {
|
|
pcap_osmux_h = osmux_xfrm_output_alloc(NULL);
|
|
osmux_xfrm_output_set_rtp_ssrc(pcap_osmux_h, 0);
|
|
osmux_xfrm_output_set_rtp_pl_type(pcap_osmux_h, 98);
|
|
osmux_xfrm_output_set_tx_cb(pcap_osmux_h, glue_cb, NULL);
|
|
}
|
|
|
|
jb = osmo_jibuf_alloc(NULL);
|
|
osmo_jibuf_set_dequeue_cb(jb, dequeue_cb, NULL);
|
|
osmo_jibuf_set_min_delay(jb, opt_buffer_min);
|
|
osmo_jibuf_set_max_delay(jb, opt_buffer_max);
|
|
osmo_jibuf_enable_skew_compensation(jb, opt_skew);
|
|
|
|
/* first run */
|
|
pcap_pkt_timer_cb(NULL);
|
|
|
|
while(!pcap_finished || !osmo_jibuf_empty(jb)) {
|
|
if (pcap_finished && opt_osmux) /* Flushing once should be enough */
|
|
osmux_xfrm_output_flush(pcap_osmux_h);
|
|
osmo_select_main(0);
|
|
}
|
|
|
|
osmo_jibuf_delete(jb);
|
|
talloc_free(pcap_osmux_h);
|
|
|
|
pcap_test_check();
|
|
}
|
|
|
|
static void print_help(void)
|
|
{
|
|
printf("jibuf_test [-r] [-p pcap] [-o] [-d] [-g] [-s] [-m ms] [-M ms]\n");
|
|
printf(" -h Print this help message\n");
|
|
printf(" -r Run test with randomly generated jitter\n");
|
|
printf(" -p Run test with specified pcap file\n");
|
|
printf(" -o The pcap contains OSMUX packets isntead of RTP\n");
|
|
printf(" -d Enable packet trace debug suitable for humans\n");
|
|
printf(" -t Enable packet trace debug suitable for gnuplot\n");
|
|
printf(" -s Enable skew estimation and compensation algorithm on the jitter-buffer\n");
|
|
printf(" -m Minimum buffer size for the jitter-buffer, in ms (only used in -p mode)\n");
|
|
printf(" -M Maximum buffer size for the jitter-buffer, in ms (only used in -p mode)\n");
|
|
}
|
|
|
|
static int parse_options(int argc, char **argv)
|
|
{
|
|
int opt;
|
|
|
|
while ((opt = getopt(argc, argv, "hdtrosp:m:M:")) != -1) {
|
|
switch (opt) {
|
|
case 'h':
|
|
print_help();
|
|
return -1;
|
|
case 'd':
|
|
opt_debug_human = true;
|
|
break;
|
|
case 't':
|
|
opt_debug_table = true;
|
|
break;
|
|
case 'r':
|
|
opt_test_rand = true;
|
|
break;
|
|
case 'o':
|
|
opt_osmux = true;
|
|
break;
|
|
case 's':
|
|
opt_skew = true;
|
|
break;
|
|
case 'p':
|
|
opt_pcap_file = strdup(optarg);
|
|
break;
|
|
case 'm':
|
|
opt_buffer_min = (uint32_t) atoi(optarg);
|
|
break;
|
|
case 'M':
|
|
opt_buffer_max = (uint32_t) atoi(optarg);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
|
|
if (signal(SIGALRM, sigalarm_handler) == SIG_ERR) {
|
|
perror("signal");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if(parse_options(argc, argv) < 0)
|
|
exit(EXIT_FAILURE);
|
|
|
|
void *tall_ctx = talloc_named_const(NULL, 1, "Root context");
|
|
msgb_talloc_ctx_init(tall_ctx, 0);
|
|
osmo_init_logging2(tall_ctx, &jibuf_test_log_info);
|
|
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
|
|
log_set_log_level(osmo_stderr_target, LOGL_DEBUG);
|
|
|
|
if(opt_debug_human && !opt_debug_table)
|
|
log_set_category_filter(osmo_stderr_target, DLMIB, 1, LOGL_DEBUG);
|
|
|
|
if(opt_pcap_file && opt_test_rand) {
|
|
print_help();
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
|
|
if(opt_pcap_file)
|
|
pcap_test();
|
|
|
|
if(opt_test_rand)
|
|
rand_test();
|
|
|
|
fprintf(stdout, "OK: Test passed\n");
|
|
return EXIT_SUCCESS;
|
|
}
|