diff --git a/CHANGES b/CHANGES index 3d117b8bed..b11994949f 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 0000000000..5a4f470aad --- /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 f199b8fefe..432a1689eb 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 9143118dd4..c088d039ac 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 c7f6511f90..c7a7f1d209 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 6ae8faf9c3..94bd8136f5 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 6d7e4f7393..fefbff4463 100644 --- a/res/res_pjsip.c +++ b/res/res_pjsip.c @@ -790,6 +790,14 @@ have this accountcode set on it. + + Number of seconds between RTP comfort noise keepalive packets. + + 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. + + Authentication type diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index e2e5e06b9e..31933e3520 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 22c4529d9e..e8654a91fa 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 ce52377178..eff8bbb12f 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 0a68a2db72..53e9b29c23 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)