TT#12800 calculate MOS values from RTCP RR

Change-Id: Ie4ba70ae8290f497a4a5d756a369911b96205299
changes/03/12603/15
Richard Fuchs 8 years ago
parent 0f285899e0
commit 84e76e7cba

@ -1232,6 +1232,8 @@ loop_ok:
if (media->protocol && media->protocol->rtp && !rtcp && !rtp_payload(&rtp_h, NULL, s)) {
i = (rtp_h->m_pt & 0x7f);
// XXX two hash table lookups for each packet, not ideal
// XXX limit size of hash tables
rtp_s = g_hash_table_lookup(stream->rtp_stats, &i);
if (!rtp_s) {
ilog(LOG_WARNING | LOG_FLAG_LIMIT,
@ -1243,6 +1245,9 @@ loop_ok:
else {
atomic64_inc(&rtp_s->packets);
atomic64_add(&rtp_s->bytes, s->len);
struct ssrc_entry *se = get_ssrc(ntohl(rtp_h->ssrc), call->ssrc_hash);
se->payload_type = i;
}
}

@ -227,6 +227,7 @@ struct rtcp_chain_element {
struct rtcp_process_ctx {
// input
struct call *call;
struct call_media *media;
const struct timeval *received;
// handler vars
@ -616,6 +617,7 @@ static int __rtcp_parse(GQueue *q, const str *_s, struct stream_fd *sfd, const e
rtcp_handler_func func;
str s = *_s;
struct call *c = sfd->call;
struct call_media *m = sfd->stream->media;
struct rtcp_process_ctx log_ctx_s,
*log_ctx;
unsigned int len;
@ -624,6 +626,7 @@ static int __rtcp_parse(GQueue *q, const str *_s, struct stream_fd *sfd, const e
ZERO(log_ctx_s);
log_ctx_s.call = c;
log_ctx_s.media = m;
log_ctx_s.received = tv;
log_ctx = &log_ctx_s;
@ -1162,10 +1165,10 @@ static void logging_destroy(struct rtcp_process_ctx *ctx) {
static void mos_sr(struct rtcp_process_ctx *ctx, const struct sender_report_packet *sr) {
ssrc_sender_report(ctx->call, &ctx->scratch.sr, ctx->received);
ssrc_sender_report(ctx->media, &ctx->scratch.sr, ctx->received);
}
static void mos_rr(struct rtcp_process_ctx *ctx, const struct report_block *rr) {
ssrc_receiver_report(ctx->call, &ctx->scratch.rr, ctx->received);
ssrc_receiver_report(ctx->media, &ctx->scratch.rr, ctx->received);
}

@ -2,6 +2,7 @@
#include <glib.h>
#include "aux.h"
#include "call.h"
#include "rtplib.h"
@ -10,6 +11,7 @@ static struct ssrc_entry *create_ssrc_entry(u_int32_t ssrc) {
ent = g_slice_alloc0(sizeof(struct ssrc_entry));
ent->ssrc = ssrc;
mutex_init(&ent->lock);
ent->payload_type = -1;
return ent;
}
static void add_ssrc_entry(struct ssrc_entry *ent, struct ssrc_hash *ht) {
@ -19,12 +21,30 @@ static void free_sender_report(void *p) {
struct ssrc_sender_report_item *i = p;
g_slice_free1(sizeof(*i), i);
}
static void free_stats_block(void *p) {
struct ssrc_stats_block *ssb = p;
g_slice_free1(sizeof(*ssb), ssb);
}
static void free_ssrc_entry(void *p) {
struct ssrc_entry *e = p;
g_queue_clear_full(&e->sender_reports, free_sender_report);
g_queue_clear_full(&e->stats_blocks, free_stats_block);
g_slice_free1(sizeof(*e), e);
}
// returned as mos * 10 (i.e. 10 - 50 for 1.0 to 5.0)
static void mos_calc(struct ssrc_stats_block *ssb) {
// as per https://www.pingman.com/kb/article/how-is-mos-calculated-in-pingplotter-pro-50.html
int eff_rtt = ssb->rtt / 1000 + ssb->jitter * 2 + 10;
double r;
if (eff_rtt < 160)
r = 93.2 - eff_rtt / 40.0;
else
r = 93.2 - (eff_rtt - 120) / 40.0;
r = r - (ssb->packetloss * 2.5);
double mos = 1.0 + (0.035) * r + (.000007) * r * (r-60) * (100-r);
ssb->mos = mos * 10;
}
struct ssrc_entry *find_ssrc(u_int32_t ssrc, struct ssrc_hash *ht) {
rwlock_lock_r(&ht->lock);
@ -82,9 +102,10 @@ struct ssrc_ctx *get_ssrc_ctx(u_int32_t ssrc, struct ssrc_hash *ht, enum ssrc_di
void ssrc_sender_report(struct call *c, const struct ssrc_sender_report *sr,
void ssrc_sender_report(struct call_media *m, const struct ssrc_sender_report *sr,
const struct timeval *tv)
{
struct call *c = m->call;
struct ssrc_entry *e;
struct ssrc_sender_report_item *seri;
@ -102,12 +123,16 @@ void ssrc_sender_report(struct call *c, const struct ssrc_sender_report *sr,
mutex_lock(&e->lock);
g_queue_push_tail(&e->sender_reports, seri);
while (e->sender_reports.length > 10)
free_sender_report(g_queue_pop_head(&e->sender_reports));
mutex_unlock(&e->lock);
}
void ssrc_receiver_report(struct call *c, const struct ssrc_receiver_report *rr,
void ssrc_receiver_report(struct call_media *m, const struct ssrc_receiver_report *rr,
const struct timeval *tv)
{
struct call *c = m->call;
ilog(LOG_DEBUG, "RR from %u about %u: FL %u TL %u HSR %u J %u LSR %u DLSR %u",
rr->from, rr->ssrc, rr->fraction_lost, rr->packets_lost,
rr->high_seq_received, rr->jitter, rr->lsr, rr->dlsr);
@ -116,18 +141,77 @@ void ssrc_receiver_report(struct call *c, const struct ssrc_receiver_report *rr,
return; // no delay to be known
struct ssrc_entry *e = get_ssrc(rr->ssrc, c->ssrc_hash);
struct ssrc_sender_report_item *seri;
mutex_lock(&e->lock);
// go through the list backwards until we find the SR referenced, up to 10 steps
int i = 0;
for (GList *l = e->sender_reports.tail;
l && i < 10;
l = l->prev, i++)
{
struct ssrc_sender_report_item *seri = l->data;
// go through the list backwards until we find the SR referenced
for (GList *l = e->sender_reports.tail; l; l = l->prev) {
seri = l->data;
if (seri->ntp_middle_bits != rr->lsr)
continue;
ilog(LOG_DEBUG, "RR from %u reports delay %u from %u", rr->from, rr->dlsr, rr->ssrc);
break;
goto found;
}
// not found
goto out;
found:
// `e` remains locked for access to `seri`
ilog(LOG_DEBUG, "RR from %u reports delay %u from %u", rr->from, rr->dlsr, rr->ssrc);
long long rtt = timeval_diff(tv, &seri->received);
mutex_unlock(&e->lock);
rtt -= (long long) rr->dlsr * 1000000LL / 65536LL;
ilog(LOG_DEBUG, "Calculated round-trip time for %u is %lli us", rr->ssrc, rtt);
if (rtt <= 0 || rtt > 10000000) {
ilog(LOG_DEBUG, "Invalid RTT - discarding");
goto out_nl;
}
e->last_rtt = rtt;
struct ssrc_entry *other_e = get_ssrc(rr->from, c->ssrc_hash);
// determine the clock rate for jitter values
int pt = e->payload_type;
if (pt < 0) {
pt = other_e->payload_type;
if (pt < 0) {
ilog(LOG_DEBUG, "No payload type known for RTCP RR, discarding");
goto out_nl;
}
}
const struct rtp_payload_type *rpt = rtp_payload_type(pt, m->rtp_payload_types);
if (!rpt) {
ilog(LOG_INFO, "Invalid RTP payload type %i, discarding RTCP RR", pt);
goto out_nl;
}
unsigned int jitter = rpt->clock_rate ? (rr->jitter * 1000 / rpt->clock_rate) : rr->jitter;
ilog(LOG_DEBUG, "Calculated jitter for %u is %u ms", rr->ssrc, jitter);
ilog(LOG_DEBUG, "Adding opposide side RTT of %u us", other_e->last_rtt);
struct ssrc_stats_block *ssb = g_slice_alloc(sizeof(*ssb));
*ssb = (struct ssrc_stats_block) {
.jitter = jitter,
.rtt = rtt + other_e->last_rtt,
.reported = *tv,
.packetloss = (unsigned int) rr->fraction_lost * 100 / 256,
};
mos_calc(ssb);
ilog(LOG_DEBUG, "Calculated MOS from RR for %u is %.1f", rr->from, (double) ssb->mos / 10.0);
// got a new stats block, add it to reporting ssrc
e = get_ssrc(rr->from, c->ssrc_hash);
mutex_lock(&e->lock);
g_queue_push_tail(&e->stats_blocks, ssb);
goto out;
out:
mutex_unlock(&e->lock);
out_nl:
;
}

@ -11,7 +11,9 @@
struct call;
struct call_media;
struct timeval;
struct rtp_payload_type;
@ -29,13 +31,24 @@ struct ssrc_entry {
u_int32_t ssrc;
struct ssrc_ctx input_ctx,
output_ctx;
GQueue sender_reports;
GQueue sender_reports; // as received via RTCP
GQueue stats_blocks; // calculated
int payload_type; // to determine the clock rate for jitter calculations
unsigned int last_rtt; // last calculated raw rtt without rtt from opposide side
};
enum ssrc_dir {
SSRC_DIR_INPUT = G_STRUCT_OFFSET(struct ssrc_entry, input_ctx),
SSRC_DIR_OUTPUT = G_STRUCT_OFFSET(struct ssrc_entry, output_ctx),
};
struct ssrc_stats_block {
struct timeval reported;
unsigned int jitter; // ms
unsigned int rtt; // us - combined from both sides
unsigned int packetloss; // percent
int mos; // nominal range of 10 - 50 for MOS values 1.0 to 5.0
};
struct ssrc_sender_report {
u_int32_t ssrc;
u_int32_t ntp_msw;
@ -102,8 +115,8 @@ struct ssrc_entry *get_ssrc(u_int32_t, struct ssrc_hash * /* , int *created */);
struct ssrc_ctx *get_ssrc_ctx(u_int32_t, struct ssrc_hash *, enum ssrc_dir); // creates new entry if not found
void ssrc_sender_report(struct call *, const struct ssrc_sender_report *, const struct timeval *);
void ssrc_receiver_report(struct call *, const struct ssrc_receiver_report *,
void ssrc_sender_report(struct call_media *, const struct ssrc_sender_report *, const struct timeval *);
void ssrc_receiver_report(struct call_media *, const struct ssrc_receiver_report *,
const struct timeval *);

Loading…
Cancel
Save