]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
res_pjsip: Add rtp_keepalive endpoint option. 65/865/5
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 17:51:39 +0000 (12:51 -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: I683bdc206c8c7def586ecaa64dcf2b86550be3bf

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 a5076aa45a68dc0819dcbfd8d7afaf36af39e0d9..3b6aa4ca0008e27dd72d945752b534f39543ef4d 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -17,6 +17,12 @@ AMI
  * Added the Linkedid header to the common channel headers listed for each
    channel in AMI events.
 
+res_pjsip
+------------------
+* 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.1.0-cert1 to Asterisk 13.1-cert2 --
 ------------------------------------------------------------------------------
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..5f06a1d
--- /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 = '45e3f47c6c44'
+
+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 1fa313b88d42dfcb469c24cc11842def4659251c..4eec344f19cbda1389217032d1917340f1fb1b84 100644 (file)
@@ -487,6 +487,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 48d5a47127ba06fd4f6818b94c6d488ac5a961c5..488f36e0ab159813ba1ee736e25cc19a0f77465d 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 11fe985cb1a261a77b9e8c4db3710bb653c833e0..5b4512d5001ab9df11632a8edd8765574042897b 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 */
@@ -2182,3 +2184,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 7ced26fff53a041ae1d9f8e401c9dc1739cc3d42..59db8c742a1969093fc12296a9e45026a20e9c01 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 30d7702ce185974956547ce9e807e675c43156cf..b2445ddf15708fd1ab7affbc9dd15516550e1597 100644 (file)
@@ -1726,6 +1726,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 a85d1e88130476e875a1996f6f03349459734ad3..8fa854474d988bc5255a2f87d1435dd80343d146 100644 (file)
@@ -106,6 +106,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)
 {
@@ -1217,6 +1250,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;
 }
 
@@ -1246,6 +1290,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 75cd7de7f3d5b3426dd778aca22d735341c902b1..a2fddaec9701a8a0787de966ef5293268ab5e0f0 100644 (file)
@@ -1035,6 +1035,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 1d7ccd9dc55df942774cc612b5c9e586dce9cbf8..7595ab30856159a18841a7ca453af2d49d4e0829 100644 (file)
@@ -2117,6 +2117,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;
 
@@ -2135,7 +2136,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)