diff --git a/CHANGES b/CHANGES
index 3d117b8bed03436c8249730069bfed372c153e1e..b11994949f86d168234152b0ba33b16dd2e14a98 100644
--- a/CHANGES
+++ b/CHANGES
@@ -209,6 +209,10 @@ res_pjsip
   'yes' and g.726 audio is negotiated, forces the codec to be treated as if it
   is AAL2 packed on the channel.
 
+* A new 'rtp_keepalive' endpoint option has been added. This option specifies
+  an interval, in seconds, at which we will send RTP comfort noise packets to
+  the endpoint. This functions identically to chan_sip's "rtpkeepalive" option.
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13.3.0 to Asterisk 13.4.0 ------------
 ------------------------------------------------------------------------------
diff --git a/contrib/ast-db-manage/config/versions/498357a710ae_add_rtp_keepalive.py b/contrib/ast-db-manage/config/versions/498357a710ae_add_rtp_keepalive.py
new file mode 100644
index 0000000000000000000000000000000000000000..5a4f470aad0a8f435a07aa2894ab5f8bfb6dc4d4
--- /dev/null
+++ b/contrib/ast-db-manage/config/versions/498357a710ae_add_rtp_keepalive.py
@@ -0,0 +1,22 @@
+"""Add RTP keepalive
+
+Revision ID: 498357a710ae
+Revises: 28b8e71e541f
+Create Date: 2015-07-10 16:42:12.244421
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '498357a710ae'
+down_revision = '28b8e71e541f'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+    op.add_column('ps_endpoints', sa.Column('rtp_keepalive', sa.Integer))
+
+
+def downgrade():
+    op.drop_column('ps_endpoints', 'rtp_keepalive')
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index f199b8fefe27ed29ae6ba4e44cab317ca7dd2cf2..432a1689ebe599509c586852a7217aeec2a40a34 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -502,6 +502,8 @@ struct ast_sip_media_rtp_configuration {
 	enum ast_sip_session_media_encryption encryption;
 	/*! Do we want to optimistically support encryption if possible? */
 	unsigned int encryption_optimistic;
+	/*! Number of seconds between RTP keepalive packets */
+	unsigned int keepalive;
 };
 
 /*!
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index 9143118dd41cf10cc038dcfed218e7662cb831aa..c088d039ac036fce36c946cdf038c3357bbcaaa5 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -77,6 +77,8 @@ struct ast_sip_session_media {
 	enum ast_sip_session_media_encryption encryption;
 	/*! \brief The media transport in use for this stream */
 	pj_str_t transport;
+	/*! \brief Scheduler ID for RTP keepalive */
+	int keepalive_sched_id;
 	/*! \brief Stream is on hold by remote side */
 	unsigned int remotely_held:1;
 	/*! \brief Stream is on hold by local side */
diff --git a/include/asterisk/rtp_engine.h b/include/asterisk/rtp_engine.h
index c7f6511f90eb135cef2b0a805e5c418b1a27e3bd..c7a7f1d20951d0c915de873e01d447241bda894c 100644
--- a/include/asterisk/rtp_engine.h
+++ b/include/asterisk/rtp_engine.h
@@ -2288,6 +2288,22 @@ void ast_rtp_publish_rtcp_message(struct ast_rtp_instance *rtp,
 		struct ast_rtp_rtcp_report *report,
 		struct ast_json *blob);
 
+/*!
+ * \brief Get the last RTP transmission time
+ *
+ * \param rtp The instance from which to get the last transmission time
+ * \return The last RTP transmission time
+ */
+time_t ast_rtp_instance_get_last_tx(const struct ast_rtp_instance *rtp);
+
+/*!
+ * \brief Set the last RTP transmission time
+ *
+ * \param rtp The instance on which to set the last transmission time
+ * \param time The last transmission time
+ */
+void ast_rtp_instance_set_last_tx(struct ast_rtp_instance *rtp, time_t time);
+
 /*! \addtogroup StasisTopicsAndMessages
  * @{
  */
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 6ae8faf9c3d79ae532a9159b55259ca2c16f10da..94bd8136f5fd8d0999598fb452e366c63e5e6617 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -190,6 +190,8 @@ struct ast_rtp_instance {
 	struct ast_srtp *srtp;
 	/*! Channel unique ID */
 	char channel_uniqueid[AST_MAX_UNIQUEID];
+	/*! Time of last packet sent */
+	time_t last_tx;
 };
 
 /*! List of RTP engines that are currently registered */
@@ -2206,3 +2208,14 @@ int ast_rtp_engine_init()
 
 	return 0;
 }
+
+
+time_t ast_rtp_instance_get_last_tx(const struct ast_rtp_instance *rtp)
+{
+	return rtp->last_tx;
+}
+
+void ast_rtp_instance_set_last_tx(struct ast_rtp_instance *rtp, time_t time)
+{
+	rtp->last_tx = time;
+}
diff --git a/res/res_pjsip.c b/res/res_pjsip.c
index 6d7e4f7393ad774aab43b64bf2fb6a7f5f203822..fefbff44636c0c2e96437c2eb992bf71c2f06432 100644
--- a/res/res_pjsip.c
+++ b/res/res_pjsip.c
@@ -790,6 +790,14 @@
 						have this accountcode set on it.
 					</para></description>
 				</configOption>
+				<configOption name="rtp_keepalive">
+					<synopsis>Number of seconds between RTP comfort noise keepalive packets.</synopsis>
+					<description><para>
+						At the specified interval, Asterisk will send an RTP comfort noise frame. This may
+						be useful for situations where Asterisk is behind a NAT or firewall and must keep
+						a hole open in order to allow for media to arrive at Asterisk.
+					</para></description>
+				</configOption>
 			</configObject>
 			<configObject name="auth">
 				<synopsis>Authentication type</synopsis>
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index e2e5e06b9eb639303cd80c2527a69daf6d2a486d..31933e3520980c9a3e37ea8707199082dc37bf18 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -1880,6 +1880,7 @@ int ast_res_pjsip_initialize_configuration(void)
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "use_avpf", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.use_avpf));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "force_avp", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.force_avp));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "media_use_received_transport", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.use_received_transport));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_keepalive", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.rtp.keepalive));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "one_touch_recording", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, info.recording.enabled));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "inband_progress", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, inband_progress));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "call_group", "", group_handler, callgroup_to_str, NULL, 0, 0);
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 22c4529d9e80d58a8378f312ee872e96379c63f9..e8654a91fa69d1ba10ca5a9d4a9973d55b6633a8 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -107,6 +107,39 @@ static void format_cap_only_type(struct ast_format_cap *caps, enum ast_media_typ
 	}
 }
 
+static int send_keepalive(const void *data)
+{
+	struct ast_sip_session_media *session_media = (struct ast_sip_session_media *) data;
+	struct ast_rtp_instance *rtp = session_media->rtp;
+	int keepalive;
+	time_t interval;
+	int send_keepalive;
+
+	if (!rtp) {
+		return 0;
+	}
+
+	keepalive = ast_rtp_instance_get_keepalive(rtp);
+
+	if (!ast_sockaddr_isnull(&session_media->direct_media_addr)) {
+		ast_debug(3, "Not sending RTP keepalive on RTP instance %p since direct media is in use\n", rtp);
+		return keepalive * 1000;
+	}
+
+	interval = time(NULL) - ast_rtp_instance_get_last_tx(rtp);
+	send_keepalive = interval >= keepalive;
+
+	ast_debug(3, "It has been %d seconds since RTP was last sent on instance %p. %sending keepalive\n",
+			(int) interval, rtp, send_keepalive ? "S" : "Not s");
+
+	if (send_keepalive) {
+		ast_rtp_instance_sendcng(rtp, 0);
+		return keepalive * 1000;
+	}
+
+	return (keepalive - interval) * 1000;
+}
+
 /*! \brief Internal function which creates an RTP instance */
 static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media, unsigned int ipv6)
 {
@@ -1228,6 +1261,17 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
 	/* This purposely resets the encryption to the configured in case it gets added later */
 	session_media->encryption = session->endpoint->media.rtp.encryption;
 
+	if (session->endpoint->media.rtp.keepalive > 0 &&
+			stream_to_media_type(session_media->stream_type) == AST_MEDIA_TYPE_AUDIO) {
+		ast_rtp_instance_set_keepalive(session_media->rtp, session->endpoint->media.rtp.keepalive);
+		/* Schedule the initial keepalive early in case this is being used to punch holes through
+		 * a NAT. This way there won't be an awkward delay before media starts flowing in some
+		 * scenarios.
+		 */
+		session_media->keepalive_sched_id = ast_sched_add_variable(sched, 500, send_keepalive,
+			session_media, 1);
+	}
+
 	return 1;
 }
 
@@ -1257,6 +1301,9 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc
 static void stream_destroy(struct ast_sip_session_media *session_media)
 {
 	if (session_media->rtp) {
+		if (session_media->keepalive_sched_id != -1) {
+			AST_SCHED_DEL(sched, session_media->keepalive_sched_id);
+		}
 		ast_rtp_instance_stop(session_media->rtp);
 		ast_rtp_instance_destroy(session_media->rtp);
 	}
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index ce523771786136252e5c4834abb85c69d4b4ee01..eff8bbb12f9cabecf8c0822815318239ea7a72ce 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -1220,6 +1220,7 @@ static int add_session_media(void *obj, void *arg, int flags)
 		return CMP_STOP;
 	}
 	session_media->encryption = session->endpoint->media.rtp.encryption;
+	session_media->keepalive_sched_id = -1;
 	/* Safe use of strcpy */
 	strcpy(session_media->stream_type, handler_list->stream_type);
 	ao2_link(session->media, session_media);
diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c
index 0a68a2db72ef4f566696126fb845b498539930d2..53e9b29c238f641972ec6f9dfd561ed5e7d4d0a6 100644
--- a/res/res_rtp_asterisk.c
+++ b/res/res_rtp_asterisk.c
@@ -2166,6 +2166,7 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
 	void *temp = buf;
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 	struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance);
+	int res;
 
 	*ice = 0;
 
@@ -2184,7 +2185,11 @@ static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t siz
 	}
 #endif
 
-	return ast_sendto(rtcp ? rtp->rtcp->s : rtp->s, temp, len, flags, sa);
+	res = ast_sendto(rtcp ? rtp->rtcp->s : rtp->s, temp, len, flags, sa);
+	if (res > 0) {
+		ast_rtp_instance_set_last_tx(instance, time(NULL));
+	}
+	return res;
 }
 
 static int rtcp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice)