diff --git a/CHANGES b/CHANGES
index b353e96a3a5df36d4710ec3181e0b3725b8ffec3..ee746253288cffb751c61782392f020e50f37b01 100644
--- a/CHANGES
+++ b/CHANGES
@@ -12,6 +12,37 @@
 --- Functionality changes from Asterisk 14 to Asterisk 15 --------------------
 ------------------------------------------------------------------------------
 
+
+------------------------------------------------------------------------------
+--- Functionality changes from Asterisk 14.0.0 to Asterisk 14.1.0 ----------
+------------------------------------------------------------------------------
+
+app_voicemail
+------------------
+ * Added "tps_queue_high" and "tps_queue_low" options.
+   The options can modify the taskprocessor alert levels for this module.
+   Additional information can be found in the sample configuration file at
+   config/samples/voicemail.conf.sample.
+
+res_pjsip_mwi
+------------------
+ * Added "mwi_tps_queue_high" and "mwi_tps_queue_low" global configuration
+   options to tune taskprocessor alert levels.
+
+ * Added "mwi_disable_initial_unsolicited" global configuration option
+   to disable sending unsolicited MWI to all endpoints on startup.
+   Additional information can be found in the sample configuration file at
+   config/samples/pjsip.conf.sample.
+
+chan_pjsip
+------------------
+ * A new dialplan function, PJSIP_SEND_SESSION_REFRESH, has been added. When
+   invoked, a re-INVITE or UPDATE request will be sent immediately to the
+   endpoint underlying the channel. When used in combination with the existing
+   dialplan function PJSIP_MEDIA_OFFER, this allows the formats on a PJSIP
+   channel to be re-negotiated and updated after session set up.
+
+
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13 to Asterisk 14 --------------------
 ------------------------------------------------------------------------------
@@ -378,26 +409,6 @@ cdr_csv
    post-1.8 CDR columns 'peeraccount', 'linkedid', and 'sequence'.
 
 
-------------------------------------------------------------------------------
---- Functionality changes from Asterisk 13.11.0 to Asterisk 13.12.0 ----------
-------------------------------------------------------------------------------
-
-app_voicemail
-------------------
- * Added "tps_queue_high" and "tps_queue_low" options.
-   The options can modify the taskprocessor alert levels for this module.
-   Additional information can be found in the sample configuration file at
-   config/samples/voicemail.conf.sample.
-
-res_pjsip_mwi
-------------------
- * Added "mwi_tps_queue_high" and "mwi_tps_queue_low" global configuration
-   options to tune taskprocessor alert levels.
- * Added "mwi_disable_initial_unsolicited" global configuration option
-   to disable sending unsolicited MWI to all endpoints on startup.
-   Additional information can be found in the sample configuration file at
-   config/samples/pjsip.conf.sample.
-
 ------------------------------------------------------------------------------
 --- Functionality changes from Asterisk 13.10.0 to Asterisk 13.11.0 ----------
 ------------------------------------------------------------------------------
diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 2e87393a6996634ac5e14be9b2d96ac05f72a225..02da0db35b15174db66cf1810f374dfc6df603ff 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -2433,6 +2433,11 @@ static struct ast_custom_function media_offer_function = {
 	.write = pjsip_acf_media_offer_write
 };
 
+static struct ast_custom_function session_refresh_function = {
+	.name = "PJSIP_SEND_SESSION_REFRESH",
+	.write = pjsip_acf_session_refresh_write,
+};
+
 /*!
  * \brief Load the module
  *
@@ -2472,6 +2477,11 @@ static int load_module(void)
 		goto end;
 	}
 
+	if (ast_custom_function_register(&session_refresh_function)) {
+		ast_log(LOG_WARNING, "Unable to register PJSIP_SEND_SESSION_REFRESH dialplan function\n");
+		goto end;
+	}
+
 	if (ast_sip_session_register_supplement(&chan_pjsip_supplement)) {
 		ast_log(LOG_ERROR, "Unable to register PJSIP supplement\n");
 		goto end;
@@ -2528,6 +2538,7 @@ end:
 	pjsip_uids_onhold = NULL;
 	ast_custom_function_unregister(&media_offer_function);
 	ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
+	ast_custom_function_unregister(&session_refresh_function);
 	ast_channel_unregister(&chan_pjsip_tech);
 	ast_rtp_glue_unregister(&chan_pjsip_rtp_glue);
 
@@ -2549,6 +2560,7 @@ static int unload_module(void)
 
 	ast_custom_function_unregister(&media_offer_function);
 	ast_custom_function_unregister(&chan_pjsip_dial_contacts_function);
+	ast_custom_function_unregister(&session_refresh_function);
 
 	ast_channel_unregister(&chan_pjsip_tech);
 	ao2_ref(chan_pjsip_tech.capabilities, -1);
diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c
index 1c0899732218433adb3ca663fd16a57af34e65a9..c1a3873d04c21b765d1e5391f94e8a397df41244 100644
--- a/channels/pjsip/dialplan_functions.c
+++ b/channels/pjsip/dialplan_functions.c
@@ -59,8 +59,55 @@
 		</parameter>
 	</syntax>
 	<description>
-		<para>Returns the codecs offered based upon the media choice</para>
+		<para>When read, returns the codecs offered based upon the media choice.</para>
+		<para>When written, sets the codecs to offer when an outbound dial attempt is made,
+		or when a session refresh is sent using <replaceable>PJSIP_SEND_SESSION_REFRESH</replaceable>.
+		</para>
 	</description>
+	<see-also>
+		<ref type="function">PJSIP_SEND_SESSION_REFRESH</ref>
+	</see-also>
+</function>
+<function name="PJSIP_SEND_SESSION_REFRESH" language="en_US">
+	<synopsis>
+		W/O: Initiate a session refresh via an UPDATE or re-INVITE on an established media session
+	</synopsis>
+	<syntax>
+		<parameter name="update_type" required="false">
+			<para>The type of update to send. Default is <literal>invite</literal>.</para>
+			<enumlist>
+				<enum name="invite">
+					<para>Send the session refresh as a re-INVITE.</para>
+				</enum>
+				<enum name="update">
+					<para>Send the session refresh as an UPDATE.</para>
+				</enum>
+			</enumlist>
+		</parameter>
+	</syntax>
+	<description>
+		<para>This function will cause the PJSIP stack to immediately refresh
+		the media session for the channel. This will be done using either a
+		re-INVITE (default) or an UPDATE request.
+		</para>
+		<para>This is most useful when combined with the <replaceable>PJSIP_MEDIA_OFFER</replaceable>
+		dialplan function, as it allows the formats in use on a channel to be
+		re-negotiated after call setup.</para>
+		<warning>
+			<para>The formats the endpoint supports are <emphasis>not</emphasis>
+			checked or enforced by this function. Using this function to offer
+			formats not supported by the endpoint <emphasis>may</emphasis> result
+			in a loss of media.</para>
+		</warning>
+		<example title="Re-negotiate format to g722">
+		 ; Within some existing extension on an answered channel
+		 same => n,Set(PJSIP_MEDIA_OFFER(audio)=!all,g722)
+		 same => n,Set(PJSIP_SEND_SESSION_REFRESH()=invite)
+		</example>
+	</description>
+	<see-also>
+		<ref type="function">PJSIP_MEDIA_OFFER</ref>
+	</see-also>
 </function>
 <info name="PJSIPCHANNEL" language="en_US" tech="PJSIP">
 	<enumlist>
@@ -961,3 +1008,70 @@ int pjsip_acf_media_offer_write(struct ast_channel *chan, const char *cmd, char
 
 	return ast_sip_push_task_synchronous(channel->session->serializer, media_offer_write_av, &mdata);
 }
+
+struct refresh_data {
+	struct ast_sip_session *session;
+	enum ast_sip_session_refresh_method method;
+};
+
+static int sip_session_response_cb(struct ast_sip_session *session, pjsip_rx_data *rdata)
+{
+	struct ast_format *fmt;
+
+	if (!session->channel) {
+		/* Egads! */
+		return 0;
+	}
+
+	fmt = ast_format_cap_get_best_by_type(ast_channel_nativeformats(session->channel), AST_MEDIA_TYPE_AUDIO);
+	if (!fmt) {
+		/* No format? That's weird. */
+		return 0;
+	}
+	ast_channel_set_writeformat(session->channel, fmt);
+	ast_channel_set_rawwriteformat(session->channel, fmt);
+	ast_channel_set_readformat(session->channel, fmt);
+	ast_channel_set_rawreadformat(session->channel, fmt);
+	ao2_ref(fmt, -1);
+
+	return 0;
+}
+
+static int refresh_write_cb(void *obj)
+{
+	struct refresh_data *data = obj;
+
+	ast_sip_session_refresh(data->session, NULL, NULL,
+		sip_session_response_cb, data->method, 1);
+
+	return 0;
+}
+
+int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
+{
+	struct ast_sip_channel_pvt *channel;
+	struct refresh_data rdata = {
+		.method = AST_SIP_SESSION_REFRESH_METHOD_INVITE,
+	};
+
+	if (!chan) {
+		ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
+		return -1;
+	}
+
+	if (strcmp(ast_channel_tech(chan)->type, "PJSIP")) {
+		ast_log(LOG_WARNING, "Cannot call %s on a non-PJSIP channel\n", cmd);
+		return -1;
+	}
+
+	channel = ast_channel_tech_pvt(chan);
+	rdata.session = channel->session;
+
+	if (!strcmp(value, "invite")) {
+		rdata.method = AST_SIP_SESSION_REFRESH_METHOD_INVITE;
+	} else if (!strcmp(value, "update")) {
+		rdata.method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
+	}
+
+	return ast_sip_push_task_synchronous(channel->session->serializer, refresh_write_cb, &rdata);
+}
diff --git a/channels/pjsip/include/dialplan_functions.h b/channels/pjsip/include/dialplan_functions.h
index cbc06f07613db363b29d35e349e4ccc2522544de..8b80bfa7474919f9d23a2b573539351179d03ffc 100644
--- a/channels/pjsip/include/dialplan_functions.h
+++ b/channels/pjsip/include/dialplan_functions.h
@@ -60,6 +60,18 @@ int pjsip_acf_media_offer_write(struct ast_channel *chan, const char *cmd, char
  */
 int pjsip_acf_media_offer_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len);
 
+/*!
+ * \brief PJSIP_SEND_SESSION_REFRESH function write callback
+ * \param chan The channel the function is called on
+ * \param cmd the Name of the function
+ * \param data Arguments passed to the function
+ * \param value Value to be set by the function
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+int pjsip_acf_session_refresh_write(struct ast_channel *chan, const char *cmd, char *data, const char *value);
+
 /*!
  * \brief PJSIP_DIAL_CONTACTS function read callback
  * \param chan The channel the function is called on