diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 32291e54c22766ae1622b95841e9cc2db884ad70..729f453f925ae8bc6a7cb862d29ba2fa92f4a128 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -223,17 +223,6 @@ static void chan_pjsip_get_codec(struct ast_channel *chan, struct ast_format_cap
 	ast_format_cap_append_from_cap(result, channel->session->endpoint->media.codecs, AST_MEDIA_TYPE_UNKNOWN);
 }
 
-static int send_direct_media_request(void *data)
-{
-	struct ast_sip_session *session = data;
-	int res;
-
-	res = ast_sip_session_refresh(session, NULL, NULL, NULL,
-		session->endpoint->media.direct_media.method, 1);
-	ao2_ref(session, -1);
-	return res;
-}
-
 /*! \brief Destructor function for \ref transport_info_data */
 static void transport_info_destroy(void *obj)
 {
@@ -302,6 +291,83 @@ static int check_for_rtp_changes(struct ast_channel *chan, struct ast_rtp_instan
 	return changed;
 }
 
+struct rtp_direct_media_data {
+	struct ast_channel *chan;
+	struct ast_rtp_instance *rtp;
+	struct ast_rtp_instance *vrtp;
+	struct ast_format_cap *cap;
+	struct ast_sip_session *session;
+};
+
+static void rtp_direct_media_data_destroy(void *data)
+{
+	struct rtp_direct_media_data *cdata = data;
+
+	ao2_cleanup(cdata->session);
+	ao2_cleanup(cdata->cap);
+	ao2_cleanup(cdata->vrtp);
+	ao2_cleanup(cdata->rtp);
+	ao2_cleanup(cdata->chan);
+}
+
+static struct rtp_direct_media_data *rtp_direct_media_data_create(
+	struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp,
+	const struct ast_format_cap *cap, struct ast_sip_session *session)
+{
+	struct rtp_direct_media_data *cdata = ao2_alloc(sizeof(*cdata), rtp_direct_media_data_destroy);
+
+	if (!cdata) {
+		return NULL;
+	}
+
+	cdata->chan = ao2_bump(chan);
+	cdata->rtp = ao2_bump(rtp);
+	cdata->vrtp = ao2_bump(vrtp);
+	cdata->cap = ao2_bump((struct ast_format_cap *)cap);
+	cdata->session = ao2_bump(session);
+
+	return cdata;
+}
+
+static int send_direct_media_request(void *data)
+{
+	struct rtp_direct_media_data *cdata = data;
+	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(cdata->chan);
+	struct chan_pjsip_pvt *pvt = channel->pvt;
+	int changed = 0;
+	int res = 0;
+
+	if (pvt->media[SIP_MEDIA_AUDIO]) {
+		changed |= check_for_rtp_changes(
+			cdata->chan, cdata->rtp, pvt->media[SIP_MEDIA_AUDIO], 1);
+	}
+	if (pvt->media[SIP_MEDIA_VIDEO]) {
+		changed |= check_for_rtp_changes(
+			cdata->chan, cdata->vrtp, pvt->media[SIP_MEDIA_VIDEO], 3);
+	}
+
+	if (direct_media_mitigate_glare(cdata->session)) {
+		ast_debug(4, "Disregarding setting RTP on %s: mitigating re-INVITE glare\n", ast_channel_name(cdata->chan));
+		return 0;
+	}
+
+	if (cdata->cap && ast_format_cap_count(cdata->cap) &&
+	    !ast_format_cap_identical(cdata->session->direct_media_cap, cdata->cap)) {
+		ast_format_cap_remove_by_type(cdata->session->direct_media_cap, AST_MEDIA_TYPE_UNKNOWN);
+		ast_format_cap_append_from_cap(cdata->session->direct_media_cap, cdata->cap, AST_MEDIA_TYPE_UNKNOWN);
+		changed = 1;
+	}
+
+	if (changed) {
+		ast_debug(4, "RTP changed on %s; initiating direct media update\n", ast_channel_name(cdata->chan));
+		res = ast_sip_session_refresh(cdata->session, NULL, NULL, NULL,
+			cdata->session->endpoint->media.direct_media.method, 1);
+	}
+
+	ao2_ref(cdata, -1);
+	return res;
+}
+
 /*! \brief Function called by RTP engine to change where the remote party should send media */
 static int chan_pjsip_set_rtp_peer(struct ast_channel *chan,
 		struct ast_rtp_instance *rtp,
@@ -311,9 +377,8 @@ static int chan_pjsip_set_rtp_peer(struct ast_channel *chan,
 		int nat_active)
 {
 	struct ast_sip_channel_pvt *channel = ast_channel_tech_pvt(chan);
-	struct chan_pjsip_pvt *pvt = channel->pvt;
 	struct ast_sip_session *session = channel->session;
-	int changed = 0;
+	struct rtp_direct_media_data *cdata;
 
 	/* Don't try to do any direct media shenanigans on early bridges */
 	if ((rtp || vrtp || tpeer) && !ast_channel_is_bridged(chan)) {
@@ -326,31 +391,14 @@ static int chan_pjsip_set_rtp_peer(struct ast_channel *chan,
 		return 0;
 	}
 
-	if (pvt->media[SIP_MEDIA_AUDIO]) {
-		changed |= check_for_rtp_changes(chan, rtp, pvt->media[SIP_MEDIA_AUDIO], 1);
-	}
-	if (pvt->media[SIP_MEDIA_VIDEO]) {
-		changed |= check_for_rtp_changes(chan, vrtp, pvt->media[SIP_MEDIA_VIDEO], 3);
-	}
-
-	if (direct_media_mitigate_glare(session)) {
-		ast_debug(4, "Disregarding setting RTP on %s: mitigating re-INVITE glare\n", ast_channel_name(chan));
+	cdata = rtp_direct_media_data_create(chan, rtp, vrtp, cap, session);
+	if (!cdata) {
 		return 0;
 	}
 
-	if (cap && ast_format_cap_count(cap) && !ast_format_cap_identical(session->direct_media_cap, cap)) {
-		ast_format_cap_remove_by_type(session->direct_media_cap, AST_MEDIA_TYPE_UNKNOWN);
-		ast_format_cap_append_from_cap(session->direct_media_cap, cap, AST_MEDIA_TYPE_UNKNOWN);
-		changed = 1;
-	}
-
-	if (changed) {
-		ao2_ref(session, +1);
-
-		ast_debug(4, "RTP changed on %s; initiating direct media update\n", ast_channel_name(chan));
-		if (ast_sip_push_task(session->serializer, send_direct_media_request, session)) {
-			ao2_cleanup(session);
-		}
+	if (ast_sip_push_task(session->serializer, send_direct_media_request, cdata)) {
+		ast_log(LOG_ERROR, "Unable to send direct media request for channel %s\n", ast_channel_name(chan));
+		ao2_ref(cdata, -1);
 	}
 
 	return 0;