diff --git a/channels/chan_gtalk.c b/channels/chan_gtalk.c
index 8a7083eb2f370c803623b855eb6d623c76ce315b..639de47493c7ad4108cc414d28e7b3e1b2328761 100644
--- a/channels/chan_gtalk.c
+++ b/channels/chan_gtalk.c
@@ -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");
diff --git a/channels/chan_gulp.c b/channels/chan_gulp.c
index 6a80651cf858627602dfbf148e93772fe82c505d..144fb6aaa9d811b5680198d2cdf037d7cd2c3273 100644
--- a/channels/chan_gulp.c
+++ b/channels/chan_gulp.c
@@ -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)
diff --git a/channels/chan_h323.c b/channels/chan_h323.c
index e26bb5fb643715c62421c570e9a0a301f2632086..5ed1c8970b1fbf0bb7cbd12d6cd0bf44efa9ae07 100644
--- a/channels/chan_h323.c
+++ b/channels/chan_h323.c
@@ -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;
diff --git a/channels/chan_jingle.c b/channels/chan_jingle.c
index 42dccf666a8083bfc4b3238b75b8f81ac4847e89..e5fd0250690be3b79c37f20f241eaf3ea366bc83 100644
--- a/channels/chan_jingle.c
+++ b/channels/chan_jingle.c
@@ -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);
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index 5a0b7ad84c80f0a65da58e2996e598bdeda9ec25..da3fefa8a0ce6d217d64e2f8401048745e5d05b5 100644
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -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);
diff --git a/channels/chan_motif.c b/channels/chan_motif.c
index 22b5eaec815bda3323069a41e8c715c588b9238f..c77d0c0d2d8f1142109eecf14a16a62937ba1eaa 100644
--- a/channels/chan_motif.c
+++ b/channels/chan_motif.c
@@ -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);
diff --git a/channels/chan_multicast_rtp.c b/channels/chan_multicast_rtp.c
index dfa0680182c50f86e46b06641755b69f3d9d72fe..4629829698ee3f754782513dfa24229767da9696 100644
--- a/channels/chan_multicast_rtp.c
+++ b/channels/chan_multicast_rtp.c
@@ -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);
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 9a5f086ad2f8930e12d12187017281f1231e6c76..9373a64351b109bec13253cb8b7aa19cad783b02 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -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.
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index 647213228e6037ea6c45e54741b2f0ed64508714..bb0888f269a8bb9502cae247712d5cbe7e226a65 100644
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -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;
diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c
index 5a5674daa61c3dffdc3f546fd70b1d127b460422..53ffa0044c7e63ff8ea56f4630ef42da09c76ab8 100644
--- a/channels/chan_unistim.c
+++ b/channels/chan_unistim.c
@@ -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;
diff --git a/include/asterisk/cdr.h b/include/asterisk/cdr.h
index 9d1f6d72d4f18247f5ce2a1c1c68a7c489fbe8e3..cd0501f06f55cb576c16482a0dfb399bc8ca4032 100644
--- a/include/asterisk/cdr.h
+++ b/include/asterisk/cdr.h
@@ -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 */
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 2e74684183c9a8ec9b10d21ca6f54b6334541a42..0f554910865c4bfdb2bdad5e1ec8944db0f14bf9 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -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 */
diff --git a/include/asterisk/json.h b/include/asterisk/json.h
index 0584c99afce65b8444d145fb944c4f31e4314cd3..a735fd36bb7542983e4183f190973de72b7b1c25 100644
--- a/include/asterisk/json.h
+++ b/include/asterisk/json.h
@@ -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);
+
 /*!@}*/
 
 /*!@{*/
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index e2567f50816116d40b27d447971502ed4231f2fa..bda13e891a4409d1724831180f4beceb91764df7 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -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; \
@@ -1540,6 +1599,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
  *
@@ -1964,27 +2047,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
  *
@@ -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
diff --git a/main/asterisk.c b/main/asterisk.c
index aa33f31e4834c14b0fd0e6a07b3b0e0ff24c7161..b8cf43e37b71e1e69190d0868764c40fbb9f82a5 100644
--- a/main/asterisk.c
+++ b/main/asterisk.c
@@ -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.
@@ -120,7 +120,7 @@
  * \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();
 
diff --git a/main/json.c b/main/json.c
index c185b053fdc9775193bee20ebe7f7d8212f89aad..1b0e4127569e7b5910e3ea71a3618c11815eaea2 100644
--- a/main/json.c
+++ b/main/json.c
@@ -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)
 {
diff --git a/main/manager.c b/main/manager.c
index 02d61dad5f1cc1ff4884d0c1b95d66078ca564e3..b1c7b5361a48ed1f1ea7ff70ee39ad18b5392064 100644
--- a/main/manager.c
+++ b/main/manager.c
@@ -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;
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 8e58f658d1f19c0d8f4355cbd610301d2ba5865b..4dd4d46def8eb09b37774606c30b2e81540bafb8 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -25,6 +25,114 @@
 
 /*** 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);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 3f19dbdebbb9125d430689efa4acb44a504f2f73..efe5b967e0974c2daf5f811460791cac8055c92a 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -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;
 }