#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",
-                        stats.local_ssrc, stats.remote_ssrc, stats.rxploss, stats.rxjitter, stats.rxcount, stats.txjitter, stats.txcount, stats.txploss, stats.rtt);
+               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);
        } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_JITTER) {
-               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));
+               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);
        } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_LOSS) {
-               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));
+               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);
        } else if (field == AST_RTP_INSTANCE_STAT_FIELD_QUALITY_RTT) {
-               snprintf(buf, size, "minrtt=%f;maxrtt=%f;avgrtt=%f;stdevrtt=%f;", stats.minrtt, stats.maxrtt, stats.normdevrtt, stats.stdevrtt);
+               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);
        }
 
        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 */
-       unsigned int reported_jitter;   /*!< The contents of their last jitter entry in the RR */
+       double reported_jitter; /*!< The contents of their last jitter entry in the RR in seconds */
        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 * ast_rtp_get_rate(rtp->f.subclass.format));
+               report_block->ia_jitter = (unsigned int)rtp->rxjitter_samples;
                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: %.4f\n", (double)report_block->ia_jitter / ast_rtp_get_rate(rtp->f.subclass.format));
+                       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("    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}",
+       message_blob = ast_json_pack("{s: s, s: s, s: f}",
                        "to", ast_sockaddr_stringify(&remote_address),
-                       "from", rtp->rtcp->local_addr_str);
+                       "from", rtp->rtcp->local_addr_str,
+                       "mes", rtp->rxmes);
+
        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(1, "(%p) RTCP starting transmission\n", instance);
+                               ast_debug_rtcp(2, "(%s) RTCP starting transmission in %u ms\n",
+                                       ast_rtp_instance_get_channel_id(instance), ast_rtcp_calc_interval(rtp));
                                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, "(%p) RTP ooh, format changed from %s to %s\n",
-                       instance, ast_format_get_name(rtp->lasttxformat),
+               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_format_get_name(frame->subclass.format));
                ao2_replace(rtp->lasttxformat, format);
                if (rtp->smoother) {
        return 0;
 }
 
-static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int timestamp, int mark)
+static void calc_rxstamp_and_jitter(struct timeval *tv,
+       struct ast_rtp *rtp, unsigned int rx_rtp_ts,
+       int mark)
 {
-       struct timeval now;
-       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 ((!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;
-       }
+       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);
-       /* 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->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;
+
+       if (d < 0) {
+               d = -d;
+       }
+
+       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.
+        */
        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)
 {
-       double reported_jitter;
+       int rate = ast_rtp_get_rate(rtp->f.subclass.format);
+
+       rtp->rtcp->reported_jitter = ast_samp2sec(ia_jitter, rate);
 
-       rtp->rtcp->reported_jitter = ia_jitter;
-       reported_jitter = (double) rtp->rtcp->reported_jitter;
        if (rtp->rtcp->reported_jitter_count == 0) {
-               rtp->rtcp->reported_minjitter = reported_jitter;
+               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_minjitter) {
+               rtp->rtcp->reported_minjitter = rtp->rtcp->reported_jitter;
        }
-       if (reported_jitter > rtp->rtcp->reported_maxjitter) {
-               rtp->rtcp->reported_maxjitter = reported_jitter;
+       if (rtp->rtcp->reported_jitter > rtp->rtcp->reported_maxjitter) {
+               rtp->rtcp->reported_maxjitter = rtp->rtcp->reported_jitter;
        }
 
-       calc_mean_and_standard_deviation(reported_jitter, &rtp->rtcp->reported_normdev_jitter,
-               &rtp->rtcp->reported_stdev_jitter, &rtp->rtcp->reported_jitter_count);
+       calc_mean_and_standard_deviation(rtp->rtcp->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(1, "(%p) RTCP got report of %d bytes from %s\n",
-               instance, len, ast_sockaddr_stringify(addr));
+       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));
 
        /*
         * Validate the RTCP packet according to an adapted and slightly
         * modified RFC3550 validation algorithm.
         */
        if (packetwords < RTCP_HEADER_SSRC_LENGTH) {
-               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);
+               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);
                return &ast_null_frame;
        }
        position = 0;
        first_word = ntohl(rtcpheader[position]);
        if ((first_word & RTCP_VALID_MASK) != RTCP_VALID_VALUE) {
-               ast_debug_rtcp(1, "(%p) RTCP %p -- from %s: Failed first packet validity check\n",
-                       instance, transport_rtp, ast_sockaddr_stringify(addr));
+               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));
                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(1, "(%p) RTCP %p -- from %s: Failed packet version or length check\n",
-                       instance, transport_rtp, ast_sockaddr_stringify(addr));
+               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));
                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
-                                       && 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), "
+                               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), "
                                                        "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: %u\n", report_block->ia_jitter);
+                                       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("  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}",
+                       message_blob = ast_json_pack("{s: s, s: s, s: f, s: f}",
                                "from", ast_sockaddr_stringify(addr),
                                "to", transport_rtp->rtcp->local_addr_str,
-                               "rtt", rtp->rtcp->rtt);
+                               "rtt", rtp->rtcp->rtt,
+                               "mes", rtp->rtcp->reported_mes);
                        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(&rxtime, rtp, timestamp, mark);
+               calc_rxstamp_and_jitter(&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(&rtp->f.delivery, rtp, timestamp, mark);
+               calc_rxstamp_and_jitter(&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(&rtp->f.delivery, rtp, timestamp, mark);
+               calc_rxstamp_and_jitter(&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, "(%p) RTCP setup on RTP instance\n", instance);
+                       ast_debug_rtcp(1, "(%s) RTCP setup on RTP instance\n",
+                               ast_rtp_instance_get_channel_id(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 / (unsigned int) 65536.0);
+       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_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 addr;
+       struct ast_sockaddr addr1;
+       struct ast_sockaddr addr2;
+       enum ast_rtp_instance_rtcp rtcp_type = AST_RTP_INSTANCE_RTCP_MUX;
 
-       ast_sockaddr_parse(&addr, "127.0.0.1", 0);
+       ast_sockaddr_parse(&addr1, "127.0.0.1", 0);
+       ast_sockaddr_parse(&addr2, "127.0.0.1", 0);
 
-       *instance1 = ast_rtp_instance_new("asterisk", test_sched, &addr, NULL);
-       *instance2 = ast_rtp_instance_new("asterisk", test_sched, &addr, NULL);
+       *instance1 = ast_rtp_instance_new("asterisk", test_sched, &addr1, "instance1");
+       *instance2 = ast_rtp_instance_new("asterisk", test_sched, &addr2, "instance2");
        if (!instance1 || !instance2) {
                return -1;
        }
 
-       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);
+       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);
 
        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, &addr);
-       ast_rtp_instance_set_remote_address(*instance2, &addr);
+       ast_rtp_instance_get_local_address(*instance1, &addr1);
+       ast_rtp_instance_set_remote_address(*instance2, &addr1);
 
-       ast_rtp_instance_get_local_address(*instance2, &addr);
-       ast_rtp_instance_set_remote_address(*instance1, &addr);
+       ast_rtp_instance_get_local_address(*instance2, &addr2);
+       ast_rtp_instance_set_remote_address(*instance1, &addr2);
 
        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;
 }