Refactor RTCP events over to Stasis; associate with channels
authorMatthew Jordan <mjordan@digium.com>
Fri, 5 Jul 2013 17:33:33 +0000 (17:33 +0000)
committerMatthew Jordan <mjordan@digium.com>
Fri, 5 Jul 2013 17:33:33 +0000 (17:33 +0000)
This patch does the following:

* It merges Jaco Kroon's patch from ASTERISK-20754, which provides channel
  information in the RTCP events. Because Stasis provides a cache, Jaco's
  patch was modified to pass the channel uniqueid to the RTP layer as
  opposed to a pointer to the channel. This has the following benefits:
  (1) It keeps the RTP engine 'clean' of references back to channels
  (2) It prevents circular dependencies and other potential ref counting issues
* The RTP engine now allows any RTP implementation to raise RTCP messages.
  Potentially, other implementations (such as res_rtp_multicast) could also
  raise RTCP information. The engine provides structs to represent RTCP headers
  and RTCP SR/RR reports.
* Some general refactoring in res_rtp_asterisk was done to try and tame the
  RTCP code. It isn't perfect - that's *way* beyond the scope of this work -
  but it does feel marginally better.
* A few random bugs were fixed in the RTCP statistics. (Example: performing an
  assignment of a = a is probably not correct)
* We now raise RTCP events for each SR/RR sent/received. Previously we wouldn't
  raise an event when we sent a RR report.

Note that this work will be of use to others who want to monitor call quality
or build modules that report call quality statistics. Since the events are now
moving across the Stasis message bus, this is far easier to accomplish. It is
also a first step (though by no means the last step) towards getting Olle's
pinefrog work incorporated.

Again: note that the patch by Jaco Kroon was modified slightly for this work;
however, he did all of the hard work in finding the right places to set the
channel in the RTP engine across the channel drivers. Much thanks goes to Jaco
for his hard work here.

Review: https://reviewboard.asterisk.org/r/2603/

(closes issue ASTERISK-20574)
Reported by: Jaco Kroon
patches:
  asterisk-rtcp-channel.patch uploaded by jkroon (License 5671)

(closes issue ASTERISK-21471)
Reported by: Matt Jordan

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393740 65c4cc65-6c06-0410-ace0-fbb531ad65f3

19 files changed:
channels/chan_gtalk.c
channels/chan_gulp.c
channels/chan_h323.c
channels/chan_jingle.c
channels/chan_mgcp.c
channels/chan_motif.c
channels/chan_multicast_rtp.c
channels/chan_sip.c
channels/chan_skinny.c
channels/chan_unistim.c
include/asterisk/cdr.h
include/asterisk/channel.h
include/asterisk/json.h
include/asterisk/rtp_engine.h
main/asterisk.c
main/json.c
main/manager.c
main/rtp_engine.c
res/res_rtp_asterisk.c

index 8a7083e..639de47 100644 (file)
@@ -210,6 +210,7 @@ static char *gtalk_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cl
 static int gtalk_update_externip(void);
 static int gtalk_parser(void *data, ikspak *pak);
 static int gtalk_create_candidates(struct gtalk *client, struct gtalk_pvt *p, char *sid, char *from, char *to);
+static void gtalk_set_owner(struct gtalk_pvt *p, struct ast_channel *chan);
 
 /*! \brief PBX interface structure for channel registration */
 static struct ast_channel_tech gtalk_tech = {
@@ -1007,6 +1008,17 @@ safeout:
        return 1;
 }
 
+static void gtalk_set_owner(struct gtalk_pvt *p, struct ast_channel *chan)
+{
+       p->owner = chan;
+       if (p->rtp) {
+               ast_rtp_instance_set_channel_id(p->rtp, chan ? ast_channel_uniqueid(chan) : "");
+       }
+       if (p->vrtp) {
+               ast_rtp_instance_set_channel_id(p->vrtp, chan ? ast_channel_uniqueid(chan) : "");
+       }
+}
+
 static struct gtalk_pvt *gtalk_alloc(struct gtalk *client, const char *us, const char *them, const char *sid)
 {
        struct gtalk_pvt *tmp = NULL;
@@ -1198,7 +1210,7 @@ static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i,
                ast_channel_musicclass_set(tmp, client->musicclass);
        if (!ast_strlen_zero(client->parkinglot))
                ast_channel_parkinglot_set(tmp, client->parkinglot);
-       i->owner = tmp;
+       gtalk_set_owner(i, tmp);
        ast_module_ref(ast_module_info->self);
        ast_channel_context_set(tmp, client->context);
        ast_channel_exten_set(tmp, i->exten);
@@ -1712,8 +1724,9 @@ static int gtalk_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
                ast_mutex_unlock(&p->lock);
                return -1;
        }
-       if (p->owner == oldchan)
-               p->owner = newchan;
+       if (p->owner == oldchan) {
+               gtalk_set_owner(p, newchan);
+       }
        ast_mutex_unlock(&p->lock);
        return 0;
 }
@@ -1889,7 +1902,7 @@ static int gtalk_hangup(struct ast_channel *ast)
 
        ast_mutex_lock(&p->lock);
        client = p->parent;
-       p->owner = NULL;
+       gtalk_set_owner(p, NULL);
        ast_channel_tech_pvt_set(ast, NULL);
        if (!p->alreadygone) {
                gtalk_action(client, p, "terminate");
index 6a80651..144fb6a 100644 (file)
@@ -429,7 +429,7 @@ static int direct_media_mitigate_glare(struct ast_sip_session *session)
 {
        RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
 
-       if (session->endpoint->direct_media_glare_mitigation == 
+       if (session->endpoint->direct_media_glare_mitigation ==
                        AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE) {
                return 0;
        }
@@ -563,6 +563,13 @@ static struct ast_channel *gulp_new(struct ast_sip_session *session, int state,
        pvt->media[SIP_MEDIA_AUDIO] = ao2_find(session->media, "audio", OBJ_KEY);
        pvt->media[SIP_MEDIA_VIDEO] = ao2_find(session->media, "video", OBJ_KEY);
        ast_channel_tech_pvt_set(chan, pvt);
+       if (pvt->media[SIP_MEDIA_AUDIO] && pvt->media[SIP_MEDIA_AUDIO]->rtp) {
+               ast_rtp_instance_set_channel_id(pvt->media[SIP_MEDIA_AUDIO]->rtp, ast_channel_uniqueid(chan));
+       }
+       if (pvt->media[SIP_MEDIA_VIDEO] && pvt->media[SIP_MEDIA_VIDEO]->rtp) {
+               ast_rtp_instance_set_channel_id(pvt->media[SIP_MEDIA_VIDEO]->rtp, ast_channel_uniqueid(chan));
+       }
+
 
        if (ast_format_cap_is_empty(session->req_caps) || !ast_format_cap_has_joint(session->req_caps, session->endpoint->codecs)) {
                ast_format_cap_copy(ast_channel_nativeformats(chan), session->endpoint->codecs);
@@ -742,8 +749,15 @@ struct fixup_data {
 static int fixup(void *data)
 {
        struct fixup_data *fix_data = data;
+       struct gulp_pvt *pvt = ast_channel_tech_pvt(fix_data->chan);
 
        fix_data->session->channel = fix_data->chan;
+       if (pvt->media[SIP_MEDIA_AUDIO] && pvt->media[SIP_MEDIA_AUDIO]->rtp) {
+               ast_rtp_instance_set_channel_id(pvt->media[SIP_MEDIA_AUDIO]->rtp, ast_channel_uniqueid(fix_data->chan));
+       }
+       if (pvt->media[SIP_MEDIA_VIDEO] && pvt->media[SIP_MEDIA_VIDEO]->rtp) {
+               ast_rtp_instance_set_channel_id(pvt->media[SIP_MEDIA_VIDEO]->rtp, ast_channel_uniqueid(fix_data->chan));
+       }
 
        return 0;
 }
@@ -1434,6 +1448,19 @@ static struct hangup_data *hangup_data_alloc(int cause, struct ast_channel *chan
        return h_data;
 }
 
+/*! \brief Clear a channel from a session along with its PVT */
+static void clear_session_and_channel(struct ast_sip_session *session, struct ast_channel *ast, struct gulp_pvt *pvt)
+{
+       session->channel = NULL;
+       if (pvt->media[SIP_MEDIA_AUDIO] && pvt->media[SIP_MEDIA_AUDIO]->rtp) {
+               ast_rtp_instance_set_channel_id(pvt->media[SIP_MEDIA_AUDIO]->rtp, "");
+       }
+       if (pvt->media[SIP_MEDIA_VIDEO] && pvt->media[SIP_MEDIA_VIDEO]->rtp) {
+               ast_rtp_instance_set_channel_id(pvt->media[SIP_MEDIA_VIDEO]->rtp, "");
+       }
+       ast_channel_tech_pvt_set(ast, NULL);
+}
+
 static int hangup(void *data)
 {
        pj_status_t status;
@@ -1453,9 +1480,7 @@ static int hangup(void *data)
                }
        }
 
-       session->channel = NULL;
-       ast_channel_tech_pvt_set(ast, NULL);
-
+       clear_session_and_channel(session, ast, pvt);
        ao2_cleanup(pvt);
        ao2_cleanup(h_data);
 
@@ -1485,11 +1510,9 @@ failure:
        /* Go ahead and do our cleanup of the session and channel even if we're not going
         * to be able to send our SIP request/response
         */
-       ao2_cleanup(h_data);
-       session->channel = NULL;
-       ast_channel_tech_pvt_set(ast, NULL);
-
+       clear_session_and_channel(session, ast, pvt);
        ao2_cleanup(pvt);
+       ao2_cleanup(h_data);
 
        return -1;
 }
@@ -1859,8 +1882,8 @@ static int gulp_incoming_ack(struct ast_sip_session *session, struct pjsip_rx_da
  * Module loading including tests for configuration or dependencies.
  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
  * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails
- * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the 
- * configuration file or other non-critical problem return 
+ * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the
+ * configuration file or other non-critical problem return
  * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS.
  */
 static int load_module(void)
index e26bb5f..5ed1c89 100644 (file)
@@ -250,6 +250,8 @@ static void delete_users(void);
 static void delete_aliases(void);
 static void prune_peers(void);
 
+static void oh323_set_owner(struct oh323_pvt *pvt, struct ast_channel *c);
+
 static struct ast_channel *oh323_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *dest, int *cause);
 static int oh323_digit_begin(struct ast_channel *c, char digit);
 static int oh323_digit_end(struct ast_channel *c, char digit, unsigned int duration);
@@ -719,7 +721,7 @@ static int oh323_hangup(struct ast_channel *c)
                return 0;
        }
 
-       pvt->owner = NULL;
+       oh323_set_owner(pvt, NULL);
        ast_channel_tech_pvt_set(c, NULL);
 
        if (ast_channel_hangupcause(c)) {
@@ -974,7 +976,7 @@ static int oh323_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
                ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, pvt->owner);
                return -1;
        }
-       pvt->owner = newchan;
+       oh323_set_owner(p, newchan);
        ast_mutex_unlock(&pvt->lock);
        return 0;
 }
@@ -1007,6 +1009,7 @@ static int __oh323_rtp_create(struct oh323_pvt *pvt)
                ast_debug(1, "Created RTP channel\n");
 
        ast_rtp_instance_set_qos(pvt->rtp, tos, cos, "H323 RTP");
+       ast_rtp_instance_set_channel_id(pvt->rtp, pvt->owner ? ast_channel_uniqueid(pvt->owner), "");
 
        if (h323debug)
                ast_debug(1, "Setting NAT on RTP to %d\n", pvt->options.nat);
@@ -1100,7 +1103,7 @@ static struct ast_channel *__oh323_new(struct oh323_pvt *pvt, int state, const c
                /* Register channel functions. */
                ast_channel_tech_pvt_set(ch, pvt);
                /* Set the owner of this channel */
-               pvt->owner = ch;
+               oh323_set_owner(pvt, ch);
 
                ast_channel_context_set(ch, pvt->context);
                ast_channel_exten_set(ch, pvt->exten);
@@ -1189,6 +1192,14 @@ static struct oh323_pvt *oh323_alloc(int callid)
        return pvt;
 }
 
+static void oh323_set_owner(struct oh323_pvt *pvt, struct ast_channel *chan)
+{
+       pvt->owner = chan;
+       if (pvt->rtp) {
+               ast_rtp_instance_set_channel_id(pvt, chan ? ast_channel_uniqueid(chan) : "");
+       }
+}
+
 static struct oh323_pvt *find_call_locked(int call_reference, const char *token)
 {
        struct oh323_pvt *pvt;
index 42dccf6..e5fd025 100644 (file)
@@ -196,6 +196,7 @@ static int jingle_sendhtml(struct ast_channel *ast, int subclass, const char *da
 static struct jingle_pvt *jingle_alloc(struct jingle *client, const char *from, const char *sid);
 static char *jingle_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static char *jingle_do_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static void jingle_set_owner(struct jingle_pvt *pvt, struct ast_channel *chan);
 
 /*! \brief PBX interface structure for channel registration */
 static struct ast_channel_tech jingle_tech = {
@@ -833,6 +834,17 @@ static struct jingle_pvt *jingle_alloc(struct jingle *client, const char *from,
        return tmp;
 }
 
+static void jingle_set_owner(struct jingle_pvt *pvt, struct ast_channel *chan)
+{
+       pvt->owner = chan;
+       if (pvt->rtp) {
+               ast_rtp_instance_set_channel_id(pvt->rtp, pvt->owner ? ast_channel_uniqueid(pvt->owner) : "");
+       }
+       if (pvt->vrtp) {
+               ast_rtp_instance_set_channel_id(pvt->vrtp, pvt->owner ? ast_channel_uniqueid(pvt->owner) : "");
+       }
+}
+
 /*! \brief Start new jingle channel */
 static struct ast_channel *jingle_new(struct jingle *client, struct jingle_pvt *i, int state, const char *title, const char *linkedid)
 {
@@ -908,7 +920,7 @@ static struct ast_channel *jingle_new(struct jingle *client, struct jingle_pvt *
                ast_channel_language_set(tmp, client->language);
        if (!ast_strlen_zero(client->musicclass))
                ast_channel_musicclass_set(tmp, client->musicclass);
-       i->owner = tmp;
+       jingle_set_owner(i, tmp);
        ast_channel_context_set(tmp, client->context);
        ast_channel_exten_set(tmp, i->exten);
        /* Don't use ast_set_callerid() here because it will
@@ -1321,8 +1333,9 @@ static int jingle_fixup(struct ast_channel *oldchan, struct ast_channel *newchan
                ast_mutex_unlock(&p->lock);
                return -1;
        }
-       if (p->owner == oldchan)
-               p->owner = newchan;
+       if (p->owner == oldchan) {
+               jingle_set_owner(p, newchan);
+       }
        ast_mutex_unlock(&p->lock);
        return 0;
 }
@@ -1540,7 +1553,7 @@ static int jingle_hangup(struct ast_channel *ast)
 
        ast_mutex_lock(&p->lock);
        client = p->parent;
-       p->owner = NULL;
+       jingle_set_owner(p, NULL);
        ast_channel_tech_pvt_set(ast, NULL);
        if (!p->alreadygone)
                jingle_action(client, p, JINGLE_TERMINATE);
index 5a0b7ad..da3fefa 100644 (file)
@@ -431,6 +431,7 @@ static int mgcpsock  = -1;
 
 static struct sockaddr_in bindaddr;
 
+static void mgcp_set_owner(struct mgcp_subchannel *sub, struct ast_channel *chan);
 static struct ast_frame  *mgcp_read(struct ast_channel *ast);
 static int transmit_response(struct mgcp_subchannel *sub, char *msg, struct mgcp_request *req, char *msgrest);
 static int transmit_notify_request(struct mgcp_subchannel *sub, char *tone);
@@ -528,7 +529,7 @@ static int unalloc_sub(struct mgcp_subchannel *sub)
        }
        ast_debug(1, "Released sub %d of channel %s@%s\n", sub->id, p->name, p->parent->name);
 
-       sub->owner = NULL;
+       mgcp_set_owner(sub, NULL);
        if (!ast_strlen_zero(sub->cxident)) {
                transmit_connection_del(sub);
        }
@@ -945,7 +946,7 @@ static int mgcp_hangup(struct ast_channel *ast)
                }
        }
 
-       sub->owner = NULL;
+       mgcp_set_owner(sub, NULL);
 
        /* for deleting gate */
        if (p->pktcgatealloc && sub->gate) {
@@ -1225,6 +1226,13 @@ static struct ast_frame *mgcp_rtp_read(struct mgcp_subchannel *sub)
        return f;
 }
 
+static void mgcp_set_owner(struct mgcp_subchannel *sub, struct ast_channel *chan)
+{
+       sub->owner = chan;
+       if (sub->rtp) {
+               ast_rtp_instance_set_channel_id(sub->rtp, sub->owner ? ast_channel_uniqueid(chan) : "");
+       }
+}
 
 static struct ast_frame *mgcp_read(struct ast_channel *ast)
 {
@@ -1288,7 +1296,7 @@ static int mgcp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
                ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
                return -1;
        }
-       sub->owner = newchan;
+       mgcp_set_owner(sub, newchan);
        ast_mutex_unlock(&sub->lock);
        return 0;
 }
@@ -1529,7 +1537,7 @@ static struct ast_channel *mgcp_new(struct mgcp_subchannel *sub, int state, cons
                        ast_channel_accountcode_set(tmp, i->accountcode);
                if (i->amaflags)
                        ast_channel_amaflags_set(tmp, i->amaflags);
-               sub->owner = tmp;
+               mgcp_set_owner(sub, tmp);
                ast_module_ref(ast_module_info->self);
                ast_channel_callgroup_set(tmp, i->callgroup);
                ast_channel_pickupgroup_set(tmp, i->pickupgroup);
index 22b5eae..c77d0c0 100644 (file)
@@ -656,6 +656,18 @@ static struct ast_rtp_glue jingle_rtp_glue = {
        .update_peer = jingle_set_rtp_peer,
 };
 
+/*! \brief Set the channel owner on the \ref jingle_session object and related objects */
+static void jingle_set_owner(struct jingle_session *session, struct ast_channel *chan)
+{
+       session->owner = chan;
+       if (session->rtp) {
+               ast_rtp_instance_set_channel_id(session->rtp, session->owner ? ast_channel_uniqueid(session->owner) : "");
+       }
+       if (session->vrtp) {
+               ast_rtp_instance_set_channel_id(session->vrtp, session->owner ? ast_channel_uniqueid(session->owner) : "");
+       }
+}
+
 /*! \brief Internal helper function which enables video support on a sesson if possible */
 static void jingle_enable_video(struct jingle_session *session)
 {
@@ -679,7 +691,7 @@ static void jingle_enable_video(struct jingle_session *session)
        }
 
        ast_rtp_instance_set_prop(session->vrtp, AST_RTP_PROPERTY_RTCP, 1);
-
+       ast_rtp_instance_set_channel_id(session->vrtp, ast_channel_uniqueid(session->owner));
        ast_channel_set_fd(session->owner, 2, ast_rtp_instance_fd(session->vrtp, 0));
        ast_channel_set_fd(session->owner, 3, ast_rtp_instance_fd(session->vrtp, 1));
        ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session->vrtp), session->vrtp, &session->prefs);
@@ -775,7 +787,7 @@ static struct ast_channel *jingle_new(struct jingle_endpoint *endpoint, struct j
 
        ast_channel_tech_set(chan, &jingle_tech);
        ast_channel_tech_pvt_set(chan, session);
-       session->owner = chan;
+       jingle_set_owner(session, chan);
 
        ast_channel_callid_set(chan, session->callid);
 
@@ -1712,7 +1724,7 @@ static int jingle_fixup(struct ast_channel *oldchan, struct ast_channel *newchan
 
        ao2_lock(session);
 
-       session->owner = newchan;
+       jingle_set_owner(session, newchan);
 
        ao2_unlock(session);
 
@@ -1862,7 +1874,7 @@ static int jingle_hangup(struct ast_channel *ast)
        }
 
        ast_channel_tech_pvt_set(ast, NULL);
-       session->owner = NULL;
+       jingle_set_owner(session, NULL);
 
        ao2_unlink(session->state->sessions, session);
        ao2_ref(session->state, -1);
index dfa0680..4629829 100644 (file)
@@ -153,7 +153,7 @@ static struct ast_channel *multicast_rtp_request(const char *type, struct ast_fo
                ast_rtp_instance_destroy(instance);
                goto failure;
        }
-
+       ast_rtp_instance_set_channel_id(instance, ast_channel_uniqueid(chan));
        ast_rtp_instance_set_remote_address(instance, &destination_address);
 
        ast_channel_tech_set(chan, &multicast_rtp_tech);
index 9a5f086..9373a64 100644 (file)
@@ -1240,6 +1240,7 @@ static enum check_auth_result register_verify(struct sip_pvt *p, struct ast_sock
 static int get_sip_pvt_from_replaces(const char *callid, const char *totag, const char *fromtag,
                struct sip_pvt **out_pvt, struct ast_channel **out_chan);
 static void check_pendings(struct sip_pvt *p);
+static void sip_set_owner(struct sip_pvt *p, struct ast_channel *chan);
 
 static void *sip_pickup_thread(void *stuff);
 static int sip_pickup(struct ast_channel *chan);
@@ -3494,7 +3495,7 @@ void dialog_unlink_all(struct sip_pvt *dialog)
                ast_channel_tech_pvt_set(owner, dialog_unref(ast_channel_tech_pvt(owner), "resetting channel dialog ptr in unlink_all"));
                ast_channel_unlock(owner);
                ast_channel_unref(owner);
-               dialog->owner = NULL;
+               sip_set_owner(dialog, NULL);
        }
        sip_pvt_unlock(dialog);
 
@@ -7183,7 +7184,7 @@ static int sip_hangup(struct ast_channel *ast)
                ast_clear_flag(&p->flags[0], SIP_DEFER_BYE_ON_TRANSFER);        /* Really hang up next time */
                ast_channel_tech_pvt_set(p->owner, dialog_unref(ast_channel_tech_pvt(p->owner), "unref p->owner->tech_pvt"));
                sip_pvt_lock(p);
-               p->owner = NULL;  /* Owner will be gone after we return, so take it away */
+               sip_set_owner(p, NULL); /* Owner will be gone after we return, so take it away */
                sip_pvt_unlock(p);
                ast_module_unref(ast_module_info->self);
                return 0;
@@ -7218,7 +7219,7 @@ static int sip_hangup(struct ast_channel *ast)
        /* Disconnect */
        disable_dsp_detect(p);
 
-       p->owner = NULL;
+       sip_set_owner(p, NULL);
        ast_channel_tech_pvt_set(ast, dialog_unref(ast_channel_tech_pvt(ast), "unref ast->tech_pvt"));
 
        ast_module_unref(ast_module_info->self);
@@ -7544,7 +7545,7 @@ static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
        if (p->owner != oldchan)
                ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
        else {
-               p->owner = newchan;
+               sip_set_owner(p, newchan);
                /* Re-invite RTP back to Asterisk. Needed if channel is masqueraded out of a native
                   RTP bridge (i.e., RTP not going through Asterisk): RTP bridge code might not be
                   able to do this if the masquerade happens before the bridge breaks (e.g., AMI
@@ -8190,7 +8191,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
                }
                ast_channel_zone_set(tmp, zone);
        }
-       i->owner = tmp;
+       sip_set_owner(i, tmp);
        ast_module_ref(ast_module_info->self);
        ast_channel_context_set(tmp, i->context);
        /*Since it is valid to have extensions in the dialplan that have unescaped characters in them
@@ -9252,6 +9253,21 @@ static struct ast_channel *sip_pvt_lock_full(struct sip_pvt *pvt)
        return pvt->owner;
 }
 
+/*! \brief Set the owning channel on the \ref sip_pvt object */
+static void sip_set_owner(struct sip_pvt *p, struct ast_channel *chan)
+{
+       p->owner = chan;
+       if (p->rtp) {
+               ast_rtp_instance_set_channel_id(p->rtp, p->owner ? ast_channel_uniqueid(p->owner) : "");
+       }
+       if (p->vrtp) {
+               ast_rtp_instance_set_channel_id(p->vrtp, p->owner ? ast_channel_uniqueid(p->owner) : "");
+       }
+       if (p->trtp) {
+               ast_rtp_instance_set_channel_id(p->trtp, p->owner ? ast_channel_uniqueid(p->owner) : "");
+       }
+}
+
 /*! \brief find or create a dialog structure for an incoming SIP message.
  * Connect incoming SIP message to current dialog or create new dialog structure
  * Returns a reference to the sip_pvt object, remember to give it back once done.
index 6472132..bb0888f 100644 (file)
@@ -1638,6 +1638,7 @@ static void mwi_event_cb(void *userdata, struct stasis_subscription *sub, struct
 static int skinny_dialer_cb(const void *data);
 static int skinny_reload(void);
 
+static void skinny_set_owner(struct skinny_subchannel* sub, struct ast_channel* chan);
 static void setsubstate(struct skinny_subchannel *sub, int state);
 static void dumpsub(struct skinny_subchannel *sub, int forcehangup);
 static void activatesub(struct skinny_subchannel *sub, int state);
@@ -4797,10 +4798,12 @@ static void start_rtp(struct skinny_subchannel *sub)
        }
 
        if (sub->rtp && sub->owner) {
+               ast_rtp_instance_set_channel_id(sub->rtp, ast_channel_uniqueid(sub->owner));
                ast_channel_set_fd(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0));
                ast_channel_set_fd(sub->owner, 1, ast_rtp_instance_fd(sub->rtp, 1));
        }
        if (hasvideo && sub->vrtp && sub->owner) {
+               ast_rtp_instance_set_channel_id(sub->vrtp, ast_channel_uniqueid(sub->owner));
                ast_channel_set_fd(sub->owner, 2, ast_rtp_instance_fd(sub->vrtp, 0));
                ast_channel_set_fd(sub->owner, 3, ast_rtp_instance_fd(sub->vrtp, 1));
        }
@@ -5009,7 +5012,7 @@ static int skinny_hangup(struct ast_channel *ast)
        SKINNY_DEBUG(DEBUG_SUB, 3, "Sub %d - Destroying\n", sub->callid);
 
        ast_mutex_lock(&sub->lock);
-       sub->owner = NULL;
+       skinny_set_owner(sub, NULL);
        ast_channel_tech_pvt_set(ast, NULL);
        destroy_rtp(sub);
        ast_free(sub->origtonum);
@@ -5133,7 +5136,7 @@ static int skinny_fixup(struct ast_channel *oldchan, struct ast_channel *newchan
                ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, sub->owner);
                return -1;
        }
-       sub->owner = newchan;
+       skinny_set_owner(sub, newchan);
        return 0;
 }
 
@@ -5361,6 +5364,17 @@ static int skinny_indicate(struct ast_channel *ast, int ind, const void *data, s
        return 0;
 }
 
+static void skinny_set_owner(struct skinny_subchannel* sub, struct ast_channel* chan)
+{
+       sub->owner = chan;
+       if (sub->rtp) {
+               ast_rtp_instance_set_channel_id(sub->rtp, sub->owner ? ast_channel_uniqueid(sub->owner) : "");
+       }
+       if (sub->vrtp) {
+               ast_rtp_instance_set_channel_id(sub->vrtp, sub->owner ? ast_channel_uniqueid(sub->owner) : "");
+       }
+}
+
 static struct ast_channel *skinny_new(struct skinny_line *l, struct skinny_subline *subline, int state, const char *linkedid, int direction)
 {
        struct ast_channel *tmp;
@@ -5386,7 +5400,7 @@ static struct ast_channel *skinny_new(struct skinny_line *l, struct skinny_subli
                } else {
                        ast_mutex_init(&sub->lock);
 
-                       sub->owner = tmp;
+                       skinny_set_owner(sub, tmp);
                        sub->callid = callnums++;
                        d->lastlineinstance = l->instance;
                        d->lastcallreference = sub->callid;
index 5a5674d..53ffa00 100644 (file)
@@ -675,6 +675,7 @@ static int load_module(void);
 static int reload(void);
 static int unload_module(void);
 static int reload_config(void);
+static void unistim_set_owner(struct unistim_subchannel *sub, struct ast_channel *chan);
 static void show_main_page(struct unistimsession *pte);
 static struct ast_channel *unistim_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor,
        const char *dest, int *cause);
@@ -2749,6 +2750,7 @@ static void start_rtp(struct unistim_subchannel *sub)
                return;
        }
        ast_rtp_instance_set_prop(sub->rtp, AST_RTP_PROPERTY_RTCP, 1);
+       ast_rtp_instance_set_channel_id(sub->rtp, ast_channel_uniqueid(sub->owner));
        ast_channel_internal_fd_set(sub->owner, 0, ast_rtp_instance_fd(sub->rtp, 0));
        ast_channel_internal_fd_set(sub->owner, 1, ast_rtp_instance_fd(sub->rtp, 1));
        ast_rtp_instance_set_qos(sub->rtp, qos.tos_audio, qos.cos_audio, "UNISTIM RTP");
@@ -4736,7 +4738,7 @@ static int unistim_call(struct ast_channel *ast, const char *dest, int timeout)
 static int unistim_hangup_clean(struct ast_channel *ast, struct unistim_subchannel *sub) {
        ast_mutex_lock(&sub->lock);
        ast_channel_tech_pvt_set(ast, NULL);
-       sub->owner = NULL;
+       unistim_set_owner(sub, NULL);
        sub->alreadygone = 0;
        ast_mutex_unlock(&sub->lock);
        if (sub->rtp) {
@@ -5072,7 +5074,7 @@ static int unistim_fixup(struct ast_channel *oldchan, struct ast_channel *newcha
                return -1;
        }
 
-       p->owner = newchan;
+       unistim_set_owner(p, newchan);
 
        ast_mutex_unlock(&p->lock);
 
@@ -5589,7 +5591,7 @@ static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state
        if (!ast_strlen_zero(l->parent->language)) {
                ast_channel_language_set(tmp, l->parent->language);
        }
-       sub->owner = tmp;
+       unistim_set_owner(sub, tmp);
        ast_update_use_count();
        ast_channel_callgroup_set(tmp, l->callgroup);
        ast_channel_pickupgroup_set(tmp, l->pickupgroup);
@@ -5623,6 +5625,14 @@ static struct ast_channel *unistim_new(struct unistim_subchannel *sub, int state
        return tmp;
 }
 
+static void unistim_set_owner(struct unistim_subchannel *sub, struct ast_channel *chan)
+{
+       sub->owner = chan;
+       if (sub->rtp) {
+               ast_rtp_instance_set_channel_id(sub->rtp, sub->owner ? ast_channel_uniqueid(sub->owner) : "");
+       }
+}
+
 static void *do_monitor(void *data)
 {
        struct unistimsession *cur = NULL;
index 9d1f6d7..cd0501f 100644 (file)
@@ -330,11 +330,10 @@ struct ast_cdr {
        char peeraccount[AST_MAX_ACCOUNT_CODE];
        /*! flags */
        unsigned int flags;
-       /*! Unique Channel Identifier
-        * 150 = 127 (max systemname) + "-" + 10 (epoch timestamp) + "." + 10 (monotonically incrementing integer) + NULL */
-       char uniqueid[150];
+       /*! Unique Channel Identifier */
+       char uniqueid[AST_MAX_UNIQUEID];
        /* Linked group Identifier */
-       char linkedid[32];
+       char linkedid[AST_MAX_UNIQUEID];
        /*! User field */
        char userfield[AST_MAX_USER_FIELD];
        /*! Sequence field */
index 2e74684..0f55491 100644 (file)
@@ -133,6 +133,12 @@ extern "C" {
 
 #define AST_MAX_EXTENSION       80  /*!< Max length of an extension */
 #define AST_MAX_CONTEXT         80  /*!< Max length of a context */
+#define AST_MAX_UNIQUEID       150  /*!< Max length of a channel uniqueid */
+/*                                   150 = 127 (max systemname) + "-" + 10 (epoch
+ *                                   timestamp) + "." + 10 (monotonically incrementing
+ *                                   integer) + NULL. Note that if this value is ever
+ *                                   changed, MAX_CHANNEL_ID should be updated in
+ *                                   rtp_engine.h */
 #define AST_MAX_ACCOUNT_CODE    20  /*!< Max length of an account code */
 #define AST_CHANNEL_NAME        80  /*!< Max length of an ast_channel name */
 #define MAX_LANGUAGE            40  /*!< Max length of the language setting */
index 0584c99..a735fd3 100644 (file)
@@ -330,6 +330,34 @@ intmax_t ast_json_integer_get(const struct ast_json *integer);
  */
 int ast_json_integer_set(struct ast_json *integer, intmax_t value);
 
+/*!
+ * \brief Create a JSON real number.
+ * \since 12.0.0
+ * \param value Value of the new JSON real number.
+ * \return Newly allocated real number.
+ * \return \c NULL on error.
+ */
+struct ast_json *ast_json_real_create(double value);
+
+/*!
+ * \brief Get the value from a JSON real number.
+ * \since 12.0.0
+ * \param real JSON real number.
+ * \return Value of a JSON real number.
+ * \return 0 if \a real is not a JSON real number.
+ */
+double ast_json_real_get(const struct ast_json *real);
+
+/*!
+ * \brief Set the value of a JSON real number.
+ * \since 12.0.0
+ * \param integer JSON real number to modify.
+ * \param value New value for \a real.
+ * \return 0 on success.
+ * \return -1 on error.
+ */
+int ast_json_real_set(struct ast_json *real, double value);
+
 /*!@}*/
 
 /*!@{*/
index e2567f5..bda13e8 100644 (file)
@@ -74,6 +74,7 @@ extern "C" {
 #include "asterisk/netsock2.h"
 #include "asterisk/sched.h"
 #include "asterisk/res_srtp.h"
+#include "asterisk/stasis.h"
 
 /* Maximum number of payloads supported */
 #if defined(LOW_MEMORY)
@@ -85,6 +86,12 @@ extern "C" {
 /* Maximum number of generations */
 #define AST_RED_MAX_GENERATION 5
 
+/* Maximum size of an Asterisk channel unique ID. Should match AST_MAX_UNIQUEID.
+ * Note that we don't use that defined value directly here to avoid a hard dependency
+ * on channel.h
+ */
+#define MAX_CHANNEL_ID 150
+
 struct ast_rtp_instance;
 struct ast_rtp_glue;
 
@@ -215,6 +222,8 @@ enum ast_rtp_instance_stat {
        AST_RTP_INSTANCE_STAT_LOCAL_SSRC,
        /*! Retrieve remote SSRC */
        AST_RTP_INSTANCE_STAT_REMOTE_SSRC,
+       /*! Retrieve channel unique ID */
+       AST_RTP_INSTANCE_STAT_CHANNEL_UNIQUEID,
 };
 
 /* Codes for RTP-specific data - not defined by our AST_FORMAT codes */
@@ -240,6 +249,46 @@ struct ast_rtp_payload_type {
        int payload;
 };
 
+/* Common RTCP report types */
+/*! Sender Report */
+#define AST_RTP_RTCP_SR 200
+/*! Receiver Report */
+#define AST_RTP_RTCP_RR 201
+
+/*!
+ * \since 12
+ * \brief A report block within a SR/RR report */
+struct ast_rtp_rtcp_report_block {
+       unsigned int source_ssrc;         /*< The SSRC of the source for this report block */
+       struct {
+               unsigned short fraction;      /*< The fraction of packets lost since last SR/RR */
+               unsigned int packets;         /*< The cumulative packets since the beginning */
+       } lost_count;                     /*< Statistics regarding missed packets */
+       unsigned int highest_seq_no;      /*< Extended highest sequence number received */
+       unsigned int ia_jitter;           /*< Calculated interarrival jitter */
+       unsigned int lsr;                 /*< The time the last SR report was received */
+       unsigned int dlsr;                /*< Delay in sending this report */
+};
+
+/*!
+ * \since 12
+ * \brief An object that represents data sent during a SR/RR RTCP report */
+struct ast_rtp_rtcp_report {
+       unsigned short reception_report_count;     /*< The number of report blocks */
+       unsigned int ssrc;                         /*< Our SSRC */
+       unsigned int type;                         /*< The type of report. 200=SR; 201=RR */
+       struct {
+               struct timeval ntp_timestamp;          /*< Our NTP timestamp */
+               unsigned int rtp_timestamp;            /*< Our last RTP timestamp */
+               unsigned int packet_count;             /*< Number of packets sent */
+               unsigned int octet_count;              /*< Number of bytes sent */
+       } sender_information;                      /*< Sender information for SR */
+       /*! A dynamic array of report blocks. The number of elements is given by
+        * \c reception_report_count.
+        */
+       struct ast_rtp_rtcp_report_block *report_block[0];
+};
+
 /*! Structure that represents statistics from an RTP instance */
 struct ast_rtp_instance_stats {
        /*! Number of packets transmitted */
@@ -300,6 +349,8 @@ struct ast_rtp_instance_stats {
        unsigned int local_ssrc;
        /*! Their SSRC */
        unsigned int remote_ssrc;
+       /*! The Asterisk channel's unique ID that owns this instance */
+       char channel_uniqueid[MAX_CHANNEL_ID];
 };
 
 #define AST_RTP_STAT_SET(current_stat, combined, placement, value) \
@@ -310,6 +361,14 @@ return 0; \
 } \
 }
 
+#define AST_RTP_STAT_STRCPY(current_stat, combined, placement, value) \
+if (stat == current_stat || stat == AST_RTP_INSTANCE_STAT_ALL || (combined >= 0 && combined == current_stat)) { \
+       ast_copy_string(placement, value, sizeof(placement)); \
+       if (stat == current_stat) { \
+               return 0; \
+       } \
+}
+
 #define AST_RTP_STAT_TERMINATOR(combined) \
 if (stat == combined) { \
 return 0; \
@@ -1541,6 +1600,30 @@ int ast_rtp_instance_fd(struct ast_rtp_instance *instance, int rtcp);
 struct ast_rtp_glue *ast_rtp_instance_get_glue(const char *type);
 
 /*!
+ * \brief Get the unique ID of the channel that owns this RTP instance
+ *
+ * Note that this should remain valid for the lifetime of the RTP instance.
+ *
+ * \param instance The RTP instance
+ *
+ * \retval The unique ID of the channel
+ * \retval Empty string if no channel owns this RTP instance
+ *
+ * \since 12
+ */
+const char *ast_rtp_instance_get_channel_id(struct ast_rtp_instance *instance);
+
+/*!
+ * \brief Set the channel that owns this RTP instance
+ *
+ * \param instance The RTP instance
+ * \param uniqueid The uniqueid of the channel
+ *
+ * \since 12
+ */
+void ast_rtp_instance_set_channel_id(struct ast_rtp_instance *instance, const char *uniqueid);
+
+/*!
  * \brief Get the other RTP instance that an instance is bridged to
  *
  * \param instance The RTP instance that we want
@@ -1965,27 +2048,6 @@ struct ast_rtp_engine *ast_rtp_instance_get_engine(struct ast_rtp_instance *inst
 struct ast_rtp_glue *ast_rtp_instance_get_active_glue(struct ast_rtp_instance *instance);
 
 /*!
- * \brief Get the channel that is associated with an RTP instance while in a bridge
- *
- * \param instance The RTP instance
- *
- * \retval pointer to the channel
- *
- * Example:
- *
- * \code
- * struct ast_channel *chan = ast_rtp_instance_get_chan(instance);
- * \endcode
- *
- * This gets the channel associated with the RTP instance pointed to by 'instance'.
- *
- * \note This will only return a channel while in a local or remote bridge.
- *
- * \since 1.8
- */
-struct ast_channel *ast_rtp_instance_get_chan(struct ast_rtp_instance *instance);
-
-/*!
  * \brief Send a comfort noise packet to the RTP instance
  *
  * \param instance The RTP instance
@@ -2073,6 +2135,62 @@ void ast_rtp_dtls_cfg_copy(const struct ast_rtp_dtls_cfg *src_cfg, struct ast_rt
  */
 void ast_rtp_dtls_cfg_free(struct ast_rtp_dtls_cfg *dtls_cfg);
 
+struct ast_json;
+
+/*!
+ * \brief Allocate an ao2 ref counted instance of \ref ast_rtp_rtcp_report
+ *
+ * \param report_blocks The number of report blocks to allocate
+ * \retval An ao2 ref counted \ref ast_rtp_rtcp_report object on success
+ * \retval NULL on error
+ */
+struct ast_rtp_rtcp_report *ast_rtp_rtcp_report_alloc(unsigned int report_blocks);
+
+/*!
+ * \since 12
+ * \brief Publish an RTCP message to \ref stasis
+ *
+ * \param rtp The rtp instance object
+ * \param message_type The RTP message type to publish
+ * \param report The RTCP report object to publish. This should be an ao2 ref counted
+ *  object. This routine will increase the reference count of the object.
+ * \param blob Additional JSON objects to publish along with the RTCP information
+ */
+void ast_rtp_publish_rtcp_message(struct ast_rtp_instance *rtp,
+               struct stasis_message_type *message_type,
+               struct ast_rtp_rtcp_report *report,
+               struct ast_json *blob);
+
+/*! \addtogroup StasisTopicsAndMessages
+ * @{
+ */
+
+/*!
+ * \since 12
+ * \brief Message type for an RTCP message sent from this Asterisk instance
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_rtp_rtcp_sent_type(void);
+
+/*!
+ * \since 12
+ * \brief Message type for an RTCP message received from some external source
+ *
+ * \retval A stasis message type
+ */
+struct stasis_message_type *ast_rtp_rtcp_received_type(void);
+
+/*!
+ * \since 12
+ * \brief \ref stasis topic for RTP and RTCP related messages
+ *
+ * \retval A \ref stasis topic
+ */
+struct stasis_topic *ast_rtp_topic(void);
+
+/* }@ */
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
index aa33f31..b8cf43e 100644 (file)
@@ -27,7 +27,7 @@
  * internals of the Asterisk software. This documentation contains basic
  * examples, developer documentation, support information, and information
  * for upgrading.
- * 
+ *
  * \section community Community
  * Asterisk is a big project and has a busy community. Look at the
  * resources for questions and stick around to help answer questions.
  * \par
  * Use http://www.freenode.net IRC server to connect with Asterisk
  * developers and users in realtime.
- * 
+ *
  * \li \verbatim #asterisk \endverbatim Asterisk Users Room
  * \li \verbatim #asterisk-dev \endverbatim Asterisk Developers Room
  *
@@ -4217,6 +4217,7 @@ int main(int argc, char *argv[])
                printf("Stasis initialization failed.\n%s", term_quit());
                exit(1);
        }
+
        if (stasis_system_topic_init()) {
                printf("Stasis system-level information initialization failed.\n%s", term_quit());
                exit(1);
@@ -4258,7 +4259,10 @@ int main(int argc, char *argv[])
 
        ast_format_attr_init();
        ast_format_list_init();
-       ast_rtp_engine_init();
+       if (ast_rtp_engine_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
 
        ast_autoservice_init();
 
index c185b05..1b0e412 100644 (file)
@@ -214,6 +214,20 @@ int ast_json_integer_set(struct ast_json *integer, intmax_t value)
        return json_integer_set((json_t *)integer, value);
 }
 
+struct ast_json *ast_json_real_create(double value)
+{
+       return (struct ast_json *)json_real(value);
+}
+
+double ast_json_real_get(const struct ast_json *real)
+{
+       return json_real_value((json_t *)real);
+}
+
+int ast_json_real_set(struct ast_json *real, double value)
+{
+       return json_real_set((json_t *)real, value);
+}
 
 int ast_json_equal(const struct ast_json *lhs, const struct ast_json *rhs)
 {
index 02d61da..b1c7b53 100644 (file)
@@ -98,6 +98,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/json.h"
 #include "asterisk/bridging.h"
 #include "asterisk/features_config.h"
+#include "asterisk/rtp_engine.h"
 
 /*** DOCUMENTATION
        <manager name="Ping" language="en_US">
@@ -1071,6 +1072,9 @@ static struct stasis_topic *manager_topic;
 /*! \brief The \ref stasis_message_router for all \ref stasis messages */
 static struct stasis_message_router *stasis_router;
 
+/*! \brief The \ref stasis_subscription for forwarding the RTP topic to the AMI topic */
+static struct stasis_subscription *rtp_topic_forwarder;
+
 #define MGR_SHOW_TERMINAL_WIDTH 80
 
 #define MAX_VARS 128
@@ -7775,6 +7779,11 @@ static int manager_subscriptions_init(void)
        if (!manager_topic) {
                return -1;
        }
+       rtp_topic_forwarder = stasis_forward_all(ast_rtp_topic(), manager_topic);
+       if (!rtp_topic_forwarder) {
+               return -1;
+       }
+
        stasis_router = stasis_message_router_create(manager_topic);
        if (!stasis_router) {
                return -1;
index 8e58f65..4dd4d46 100644 (file)
 
 /*** MODULEINFO
        <support_level>core</support_level>
+***/
+
+/*** DOCUMENTATION
+       <managerEvent language="en_US" name="RTCPSent">
+               <managerEventInstance class="EVENT_FLAG_REPORTING">
+                       <synopsis>Raised when an RTCP packet is sent.</synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                               <parameter name="SSRC">
+                                       <para>The SSRC identifier for our stream</para>
+                               </parameter>
+                               <parameter name="PT">
+                                       <para>The type of packet for this RTCP report.</para>
+                                       <enumlist>
+                                               <enum name="200(SR)"/>
+                                               <enum name="201(RR)"/>
+                                       </enumlist>
+                               </parameter>
+                               <parameter name="To">
+                                       <para>The address the report is sent to.</para>
+                               </parameter>
+                               <parameter name="ReportCount">
+                                       <para>The number of reports that were sent.</para>
+                                       <para>The report count determines the number of ReportX headers in
+                                       the message. The X for each set of report headers will range from 0 to
+                                       <literal>ReportCount - 1</literal>.</para>
+                               </parameter>
+                               <parameter name="SentNTP" required="false">
+                                       <para>The time the sender generated the report. Only valid when
+                                       PT is <literal>200(SR)</literal>.</para>
+                               </parameter>
+                               <parameter name="SentRTP" required="false">
+                                       <para>The sender's last RTP timestamp. Only valid when PT is
+                                       <literal>200(SR)</literal>.</para>
+                               </parameter>
+                               <parameter name="SentPackets" required="false">
+                                       <para>The number of packets the sender has sent. Only valid when PT
+                                       is <literal>200(SR)</literal>.</para>
+                               </parameter>
+                               <parameter name="SentOctets" required="false">
+                                       <para>The number of bytes the sender has sent. Only valid when PT is
+                                       <literal>200(SR)</literal>.</para>
+                               </parameter>
+                               <parameter name="ReportXSourceSSRC">
+                                       <para>The SSRC for the source of this report block.</para>
+                               </parameter>
+                               <parameter name="ReportXFractionLost">
+                                       <para>The fraction of RTP data packets from <literal>ReportXSourceSSRC</literal>
+                                       lost since the previous SR or RR report was sent.</para>
+                               </parameter>
+                               <parameter name="ReportXCumulativeLost">
+                                       <para>The total number of RTP data packets from <literal>ReportXSourceSSRC</literal>
+                                       lost since the beginning of reception.</para>
+                               </parameter>
+                               <parameter name="ReportXHighestSequence">
+                                       <para>The highest sequence number received in an RTP data packet from
+                                       <literal>ReportXSourceSSRC</literal>.</para>
+                               </parameter>
+                               <parameter name="ReportXSequenceNumberCycles">
+                                       <para>The number of sequence number cycles seen for the RTP data
+                                       received from <literal>ReportXSourceSSRC</literal>.</para>
+                               </parameter>
+                               <parameter name="ReportXIAJitter">
+                                       <para>An estimate of the statistical variance of the RTP data packet
+                                       interarrival time, measured in timestamp units.</para>
+                               </parameter>
+                               <parameter name="ReportXLSR">
+                                       <para>The last SR timestamp received from <literal>ReportXSourceSSRC</literal>.
+                                       If no SR has been received from <literal>ReportXSourceSSRC</literal>,
+                                       then 0.</para>
+                               </parameter>
+                               <parameter name="ReportXDLSR">
+                                       <para>The delay, expressed in units of 1/65536 seconds, between
+                                       receiving the last SR packet from <literal>ReportXSourceSSRC</literal>
+                                       and sending this report.</para>
+                               </parameter>
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
+       <managerEvent language="en_US" name="RTCPReceived">
+               <managerEventInstance class="EVENT_FLAG_REPORTING">
+                       <synopsis>Raised when an RTCP packet is received.</synopsis>
+                       <syntax>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='Newchannel']/managerEventInstance/syntax/parameter)" />
+                               <parameter name="SSRC">
+                                       <para>The SSRC identifier for the remote system</para>
+                               </parameter>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[@name='PT'])" />
+                               <parameter name="From">
+                                       <para>The address the report was received from.</para>
+                               </parameter>
+                               <parameter name="RTT">
+                                       <para>Calculated Round-Trip Time in seconds</para>
+                               </parameter>
+                               <parameter name="ReportCount">
+                                       <para>The number of reports that were received.</para>
+                                       <para>The report count determines the number of ReportX headers in
+                                       the message. The X for each set of report headers will range from 0 to
+                                       <literal>ReportCount - 1</literal>.</para>
+                               </parameter>
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[@name='SentNTP'])" />
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[@name='SentRTP'])" />
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[@name='SentPackets'])" />
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[@name='SentOctets'])" />
+                               <xi:include xpointer="xpointer(/docs/managerEvent[@name='RTCPSent']/managerEventInstance/syntax/parameter[contains(@name, 'ReportX')])" />
+                       </syntax>
+               </managerEventInstance>
+       </managerEvent>
  ***/
 
 #include "asterisk.h"
@@ -45,6 +153,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/netsock2.h"
 #include "asterisk/_private.h"
 #include "asterisk/framehook.h"
+#include "asterisk/stasis.h"
+#include "asterisk/json.h"
+#include "asterisk/stasis_channels.h"
 
 struct ast_srtp_res *res_srtp = NULL;
 struct ast_srtp_policy_res *res_srtp_policy = NULL;
@@ -73,10 +184,10 @@ struct ast_rtp_instance {
        int keepalive;
        /*! Glue currently in use */
        struct ast_rtp_glue *glue;
-       /*! Channel associated with the instance */
-       struct ast_channel *chan;
        /*! SRTP info associated with the instance */
        struct ast_srtp *srtp;
+       /*! Channel unique ID */
+       char channel_uniqueid[AST_MAX_UNIQUEID];
 };
 
 /*! List of RTP engines that are currently registered */
@@ -109,6 +220,9 @@ static int mime_types_len = 0;
 static struct ast_rtp_payload_type static_RTP_PT[AST_RTP_MAX_PT];
 static ast_rwlock_t static_RTP_PT_lock;
 
+/*! \brief \ref stasis topic for RTP related messages */
+static struct stasis_topic *rtp_topic;
+
 int ast_rtp_engine_register2(struct ast_rtp_engine *engine, struct ast_module *module)
 {
        struct ast_rtp_engine *current_engine;
@@ -292,6 +406,16 @@ struct ast_rtp_instance *ast_rtp_instance_new(const char *engine_name,
        return instance;
 }
 
+const char *ast_rtp_instance_get_channel_id(struct ast_rtp_instance *instance)
+{
+       return instance->channel_uniqueid;
+}
+
+void ast_rtp_instance_set_channel_id(struct ast_rtp_instance *instance, const char *uniqueid)
+{
+       ast_copy_string(instance->channel_uniqueid, uniqueid, sizeof(instance->channel_uniqueid));
+}
+
 void ast_rtp_instance_set_data(struct ast_rtp_instance *instance, void *data)
 {
        instance->data = data;
@@ -1317,11 +1441,6 @@ struct ast_rtp_glue *ast_rtp_instance_get_active_glue(struct ast_rtp_instance *i
        return instance->glue;
 }
 
-struct ast_channel *ast_rtp_instance_get_chan(struct ast_rtp_instance *instance)
-{
-       return instance->chan;
-}
-
 int ast_rtp_engine_register_srtp(struct ast_srtp_res *srtp_res, struct ast_srtp_policy_res *policy_res)
 {
        if (res_srtp || res_srtp_policy) {
@@ -1552,6 +1671,256 @@ int ast_rtp_engine_unload_format(const struct ast_format *format)
        return 0;
 }
 
+/*! \internal \brief \ref stasis message payload for RTCP messages */
+struct rtcp_message_payload {
+       struct ast_channel_snapshot *snapshot;  /*< The channel snapshot, if available */
+       struct ast_rtp_rtcp_report *report;     /*< The RTCP report */
+       struct ast_json *blob;                  /*< Extra JSON data to publish */
+};
+
+static void rtcp_message_payload_dtor(void *obj)
+{
+       struct rtcp_message_payload *payload = obj;
+
+       ao2_cleanup(payload->report);
+       ao2_cleanup(payload->snapshot);
+       ast_json_unref(payload->blob);
+}
+
+static struct ast_manager_event_blob *rtcp_report_to_ami(struct stasis_message *msg)
+{
+       struct rtcp_message_payload *payload = stasis_message_data(msg);
+       RAII_VAR(struct ast_str *, channel_string, NULL, ast_free);
+       RAII_VAR(struct ast_str *, packet_string, ast_str_create(512), ast_free);
+       unsigned int ssrc = payload->report->ssrc;
+       unsigned int type = payload->report->type;
+       unsigned int report_count = payload->report->reception_report_count;
+       int i;
+
+       if (!packet_string) {
+               return NULL;
+       }
+
+       if (payload->snapshot) {
+               channel_string = ast_manager_build_channel_state_string(payload->snapshot);
+               if (!channel_string) {
+                       return NULL;
+               }
+       }
+
+       if (payload->blob) {
+               /* Optional data */
+               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");
+               if (to) {
+                       ast_str_append(&packet_string, 0, "To: %s\r\n", ast_json_string_get(to));
+               }
+               if (from) {
+                       ast_str_append(&packet_string, 0, "From: %s\r\n", ast_json_string_get(from));
+               }
+               if (rtt) {
+                       ast_str_append(&packet_string, 0, "RTT: %4.4f\r\n", ast_json_real_get(rtt));
+               }
+       }
+
+       ast_str_append(&packet_string, 0, "SSRC: 0x%.8x\r\n", ssrc);
+       ast_str_append(&packet_string, 0, "PT: %u(%s)\r\n", type, type== AST_RTP_RTCP_SR ? "SR" : "RR");
+       ast_str_append(&packet_string, 0, "ReportCount: %u\r\n", report_count);
+       if (type == AST_RTP_RTCP_SR) {
+               ast_str_append(&packet_string, 0, "SentNTP: %lu.%06lu\r\n",
+                       (unsigned long)payload->report->sender_information.ntp_timestamp.tv_sec,
+                       (unsigned long)payload->report->sender_information.ntp_timestamp.tv_usec * 4096);
+               ast_str_append(&packet_string, 0, "SentRTP: %u\r\n",
+                               payload->report->sender_information.rtp_timestamp);
+               ast_str_append(&packet_string, 0, "SentPackets: %u\r\n",
+                               payload->report->sender_information.packet_count);
+               ast_str_append(&packet_string, 0, "SentOctets: %u\r\n",
+                               payload->report->sender_information.octet_count);
+       }
+
+       for (i = 0; i < report_count; i++) {
+               RAII_VAR(struct ast_str *, report_string, NULL, ast_free);
+
+               if (!payload->report->report_block[i]) {
+                       break;
+               }
+
+               report_string = ast_str_create(256);
+               if (!report_string) {
+                       return NULL;
+               }
+
+               ast_str_append(&report_string, 0, "Report%dSourceSSRC: 0x%.8x\r\n",
+                               i, payload->report->report_block[i]->source_ssrc);
+               ast_str_append(&report_string, 0, "Report%dFractionLost: %u\r\n",
+                               i, payload->report->report_block[i]->lost_count.fraction);
+               ast_str_append(&report_string, 0, "Report%dCumulativeLost: %u\r\n",
+                               i, payload->report->report_block[i]->lost_count.packets);
+               ast_str_append(&report_string, 0, "Report%dHighestSequence: %u\r\n",
+                               i, payload->report->report_block[i]->highest_seq_no & 0xffff);
+               ast_str_append(&report_string, 0, "Report%dSequenceNumberCycles: %u\r\n",
+                               i, payload->report->report_block[i]->highest_seq_no >> 16);
+               ast_str_append(&report_string, 0, "Report%dIAJitter: %u\r\n",
+                               i, payload->report->report_block[i]->ia_jitter);
+               ast_str_append(&report_string, 0, "Report%dLSR: %u\r\n",
+                               i, payload->report->report_block[i]->lsr);
+               ast_str_append(&report_string, 0, "Report%dDLSR: %4.4f\r\n",
+                               i, ((double)payload->report->report_block[i]->dlsr) / 65536);
+               ast_str_append(&packet_string, 0, "%s", ast_str_buffer(report_string));
+       }
+
+       return ast_manager_event_blob_create(EVENT_FLAG_REPORTING,
+               stasis_message_type(msg) == ast_rtp_rtcp_received_type() ? "RTCPReceived" : "RTCPSent",
+               "%s%s",
+               AS_OR(channel_string, ""),
+               ast_str_buffer(packet_string));
+}
+
+static struct ast_json *rtcp_report_to_json(struct stasis_message *msg)
+{
+       struct rtcp_message_payload *payload = stasis_message_data(msg);
+       RAII_VAR(struct ast_json *, json_rtcp_report, NULL, ast_json_unref);
+       RAII_VAR(struct ast_json *, json_rtcp_report_blocks, NULL, ast_json_unref);
+       RAII_VAR(struct ast_json *, json_rtcp_sender_info, NULL, ast_json_unref);
+       struct ast_json * json_payload;
+       int i;
+
+       json_rtcp_report_blocks = ast_json_array_create();
+       if (!json_rtcp_report_blocks) {
+               return NULL;
+       }
+
+       for (i = 0; i < payload->report->reception_report_count; i++) {
+               struct ast_json *json_report_block;
+               json_report_block = ast_json_pack("{s: i, s: i, s: i, s: i, s: i, s: i, s: i}",
+                               "source_ssrc", payload->report->report_block[i]->source_ssrc,
+                               "fraction_lost", payload->report->report_block[i]->lost_count.fraction,
+                               "packets_lost", payload->report->report_block[i]->lost_count.packets,
+                               "highest_seq_no", payload->report->report_block[i]->highest_seq_no,
+                               "ia_jitter", payload->report->report_block[i]->ia_jitter,
+                               "lsr", payload->report->report_block[i]->lsr,
+                               "dlsr", payload->report->report_block[i]->dlsr);
+               if (!json_report_block) {
+                       return NULL;
+               }
+
+               if (ast_json_array_append(json_rtcp_report_blocks, json_report_block)) {
+                       return NULL;
+               }
+       }
+
+       if (payload->report->type == AST_RTP_RTCP_SR) {
+               json_rtcp_sender_info = ast_json_pack("{s: i, s: i, s: i, s: i, s: i}",
+                               "ntp_timestamp_sec", payload->report->sender_information.ntp_timestamp.tv_sec,
+                               "ntp_timestamp_usec", payload->report->sender_information.ntp_timestamp.tv_usec,
+                               "rtp_timestamp", payload->report->sender_information.rtp_timestamp,
+                               "packets", payload->report->sender_information.packet_count,
+                               "octets", payload->report->sender_information.octet_count);
+               if (!json_rtcp_sender_info) {
+                       return NULL;
+               }
+       }
+
+       json_rtcp_report = ast_json_pack("{s: i, s: i, s: i, s: O, s: O}",
+                       "ssrc", payload->report->ssrc,
+                       "type", payload->report->type,
+                       "report_count", payload->report->reception_report_count,
+                       "sender_information", json_rtcp_sender_info ? json_rtcp_sender_info : ast_json_null(),
+                       "report_blocks", json_rtcp_report_blocks);
+       if (!json_rtcp_report) {
+               return NULL;
+       }
+
+       json_payload = ast_json_pack("{s: O, s: O, s: O}",
+               "channel", payload->snapshot ? ast_channel_snapshot_to_json(payload->snapshot) : ast_json_null(),
+               "rtcp_report", json_rtcp_report,
+               "blob", payload->blob);
+       return json_payload;
+}
+
+static void rtp_rtcp_report_dtor(void *obj)
+{
+       int i;
+       struct ast_rtp_rtcp_report *rtcp_report = obj;
+
+       for (i = 0; i < rtcp_report->reception_report_count; i++) {
+               ast_free(rtcp_report->report_block[i]);
+       }
+}
+
+struct ast_rtp_rtcp_report *ast_rtp_rtcp_report_alloc(unsigned int report_blocks)
+{
+       struct ast_rtp_rtcp_report *rtcp_report;
+
+       /* Size of object is sizeof the report + the number of report_blocks * sizeof pointer */
+       rtcp_report = ao2_alloc((sizeof(*rtcp_report) + report_blocks * sizeof(struct ast_rtp_rtcp_report_block *)),
+               rtp_rtcp_report_dtor);
+
+       return rtcp_report;
+}
+
+void ast_rtp_publish_rtcp_message(struct ast_rtp_instance *rtp,
+               struct stasis_message_type *message_type,
+               struct ast_rtp_rtcp_report *report,
+               struct ast_json *blob)
+{
+       RAII_VAR(struct rtcp_message_payload *, payload,
+                       ao2_alloc(sizeof(*payload), rtcp_message_payload_dtor), ao2_cleanup);
+       RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
+       RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+
+       if (!payload || !report) {
+               return;
+       }
+
+       if (!ast_strlen_zero(rtp->channel_uniqueid)) {
+               snapshot = ast_channel_snapshot_get_latest(rtp->channel_uniqueid);
+               if (snapshot) {
+                       ao2_ref(snapshot, +1);
+               }
+       }
+
+       if (blob) {
+               ast_json_ref(blob);
+       }
+       ao2_ref(report, 1);
+       payload->snapshot = snapshot;
+       payload->blob = blob;
+       payload->report = report;
+
+       message = stasis_message_create(message_type, payload);
+       if (!message) {
+               return;
+       }
+
+       stasis_publish(ast_rtp_topic(), message);
+}
+
+/*!
+ * @{ \brief Define RTCP/RTP message types.
+ */
+STASIS_MESSAGE_TYPE_DEFN(ast_rtp_rtcp_sent_type,
+               .to_ami = rtcp_report_to_ami,
+               .to_json = rtcp_report_to_json,);
+STASIS_MESSAGE_TYPE_DEFN(ast_rtp_rtcp_received_type,
+               .to_ami = rtcp_report_to_ami,
+               .to_json = rtcp_report_to_json,);
+/*! @} */
+
+struct stasis_topic *ast_rtp_topic(void)
+{
+       return rtp_topic;
+}
+
+static void rtp_engine_shutdown(void)
+{
+       ao2_cleanup(rtp_topic);
+       rtp_topic = NULL;
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_rtp_rtcp_received_type);
+       STASIS_MESSAGE_TYPE_CLEANUP(ast_rtp_rtcp_sent_type);
+}
+
 int ast_rtp_engine_init()
 {
        struct ast_format tmpfmt;
@@ -1559,6 +1928,14 @@ int ast_rtp_engine_init()
        ast_rwlock_init(&mime_types_lock);
        ast_rwlock_init(&static_RTP_PT_lock);
 
+       rtp_topic = stasis_topic_create("rtp_topic");
+       if (!rtp_topic) {
+               return -1;
+       }
+       STASIS_MESSAGE_TYPE_INIT(ast_rtp_rtcp_sent_type);
+       STASIS_MESSAGE_TYPE_INIT(ast_rtp_rtcp_received_type);
+       ast_register_atexit(rtp_engine_shutdown);
+
        /* Define all the RTP mime types available */
        set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_G723_1, 0), 0, "audio", "G723", 8000);
        set_next_mime_type(ast_format_set(&tmpfmt, AST_FORMAT_GSM, 0), 0, "audio", "GSM", 8000);
index 3f19dbd..efe5b96 100644 (file)
@@ -85,8 +85,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #define TURN_ALLOCATION_WAIT_TIME 2000
 
 #define RTCP_PT_FUR     192
-#define RTCP_PT_SR      200
-#define RTCP_PT_RR      201
+#define RTCP_PT_SR      AST_RTP_RTCP_SR
+#define RTCP_PT_RR      AST_RTP_RTCP_RR
 #define RTCP_PT_SDES    202
 #define RTCP_PT_BYE     203
 #define RTCP_PT_APP     204
@@ -2182,52 +2182,46 @@ static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw)
        *lsw = frac;
 }
 
-/*! \brief Send RTCP recipient's report */
-static int ast_rtcp_write_rr(struct ast_rtp_instance *instance)
+static void ntp2timeval(unsigned int msw, unsigned int lsw, struct timeval *tv)
 {
-       struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
-       int res;
-       int len = 32;
-       unsigned int lost;
-       unsigned int extended;
-       unsigned int expected;
+       tv->tv_sec = msw - 2208988800u;
+       tv->tv_usec = ((lsw << 6) / 3650) - (lsw >> 12) - (lsw >> 8);
+}
+
+static void calculate_lost_packet_statistics(struct ast_rtp *rtp,
+               unsigned int *lost_packets,
+               int *fraction_lost)
+{
+       unsigned int extended_seq_no;
+       unsigned int expected_packets;
        unsigned int expected_interval;
        unsigned int received_interval;
-       int lost_interval;
-       struct timeval now;
-       unsigned int *rtcpheader;
-       char bdata[1024];
-       struct timeval dlsr;
-       int fraction;
-       int rate = rtp_get_rate(&rtp->f.subclass.format);
-       int ice;
        double rxlost_current;
-       struct ast_sockaddr remote_address = { {0,} };
+       int lost_interval;
 
-       if (!rtp || !rtp->rtcp) {
-               return 0;
+       /* Compute statistics */
+       extended_seq_no = rtp->cycles + rtp->lastrxseqno;
+       expected_packets = extended_seq_no - rtp->seedrxseqno + 1;
+       if (rtp->rxcount > expected_packets) {
+               expected_packets += rtp->rxcount - expected_packets;
        }
-
-       if (ast_sockaddr_isnull(&rtp->rtcp->them)) {
-               /*
-                * RTCP was stopped.
-                */
-               return 0;
-       }
-
-       extended = rtp->cycles + rtp->lastrxseqno;
-       expected = extended - rtp->seedrxseqno + 1;
-       lost = expected - rtp->rxcount;
-       expected_interval = expected - rtp->rtcp->expected_prior;
-       rtp->rtcp->expected_prior = expected;
+       *lost_packets = expected_packets - rtp->rxcount;
+       expected_interval = expected_packets - rtp->rtcp->expected_prior;
        received_interval = rtp->rxcount - rtp->rtcp->received_prior;
-       rtp->rtcp->received_prior = rtp->rxcount;
        lost_interval = expected_interval - received_interval;
+       if (expected_interval == 0 || lost_interval <= 0) {
+               *fraction_lost = 0;
+       } else {
+               *fraction_lost = (lost_interval << 8) / expected_interval;
+       }
 
+       /* Update RTCP statistics */
+       rtp->rtcp->received_prior = rtp->rxcount;
+       rtp->rtcp->expected_prior = expected_packets;
        if (lost_interval <= 0) {
                rtp->rtcp->rxlost = 0;
        } else {
-               rtp->rtcp->rxlost = rtp->rtcp->rxlost;
+               rtp->rtcp->rxlost = lost_interval;
        }
        if (rtp->rtcp->rxlost_count == 0) {
                rtp->rtcp->minrxlost = rtp->rtcp->rxlost;
@@ -2238,200 +2232,162 @@ static int ast_rtcp_write_rr(struct ast_rtp_instance *instance)
        if (lost_interval > rtp->rtcp->maxrxlost) {
                rtp->rtcp->maxrxlost = rtp->rtcp->rxlost;
        }
-
-       rxlost_current = normdev_compute(rtp->rtcp->normdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->rxlost_count);
-       rtp->rtcp->stdev_rxlost = stddev_compute(rtp->rtcp->stdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->normdev_rxlost, rxlost_current, rtp->rtcp->rxlost_count);
+       rxlost_current = normdev_compute(rtp->rtcp->normdev_rxlost,
+                       rtp->rtcp->rxlost,
+                       rtp->rtcp->rxlost_count);
+       rtp->rtcp->stdev_rxlost = stddev_compute(rtp->rtcp->stdev_rxlost,
+                       rtp->rtcp->rxlost,
+                       rtp->rtcp->normdev_rxlost,
+                       rxlost_current,
+                       rtp->rtcp->rxlost_count);
        rtp->rtcp->normdev_rxlost = rxlost_current;
        rtp->rtcp->rxlost_count++;
-
-       if (expected_interval == 0 || lost_interval <= 0) {
-               fraction = 0;
-       } else {
-               fraction = (lost_interval << 8) / expected_interval;
-       }
-       gettimeofday(&now, NULL);
-       timersub(&now, &rtp->rtcp->rxlsr, &dlsr);
-       rtcpheader = (unsigned int *)bdata;
-       rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_RR << 16) | ((len/4)-1));
-       rtcpheader[1] = htonl(rtp->ssrc);
-       rtcpheader[2] = htonl(rtp->themssrc);
-       rtcpheader[3] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff));
-       rtcpheader[4] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff)));
-       rtcpheader[5] = htonl((unsigned int)(rtp->rxjitter * rate));
-       rtcpheader[6] = htonl(rtp->rtcp->themrxlsr);
-       rtcpheader[7] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000);
-
-       /*! \note Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos
-         it can change mid call, and SDES can't) */
-       rtcpheader[len/4]     = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2);
-       rtcpheader[(len/4)+1] = htonl(rtp->ssrc);               /* Our SSRC */
-       rtcpheader[(len/4)+2] = htonl(0x01 << 24);              /* Empty for the moment */
-       len += 12;
-
-       ast_sockaddr_copy(&remote_address, &rtp->rtcp->them);
-
-       res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, &remote_address, &ice);
-
-       if (res < 0) {
-               ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted: %s\n",strerror(errno));
-               return 0;
-       }
-
-       rtp->rtcp->rr_count++;
-
-       update_address_with_ice_candidate(rtp, COMPONENT_RTCP, &remote_address);
-
-       if (rtcp_debug_test_addr(&remote_address)) {
-               ast_verbose("\n* Sending RTCP RR to %s%s\n"
-                       "  Our SSRC: %u\nTheir SSRC: %u\niFraction lost: %d\nCumulative loss: %u\n"
-                       "  IA jitter: %.4f\n"
-                       "  Their last SR: %u\n"
-                           "  DLSR: %4.4f (sec)\n\n",
-                           ast_sockaddr_stringify(&remote_address),
-                           ice ? " (via ICE)" : "",
-                           rtp->ssrc, rtp->themssrc, fraction, lost,
-                           rtp->rxjitter,
-                           rtp->rtcp->themrxlsr,
-                           (double)(ntohl(rtcpheader[7])/65536.0));
-       }
-
-       return res;
 }
 
-/*! \brief Send RTCP sender's report */
-static int ast_rtcp_write_sr(struct ast_rtp_instance *instance)
+/*! \brief Send RTCP SR or RR report */
+static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+       RAII_VAR(struct ast_json *, message_blob, NULL, ast_json_unref);
        int res;
        int len = 0;
        struct timeval now;
        unsigned int now_lsw;
        unsigned int now_msw;
        unsigned int *rtcpheader;
-       unsigned int lost;
-       unsigned int extended;
-       unsigned int expected;
-       unsigned int expected_interval;
-       unsigned int received_interval;
-       int lost_interval;
-       int fraction;
-       struct timeval dlsr;
+       unsigned int lost_packets;
+       int fraction_lost;
+       struct timeval dlsr = { 0, };
        char bdata[512];
        int rate = rtp_get_rate(&rtp->f.subclass.format);
        int ice;
+       int header_offset = 0;
        struct ast_sockaddr remote_address = { {0,} };
+       struct ast_rtp_rtcp_report_block *report_block;
+       RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,
+                       ast_rtp_rtcp_report_alloc(1),
+                       ao2_cleanup);
 
        if (!rtp || !rtp->rtcp) {
                return 0;
        }
 
        if (ast_sockaddr_isnull(&rtp->rtcp->them)) {  /* This'll stop rtcp for this rtp session */
-               /*
-                * RTCP was stopped.
-                */
+               /* RTCP was stopped. */
                return 0;
        }
 
+       if (!rtcp_report) {
+               return 1;
+       }
+
+       report_block = ast_calloc(1, sizeof(*report_block));
+       if (!report_block) {
+               return 1;
+       }
+
+       /* Compute statistics */
+       calculate_lost_packet_statistics(rtp, &lost_packets, &fraction_lost);
+
        gettimeofday(&now, NULL);
-       timeval2ntp(now, &now_msw, &now_lsw); /* fill thses ones in from utils.c*/
+       rtcp_report->reception_report_count = 1;
+       rtcp_report->ssrc = rtp->ssrc;
+       rtcp_report->type = sr ? RTCP_PT_SR : RTCP_PT_RR;
+       if (sr) {
+               rtcp_report->sender_information.ntp_timestamp = now;
+               rtcp_report->sender_information.rtp_timestamp = rtp->lastts;
+               rtcp_report->sender_information.packet_count = rtp->txcount;
+               rtcp_report->sender_information.octet_count = rtp->txoctetcount;
+       }
+       rtcp_report->report_block[0] = report_block;
+       report_block->source_ssrc = rtp->themssrc;
+       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 * rate);
+       report_block->lsr = rtp->rtcp->themrxlsr;
+       /* If we haven't received an SR report, DLSR should be 0 */
+       if (!ast_tvzero(rtp->rtcp->rxlsr)) {
+               timersub(&now, &rtp->rtcp->rxlsr, &dlsr);
+               report_block->dlsr = (((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000;
+       }
+       timeval2ntp(rtcp_report->sender_information.ntp_timestamp, &now_msw, &now_lsw);
        rtcpheader = (unsigned int *)bdata;
-       rtcpheader[1] = htonl(rtp->ssrc);               /* Our SSRC */
-       rtcpheader[2] = htonl(now_msw);                 /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/
-       rtcpheader[3] = htonl(now_lsw);                 /* now, LSW */
-       rtcpheader[4] = htonl(rtp->lastts);             /* FIXME shouldn't be that, it should be now */
-       rtcpheader[5] = htonl(rtp->txcount);            /* No. packets sent */
-       rtcpheader[6] = htonl(rtp->txoctetcount);       /* No. bytes sent */
-       len += 28;
-
-       extended = rtp->cycles + rtp->lastrxseqno;
-       expected = extended - rtp->seedrxseqno + 1;
-       if (rtp->rxcount > expected) {
-               expected += rtp->rxcount - expected;
-       }
-       lost = expected - rtp->rxcount;
-       expected_interval = expected - rtp->rtcp->expected_prior;
-       rtp->rtcp->expected_prior = expected;
-       received_interval = rtp->rxcount - rtp->rtcp->received_prior;
-       rtp->rtcp->received_prior = rtp->rxcount;
-       lost_interval = expected_interval - received_interval;
-       if (expected_interval == 0 || lost_interval <= 0) {
-               fraction = 0;
-       } else {
-               fraction = (lost_interval << 8) / expected_interval;
-       }
-       timersub(&now, &rtp->rtcp->rxlsr, &dlsr);
-       rtcpheader[7] = htonl(rtp->themssrc);
-       rtcpheader[8] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff));
-       rtcpheader[9] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff)));
-       rtcpheader[10] = htonl((unsigned int)(rtp->rxjitter * rate));
-       rtcpheader[11] = htonl(rtp->rtcp->themrxlsr);
-       rtcpheader[12] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000);
+       rtcpheader[1] = htonl(rtcp_report->ssrc);            /* Our SSRC */
+       len += 8;
+       if (sr) {
+               header_offset = 5;
+               rtcpheader[2] = htonl(now_msw);                 /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/
+               rtcpheader[3] = htonl(now_lsw);                 /* now, LSW */
+               rtcpheader[4] = htonl(rtcp_report->sender_information.rtp_timestamp);
+               rtcpheader[5] = htonl(rtcp_report->sender_information.packet_count);
+               rtcpheader[6] = htonl(rtcp_report->sender_information.octet_count);
+               len += 20;
+       }
+       rtcpheader[2 + header_offset] = htonl(report_block->source_ssrc);     /* Their SSRC */
+       rtcpheader[3 + header_offset] = htonl((report_block->lost_count.fraction << 24) | report_block->lost_count.packets);
+       rtcpheader[4 + header_offset] = htonl(report_block->highest_seq_no);
+       rtcpheader[5 + header_offset] = htonl(report_block->ia_jitter);
+       rtcpheader[6 + header_offset] = htonl(report_block->lsr);
+       rtcpheader[7 + header_offset] = htonl(report_block->dlsr);
        len += 24;
-
-       rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SR << 16) | ((len/4)-1));
+       rtcpheader[0] = htonl((2 << 30) | (1 << 24) | ((sr ? RTCP_PT_SR : RTCP_PT_RR) << 16) | ((len/4)-1));
 
        /* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */
        /* it can change mid call, and SDES can't) */
        rtcpheader[len/4]     = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2);
-       rtcpheader[(len/4)+1] = htonl(rtp->ssrc);               /* Our SSRC */
-       rtcpheader[(len/4)+2] = htonl(0x01 << 24);                    /* Empty for the moment */
+       rtcpheader[(len/4)+1] = htonl(rtcp_report->ssrc);
+       rtcpheader[(len/4)+2] = htonl(0x01 << 24);
        len += 12;
 
        ast_sockaddr_copy(&remote_address, &rtp->rtcp->them);
-
        res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, &remote_address, &ice);
        if (res < 0) {
-               ast_log(LOG_ERROR, "RTCP SR transmission error to %s, rtcp halted %s\n",
+               ast_log(LOG_ERROR, "RTCP %s transmission error to %s, rtcp halted %s\n",
+                       sr ? "SR" : "RR",
                        ast_sockaddr_stringify(&rtp->rtcp->them),
                        strerror(errno));
                return 0;
        }
 
-       /* FIXME Don't need to get a new one */
-       gettimeofday(&rtp->rtcp->txlsr, NULL);
-       rtp->rtcp->sr_count++;
-
-       rtp->rtcp->lastsrtxcount = rtp->txcount;
+       /* Update RTCP SR/RR statistics */
+       if (sr) {
+               rtp->rtcp->txlsr = rtcp_report->sender_information.ntp_timestamp;
+               rtp->rtcp->sr_count++;
+               rtp->rtcp->lastsrtxcount = rtp->txcount;
+       } else {
+               rtp->rtcp->rr_count++;
+       }
 
        update_address_with_ice_candidate(rtp, COMPONENT_RTCP, &remote_address);
 
        if (rtcp_debug_test_addr(&rtp->rtcp->them)) {
-               ast_verbose("* Sent RTCP SR to %s%s\n", ast_sockaddr_stringify(&remote_address), ice ? " (via ICE)" : "");
-               ast_verbose("  Our SSRC: %u\n", rtp->ssrc);
-               ast_verbose("  Sent(NTP): %u.%010u\n", (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096);
-               ast_verbose("  Sent(RTP): %u\n", rtp->lastts);
-               ast_verbose("  Sent packets: %u\n", rtp->txcount);
-               ast_verbose("  Sent octets: %u\n", rtp->txoctetcount);
+               ast_verbose("* Sent RTCP %s to %s%s\n", sr ? "SR" : "RR",
+                               ast_sockaddr_stringify(&remote_address), ice ? " (via ICE)" : "");
+               ast_verbose("  Our SSRC: %u\n", rtcp_report->ssrc);
+               if (sr) {
+                       ast_verbose("  Sent(NTP): %u.%010u\n",
+                               (unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_sec,
+                               (unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_usec * 4096);
+                       ast_verbose("  Sent(RTP): %u\n", rtcp_report->sender_information.rtp_timestamp);
+                       ast_verbose("  Sent packets: %u\n", rtcp_report->sender_information.packet_count);
+                       ast_verbose("  Sent octets: %u\n", rtcp_report->sender_information.octet_count);
+               }
                ast_verbose("  Report block:\n");
-               ast_verbose("  Fraction lost: %u\n", fraction);
-               ast_verbose("  Cumulative loss: %u\n", lost);
-               ast_verbose("  IA jitter: %.4f\n", rtp->rxjitter);
-               ast_verbose("  Their last SR: %u\n", rtp->rtcp->themrxlsr);
-               ast_verbose("  DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[12])/65536.0));
-       }
-       manager_event(EVENT_FLAG_REPORTING, "RTCPSent", "To: %s\r\n"
-                                           "OurSSRC: %u\r\n"
-                                           "SentNTP: %u.%010u\r\n"
-                                           "SentRTP: %u\r\n"
-                                           "SentPackets: %u\r\n"
-                                           "SentOctets: %u\r\n"
-                                           "ReportBlock:\r\n"
-                                           "FractionLost: %u\r\n"
-                                           "CumulativeLoss: %u\r\n"
-                                           "IAJitter: %.4f\r\n"
-                                           "TheirLastSR: %u\r\n"
-                     "DLSR: %4.4f (sec)\r\n",
-                     ast_sockaddr_stringify(&remote_address),
-                     rtp->ssrc,
-                     (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096,
-                     rtp->lastts,
-                     rtp->txcount,
-                     rtp->txoctetcount,
-                     fraction,
-                     lost,
-                     rtp->rxjitter,
-                     rtp->rtcp->themrxlsr,
-                     (double)(ntohl(rtcpheader[12])/65536.0));
+               ast_verbose("    Their SSRC: %u\n", report_block->source_ssrc);
+               ast_verbose("    Fraction lost: %u\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 / 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}",
+                       "to", ast_sockaddr_stringify(&remote_address));
+       ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_sent_type(),
+                       rtcp_report,
+                       message_blob);
        return res;
 }
 
@@ -2450,9 +2406,11 @@ static int ast_rtcp_write(const void *data)
        }
 
        if (rtp->txcount > rtp->rtcp->lastsrtxcount) {
-               res = ast_rtcp_write_sr(instance);
+               /* Send an SR */
+               res = ast_rtcp_write_report(instance, 1);
        } else {
-               res = ast_rtcp_write_rr(instance);
+               /* Send an RR */
+               res = ast_rtcp_write_report(instance, 0);
        }
 
        if (!res) {
@@ -2791,7 +2749,6 @@ static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int t
                d=-d;
        }
        rtp->rxjitter += (1./16.) * (d - rtp->rxjitter);
-
        if (rtp->rtcp) {
                if (rtp->rxjitter > rtp->rtcp->maxrxjitter)
                        rtp->rtcp->maxrxjitter = rtp->rxjitter;
@@ -3100,6 +3057,98 @@ static struct ast_frame *process_cn_rfc3389(struct ast_rtp_instance *instance, u
        return &rtp->f;
 }
 
+static int update_rtt_stats(struct ast_rtp *rtp, unsigned int lsr, unsigned int dlsr)
+{
+       struct timeval now;
+       struct timeval rtt_tv;
+       unsigned int msw;
+       unsigned int lsw;
+       unsigned int rtt_msw;
+       unsigned int rtt_lsw;
+       unsigned int lsr_a;
+       unsigned int rtt;
+       double normdevrtt_current;
+
+       gettimeofday(&now, NULL);
+       timeval2ntp(now, &msw, &lsw);
+
+       lsr_a = ((msw & 0x0000ffff) << 16) | ((lsw & 0xffff0000) >> 16);
+       rtt = lsr_a - lsr - dlsr;
+       rtt_msw = (rtt & 0xffff0000) >> 16;
+       rtt_lsw = (rtt & 0x0000ffff) << 16;
+       rtt_tv.tv_sec = rtt_msw;
+       rtt_tv.tv_usec = ((rtt_lsw << 6) / 3650) - (rtt_lsw >> 12) - (rtt_lsw >> 8);
+       rtp->rtcp->rtt = (double)rtt_tv.tv_sec + ((double)rtt_tv.tv_usec / 1000000);
+       if (lsr_a - dlsr < lsr) {
+               return 1;
+       }
+
+       rtp->rtcp->accumulated_transit += rtp->rtcp->rtt;
+       if (rtp->rtcp->rtt_count == 0 || rtp->rtcp->minrtt > rtp->rtcp->rtt) {
+               rtp->rtcp->minrtt = rtp->rtcp->rtt;
+       }
+       if (rtp->rtcp->maxrtt < rtp->rtcp->rtt) {
+               rtp->rtcp->maxrtt = rtp->rtcp->rtt;
+       }
+
+       normdevrtt_current = normdev_compute(rtp->rtcp->normdevrtt,
+                       rtp->rtcp->rtt,
+                       rtp->rtcp->rtt_count);
+       rtp->rtcp->stdevrtt = stddev_compute(rtp->rtcp->stdevrtt,
+                       rtp->rtcp->rtt,
+                       rtp->rtcp->normdevrtt,
+                       normdevrtt_current,
+                       rtp->rtcp->rtt_count);
+       rtp->rtcp->normdevrtt = normdevrtt_current;
+       rtp->rtcp->rtt_count++;
+
+       return 0;
+}
+
+/*! \internal \brief Update RTCP interarrival jitter stats */
+static void update_jitter_stats(struct ast_rtp *rtp, unsigned int ia_jitter)
+{
+       double reported_jitter;
+       double reported_normdev_jitter_current;
+
+       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;
+       }
+       if (reported_jitter < rtp->rtcp->reported_minjitter) {
+               rtp->rtcp->reported_minjitter = reported_jitter;
+       }
+       if (reported_jitter > rtp->rtcp->reported_maxjitter) {
+               rtp->rtcp->reported_maxjitter = reported_jitter;
+       }
+       reported_normdev_jitter_current = normdev_compute(rtp->rtcp->reported_normdev_jitter, reported_jitter, rtp->rtcp->reported_jitter_count);
+       rtp->rtcp->reported_stdev_jitter = stddev_compute(rtp->rtcp->reported_stdev_jitter, reported_jitter, rtp->rtcp->reported_normdev_jitter, reported_normdev_jitter_current, rtp->rtcp->reported_jitter_count);
+       rtp->rtcp->reported_normdev_jitter = reported_normdev_jitter_current;
+}
+
+/*! \internal \brief Update RTCP lost packet stats */
+static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets)
+{
+       double reported_lost;
+       double reported_normdev_lost_current;
+
+       rtp->rtcp->reported_lost = lost_packets;
+       reported_lost = (double)rtp->rtcp->reported_lost;
+       if (rtp->rtcp->reported_jitter_count == 0) {
+               rtp->rtcp->reported_minlost = reported_lost;
+       }
+       if (reported_lost < rtp->rtcp->reported_minlost) {
+               rtp->rtcp->reported_minlost = reported_lost;
+       }
+       if (reported_lost > rtp->rtcp->reported_maxlost) {
+               rtp->rtcp->reported_maxlost = reported_lost;
+       }
+       reported_normdev_lost_current = normdev_compute(rtp->rtcp->reported_normdev_lost, reported_lost, rtp->rtcp->reported_jitter_count);
+       rtp->rtcp->reported_stdev_lost = stddev_compute(rtp->rtcp->reported_stdev_lost, reported_lost, rtp->rtcp->reported_normdev_lost, reported_normdev_lost_current, rtp->rtcp->reported_jitter_count);
+       rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current;
+}
+
 static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
 {
        struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
@@ -3107,6 +3156,12 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
        unsigned char rtcpdata[8192 + AST_FRIENDLY_OFFSET];
        unsigned int *rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET);
        int res, packetwords, position = 0;
+       int report_counter = 0;
+       struct ast_rtp_rtcp_report_block *report_block;
+       RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,
+                       NULL,
+                       ao2_cleanup);
+       RAII_VAR(struct ast_json *, message_blob, NULL, ast_json_unref);
        struct ast_frame *f = &ast_null_frame;
 
        /* Read in RTCP data from the socket */
@@ -3166,10 +3221,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
 
        while (position < packetwords) {
                int i, pt, rc;
-               unsigned int length, dlsr, lsr, msw, lsw, comp;
-               struct timeval now;
-               double rttsec, reported_jitter, reported_normdev_jitter_current, normdevrtt_current, reported_lost, reported_normdev_lost_current;
-               uint64_t rtt = 0;
+               unsigned int length;
 
                i = position;
                length = ntohl(rtcpheader[i]);
@@ -3177,6 +3229,13 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
                rc = (length & 0x1f000000) >> 24;
                length &= 0xffff;
 
+               rtcp_report = ast_rtp_rtcp_report_alloc(rc);
+               if (!rtcp_report) {
+                       return &ast_null_frame;
+               }
+               rtcp_report->reception_report_count = rc;
+               rtcp_report->ssrc = ntohl(rtcpheader[i + 1]);
+
                if ((i + length) > packetwords) {
                        if (rtpdebug) {
                                ast_debug(1, "RTCP Read too short\n");
@@ -3187,28 +3246,41 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
                if (rtcp_debug_test_addr(&addr)) {
                        ast_verbose("\n\nGot RTCP from %s\n",
                                    ast_sockaddr_stringify(&addr));
-                       ast_verbose("PT: %d(%s)\n", pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown");
+                       ast_verbose("PT: %d(%s)\n", pt, (pt == RTCP_PT_SR) ? "Sender Report" :
+                                                       (pt == RTCP_PT_RR) ? "Receiver Report" :
+                                                       (pt == RTCP_PT_FUR) ? "H.261 FUR" : "Unknown");
                        ast_verbose("Reception reports: %d\n", rc);
-                       ast_verbose("SSRC of sender: %u\n", rtcpheader[i + 1]);
+                       ast_verbose("SSRC of sender: %u\n", rtcp_report->ssrc);
                }
 
                i += 2; /* Advance past header and ssrc */
-               if (rc == 0 && pt == RTCP_PT_RR) {      /* We're receiving a receiver report with no reports, which is ok */
+               if (rc == 0 && pt == RTCP_PT_RR) {
+                       /* We're receiving a receiver report with no reports, which is ok */
                        position += (length + 1);
                        continue;
                }
-
                switch (pt) {
                case RTCP_PT_SR:
-                       gettimeofday(&rtp->rtcp->rxlsr,NULL); /* To be able to populate the dlsr */
-                       rtp->rtcp->spc = ntohl(rtcpheader[i+3]);
+                       gettimeofday(&rtp->rtcp->rxlsr, NULL);
+                       rtp->rtcp->themrxlsr = ((ntohl(rtcpheader[i]) & 0x0000ffff) << 16) | ((ntohl(rtcpheader[i + 1]) & 0xffff0000) >> 16);
+                       rtp->rtcp->spc = ntohl(rtcpheader[i + 3]);
                        rtp->rtcp->soc = ntohl(rtcpheader[i + 4]);
-                       rtp->rtcp->themrxlsr = ((ntohl(rtcpheader[i]) & 0x0000ffff) << 16) | ((ntohl(rtcpheader[i + 1]) & 0xffff0000) >> 16); /* Going to LSR in RR*/
 
+                       rtcp_report->type = RTCP_PT_SR;
+                       rtcp_report->sender_information.packet_count = rtp->rtcp->spc;
+                       rtcp_report->sender_information.octet_count = rtp->rtcp->soc;
+                       ntp2timeval((unsigned int)ntohl(rtcpheader[i]),
+                                       (unsigned int)ntohl(rtcpheader[i + 1]),
+                                       &rtcp_report->sender_information.ntp_timestamp);
+                       rtcp_report->sender_information.rtp_timestamp = ntohl(rtcpheader[i + 2]);
                        if (rtcp_debug_test_addr(&addr)) {
-                               ast_verbose("NTP timestamp: %lu.%010lu\n", (unsigned long) ntohl(rtcpheader[i]), (unsigned long) ntohl(rtcpheader[i + 1]) * 4096);
-                               ast_verbose("RTP timestamp: %lu\n", (unsigned long) ntohl(rtcpheader[i + 2]));
-                               ast_verbose("SPC: %lu\tSOC: %lu\n", (unsigned long) ntohl(rtcpheader[i + 3]), (unsigned long) ntohl(rtcpheader[i + 4]));
+                               ast_verbose("NTP timestamp: %u.%010u\n",
+                                               (unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_sec,
+                                               (unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_usec * 4096);
+                               ast_verbose("RTP timestamp: %u\n", rtcp_report->sender_information.rtp_timestamp);
+                               ast_verbose("SPC: %u\tSOC: %u\n",
+                                               rtcp_report->sender_information.packet_count,
+                                               rtcp_report->sender_information.octet_count);
                        }
                        i += 5;
                        if (rc < 1) {
@@ -3216,166 +3288,63 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
                        }
                        /* Intentional fall through */
                case RTCP_PT_RR:
-                       /* Don't handle multiple reception reports (rc > 1) yet */
-                       /* Calculate RTT per RFC */
-                       gettimeofday(&now, NULL);
-                       timeval2ntp(now, &msw, &lsw);
-                       if (ntohl(rtcpheader[i + 4]) && ntohl(rtcpheader[i + 5])) { /* We must have the LSR && DLSR */
-                               comp = ((msw & 0xffff) << 16) | ((lsw & 0xffff0000) >> 16);
-                               lsr = ntohl(rtcpheader[i + 4]);
-                               dlsr = ntohl(rtcpheader[i + 5]);
-                               rtt = comp - lsr - dlsr;
-
-                               /* Convert end to end delay to usec (keeping the calculation in 64bit space)
-                                  sess->ee_delay = (eedelay * 1000) / 65536; */
-                               if (rtt < 4294) {
-                                       rtt = (rtt * 1000000) >> 16;
-                               } else {
-                                       rtt = (rtt * 1000) >> 16;
-                                       rtt *= 1000;
-                               }
-                               rtt = rtt / 1000.;
-                               rttsec = rtt / 1000.;
-                               rtp->rtcp->rtt = rttsec;
-
-                               if (comp - dlsr >= lsr) {
-                                       rtp->rtcp->accumulated_transit += rttsec;
-
-                                       if (rtp->rtcp->rtt_count == 0) {
-                                               rtp->rtcp->minrtt = rttsec;
-                                       }
-
-                                       if (rtp->rtcp->maxrtt<rttsec) {
-                                               rtp->rtcp->maxrtt = rttsec;
-                                       }
-                                       if (rtp->rtcp->minrtt>rttsec) {
-                                               rtp->rtcp->minrtt = rttsec;
-                                       }
-
-                                       normdevrtt_current = normdev_compute(rtp->rtcp->normdevrtt, rttsec, rtp->rtcp->rtt_count);
-
-                                       rtp->rtcp->stdevrtt = stddev_compute(rtp->rtcp->stdevrtt, rttsec, rtp->rtcp->normdevrtt, normdevrtt_current, rtp->rtcp->rtt_count);
-
-                                       rtp->rtcp->normdevrtt = normdevrtt_current;
-
-                                       rtp->rtcp->rtt_count++;
-                               } else if (rtcp_debug_test_addr(&addr)) {
-                                       ast_verbose("Internal RTCP NTP clock skew detected: "
-                                                          "lsr=%u, now=%u, dlsr=%u (%d:%03dms), "
-                                                   "diff=%d\n",
-                                                   lsr, comp, dlsr, dlsr / 65536,
-                                                   (dlsr % 65536) * 1000 / 65536,
-                                                   dlsr - (comp - lsr));
-                               }
-                       }
-
-                       rtp->rtcp->reported_jitter = ntohl(rtcpheader[i + 3]);
-                       reported_jitter = (double) rtp->rtcp->reported_jitter;
-
-                       if (rtp->rtcp->reported_jitter_count == 0) {
-                               rtp->rtcp->reported_minjitter = reported_jitter;
-                       }
-
-                       if (reported_jitter < rtp->rtcp->reported_minjitter) {
-                               rtp->rtcp->reported_minjitter = reported_jitter;
-                       }
-
-                       if (reported_jitter > rtp->rtcp->reported_maxjitter) {
-                               rtp->rtcp->reported_maxjitter = reported_jitter;
-                       }
-
-                       reported_normdev_jitter_current = normdev_compute(rtp->rtcp->reported_normdev_jitter, reported_jitter, rtp->rtcp->reported_jitter_count);
-
-                       rtp->rtcp->reported_stdev_jitter = stddev_compute(rtp->rtcp->reported_stdev_jitter, reported_jitter, rtp->rtcp->reported_normdev_jitter, reported_normdev_jitter_current, rtp->rtcp->reported_jitter_count);
-
-                       rtp->rtcp->reported_normdev_jitter = reported_normdev_jitter_current;
-
-                       rtp->rtcp->reported_lost = ntohl(rtcpheader[i + 1]) & 0xffffff;
-
-                       reported_lost = (double) rtp->rtcp->reported_lost;
-
-                       /* using same counter as for jitter */
-                       if (rtp->rtcp->reported_jitter_count == 0) {
-                               rtp->rtcp->reported_minlost = reported_lost;
+                       if (rtcp_report->type != RTCP_PT_SR) {
+                               rtcp_report->type = RTCP_PT_RR;
                        }
 
-                       if (reported_lost < rtp->rtcp->reported_minlost) {
-                               rtp->rtcp->reported_minlost = reported_lost;
+                       /* Don't handle multiple reception reports (rc > 1) yet */
+                       report_block = ast_calloc(1, sizeof(*report_block));
+                       if (!report_block) {
+                               return &ast_null_frame;
                        }
-
-                       if (reported_lost > rtp->rtcp->reported_maxlost) {
-                               rtp->rtcp->reported_maxlost = reported_lost;
+                       rtcp_report->report_block[report_counter] = report_block;
+                       report_block->source_ssrc = ntohl(rtcpheader[i]);
+                       report_block->lost_count.packets = ntohl(rtcpheader[i + 1]) & 0x00ffffff;
+                       report_block->lost_count.fraction = ((ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24);
+                       report_block->highest_seq_no = ntohl(rtcpheader[i + 2]);
+                       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 (%d:%03dms), "
+                                               "diff=%d\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));
                        }
-                       reported_normdev_lost_current = normdev_compute(rtp->rtcp->reported_normdev_lost, reported_lost, rtp->rtcp->reported_jitter_count);
-
-                       rtp->rtcp->reported_stdev_lost = stddev_compute(rtp->rtcp->reported_stdev_lost, reported_lost, rtp->rtcp->reported_normdev_lost, reported_normdev_lost_current, rtp->rtcp->reported_jitter_count);
-
-                       rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current;
-
+                       update_jitter_stats(rtp, report_block->ia_jitter);
+                       update_lost_stats(rtp, report_block->lost_count.packets);
                        rtp->rtcp->reported_jitter_count++;
 
                        if (rtcp_debug_test_addr(&addr)) {
-                               ast_verbose("  Fraction lost: %ld\n", (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24));
-                               ast_verbose("  Packets lost so far: %d\n", rtp->rtcp->reported_lost);
-                               ast_verbose("  Highest sequence number: %ld\n", (long) (ntohl(rtcpheader[i + 2]) & 0xffff));
-                               ast_verbose("  Sequence number cycles: %ld\n", (long) (ntohl(rtcpheader[i + 2])) >> 16);
-                               ast_verbose("  Interarrival jitter: %u\n", rtp->rtcp->reported_jitter);
-                               ast_verbose("  Last SR(our NTP): %lu.%010lu\n",(unsigned long) ntohl(rtcpheader[i + 4]) >> 16,((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096);
-                               ast_verbose("  DLSR: %4.4f (sec)\n",ntohl(rtcpheader[i + 5])/65536.0);
-                               if (rtt) {
-                                       ast_verbose("  RTT: %lu(sec)\n", (unsigned long) rtt);
-                               }
-                       }
-                       if (rtt) {
-                               manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From: %s\r\n"
-                                                                   "PT: %d(%s)\r\n"
-                                                                   "ReceptionReports: %d\r\n"
-                                                                   "SenderSSRC: %u\r\n"
-                                                                   "FractionLost: %ld\r\n"
-                                                                   "PacketsLost: %d\r\n"
-                                                                   "HighestSequence: %ld\r\n"
-                                                                   "SequenceNumberCycles: %ld\r\n"
-                                                                   "IAJitter: %u\r\n"
-                                                                   "LastSR: %lu.%010lu\r\n"
-                                                                   "DLSR: %4.4f(sec)\r\n"
-                                             "RTT: %llu(sec)\r\n",
-                                             ast_sockaddr_stringify(&addr),
-                                             pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown",
-                                             rc,
-                                             rtcpheader[i + 1],
-                                             (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24),
-                                             rtp->rtcp->reported_lost,
-                                             (long) (ntohl(rtcpheader[i + 2]) & 0xffff),
-                                             (long) (ntohl(rtcpheader[i + 2])) >> 16,
-                                             rtp->rtcp->reported_jitter,
-                                             (unsigned long) ntohl(rtcpheader[i + 4]) >> 16, ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096,
-                                             ntohl(rtcpheader[i + 5])/65536.0,
-                                             (unsigned long long)rtt);
-                       } else {
-                               manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From: %s\r\n"
-                                                                   "PT: %d(%s)\r\n"
-                                                                   "ReceptionReports: %d\r\n"
-                                                                   "SenderSSRC: %u\r\n"
-                                                                   "FractionLost: %ld\r\n"
-                                                                   "PacketsLost: %d\r\n"
-                                                                   "HighestSequence: %ld\r\n"
-                                                                   "SequenceNumberCycles: %ld\r\n"
-                                                                   "IAJitter: %u\r\n"
-                                                                   "LastSR: %lu.%010lu\r\n"
-                                             "DLSR: %4.4f(sec)\r\n",
-                                             ast_sockaddr_stringify(&addr),
-                                             pt, (pt == 200) ? "Sender Report" : (pt == 201) ? "Receiver Report" : (pt == 192) ? "H.261 FUR" : "Unknown",
-                                             rc,
-                                             rtcpheader[i + 1],
-                                             (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24),
-                                             rtp->rtcp->reported_lost,
-                                             (long) (ntohl(rtcpheader[i + 2]) & 0xffff),
-                                             (long) (ntohl(rtcpheader[i + 2])) >> 16,
-                                             rtp->rtcp->reported_jitter,
-                                             (unsigned long) ntohl(rtcpheader[i + 4]) >> 16,
-                                             ((unsigned long) ntohl(rtcpheader[i + 4]) << 16) * 4096,
-                                             ntohl(rtcpheader[i + 5])/65536.0);
+                       ast_verbose("  Fraction lost: %u\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("  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);
                        }
+                       report_counter++;
+
+                       /* If and when we handle more than one report block, this should occur outside
+                        * this loop.
+                        */
+                       message_blob = ast_json_pack("{s: s, s: f}",
+                                       "from", ast_sockaddr_stringify(&addr),
+                                       "rtt", rtp->rtcp->rtt);
+                       ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_received_type(),
+                                       rtcp_report,
+                                       message_blob);
                        break;
                case RTCP_PT_FUR:
                        if (rtcp_debug_test_addr(&addr)) {
@@ -3408,7 +3377,6 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
                }
                position += (length + 1);
        }
-
        rtp->rtcp->rtcp_info = 1;
 
        return f;
@@ -4111,6 +4079,7 @@ static int ast_rtp_get_stat(struct ast_rtp_instance *instance, struct ast_rtp_in
 
        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));
 
        return 0;
 }