MT#61263 update MOS formula

Add support for simplified G.107 formula, with math changed to
fixed-point. Retain legacy formula as an option for backwards
compatibility.

Change-Id: I999fc7de7be1407876c201c251538cea72b04008
pull/1872/head^2
Richard Fuchs 6 months ago
parent 50b1d03e61
commit ef4e548dd8

@ -1039,6 +1039,8 @@ static void options(int *argc, char ***argv) {
#ifdef WITH_TRANSCODING
else if (!strcasecmp(mos, "legacy"))
rtpe_config.common.mos_type = MOS_LEGACY;
else if (!strcasecmp(mos, "g107") || !strcasecmp(mos, "g.107"))
rtpe_config.common.mos_type = MOS_LEGACY;
#endif
else
die("Invalid --mos option ('%s')", mos);

@ -12,7 +12,10 @@ typedef void mos_calc_fn(struct ssrc_stats_block *ssb);
static mos_calc_fn mos_calc_legacy;
#ifdef WITH_TRANSCODING
static mos_calc_fn mos_calc_nb;
static mos_calc_fn *mos_calcs[__MOS_TYPES] = {
[MOS_NB] = mos_calc_nb,
[MOS_LEGACY] = mos_calc_legacy,
};
#endif
@ -75,6 +78,60 @@ static void ssrc_entry_put(void *ep) {
obj_put(&e->h);
}
#ifdef WITH_TRANSCODING
// returned as mos * 10 (i.e. 10 - 50 for 1.0 to 5.0)
static int64_t mos_from_rx(int64_t Rx) {
// Rx in e5
int64_t intmos;
if (Rx < 0)
intmos = 10; // e1
else if (Rx > 10000000) // e5
intmos = 45; // e1
else {
Rx /= 100; // e5 -> e3
intmos = 100; // e2
intmos += 35 * Rx / 10000; // e2
int64_t RxRx = (Rx - 60000) * (100000 - Rx); // e6
RxRx /= 1000; // e6 -> e3
RxRx = Rx * RxRx; // e6
RxRx /= 1000; // e6 -> e3
RxRx *= 7; // e9
RxRx /= 10000000; // e9 -> e2
intmos += RxRx; // e2
intmos /= 10; // e2 -> e1
if (intmos < 10)
intmos = 10;
}
return intmos;
}
static void mos_calc_nb(struct ssrc_stats_block *ssb) {
uint64_t rtt = ssb->rtt;
if (rtpe_config.mos == MOS_CQ && !rtt)
return; // can not compute the MOS-CQ unless we have a valid RTT
else if (rtpe_config.mos == MOS_LQ)
rtt = 0; // ignore RTT
// G.107 simplified, original formula in milliseconds (e0)
rtt /= 2;
rtt += ssb->jitter * 1000; // ms -> us, e0 -> e3
uint64_t Id = (24 * rtt) / 1000; // e3
if (rtt > 177300)
Id += ((rtt - 177300) * 11) / 100; // e3
uint64_t r_factor = 0;
if (ssb->packetloss <= 93)
r_factor = 9320 - ssb->packetloss * 100; // e2
int64_t Rx = 18 * r_factor * r_factor; // e6
Rx /= 10; // e6 -> e5
Rx -= 279 * r_factor * 100; // e5
Rx += 112662000; // e5
Rx -= Id * 100; // e5
ssb->mos = mos_from_rx(Rx);
}
#endif
// returned as mos * 10 (i.e. 10 - 50 for 1.0 to 5.0)
static void mos_calc_legacy(struct ssrc_stats_block *ssb) {
uint64_t rtt = ssb->rtt;
@ -431,10 +488,13 @@ void ssrc_receiver_report(struct call_media *m, stream_fd *sfd, const struct ssr
RTPE_SAMPLE_SFD(rtt_dsct, rtt, sfd);
RTPE_SAMPLE_SFD(packetloss, ssb->packetloss, sfd);
mos_calc_fn *mos_calc = mos_calc_legacy;
mos_calc_fn *mos_calc;
#ifdef WITH_TRANSCODING
mos_calc = mos_calc_nb;
if (rpt->codec_def)
mos_calc = mos_calcs[rpt->codec_def->mos_type];
#else
mos_calc = mos_calc_legacy;
#endif
other_e->packets_lost = rr->packets_lost;

@ -1300,12 +1300,23 @@ call to inject-DTMF won't be sent to __\-\-dtmf-log-dest=__ or __\-\-listen-tcp-
with stats for that call media every *interval* milliseconds, plus one message
every *interval* milliseconds with global stats.
- __\-\-mos=CQ__\|__LQ__
MOS (Mean Opinion Score) calculation formula. Defaults to __CQ__ (conversational
quality) which takes RTT into account and therefore requires peers to correctly
send RTCP. If set to __LQ__ (listening quality) RTT is ignored, allowing a MOS to
be calculated in the absence of RTCP.
- __\-\-mos=CQ__\|__LQ__\|__G.107__\|__legacy__
Options influencing the MOS (Mean Opinion Score) calculation formula.
Multiple options can be listed, using multiple __\-\-mos=...__ arguments at
the command line, or using a semicolon-separated list in a single
__mos=...__ line in the config file.
__CQ__ and __LQ__ are mutually exclusive and only one of them can be in
effect. Defaults to __CQ__ (conversational quality) which takes RTT into
account and therefore requires peers to correctly send RTCP. If set to
__LQ__ (listening quality) RTT is ignored, allowing a MOS to be calculated
in the absence of RTCP.
The remaining options select a MOS formula and are mutually exclusive. The
default is __G.107__, which uses a simplified version of the G.107 formula.
The previous default (and only option) was __legacy__, which uses a custom
formula which yields slightly higher MOS values than G.107.
- __\-\-measure-rtp__

@ -199,7 +199,8 @@ struct codec_def_s {
const enum media_type media_type;
const str silence_pattern;
enum {
MOS_LEGACY = 0, // default
MOS_NB = 0, // default
MOS_LEGACY,
__MOS_TYPES
} mos_type;

@ -82,14 +82,14 @@ $resp = rtpe_req('delete', 'MOS basic', { });
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{samples}, '>=', 1, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{samples}, '<=', 3, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 44, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 43, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, '>=', 0, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, '<=', 1, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, 0, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, '>=', 1, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, '<=', 3, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 44, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 43, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, '>=', 0, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, '<=', 1, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, 0, 'metric matches';
@ -154,7 +154,8 @@ $resp = rtpe_req('delete', 'MOS PL', { });
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{samples}, '>=', 1, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{samples}, '<=', 3, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 41, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '>=', 35, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '<=', 36, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, '>=', 0, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, '<=', 1, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, '>=', 3, 'metric matches';
@ -162,7 +163,8 @@ cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, '<=', 4, 'metric
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, '>=', 1, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, '<=', 3, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 41, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '>=', 35, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '<=', 36, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, '>=', 0, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, '<=', 1, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, '>=', 3, 'metric matches';
@ -229,8 +231,8 @@ $resp = rtpe_req('delete', 'MOS very degraded', { });
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{samples}, '>=', 1, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{samples}, '<=', 4, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '>=', 35, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '<=', 36, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '>=', 27, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '<=', 28, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, '>=', 4, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, '<=', 12, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, '>=', 8, 'metric matches';
@ -238,8 +240,8 @@ cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, '<=', 9, 'metric
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, '>=', 1, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, '<=', 4, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '>=', 35, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '<=', 36, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '>=', 27, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '<=', 28, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, '>=', 4, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, '<=', 12, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, '>=', 8, 'metric matches';

@ -228,7 +228,7 @@ my $processing_us = 10000; # allow for 10 ms processing time
is $resp->{SSRC}{0x1234567}{'average MOS'}{samples}, 1, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 44, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 43, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, 0, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, 0, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '>=', 0, 'metric matches';
@ -237,7 +237,7 @@ cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '>=', 0,
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '<', $processing_us, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, 1, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 44, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 43, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, 0, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, 0, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '>=', 0, 'metric matches';
@ -389,8 +389,8 @@ snd($sock_bx, $port_ax, pack("CC n N NN N N N N N N N N N",
$resp = rtpe_req('delete', 'MOS degraded', { });
is $resp->{SSRC}{0x1234567}{'average MOS'}{samples}, 1, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '>=', 34, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '<=', 35, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '>=', 35, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, '<=', 36, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, 15, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, 3, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '>=', 250000, 'metric matches';
@ -399,8 +399,8 @@ cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '>=', 130
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '<', 130000 + $processing_us, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, 1, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '>=', 34, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '<=', 35, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '>=', 35, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, '<=', 36, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, 15, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, 3, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '>=', 250000, 'metric matches';
@ -551,7 +551,7 @@ snd($sock_bx, $port_ax, pack("CC n N NN N N N N N N N N N",
$resp = rtpe_req('delete', 'MOS very degraded', { });
is $resp->{SSRC}{0x1234567}{'average MOS'}{samples}, 1, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 24, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{MOS}, 29, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{jitter}, 20, 'metric matches';
is $resp->{SSRC}{0x1234567}{'average MOS'}{'packet loss'}, 5, 'metric matches';
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time'}, '>=', 400000, 'metric matches';
@ -560,7 +560,7 @@ cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '>=', 200
cmp_ok $resp->{SSRC}{0x1234567}{'average MOS'}{'round-trip time leg'}, '<', 200000 + $processing_us, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{samples}, 1, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 24, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{MOS}, 29, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{jitter}, 20, 'metric matches';
is $resp->{SSRC}{0x7654321}{'average MOS'}{'packet loss'}, 5, 'metric matches';
cmp_ok $resp->{SSRC}{0x7654321}{'average MOS'}{'round-trip time'}, '>=', 400000, 'metric matches';

Loading…
Cancel
Save