#include "asterisk.h"
+#include <math.h> /* for sqrt, MAX */
#include <sched.h> /* for sched_yield */
#include <sys/time.h> /* for timeval */
#include <time.h> /* for time_t */
int ast_rtp_instance_destroy(struct ast_rtp_instance *instance)
{
- if (!instance) {
- return 0;
- }
- if (ast_debug_rtp_is_allowed) {
- char buffer[4][512];
- ast_debug_rtp(1, "%s:\n"
- " RTT: %s\n"
- " Loss: %s\n"
- " Jitter: %s\n"
- " MES: %s\n",
- instance->channel_uniqueid,
- ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT,
- buffer[0], sizeof(buffer[0])),
- ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS,
- buffer[1], sizeof(buffer[1])),
- ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER,
- buffer[2], sizeof(buffer[2])),
- ast_rtp_instance_get_quality(instance, AST_RTP_INSTANCE_STAT_FIELD_QUALITY_MES,
- buffer[3], sizeof(buffer[3]))
- );
- }
-
ao2_cleanup(instance);
return 0;
stat = AST_RTP_INSTANCE_STAT_COMBINED_LOSS;
} else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT) {
stat = AST_RTP_INSTANCE_STAT_COMBINED_RTT;
- } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_MES) {
- stat = AST_RTP_INSTANCE_STAT_COMBINED_MES;
} else {
return NULL;
}
/* Now actually fill the buffer with the good information */
if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY) {
- snprintf(buf, size, "ssrc=%u;themssrc=%u;lp=%u;rxjitter=%f;rxcount=%u;"
- "txjitter=%f;txcount=%u;rlp=%u;rtt=%f;rxmes=%f;txmes=%f",
- stats.local_ssrc, stats.remote_ssrc, stats.rxploss, stats.rxjitter,
- stats.rxcount, stats.txjitter, stats.txcount, stats.txploss, stats.rtt,
- stats.rxmes, stats.txmes);
+ snprintf(buf, size, "ssrc=%u;themssrc=%u;lp=%u;rxjitter=%f;rxcount=%u;txjitter=%f;txcount=%u;rlp=%u;rtt=%f",
+ stats.local_ssrc, stats.remote_ssrc, stats.rxploss, stats.rxjitter, stats.rxcount, stats.txjitter, stats.txcount, stats.txploss, stats.rtt);
} else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER) {
- snprintf(buf, size, "minrxjitter=%010.6f;maxrxjitter=%010.6f;avgrxjitter=%010.6f;stdevrxjitter=%010.6f;mintxjitter=%010.6f;maxtxjitter=%010.6f;avgtxjitter=%010.6f;stdevtxjitter=%010.6f;",
- stats.local_minjitter, stats.local_maxjitter, stats.local_normdevjitter, stats.local_stdevjitter, stats.remote_minjitter, stats.remote_maxjitter, stats.remote_normdevjitter, stats.remote_stdevjitter);
+ snprintf(buf, size, "minrxjitter=%f;maxrxjitter=%f;avgrxjitter=%f;stdevrxjitter=%f;reported_minjitter=%f;reported_maxjitter=%f;reported_avgjitter=%f;reported_stdevjitter=%f;",
+ stats.local_minjitter, stats.local_maxjitter, stats.local_normdevjitter, sqrt(stats.local_stdevjitter), stats.remote_minjitter, stats.remote_maxjitter, stats.remote_normdevjitter, sqrt(stats.remote_stdevjitter));
} else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS) {
- snprintf(buf, size, " minrxlost=%010.6f; maxrxlost=%010.6f; avgrxlost=%010.6f; stdevrxlost=%010.6f; mintxlost=%010.6f; maxtxlost=%010.6f; avgtxlost=%010.6f; stdevtxlost=%010.6f;",
- stats.local_minrxploss, stats.local_maxrxploss, stats.local_normdevrxploss, stats.local_stdevrxploss, stats.remote_minrxploss, stats.remote_maxrxploss, stats.remote_normdevrxploss, stats.remote_stdevrxploss);
+ snprintf(buf, size, "minrxlost=%f;maxrxlost=%f;avgrxlost=%f;stdevrxlost=%f;reported_minlost=%f;reported_maxlost=%f;reported_avglost=%f;reported_stdevlost=%f;",
+ stats.local_minrxploss, stats.local_maxrxploss, stats.local_normdevrxploss, sqrt(stats.local_stdevrxploss), stats.remote_minrxploss, stats.remote_maxrxploss, stats.remote_normdevrxploss, sqrt(stats.remote_stdevrxploss));
} else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT) {
- snprintf(buf, size, " minrtt=%010.6f; maxrtt=%010.6f; avgrtt=%010.6f; stdevrtt=%010.6f;", stats.minrtt, stats.maxrtt, stats.normdevrtt, stats.stdevrtt);
- } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_MES) {
- snprintf(buf, size, " minrxmes=%010.6f; maxrxmes=%010.6f; avgrxmes=%010.6f; stdevrxmes=%010.6f; mintxmes=%010.6f; maxtxmes=%010.6f; avgtxmes=%010.6f; stdevtxmes=%010.6f;",
- stats.local_minmes, stats.local_maxmes,
- stats.local_normdevmes, stats.local_stdevmes,
- stats.remote_minmes, stats.remote_maxmes,
- stats.remote_normdevmes, stats.remote_stdevmes);
+ snprintf(buf, size, "minrtt=%f;maxrtt=%f;avgrtt=%f;stdevrtt=%f;", stats.minrtt, stats.maxrtt, stats.normdevrtt, stats.stdevrtt);
}
return buf;
}
}
- quality = ast_rtp_instance_get_quality(instance,
- AST_RTP_INSTANCE_STAT_FIELD_QUALITY_MES, quality_buf, sizeof(quality_buf));
- if (quality) {
- pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSMES", quality);
- if (bridge) {
- pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSMESBRIDGED", quality);
- }
- }
-
ast_channel_stage_snapshot_done(chan);
ast_channel_unlock(chan);
if (bridge) {
struct ast_json *to = ast_json_object_get(payload->blob, "to");
struct ast_json *from = ast_json_object_get(payload->blob, "from");
struct ast_json *rtt = ast_json_object_get(payload->blob, "rtt");
- struct ast_json *mes = ast_json_object_get(payload->blob, "mes");
if (to) {
ast_str_append(&packet_string, 0, "To: %s\r\n", ast_json_string_get(to));
}
if (rtt) {
ast_str_append(&packet_string, 0, "RTT: %4.4f\r\n", ast_json_real_get(rtt));
}
- if (mes) {
- ast_str_append(&packet_string, 0, "MES: %4.1f\r\n", ast_json_real_get(mes));
- }
}
ast_str_append(&packet_string, 0, "SSRC: 0x%.8x\r\n", ssrc);
SET_AST_JSON_OBJ(j_res, "normdevrtt", ast_json_real_create(stats->normdevrtt));
SET_AST_JSON_OBJ(j_res, "stdevrtt", ast_json_real_create(stats->stdevrtt));
- SET_AST_JSON_OBJ(j_res, "txmes", ast_json_integer_create(stats->txmes));
- SET_AST_JSON_OBJ(j_res, "rxmes", ast_json_integer_create(stats->rxmes));
-
- SET_AST_JSON_OBJ(j_res, "remote_maxmes", ast_json_real_create(stats->remote_maxmes));
- SET_AST_JSON_OBJ(j_res, "remote_minmes", ast_json_real_create(stats->remote_minmes));
- SET_AST_JSON_OBJ(j_res, "remote_normdevmes", ast_json_real_create(stats->remote_normdevmes));
- SET_AST_JSON_OBJ(j_res, "remote_stdevmes", ast_json_real_create(stats->remote_stdevmes));
-
- SET_AST_JSON_OBJ(j_res, "local_maxmes", ast_json_real_create(stats->local_maxmes));
- SET_AST_JSON_OBJ(j_res, "local_minmes", ast_json_real_create(stats->local_minmes));
- SET_AST_JSON_OBJ(j_res, "local_normdevmes", ast_json_real_create(stats->local_normdevmes));
- SET_AST_JSON_OBJ(j_res, "local_stdevmes", ast_json_real_create(stats->local_stdevmes));
-
return j_res;
}
#define DEFAULT_STUN_SOFTWARE_ATTRIBUTE 1
#define DEFAULT_DTLS_MTU 1200
-/*!
- * Because both ends usually don't start sending RTP
- * at the same time, some of the calculations like
- * rtt and jitter will probably be unstable for a while
- * so we'll skip some received packets before starting
- * analyzing. This just affects analyzing; we still
- * process the RTP as normal.
- */
-#define RTP_IGNORE_FIRST_PACKETS_COUNT 15
-
extern struct ast_srtp_res *res_srtp;
extern struct ast_srtp_policy_res *res_srtp_policy;
unsigned int lastovidtimestamp;
unsigned int lastitexttimestamp;
unsigned int lastotexttimestamp;
- int prevrxseqno; /*!< Previous received packeted sequence number, from the network */
int lastrxseqno; /*!< Last received sequence number, from the network */
- int expectedrxseqno; /*!< Next expected sequence number, from the network */
+ int expectedrxseqno; /*!< Next expected sequence number, from the network */
AST_VECTOR(, int) missing_seqno; /*!< A vector of sequence numbers we never received */
int expectedseqno; /*!< Next expected sequence number, from the core */
unsigned short seedrxseqno; /*!< What sequence number did they start with?*/
+ unsigned int seedrxts; /*!< What RTP timestamp did they start with? */
unsigned int rxcount; /*!< How many packets have we received? */
unsigned int rxoctetcount; /*!< How many octets have we received? should be rxcount *160*/
unsigned int txcount; /*!< How many packets have we sent? */
unsigned int txoctetcount; /*!< How many octets have we sent? (txcount*160)*/
unsigned int cycles; /*!< Shifted count of sequence number cycles */
+ double rxjitter; /*!< Interarrival jitter at the moment in seconds to be reported */
+ double rxtransit; /*!< Relative transit time for previous packet */
struct ast_format *lasttxformat;
struct ast_format *lastrxformat;
- /*
- * RX RTP Timestamp and Jitter calculation.
- */
- double rxstart; /*!< RX time of the first packet in the session in seconds since EPOCH. */
- double rxstart_stable; /*!< RX time of the first packet after RTP_IGNORE_FIRST_PACKETS_COUNT */
- unsigned int remote_seed_rx_rtp_ts; /*!< RTP timestamp of first RX packet. */
- unsigned int remote_seed_rx_rtp_ts_stable; /*!< RTP timestamp of first packet after RTP_IGNORE_FIRST_PACKETS_COUNT */
- unsigned int last_transit_time_samples; /*!< The last transit time in samples */
- double rxjitter; /*!< Last calculated Interarrival jitter in seconds. */
- double rxjitter_samples; /*!< Last calculated Interarrival jitter in samples. */
- double rxmes; /*!< Media Experince Score at the moment to be reported */
-
/* DTMF Reception Variables */
char resp; /*!< The current digit being processed */
unsigned int last_seqno; /*!< The last known sequence number for any DTMF packet */
int send_payload;
int send_duration;
unsigned int flags;
+ struct timeval rxcore;
struct timeval txcore;
-
+ double drxcore; /*!< The double representation of the first received packet */
struct timeval dtmfmute;
struct ast_smoother *smoother;
unsigned short seqno; /*!< Sequence number, RFC 3550, page 13. */
unsigned int asymmetric_codec; /*!< Indicate if asymmetric send/receive codecs are allowed */
struct ast_rtp_instance *bundled; /*!< The RTP instance we are bundled to */
- /*!
- * \brief The RTP instance owning us (used for debugging purposes)
- * We don't hold a reference to the instance because it created
- * us in the first place. It can't go away.
- */
- struct ast_rtp_instance *owner;
int stream_num; /*!< Stream num for this RTP instance */
AST_VECTOR(, struct rtp_ssrc_mapping) ssrc_mapping; /*!< Mappings of SSRC to RTP instances */
struct ast_sockaddr bind_address; /*!< Requested bind address for the sockets */
unsigned int lastsrtxcount; /*!< Transmit packet count when last SR sent */
double accumulated_transit; /*!< accumulated a-dlsr-lsr */
double rtt; /*!< Last reported rtt */
- double reported_jitter; /*!< The contents of their last jitter entry in the RR in seconds */
+ unsigned int reported_jitter; /*!< The contents of their last jitter entry in the RR */
unsigned int reported_lost; /*!< Reported lost packets in their RR */
double reported_maxjitter; /*!< Maximum reported interarrival jitter */
double stdevrtt; /*!< Standard deviation of calculated round trip time */
unsigned int rtt_count; /*!< Calculated round trip time count */
- double reported_mes; /*!< The calculated MES from their last RR */
- double reported_maxmes; /*!< Maximum reported mes */
- double reported_minmes; /*!< Minimum reported mes */
- double reported_normdev_mes; /*!< Mean of reported mes */
- double reported_stdev_mes; /*!< Standard deviation of reported mes */
- unsigned int reported_mes_count; /*!< Reported mes count */
-
- double maxrxmes; /*!< Maximum of calculated mes */
- double minrxmes; /*!< Minimum of calculated mes */
- double normdev_rxmes; /*!< Mean of calculated mes */
- double stdev_rxmes; /*!< Standard deviation of calculated mes */
- unsigned int rxmes_count; /*!< mes count */
-
/* VP8: sequence number for the RTCP FIR FCI */
int firseq;
static void ast_rtp_set_stream_num(struct ast_rtp_instance *instance, int stream_num);
static int ast_rtp_extension_enable(struct ast_rtp_instance *instance, enum ast_rtp_extension extension);
static int ast_rtp_bundle(struct ast_rtp_instance *child, struct ast_rtp_instance *parent);
-static void update_reported_mes_stats(struct ast_rtp *rtp);
-static void update_local_mes_stats(struct ast_rtp *rtp);
#if defined(HAVE_OPENSSL) && (OPENSSL_VERSION_NUMBER >= 0x10001000L) && !defined(OPENSSL_NO_SRTP)
static int ast_rtp_activate(struct ast_rtp_instance *instance);
if (!(rtp = ast_calloc(1, sizeof(*rtp)))) {
return -1;
}
- rtp->owner = instance;
+
/* Set default parameters on the newly created RTP structure */
rtp->ssrc = ast_random();
ast_uuid_generate_str(rtp->cname, sizeof(rtp->cname));
rtp->seqno = ast_random() & 0x7fff;
rtp->expectedrxseqno = -1;
rtp->expectedseqno = -1;
- rtp->rxstart = -1;
rtp->sched = sched;
ast_sockaddr_copy(&rtp->bind_address, addr);
+
/* Transport creation operations can grab the RTP data from the instance, so set it */
ast_rtp_instance_set_data(instance, rtp);
AST_VECTOR_FREE(&rtp->missing_seqno);
/* Finally destroy ourselves */
- rtp->owner = NULL;
ast_free(rtp);
return 0;
/* Compute statistics */
calculate_lost_packet_statistics(rtp, &lost_packets, &fraction_lost);
- /*
- * update_local_mes_stats must be called AFTER
- * calculate_lost_packet_statistics
- */
- update_local_mes_stats(rtp);
gettimeofday(&now, NULL);
rtcp_report->reception_report_count = rtp->themssrc_valid ? 1 : 0;
report_block->lost_count.fraction = (fraction_lost & 0xff);
report_block->lost_count.packets = (lost_packets & 0xffffff);
report_block->highest_seq_no = (rtp->cycles | (rtp->lastrxseqno & 0xffff));
- report_block->ia_jitter = (unsigned int)rtp->rxjitter_samples;
+ report_block->ia_jitter = (unsigned int)(rtp->rxjitter * ast_rtp_get_rate(rtp->f.subclass.format));
report_block->lsr = rtp->rtcp->themrxlsr;
/* If we haven't received an SR report, DLSR should be 0 */
if (!ast_tvzero(rtp->rtcp->rxlsr)) {
ast_verbose(" Sent octets: %u\n", rtcp_report->sender_information.octet_count);
}
if (report_block) {
- int rate = ast_rtp_get_rate(rtp->f.subclass.format);
ast_verbose(" Report block:\n");
ast_verbose(" Their SSRC: %u\n", report_block->source_ssrc);
ast_verbose(" Fraction lost: %d\n", report_block->lost_count.fraction);
ast_verbose(" Cumulative loss: %u\n", report_block->lost_count.packets);
ast_verbose(" Highest seq no: %u\n", report_block->highest_seq_no);
- ast_verbose(" IA jitter (samp): %u\n", report_block->ia_jitter);
- ast_verbose(" IA jitter (secs): %.6f\n", ast_samp2sec(report_block->ia_jitter, rate));
+ ast_verbose(" IA jitter: %.4f\n", (double)report_block->ia_jitter / ast_rtp_get_rate(rtp->f.subclass.format));
ast_verbose(" Their last SR: %u\n", report_block->lsr);
ast_verbose(" DLSR: %4.4f (sec)\n\n", (double)(report_block->dlsr / 65536.0));
}
}
- message_blob = ast_json_pack("{s: s, s: s, s: f}",
+ message_blob = ast_json_pack("{s: s, s: s}",
"to", ast_sockaddr_stringify(&remote_address),
- "from", rtp->rtcp->local_addr_str,
- "mes", rtp->rxmes);
-
+ "from", rtp->rtcp->local_addr_str);
ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_sent_type(),
rtcp_report, message_blob);
}
} else {
if (rtp->rtcp && rtp->rtcp->schedid < 0) {
- ast_debug_rtcp(2, "(%s) RTCP starting transmission in %u ms\n",
- ast_rtp_instance_get_channel_id(instance), ast_rtcp_calc_interval(rtp));
+ ast_debug_rtcp(1, "(%p) RTCP starting transmission\n", instance);
ao2_ref(instance, +1);
rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, instance);
if (rtp->rtcp->schedid < 0) {
format = frame->subclass.format;
if (ast_format_cmp(rtp->lasttxformat, format) == AST_FORMAT_CMP_NOT_EQUAL) {
/* Oh dear, if the format changed we will have to set up a new smoother */
- ast_debug_rtp(1, "(%s) RTP ooh, format changed from %s to %s\n",
- ast_rtp_instance_get_channel_id(instance),
- ast_format_get_name(rtp->lasttxformat),
+ ast_debug_rtp(1, "(%p) RTP ooh, format changed from %s to %s\n",
+ instance, ast_format_get_name(rtp->lasttxformat),
ast_format_get_name(frame->subclass.format));
ao2_replace(rtp->lasttxformat, format);
if (rtp->smoother) {
return 0;
}
-static void calc_rxstamp_and_jitter(struct timeval *tv,
- struct ast_rtp *rtp, unsigned int rx_rtp_ts,
- int mark)
+static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int timestamp, int mark)
{
- int rate = ast_rtp_get_rate(rtp->f.subclass.format);
-
- double estimated_elapsed;
- double jitter = 0.0;
- double prev_jitter = 0.0;
struct timeval now;
- double rxnow;
- double arrival_sec;
- unsigned int arrival;
- int transit;
- int d;
-
- gettimeofday(&now,NULL);
-
- if (rtp->rxcount == 1 || mark) {
- rtp->rxstart = ast_tv2double(&now);
- rtp->remote_seed_rx_rtp_ts = rx_rtp_ts;
-
- *tv = ast_double2tv(rtp->rxstart);
-
- ast_debug_rtcp(3, "%s: "
- "Seed ts: %u current time: %f\n",
- ast_rtp_instance_get_channel_id(rtp->owner)
- , rx_rtp_ts
- , rtp->rxstart
- );
-
- return;
- }
-
- estimated_elapsed = ast_samp2sec(rx_rtp_ts - rtp->remote_seed_rx_rtp_ts, rate);
- *tv = ast_double2tv(rtp->rxstart + estimated_elapsed);
-
- /*
- * The first few packets are generally unstable so let's
- * not use them in the calculations.
- */
- if (rtp->rxcount < RTP_IGNORE_FIRST_PACKETS_COUNT) {
- ast_debug_rtcp(3, "%s: Packet %d < %d. Ignoring\n",
- ast_rtp_instance_get_channel_id(rtp->owner)
- , rtp->rxcount
- , RTP_IGNORE_FIRST_PACKETS_COUNT
- );
-
- return;
- }
-
- /*
- * First good packet. Capture the start time and timestamp
- * but don't actually use this packet for calculation.
- */
- if (rtp->rxcount == RTP_IGNORE_FIRST_PACKETS_COUNT) {
- rtp->rxstart_stable = ast_tv2double(&now);
- rtp->remote_seed_rx_rtp_ts_stable = rx_rtp_ts;
- rtp->last_transit_time_samples = -rx_rtp_ts;
-
- ast_debug_rtcp(3, "%s: "
- "pkt: %5u Stable Seed ts: %u current time: %f\n",
- ast_rtp_instance_get_channel_id(rtp->owner)
- , rtp->rxcount
- , rx_rtp_ts
- , rtp->rxstart_stable
- );
-
- return;
- }
-
- /*
- * If the current packet isn't in sequence, don't
- * use it in any calculations as remote_current_rx_rtp_ts
- * is not going to be correct.
- */
- if (rtp->lastrxseqno != rtp->prevrxseqno + 1) {
- ast_debug_rtcp(3, "%s: Current packet seq %d != last packet seq %d + 1. Ignoring\n",
- ast_rtp_instance_get_channel_id(rtp->owner)
- , rtp->lastrxseqno
- , rtp->prevrxseqno
- );
-
- return;
- }
-
- /*
- * The following calculations are taken from
- * https://www.rfc-editor.org/rfc/rfc3550#appendix-A.8
- *
- * The received rtp timestamp is the random "seed"
- * timestamp chosen by the sender when they sent the
- * first packet, plus the number of samples since then.
- *
- * To get our arrival time in the same units, we
- * calculate the time difference in seconds between
- * when we received the first packet and when we
- * received this packet and convert that to samples.
- */
- rxnow = ast_tv2double(&now);
- arrival_sec = rxnow - rtp->rxstart_stable;
- arrival = ast_sec2samp(arrival_sec, rate);
-
- /*
- * Now we can use the exact formula in
- * https://www.rfc-editor.org/rfc/rfc3550#appendix-A.8 :
- *
- * int transit = arrival - r->ts;
- * int d = transit - s->transit;
- * s->transit = transit;
- * if (d < 0) d = -d;
- * s->jitter += (1./16.) * ((double)d - s->jitter);
- *
- * Our rx_rtp_ts is their r->ts.
- * Our rtp->last_transit_time_samples is their s->transit.
- * Our rtp->rxjitter is their s->jitter.
- */
- transit = arrival - rx_rtp_ts;
- d = transit - rtp->last_transit_time_samples;
+ struct timeval tmp;
+ double transit;
+ double current_time;
+ double d;
+ double dtv;
+ double prog;
+ int rate = ast_rtp_get_rate(rtp->f.subclass.format);
- if (d < 0) {
- d = -d;
+ if ((!rtp->rxcore.tv_sec && !rtp->rxcore.tv_usec) || mark) {
+ gettimeofday(&rtp->rxcore, NULL);
+ rtp->drxcore = (double) rtp->rxcore.tv_sec + (double) rtp->rxcore.tv_usec / 1000000;
+ /* map timestamp to a real time */
+ rtp->seedrxts = timestamp; /* Their RTP timestamp started with this */
+ tmp = ast_samp2tv(timestamp, rate);
+ rtp->rxcore = ast_tvsub(rtp->rxcore, tmp);
+ /* Round to 0.1ms for nice, pretty timestamps */
+ rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 100;
}
- prev_jitter = rtp->rxjitter_samples;
- jitter = (1.0/16.0) * (((double)d) - prev_jitter);
- rtp->rxjitter_samples = prev_jitter + jitter;
-
- /*
- * We need to hang on to jitter in both samples and seconds.
- */
- rtp->rxjitter = ast_samp2sec(rtp->rxjitter_samples, rate);
-
- ast_debug_rtcp(3, "%s: pkt: %5u "
- "Arrival sec: %7.3f Arrival ts: %10u RX ts: %10u "
- "Transit samp: %6d Last transit samp: %6d d: %4d "
- "Curr jitter: %7.0f(%7.3f) Prev Jitter: %7.0f(%7.3f) New Jitter: %7.0f(%7.3f)\n",
- ast_rtp_instance_get_channel_id(rtp->owner)
- , rtp->rxcount
- , arrival_sec
- , arrival
- , rx_rtp_ts
- , transit
- , rtp->last_transit_time_samples
- , d
- , jitter
- , ast_samp2sec(jitter, rate)
- , prev_jitter
- , ast_samp2sec(prev_jitter, rate)
- , rtp->rxjitter_samples
- , rtp->rxjitter
- );
-
- rtp->last_transit_time_samples = transit;
-
- /*
- * Update all the stats.
- */
+ gettimeofday(&now,NULL);
+ /* rxcore is the mapping between the RTP timestamp and _our_ real time from gettimeofday() */
+ tmp = ast_samp2tv(timestamp, rate);
+ *tv = ast_tvadd(rtp->rxcore, tmp);
+
+ prog = (double)((timestamp-rtp->seedrxts)/(float)(rate));
+ dtv = (double)rtp->drxcore + (double)(prog);
+ current_time = (double)now.tv_sec + (double)now.tv_usec/1000000;
+ transit = current_time - dtv;
+ d = transit - rtp->rxtransit;
+ rtp->rxtransit = transit;
+ if (d<0) {
+ d=-d;
+ }
+ rtp->rxjitter += (1./16.) * (d - rtp->rxjitter);
if (rtp->rtcp) {
if (rtp->rxjitter > rtp->rtcp->maxrxjitter)
rtp->rtcp->maxrxjitter = rtp->rxjitter;
if (rtp->rtcp && rtp->rxjitter < rtp->rtcp->minrxjitter)
rtp->rtcp->minrxjitter = rtp->rxjitter;
- calc_mean_and_standard_deviation(rtp->rxjitter,
- &rtp->rtcp->normdev_rxjitter, &rtp->rtcp->stdev_rxjitter,
- &rtp->rtcp->rxjitter_count);
+ calc_mean_and_standard_deviation(rtp->rxjitter, &rtp->rtcp->normdev_rxjitter,
+ &rtp->rtcp->stdev_rxjitter, &rtp->rtcp->rxjitter_count);
}
-
- return;
}
static struct ast_frame *create_dtmf_frame(struct ast_rtp_instance *instance, enum ast_frame_type type, int compensate)
*/
static void update_jitter_stats(struct ast_rtp *rtp, unsigned int ia_jitter)
{
- int rate = ast_rtp_get_rate(rtp->f.subclass.format);
-
- rtp->rtcp->reported_jitter = ast_samp2sec(ia_jitter, rate);
+ double reported_jitter;
+ rtp->rtcp->reported_jitter = ia_jitter;
+ reported_jitter = (double) rtp->rtcp->reported_jitter;
if (rtp->rtcp->reported_jitter_count == 0) {
- rtp->rtcp->reported_minjitter = rtp->rtcp->reported_jitter;
+ rtp->rtcp->reported_minjitter = reported_jitter;
}
- if (rtp->rtcp->reported_jitter < rtp->rtcp->reported_minjitter) {
- rtp->rtcp->reported_minjitter = rtp->rtcp->reported_jitter;
+ if (reported_jitter < rtp->rtcp->reported_minjitter) {
+ rtp->rtcp->reported_minjitter = reported_jitter;
}
- if (rtp->rtcp->reported_jitter > rtp->rtcp->reported_maxjitter) {
- rtp->rtcp->reported_maxjitter = rtp->rtcp->reported_jitter;
+ if (reported_jitter > rtp->rtcp->reported_maxjitter) {
+ rtp->rtcp->reported_maxjitter = reported_jitter;
}
- calc_mean_and_standard_deviation(rtp->rtcp->reported_jitter,
- &rtp->rtcp->reported_normdev_jitter, &rtp->rtcp->reported_stdev_jitter,
- &rtp->rtcp->reported_jitter_count);
+ calc_mean_and_standard_deviation(reported_jitter, &rtp->rtcp->reported_normdev_jitter,
+ &rtp->rtcp->reported_stdev_jitter, &rtp->rtcp->reported_jitter_count);
}
/*!
&rtp->rtcp->reported_stdev_lost, &rtp->rtcp->reported_lost_count);
}
-#define RESCALE(in, inmin, inmax, outmin, outmax) ((((in - inmin)/(inmax-inmin))*(outmax-outmin))+outmin)
-/*!
- * \brief Calculate a "media experience score" based on given data
- *
- * Technically, a mean opinion score (MOS) cannot be calculated without the involvement
- * of human eyes (video) and ears (audio). Thus instead we'll approximate an opinion
- * using the given parameters, and call it a media experience score.
- *
- * The tallied score is based upon recommendations and formulas from ITU-T G.107,
- * ITU-T G.109, ITU-T G.113, and other various internet sources.
- *
- * \param normdevrtt The average round trip time
- * \param rxjitter The smoothed jitter
- * \param stdev_rxjitter The jitter standard deviation value
- * \param normdev_rxlost The average number of packets lost since last check
- *
- * \return A media experience score.
- *
- * \note The calculations in this function could probably be simplified
- * but calculating a MOS using the information available publicly,
- * then re-scaling it to 0.0 -> 100.0 makes the process clearer and
- * easier to troubleshoot or change.
- */
-static double calc_media_experience_score(struct ast_rtp_instance *instance,
- double normdevrtt, double normdev_rxjitter, double stdev_rxjitter,
- double normdev_rxlost)
-{
- double r_value;
- double pseudo_mos;
- double mes = 0;
-
- /*
- * While the media itself might be okay, a significant enough delay could make
- * for an unpleasant user experience.
- *
- * Calculate the effective latency by using the given round trip time, and adding
- * jitter scaled according to its standard deviation. The scaling is done in order
- * to increase jitter's weight since a higher deviation can result in poorer overall
- * quality.
- */
- double effective_latency = (normdevrtt * 1000)
- + ((normdev_rxjitter * 2) * (stdev_rxjitter / 3))
- + 10;
-
- /*
- * Using the defaults for the standard transmission rating factor ("R" value)
- * one arrives at 93.2 (see ITU-T G.107 for more details), so we'll use that
- * as the starting value and subtract deficiencies that could affect quality.
- *
- * Calculate the impact of the effective latency. Influence increases with
- * values over 160 as the significant "lag" can degrade user experience.
- */
- if (effective_latency < 160) {
- r_value = 93.2 - (effective_latency / 40);
- } else {
- r_value = 93.2 - (effective_latency - 120) / 10;
- }
-
- /* Next evaluate the impact of lost packets */
- r_value = r_value - (normdev_rxlost * 2.0);
-
- /*
- * Finally convert the "R" value into a opinion/quality score between 1 (really anything
- * below 3 should be considered poor) and 4.5 (the highest achievable for VOIP).
- */
- if (r_value < 0) {
- pseudo_mos = 1.0;
- } else if (r_value > 100) {
- pseudo_mos = 4.5;
- } else {
- pseudo_mos = 1 + (0.035 * r_value) + (r_value * (r_value - 60) * (100 - r_value) * 0.0000007);
- }
-
- /*
- * We're going to rescale the 0.0->5.0 pseudo_mos to the 0.0->100.0 MES.
- * For those ranges, we could actually just multiply the pseudo_mos
- * by 20 but we may want to change the scale later.
- */
- mes = RESCALE(pseudo_mos, 0.0, 5.0, 0.0, 100.0);
-
- return mes;
-}
-
-/*!
- * \internal
- * \brief Update MES stats based on info received in an SR or RR.
- * This is RTP we sent and they received.
- */
-static void update_reported_mes_stats(struct ast_rtp *rtp)
-{
- double mes = calc_media_experience_score(rtp->owner,
- rtp->rtcp->normdevrtt,
- rtp->rtcp->reported_jitter,
- rtp->rtcp->reported_stdev_jitter,
- rtp->rtcp->reported_normdev_lost);
-
- rtp->rtcp->reported_mes = mes;
- if (rtp->rtcp->reported_mes_count == 0) {
- rtp->rtcp->reported_minmes = mes;
- }
- if (mes < rtp->rtcp->reported_minmes) {
- rtp->rtcp->reported_minmes = mes;
- }
- if (mes > rtp->rtcp->reported_maxmes) {
- rtp->rtcp->reported_maxmes = mes;
- }
-
- calc_mean_and_standard_deviation(mes, &rtp->rtcp->reported_normdev_mes,
- &rtp->rtcp->reported_stdev_mes, &rtp->rtcp->reported_mes_count);
-
- ast_debug_rtcp(2, "%s: rtt: %.9f j: %.9f sjh: %.9f lost: %.9f mes: %4.1f\n",
- ast_rtp_instance_get_channel_id(rtp->owner),
- rtp->rtcp->normdevrtt,
- rtp->rtcp->reported_jitter,
- rtp->rtcp->reported_stdev_jitter,
- rtp->rtcp->reported_normdev_lost, mes);
-}
-
-/*!
- * \internal
- * \brief Update MES stats based on info we will send in an SR or RR.
- * This is RTP they sent and we received.
- */
-static void update_local_mes_stats(struct ast_rtp *rtp)
-{
- rtp->rxmes = calc_media_experience_score(rtp->owner,
- rtp->rtcp->normdevrtt,
- rtp->rxjitter,
- rtp->rtcp->stdev_rxjitter,
- rtp->rtcp->normdev_rxlost);
-
- if (rtp->rtcp->rxmes_count == 0) {
- rtp->rtcp->minrxmes = rtp->rxmes;
- }
- if (rtp->rxmes < rtp->rtcp->minrxmes) {
- rtp->rtcp->minrxmes = rtp->rxmes;
- }
- if (rtp->rxmes > rtp->rtcp->maxrxmes) {
- rtp->rtcp->maxrxmes = rtp->rxmes;
- }
-
- calc_mean_and_standard_deviation(rtp->rxmes, &rtp->rtcp->normdev_rxmes,
- &rtp->rtcp->stdev_rxmes, &rtp->rtcp->rxmes_count);
-
- ast_debug_rtcp(2, " %s: rtt: %.9f j: %.9f sjh: %.9f lost: %.9f mes: %4.1f\n",
- ast_rtp_instance_get_channel_id(rtp->owner),
- rtp->rtcp->normdevrtt,
- rtp->rxjitter,
- rtp->rtcp->stdev_rxjitter,
- rtp->rtcp->normdev_rxlost, rtp->rxmes);
-}
-
/*! \pre instance is locked */
static struct ast_rtp_instance *__rtp_find_instance_by_ssrc(struct ast_rtp_instance *instance,
struct ast_rtp *rtp, unsigned int ssrc, int source)
packetwords = len / 4;
- ast_debug_rtcp(2, "(%s) RTCP got report of %d bytes from %s\n",
- ast_rtp_instance_get_channel_id(instance),
- len, ast_sockaddr_stringify(addr));
+ ast_debug_rtcp(1, "(%p) RTCP got report of %d bytes from %s\n",
+ instance, len, ast_sockaddr_stringify(addr));
/*
* Validate the RTCP packet according to an adapted and slightly
* modified RFC3550 validation algorithm.
*/
if (packetwords < RTCP_HEADER_SSRC_LENGTH) {
- ast_debug_rtcp(2, "(%s) RTCP %p -- from %s: Frame size (%u words) is too short\n",
- ast_rtp_instance_get_channel_id(instance),
- transport_rtp, ast_sockaddr_stringify(addr), packetwords);
+ ast_debug_rtcp(1, "(%p) RTCP %p -- from %s: Frame size (%u words) is too short\n",
+ instance, transport_rtp, ast_sockaddr_stringify(addr), packetwords);
return &ast_null_frame;
}
position = 0;
first_word = ntohl(rtcpheader[position]);
if ((first_word & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
- ast_debug_rtcp(2, "(%s) RTCP %p -- from %s: Failed first packet validity check\n",
- ast_rtp_instance_get_channel_id(instance),
- transport_rtp, ast_sockaddr_stringify(addr));
+ ast_debug_rtcp(1, "(%p) RTCP %p -- from %s: Failed first packet validity check\n",
+ instance, transport_rtp, ast_sockaddr_stringify(addr));
return &ast_null_frame;
}
do {
first_word = ntohl(rtcpheader[position]);
} while ((first_word & RTCP_VERSION_MASK_SHIFTED) == RTCP_VERSION_SHIFTED);
if (position != packetwords) {
- ast_debug_rtcp(2, "(%s) RTCP %p -- from %s: Failed packet version or length check\n",
- ast_rtp_instance_get_channel_id(instance),
- transport_rtp, ast_sockaddr_stringify(addr));
+ ast_debug_rtcp(1, "(%p) RTCP %p -- from %s: Failed packet version or length check\n",
+ instance, transport_rtp, ast_sockaddr_stringify(addr));
return &ast_null_frame;
}
report_block->ia_jitter = ntohl(rtcpheader[i + 3]);
report_block->lsr = ntohl(rtcpheader[i + 4]);
report_block->dlsr = ntohl(rtcpheader[i + 5]);
- if (report_block->lsr) {
- int skewed = update_rtt_stats(rtp, report_block->lsr, report_block->dlsr);
- if (skewed && rtcp_debug_test_addr(addr)) {
- struct timeval now;
- unsigned int lsr_now, lsw, msw;
- gettimeofday(&now, NULL);
- timeval2ntp(now, &msw, &lsw);
- lsr_now = (((msw & 0xffff) << 16) | ((lsw & 0xffff0000) >> 16));
- ast_verbose("Internal RTCP NTP clock skew detected: "
- "lsr=%u, now=%u, dlsr=%u (%u:%03ums), "
+ if (report_block->lsr
+ && update_rtt_stats(rtp, report_block->lsr, report_block->dlsr)
+ && rtcp_debug_test_addr(addr)) {
+ struct timeval now;
+ unsigned int lsr_now, lsw, msw;
+ gettimeofday(&now, NULL);
+ timeval2ntp(now, &msw, &lsw);
+ lsr_now = (((msw & 0xffff) << 16) | ((lsw & 0xffff0000) >> 16));
+ ast_verbose("Internal RTCP NTP clock skew detected: "
+ "lsr=%u, now=%u, dlsr=%u (%u:%03ums), "
"diff=%u\n",
report_block->lsr, lsr_now, report_block->dlsr, report_block->dlsr / 65536,
(report_block->dlsr % 65536) * 1000 / 65536,
report_block->dlsr - (lsr_now - report_block->lsr));
- }
}
update_jitter_stats(rtp, report_block->ia_jitter);
update_lost_stats(rtp, report_block->lost_count.packets);
- /*
- * update_reported_mes_stats must be called AFTER
- * update_rtt_stats, update_jitter_stats and
- * update_lost_stats.
- */
- update_reported_mes_stats(rtp);
if (rtcp_debug_test_addr(addr)) {
- int rate = ast_rtp_get_rate(rtp->f.subclass.format);
-
ast_verbose(" Fraction lost: %d\n", report_block->lost_count.fraction);
ast_verbose(" Packets lost so far: %u\n", report_block->lost_count.packets);
ast_verbose(" Highest sequence number: %u\n", report_block->highest_seq_no & 0x0000ffff);
ast_verbose(" Sequence number cycles: %u\n", report_block->highest_seq_no >> 16);
- ast_verbose(" Interarrival jitter (samp): %u\n", report_block->ia_jitter);
- ast_verbose(" Interarrival jitter (secs): %.6f\n", ast_samp2sec(report_block->ia_jitter, rate));
+ ast_verbose(" Interarrival jitter: %u\n", report_block->ia_jitter);
ast_verbose(" Last SR(our NTP): %lu.%010lu\n",(unsigned long)(report_block->lsr) >> 16,((unsigned long)(report_block->lsr) << 16) * 4096);
ast_verbose(" DLSR: %4.4f (sec)\n",(double)report_block->dlsr / 65536.0);
ast_verbose(" RTT: %4.4f(sec)\n", rtp->rtcp->rtt);
- ast_verbose(" MES: %4.1f\n", rtp->rtcp->reported_mes);
}
}
/* If and when we handle more than one report block, this should occur outside
* this loop.
*/
- message_blob = ast_json_pack("{s: s, s: s, s: f, s: f}",
+ message_blob = ast_json_pack("{s: s, s: s, s: f}",
"from", ast_sockaddr_stringify(addr),
"to", transport_rtp->rtcp->local_addr_str,
- "rtt", rtp->rtcp->rtt,
- "mes", rtp->rtcp->reported_mes);
+ "rtt", rtp->rtcp->rtt);
ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_received_type(),
rtcp_report,
message_blob);
struct ast_frame *f;
/* Update statistics for jitter so they are correct in RTCP */
- calc_rxstamp_and_jitter(&rxtime, rtp, timestamp, mark);
-
+ calc_rxstamp(&rxtime, rtp, timestamp, mark);
/* When doing P2P we don't need to raise any frames about SSRC change to the core */
while ((f = AST_LIST_REMOVE_HEAD(&frames, frame_list)) != NULL) {
if (ast_format_cache_is_slinear(rtp->f.subclass.format)) {
ast_frame_byteswap_be(&rtp->f);
}
- calc_rxstamp_and_jitter(&rtp->f.delivery, rtp, timestamp, mark);
+ calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark);
/* Add timing data to let ast_generic_bridge() put the frame into a jitterbuf */
ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO);
rtp->f.ts = timestamp / (ast_rtp_get_rate(rtp->f.subclass.format) / 1000);
/* Video -- samples is # of samples vs. 90000 */
if (!rtp->lastividtimestamp)
rtp->lastividtimestamp = timestamp;
- calc_rxstamp_and_jitter(&rtp->f.delivery, rtp, timestamp, mark);
+ calc_rxstamp(&rtp->f.delivery, rtp, timestamp, mark);
ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO);
rtp->f.ts = timestamp / (ast_rtp_get_rate(rtp->f.subclass.format) / 1000);
rtp->f.samples = timestamp - rtp->lastividtimestamp;
bundled = (child || AST_VECTOR_SIZE(&rtp->ssrc_mapping)) ? 1 : 0;
prev_seqno = rtp->lastrxseqno;
- /* We need to save lastrxseqno for use by jitter before resetting it. */
- rtp->prevrxseqno = rtp->lastrxseqno;
rtp->lastrxseqno = seqno;
if (!rtp->recv_buffer) {
#endif
}
- ast_debug_rtcp(1, "(%s) RTCP setup on RTP instance\n",
- ast_rtp_instance_get_channel_id(instance));
+ ast_debug_rtcp(1, "(%p) RTCP setup on RTP instance\n", instance);
} else {
if (rtp->rtcp) {
if (rtp->rtcp->schedid > -1) {
ast_free(rtp->rtcp->local_addr_str);
ast_free(rtp->rtcp);
rtp->rtcp = NULL;
- ast_debug_rtcp(1, "(%s) RTCP torn down on RTP instance\n",
- ast_rtp_instance_get_channel_id(instance));
}
}
} else if (property == AST_RTP_PROPERTY_ASYMMETRIC_CODEC) {
AST_RTP_STAT_TERMINATOR(AST_RTP_INSTANCE_STAT_COMBINED_LOSS);
AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_TXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->txjitter, rtp->rxjitter);
- AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_RXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->rxjitter, rtp->rtcp->reported_jitter);
+ AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_RXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->rxjitter, rtp->rtcp->reported_jitter / (unsigned int) 65536.0);
AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MAXJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->remote_maxjitter, rtp->rtcp->reported_maxjitter);
AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MINJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->remote_minjitter, rtp->rtcp->reported_minjitter);
AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_NORMDEVJITTER, AST_RTP_INSTANCE_STAT_COMBINED_JITTER, stats->remote_normdevjitter, rtp->rtcp->reported_normdev_jitter);
AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_STDEVRTT, AST_RTP_INSTANCE_STAT_COMBINED_RTT, stats->stdevrtt, rtp->rtcp->stdevrtt);
AST_RTP_STAT_TERMINATOR(AST_RTP_INSTANCE_STAT_COMBINED_RTT);
- AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_TXMES, AST_RTP_INSTANCE_STAT_COMBINED_MES, stats->txmes, rtp->rxmes);
- AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_RXMES, AST_RTP_INSTANCE_STAT_COMBINED_MES, stats->rxmes, rtp->rtcp->reported_mes);
- AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MAXMES, AST_RTP_INSTANCE_STAT_COMBINED_MES, stats->remote_maxmes, rtp->rtcp->reported_maxmes);
- AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_MINMES, AST_RTP_INSTANCE_STAT_COMBINED_MES, stats->remote_minmes, rtp->rtcp->reported_minmes);
- AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_NORMDEVMES, AST_RTP_INSTANCE_STAT_COMBINED_MES, stats->remote_normdevmes, rtp->rtcp->reported_normdev_mes);
- AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_STDEVMES, AST_RTP_INSTANCE_STAT_COMBINED_MES, stats->remote_stdevmes, rtp->rtcp->reported_stdev_mes);
- AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_MAXMES, AST_RTP_INSTANCE_STAT_COMBINED_MES, stats->local_maxmes, rtp->rtcp->maxrxmes);
- AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_MINMES, AST_RTP_INSTANCE_STAT_COMBINED_MES, stats->local_minmes, rtp->rtcp->minrxmes);
- AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_NORMDEVMES, AST_RTP_INSTANCE_STAT_COMBINED_MES, stats->local_normdevmes, rtp->rtcp->normdev_rxmes);
- AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_STDEVMES, AST_RTP_INSTANCE_STAT_COMBINED_MES, stats->local_stdevmes, rtp->rtcp->stdev_rxjitter);
- AST_RTP_STAT_TERMINATOR(AST_RTP_INSTANCE_STAT_COMBINED_MES);
-
-
AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_LOCAL_SSRC, -1, stats->local_ssrc, rtp->ssrc);
AST_RTP_STAT_SET(AST_RTP_INSTANCE_STAT_REMOTE_SSRC, -1, stats->remote_ssrc, rtp->themssrc);
AST_RTP_STAT_STRCPY(AST_RTP_INSTANCE_STAT_CHANNEL_UNIQUEID, -1, stats->channel_uniqueid, ast_rtp_instance_get_channel_id(instance));
}
ao2_lock(instance);
#endif
- ast_debug_rtp(1, "(%s) RTP Stop\n",
- ast_rtp_instance_get_channel_id(instance));
if (rtp->rtcp && rtp->rtcp->schedid > -1) {
ao2_unlock(instance);
#include "asterisk/rtp_engine.h"
#include "asterisk/data_buffer.h"
#include "asterisk/format_cache.h"
-#include <assert.h>
-#include <sched.h>
enum test_type {
TEST_TYPE_NONE = 0, /* No special setup required */
TEST_TYPE_NACK, /* Enable NACK */
TEST_TYPE_REMB, /* Enable REMB */
- TEST_TYPE_STD_RTCP, /* Let the stack do RTCP */
};
static void ast_sched_context_destroy_wrapper(struct ast_sched_context *sched)
struct ast_rtp_instance **instance2, struct ast_sched_context *test_sched,
enum test_type type)
{
- struct ast_sockaddr addr1;
- struct ast_sockaddr addr2;
- enum ast_rtp_instance_rtcp rtcp_type = AST_RTP_INSTANCE_RTCP_MUX;
+ struct ast_sockaddr addr;
- ast_sockaddr_parse(&addr1, "127.0.0.1", 0);
- ast_sockaddr_parse(&addr2, "127.0.0.1", 0);
+ ast_sockaddr_parse(&addr, "127.0.0.1", 0);
- *instance1 = ast_rtp_instance_new("asterisk", test_sched, &addr1, "instance1");
- *instance2 = ast_rtp_instance_new("asterisk", test_sched, &addr2, "instance2");
+ *instance1 = ast_rtp_instance_new("asterisk", test_sched, &addr, NULL);
+ *instance2 = ast_rtp_instance_new("asterisk", test_sched, &addr, NULL);
if (!instance1 || !instance2) {
return -1;
}
- ast_rtp_instance_set_channel_id(*instance1, "instance1");
- ast_rtp_instance_set_channel_id(*instance2, "instance2");
-
- if (type == TEST_TYPE_STD_RTCP) {
- rtcp_type = AST_RTP_INSTANCE_RTCP_STANDARD;
- }
-
- ast_rtp_instance_set_prop(*instance1,
- AST_RTP_PROPERTY_RTCP, rtcp_type);
- ast_rtp_instance_set_prop(*instance2,
- AST_RTP_PROPERTY_RTCP, rtcp_type);
+ ast_rtp_instance_set_prop(*instance1, AST_RTP_PROPERTY_RTCP, AST_RTP_INSTANCE_RTCP_MUX);
+ ast_rtp_instance_set_prop(*instance2, AST_RTP_PROPERTY_RTCP, AST_RTP_INSTANCE_RTCP_MUX);
if (type == TEST_TYPE_NACK) {
ast_rtp_instance_set_prop(*instance1, AST_RTP_PROPERTY_RETRANS_RECV, 1);
ast_rtp_instance_set_prop(*instance2, AST_RTP_PROPERTY_REMB, 1);
}
- ast_rtp_instance_get_local_address(*instance1, &addr1);
- ast_rtp_instance_set_remote_address(*instance2, &addr1);
+ ast_rtp_instance_get_local_address(*instance1, &addr);
+ ast_rtp_instance_set_remote_address(*instance2, &addr);
- ast_rtp_instance_get_local_address(*instance2, &addr2);
- ast_rtp_instance_set_remote_address(*instance1, &addr2);
+ ast_rtp_instance_get_local_address(*instance2, &addr);
+ ast_rtp_instance_set_remote_address(*instance1, &addr);
ast_rtp_instance_reset_test_engine(*instance1);
test_read_frames(instance2, num);
}
-
-/*
- * Unfortunately, we can't use usleep() to create
- * packet spacing because there are signals in use
- * which cause usleep to immediately return. Instead
- * we have to spin. :(
- */
-static void SLEEP_SPINNER(int ms)
-{
- struct timeval a = ast_tvnow();
-
- while(1) {
- sched_yield();
- if (ast_remaining_ms(a, ms) <= 0) {
- break;
- }
- }
-}
-
-/*
- * This function is NOT really a reliable implementation.
- * Its purpose is only to aid in code development in res_rtp_asterisk.
- */
-static void test_write_and_read_interleaved_frames(struct ast_rtp_instance *instance1,
- struct ast_rtp_instance *instance2, int howlong, int rtcp_interval)
-{
- char data[320] = "";
- int pktinterval = 20;
-
- struct ast_frame frame_out1 = {
- .frametype = AST_FRAME_VOICE,
- .subclass.format = ast_format_ulaw,
- .seqno = 4556,
- .data.ptr = data,
- .datalen = 160,
- .samples = 1,
- .len = pktinterval,
- .ts = 4622295,
- };
- struct ast_frame frame_out2 = {
- .frametype = AST_FRAME_VOICE,
- .subclass.format = ast_format_ulaw,
- .seqno = 6554,
- .data.ptr = data,
- .datalen = 160,
- .samples = 1,
- .len = pktinterval,
- .ts = 8622295,
- };
- struct ast_frame *frame_in1;
- struct ast_frame *frame_in2;
- int index;
- int num;
- int rtcpnum;
- int reverse = 1;
- int send_rtcp = 0;
-
- num = howlong / pktinterval;
-
- rtcpnum = rtcp_interval / pktinterval;
-
- ast_set_flag(&frame_out1, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
- ast_set_flag(&frame_out1, AST_FRFLAG_HAS_TIMING_INFO);
- ast_set_flag(&frame_out2, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
- ast_set_flag(&frame_out2, AST_FRFLAG_HAS_TIMING_INFO);
-
- for (index = 0; index < num; index++) {
- struct timeval start = ast_tvnow();
- time_t ms;
-
- if (index == 1) {
- ast_clear_flag(&frame_out1, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
- ast_clear_flag(&frame_out1, AST_FRFLAG_HAS_TIMING_INFO);
- ast_clear_flag(&frame_out2, AST_FRFLAG_HAS_SEQUENCE_NUMBER);
- ast_clear_flag(&frame_out2, AST_FRFLAG_HAS_TIMING_INFO);
- }
- frame_out1.seqno += index;
- frame_out1.delivery = start;
- frame_out1.ts += frame_out1.len;
- ast_rtp_instance_write(instance1, &frame_out1);
-
- if (send_rtcp && index && (index % rtcpnum == 0)) {
- ast_rtp_instance_queue_report(instance1);
- }
-
- frame_in2 = ast_rtp_instance_read(instance2, 0);
- ast_frfree(frame_in2);
- frame_in2 = ast_rtp_instance_read(instance2, 1);
- ast_frfree(frame_in2);
-
- if (reverse) {
- frame_out2.seqno += index;
- frame_out2.delivery = ast_tvnow();
- frame_out2.ts += frame_out2.len;
- ast_rtp_instance_write(instance2, &frame_out2);
-
- if (send_rtcp && index && (index % rtcpnum == 0)) {
- ast_rtp_instance_queue_report(instance2);
- }
-
- frame_in1 = ast_rtp_instance_read(instance1, 0);
- ast_frfree(frame_in1);
- frame_in1 = ast_rtp_instance_read(instance1, 1);
- ast_frfree(frame_in1);
-
- }
-
- ms = frame_out1.len - ast_tvdiff_ms(ast_tvnow(),start);
- ms += (index % 2 ? 5 : 12);
- ms += (index % 3 ? 2 : 30);
- SLEEP_SPINNER(ms);
- }
-}
-
AST_TEST_DEFINE(nack_no_packet_loss)
{
RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
return AST_TEST_PASS;
}
-/*
- * This test should not normally be run. Its only purpose is to
- * aid in code development.
- */
-AST_TEST_DEFINE(mes)
-{
- RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ast_rtp_instance_destroy);
- RAII_VAR(struct ast_rtp_instance *, instance2, NULL, ast_rtp_instance_destroy);
- RAII_VAR(struct ast_sched_context *, test_sched, NULL, ast_sched_context_destroy_wrapper);
-
- switch (cmd) {
- case TEST_INIT:
- info->name = "mes";
- info->category = "/res/res_rtp/";
- info->summary = "Media Experience Score";
- info->description =
- "Tests calculation of Media Experience Score (only run by explicit request)";
- info->explicit_only = 1;
- return AST_TEST_NOT_RUN;
- case TEST_EXECUTE:
- break;
- }
-
- test_sched = ast_sched_context_create();
- ast_sched_start_thread(test_sched);
-
- if ((test_init_rtp_instances(&instance1, &instance2,
- test_sched, TEST_TYPE_NONE)) < 0) {
- ast_log(LOG_ERROR, "Failed to initialize test!\n");
- return AST_TEST_FAIL;
- }
-
- test_write_and_read_interleaved_frames(
- instance1, instance2, 1000, 5000);
-
- return AST_TEST_PASS;
-}
-
static int unload_module(void)
{
- AST_TEST_UNREGISTER(mes);
AST_TEST_UNREGISTER(nack_no_packet_loss);
AST_TEST_UNREGISTER(nack_nominal);
AST_TEST_UNREGISTER(nack_overflow);
AST_TEST_REGISTER(remb_nominal);
AST_TEST_REGISTER(sr_rr_nominal);
AST_TEST_REGISTER(fir_nominal);
- AST_TEST_REGISTER(mes);
return AST_MODULE_LOAD_SUCCESS;
}