]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_pjsip: Add rtp_keepalive endpoint option. 64/864/6
authorMark Michelson <mmichelson@digium.com>
Thu, 9 Jul 2015 19:17:53 +0000 (14:17 -0500)
committerMark Michelson <mmichelson@digium.com>
Mon, 20 Jul 2015 14:52:10 +0000 (09:52 -0500)
This adds an "rtp_keepalive" option for PJSIP endpoints. Similar to the
chan_sip option, this specifies an interval, in seconds, at which we
will send RTP comfort noise frames. This can be useful for keeping RTP
sessions alive as well as keeping NAT associations alive during lulls.

ASTERISK-25242 #close
Reported by Mark Michelson

Change-Id: I06660ba672c0a343814af4cec838e6025cafd54b

CHANGES
contrib/ast-db-manage/config/versions/498357a710ae_add_rtp_keepalive.py [new file with mode: 0644]
include/asterisk/res_pjsip.h
include/asterisk/res_pjsip_session.h
include/asterisk/rtp_engine.h
main/rtp_engine.c
res/res_pjsip.c
res/res_pjsip/pjsip_configuration.c
res/res_pjsip_sdp_rtp.c
res/res_pjsip_session.c
res/res_rtp_asterisk.c

diff --git a/CHANGES b/CHANGES
index bd141ff6d58264b2801e6a36c58d4ec1fe06a887..6d30f5be600065ad3be7bfe4efc2552c38d0aecb 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -40,6 +40,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 (file)
index 0000000..5a4f470
--- /dev/null
@@ -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')
index 1f9276b4116318faa397e57c572ec12a62ecc4be..cbd09e0e0b310b565697f1a16d800828a6651c22 100644 (file)
@@ -500,6 +500,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;
 };
 
 /*!
index 05548d5fc80e81b11eb32f8e21ca399354bcc4ed..5489979ed7db4a5569745240840ef60957c44d65 100644 (file)
@@ -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 */
        unsigned int held:1;
        /*! \brief Stream type this session media handles */
index a1a17da43462854af80a60061d3b3d87cd8c3763..f57f4ea35a61bb6c042f127e11efce18de5e6824 100644 (file)
@@ -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
  * @{
  */
index 2d61c89b6acf9f580c1db3da690afded63c9546c..8562558b818ed2f4909683ee43afc6afa4353c1b 100644 (file)
@@ -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 */
@@ -2191,3 +2193,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;
+}
index 658a55e88187174b8cb30299e498a0aedbba0b53..5fc6f0d230d051f94b3e133934b492e15505e8a0 100644 (file)
                                                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>
index 90a2650c0c352ebb8f57ddd1fd804f94071a3d7d..5d85ec880338cb79ac731ef5bdccb07d619c2f09 100644 (file)
@@ -1880,6 +1880,7 @@ int ast_res_pjsip_initialize_configuration(const struct ast_module_info *ast_mod
        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);
index fb70dd331d90fbede521a7e08329fbd1dc0ed669..100224c10e27b2f9ffacb8e62250ec2930535bf7 100644 (file)
@@ -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)
 {
@@ -1227,6 +1260,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;
 }
 
@@ -1256,6 +1300,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);
        }
index 6389ff848f398486f6ee96f2550e6c189ac314f2..9c492b3fcb39d5b9c2585738cf317581fac5f53e 100644 (file)
@@ -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);
index adce9e7ed4a3e4a7a5fc2968cae7c8b7c4b8635f..5d206c1a0201a04b9b6677bd9fdbb96c7e36315c 100644 (file)
@@ -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)