pjsip: Add rtp_timeout and rtp_timeout_hold endpoint options.

This change adds support for the 'rtp_timeout' and 'rtp_timeout_hold'
endpoint options. These allow the channel to be hung up if RTP
is not received from the remote endpoint for a specified number of
seconds.

ASTERISK-25259 #close

Change-Id: I3f39daaa7da2596b5022737b77799d16204175b9
changes/42/942/3
Joshua Colp 10 years ago
parent a105461f9e
commit 309dd2a409

@ -213,6 +213,11 @@ res_pjsip
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.
* New 'rtp_timeout' and 'rtp_timeout_hold' endpoint options have been added.
These options specify the amount of time, in seconds, that Asterisk will wait
before terminating the call due to lack of received RTP. These are identical
to chan_sip's rtptimeout and rtpholdtimeout options.
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 13.3.0 to Asterisk 13.4.0 ------------
------------------------------------------------------------------------------

@ -625,6 +625,8 @@ static struct ast_frame *chan_pjsip_read(struct ast_channel *ast)
return f;
}
ast_rtp_instance_set_last_rx(media->rtp, time(NULL));
if (f->frametype != AST_FRAME_VOICE) {
return f;
}

@ -737,6 +737,12 @@
;rtp_keepalive= ; Interval, in seconds, between comfort noise RTP packets if
; RTP is not flowing. This setting is useful for ensuring that
; holes in NATs and firewalls are kept open throughout a call.
;rtp_timeout= ; Hang up channel if RTP is not received for the specified
; number of seconds when the channel is off hold (default:
; "0" or not enabled)
;rtp_timeout_hold= ; Hang up channel if RTP is not received for the specified
; number of seconds when the channel is on hold (default:
; "0" or not enabled)
;==========================AUTH SECTION OPTIONS=========================
;[auth]

@ -0,0 +1,24 @@
"""add pjsip timeout options
Revision ID: 26f10cadc157
Revises: 498357a710ae
Create Date: 2015-07-21 07:45:00.696965
"""
# revision identifiers, used by Alembic.
revision = '26f10cadc157'
down_revision = '498357a710ae'
from alembic import op
import sqlalchemy as sa
def upgrade():
op.add_column('ps_endpoints', sa.Column('rtp_timeout', sa.Integer))
op.add_column('ps_endpoints', sa.Column('rtp_timeout_hold', sa.Integer))
def downgrade():
op.drop_column('ps_endpoints', 'rtp_timeout')
op.drop_column('ps_endpoints', 'rtp_timeout_hold')

@ -504,6 +504,10 @@ struct ast_sip_media_rtp_configuration {
unsigned int encryption_optimistic;
/*! Number of seconds between RTP keepalive packets */
unsigned int keepalive;
/*! Number of seconds before terminating channel due to lack of RTP (when not on hold) */
unsigned int timeout;
/*! Number of seconds before terminating channel due to lack of RTP (when on hold) */
unsigned int timeout_hold;
};
/*!

@ -79,6 +79,8 @@ struct ast_sip_session_media {
pj_str_t transport;
/*! \brief Scheduler ID for RTP keepalive */
int keepalive_sched_id;
/*! \brief Scheduler ID for RTP timeout */
int timeout_sched_id;
/*! \brief Stream is on hold by remote side */
unsigned int remotely_held:1;
/*! \brief Stream is on hold by local side */

@ -2304,6 +2304,22 @@ time_t ast_rtp_instance_get_last_tx(const struct ast_rtp_instance *rtp);
*/
void ast_rtp_instance_set_last_tx(struct ast_rtp_instance *rtp, time_t time);
/*
* \brief Get the last RTP reception time
*
* \param rtp The instance from which to get the last reception time
* \return The last RTP reception time
*/
time_t ast_rtp_instance_get_last_rx(const struct ast_rtp_instance *rtp);
/*!
* \brief Set the last RTP reception time
*
* \param rtp The instance on which to set the last reception time
* \param time The last reception time
*/
void ast_rtp_instance_set_last_rx(struct ast_rtp_instance *rtp, time_t time);
/*! \addtogroup StasisTopicsAndMessages
* @{
*/

@ -192,6 +192,8 @@ struct ast_rtp_instance {
char channel_uniqueid[AST_MAX_UNIQUEID];
/*! Time of last packet sent */
time_t last_tx;
/*! Time of last packet received */
time_t last_rx;
};
/*! List of RTP engines that are currently registered */
@ -2209,7 +2211,6 @@ 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;
@ -2219,3 +2220,13 @@ void ast_rtp_instance_set_last_tx(struct ast_rtp_instance *rtp, time_t time)
{
rtp->last_tx = time;
}
time_t ast_rtp_instance_get_last_rx(const struct ast_rtp_instance *rtp)
{
return rtp->last_rx;
}
void ast_rtp_instance_set_last_rx(struct ast_rtp_instance *rtp, time_t time)
{
rtp->last_rx = time;
}

@ -798,6 +798,22 @@
a hole open in order to allow for media to arrive at Asterisk.
</para></description>
</configOption>
<configOption name="rtp_timeout" default="0">
<synopsis>Maximum number of seconds without receiving RTP (while off hold) before terminating call.</synopsis>
<description><para>
This option configures the number of seconds without RTP (while off hold) before
considering a channel as dead. When the number of seconds is reached the underlying
channel is hung up. By default this option is set to 0, which means do not check.
</para></description>
</configOption>
<configOption name="rtp_timeout_hold" default="0">
<synopsis>Maximum number of seconds without receiving RTP (while on hold) before terminating call.</synopsis>
<description><para>
This option configures the number of seconds without RTP (while on hold) before
considering a channel as dead. When the number of seconds is reached the underlying
channel is hung up. By default this option is set to 0, which means do not check.
</para></description>
</configOption>
</configObject>
<configObject name="auth">
<synopsis>Authentication type</synopsis>

@ -1881,6 +1881,8 @@ int ast_res_pjsip_initialize_configuration(void)
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", "rtp_timeout", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.rtp.timeout));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_timeout_hold", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.rtp.timeout_hold));
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);

@ -115,10 +115,6 @@ static int send_keepalive(const void *data)
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)) {
@ -140,6 +136,37 @@ static int send_keepalive(const void *data)
return (keepalive - interval) * 1000;
}
/*! \brief Check whether RTP is being received or not */
static int rtp_check_timeout(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 elapsed;
struct ast_channel *chan;
if (!rtp) {
return 0;
}
elapsed = time(NULL) - ast_rtp_instance_get_last_rx(rtp);
if (elapsed < ast_rtp_instance_get_timeout(rtp)) {
return (ast_rtp_instance_get_timeout(rtp) - elapsed) * 1000;
}
chan = ast_channel_get_by_name(ast_rtp_instance_get_channel_id(rtp));
if (!chan) {
return 0;
}
ast_log(LOG_NOTICE, "Disconnecting channel '%s' for lack of RTP activity in %d seconds\n",
ast_channel_name(chan), elapsed);
ast_softhangup(chan, AST_SOFTHANGUP_DEV);
ast_channel_unref(chan);
return 0;
}
/*! \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)
{
@ -174,6 +201,8 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
session->endpoint->media.cos_video, "SIP RTP Video");
}
ast_rtp_instance_set_last_rx(session_media->rtp, time(NULL));
return 0;
}
@ -1272,6 +1301,28 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
session_media, 1);
}
/* As the channel lock is not held during this process the scheduled item won't block if
* it is hanging up the channel at the same point we are applying this negotiated SDP.
*/
AST_SCHED_DEL(sched, session_media->timeout_sched_id);
/* Due to the fact that we only ever have one scheduled timeout item for when we are both
* off hold and on hold we don't need to store the two timeouts differently on the RTP
* instance itself.
*/
ast_rtp_instance_set_timeout(session_media->rtp, 0);
if (session->endpoint->media.rtp.timeout && !session_media->remotely_held) {
ast_rtp_instance_set_timeout(session_media->rtp, session->endpoint->media.rtp.timeout);
} else if (session->endpoint->media.rtp.timeout_hold && session_media->remotely_held) {
ast_rtp_instance_set_timeout(session_media->rtp, session->endpoint->media.rtp.timeout_hold);
}
if (ast_rtp_instance_get_timeout(session_media->rtp)) {
session_media->timeout_sched_id = ast_sched_add_variable(sched,
ast_rtp_instance_get_timeout(session_media->rtp) * 1000, rtp_check_timeout,
session_media, 1);
}
return 1;
}
@ -1301,9 +1352,8 @@ 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_SCHED_DEL(sched, session_media->keepalive_sched_id);
AST_SCHED_DEL(sched, session_media->timeout_sched_id);
ast_rtp_instance_stop(session_media->rtp);
ast_rtp_instance_destroy(session_media->rtp);
}

@ -1221,6 +1221,7 @@ static int add_session_media(void *obj, void *arg, int flags)
}
session_media->encryption = session->endpoint->media.rtp.encryption;
session_media->keepalive_sched_id = -1;
session_media->timeout_sched_id = -1;
/* Safe use of strcpy */
strcpy(session_media->stream_type, handler_list->stream_type);
ao2_link(session->media, session_media);

Loading…
Cancel
Save