Merge "Add rtcp-mux support"

This commit is contained in:
Joshua Colp 2017-03-16 10:46:01 -05:00 committed by Gerrit Code Review
commit 84f0871cba
10 changed files with 397 additions and 129 deletions

View File

@ -140,6 +140,13 @@ res_pjsip_endpoint_identifier_ip
appropriate, as it now matches inbound requests on more than just IP
address.
res_rtp_asterisk
-----------------
* The RTP layer of Asterisk now has support for RFC 5761: "Multiplexing RTP
Data and Control Packets on a Single Port." So far, the only channel driver
that supports this feature is chan_pjsip. You can set "rtcp_mux = yes" on
a PJSIP endpoint in pjsip.conf to enable the feature.
------------------------------------------------------------------------------
--- Functionality changes from Asterisk 14.2.0 to Asterisk 14.3.0 ------------
------------------------------------------------------------------------------

View File

@ -23,9 +23,23 @@
=== UPGRADE-14.txt -- Upgrade info for 13 to 14
===========================================================
Build System:
- The LOW_MEMORY compile option no longer disables inline API. To disable
inline API you must use the DISABLE_INLINE option.
From 14.3.0 to 14.4.0:
res_rtp_asterisk:
- The RTP layer of Asterisk now has support for RFC 5761: "Multiplexing RTP
Data and Control Packets on a Single Port." So far, the only channel driver
that supports this feature is chan_pjsip. You can set "rtcp_mux = yes" on
a PJSIP endpoint in pjsip.conf to enable the feature.
New in 14.0.0
ARI:
- The policy for when to send "Dial" events has changed. Previously, "Dial"
events were sent on the calling channel's topic. However, starting in Asterisk
14, if there is no calling channel on which to send the event, the event is
instead sent on the called channel's topic. Note that for the ARI channels
resource's dial operation, this means that the "Dial" events will always be
sent on the called channel's topic.
Queue:
- When reloading the members of a queue, the members added dynamically (i.e.

View File

@ -0,0 +1,30 @@
"""empty message
Revision ID: 15db7b91a97a
Revises: 465e70e8c337
Create Date: 2017-03-08 16:56:38.108162
"""
# revision identifiers, used by Alembic.
revision = '15db7b91a97a'
down_revision = '465e70e8c337'
from alembic import op
import sqlalchemy as sa
YESNO_NAME = 'yesno_values'
YESNO_VALUES = ['yes', 'no']
def upgrade():
############################# Enums ##############################
# yesno_values have already been created, so use postgres enum object
# type to get around "already created" issue - works okay with mysql
yesno_values = ENUM(*YESNO_VALUES, name=YESNO_NAME, create_type=False)
op.add_column('ps_endpoints', sa.Column('rtcp_mux', yesno_values))
def downgrade():
op.drop_column('ps_endpoints', 'rtcp_mux')

View File

@ -676,6 +676,8 @@ struct ast_sip_endpoint_media_configuration {
unsigned int g726_non_standard;
/*! Bind the RTP instance to the media_address */
unsigned int bind_rtp_to_media_address;
/*! Use RTCP-MUX */
unsigned int rtcp_mux;
};
/*!

View File

@ -85,6 +85,8 @@ struct ast_sip_session_media {
unsigned int remotely_held:1;
/*! \brief Stream is on hold by local side */
unsigned int locally_held:1;
/*! \brief Does remote support rtcp_mux */
unsigned int remote_rtcp_mux:1;
/*! \brief Stream type this session media handles */
char stream_type[1];
};

View File

@ -237,6 +237,15 @@ enum ast_rtp_instance_stat {
AST_RTP_INSTANCE_STAT_RXOCTETCOUNT,
};
enum ast_rtp_instance_rtcp {
/*! RTCP should not be sent/received */
AST_RTP_INSTANCE_RTCP_DISABLED = 0,
/*! RTCP should be sent/received based on standard port rules */
AST_RTP_INSTANCE_RTCP_STANDARD,
/*! RTCP should be sent/received on the same port as RTP */
AST_RTP_INSTANCE_RTCP_MUX,
};
/* Codes for RTP-specific data - not defined by our AST_FORMAT codes */
/*! DTMF (RFC2833) */
#define AST_RTP_DTMF (1 << 0)
@ -447,6 +456,8 @@ struct ast_rtp_engine_ice {
void (*turn_request)(struct ast_rtp_instance *instance, enum ast_rtp_ice_component_type component,
enum ast_transport transport, const char *server, unsigned int port,
const char *username, const char *password);
/*! Callback to alter the number of ICE components on a session */
void (*change_components)(struct ast_rtp_instance *instance, int num_components);
};
/*! \brief DTLS setup types */

View File

@ -950,6 +950,16 @@
to the receiving one.
</para></description>
</configOption>
<configOption name="rtcp_mux" default="no">
<synopsis>Enable RFC 5761 RTCP multiplexing on the RTP port</synopsis>
<description><para>
With this option enabled, Asterisk will attempt to negotiate the use of the "rtcp-mux"
attribute on all media streams. This will result in RTP and RTCP being sent and received
on the same port. This shifts the demultiplexing logic to the application rather than
the transport layer. This option is useful when interoperating with WebRTC endpoints
since they mandate this option's use.
</para></description>
</configOption>
</configObject>
<configObject name="auth">
<synopsis>Authentication type</synopsis>

View File

@ -1937,6 +1937,7 @@ int ast_res_pjsip_initialize_configuration(void)
ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "contact_user", "", contact_user_handler, contact_user_to_str, NULL, 0, 0);
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "preferred_codec_only", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, preferred_codec_only));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "asymmetric_rtp_codec", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, asymmetric_rtp_codec));
ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtcp_mux", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtcp_mux));
if (ast_sip_initialize_sorcery_transport()) {
ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");

View File

@ -169,6 +169,23 @@ static int rtp_check_timeout(const void *data)
return 0;
}
/*!
* \brief Enable RTCP on an RTP session.
*/
static void enable_rtcp(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
const struct pjmedia_sdp_media *remote_media)
{
enum ast_rtp_instance_rtcp rtcp_type;
if (session->endpoint->media.rtcp_mux && session_media->remote_rtcp_mux) {
rtcp_type = AST_RTP_INSTANCE_RTCP_MUX;
} else {
rtcp_type = AST_RTP_INSTANCE_RTCP_STANDARD;
}
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, rtcp_type);
}
/*! \brief Internal function which creates an RTP instance */
static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media)
{
@ -186,7 +203,6 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me
return -1;
}
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, 1);
ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_NAT, session->endpoint->media.rtp.symmetric);
if (!session->endpoint->media.rtp.ice_support && (ice = ast_rtp_instance_get_ice(session_media->rtp))) {
@ -569,6 +585,13 @@ static void process_ice_attributes(struct ast_sip_session *session, struct ast_s
continue;
}
if (session->endpoint->media.rtcp_mux && session_media->remote_rtcp_mux && candidate.id > 1) {
/* Remote side may have offered RTP and RTCP candidates. However, if we're using RTCP MUX,
* then we should ignore RTCP candidates.
*/
continue;
}
candidate.foundation = foundation;
candidate.transport = transport;
@ -865,6 +888,26 @@ static int setup_media_encryption(struct ast_sip_session *session,
return 0;
}
static void set_ice_components(struct ast_sip_session *session, struct ast_sip_session_media *session_media)
{
struct ast_rtp_engine_ice *ice;
ast_assert(session_media->rtp != NULL);
ice = ast_rtp_instance_get_ice(session_media->rtp);
if (!session->endpoint->media.rtp.ice_support || !ice) {
return;
}
if (session->endpoint->media.rtcp_mux && session_media->remote_rtcp_mux) {
/* We both support RTCP mux. Only one ICE component necessary */
ice->change_components(session_media->rtp, 1);
} else {
/* They either don't support RTCP mux or we don't know if they do yet. */
ice->change_components(session_media->rtp, 2);
}
}
/*! \brief Function which negotiates an incoming media stream */
static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media,
const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)
@ -909,6 +952,11 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct
return -1;
}
session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(stream, "rtcp-mux", NULL) != NULL);
set_ice_components(session, session_media);
enable_rtcp(session, session_media, stream);
res = setup_media_encryption(session, session_media, sdp, stream);
if (res) {
if (!session->endpoint->media.rtp.encryption_optimistic ||
@ -1087,6 +1135,9 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
return -1;
}
set_ice_components(session, session_media);
enable_rtcp(session, session_media, NULL);
if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) ||
!(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) {
return -1;
@ -1250,6 +1301,12 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as
attr->name = !session_media->locally_held ? STR_SENDRECV : STR_SENDONLY;
media->attr[media->attr_count++] = attr;
/* If we've got rtcp-mux enabled, just unconditionally offer it in all SDPs */
if (session->endpoint->media.rtcp_mux) {
attr = pjmedia_sdp_attr_create(pool, "rtcp-mux", NULL);
pjmedia_sdp_attr_add(&media->attr_count, media->attr, attr);
}
/* Add the media stream to the SDP */
sdp->media[sdp->media_count++] = media;
@ -1284,6 +1341,11 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
return -1;
}
session_media->remote_rtcp_mux = (pjmedia_sdp_media_find_attr2(remote_stream, "rtcp-mux", NULL) != NULL);
set_ice_components(session, session_media);
enable_rtcp(session, session_media, remote_stream);
res = setup_media_encryption(session, session_media, remote, remote_stream);
if (!session->endpoint->media.rtp.encryption_optimistic && res) {
/* If optimistic encryption is disabled and crypto should have been enabled but was not
@ -1315,7 +1377,9 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a
return -1;
}
ast_channel_set_fd(session->channel, fdno, ast_rtp_instance_fd(session_media->rtp, 0));
ast_channel_set_fd(session->channel, fdno + 1, ast_rtp_instance_fd(session_media->rtp, 1));
if (!session->endpoint->media.rtcp_mux || !session_media->remote_rtcp_mux) {
ast_channel_set_fd(session->channel, fdno + 1, ast_rtp_instance_fd(session_media->rtp, 1));
}
/* If ICE support is enabled find all the needed attributes */
process_ice_attributes(session, session_media, remote, remote_stream);

View File

@ -329,6 +329,7 @@ struct ast_rtp {
struct ao2_container *ice_active_remote_candidates; /*!< The remote ICE candidates */
struct ao2_container *ice_proposed_remote_candidates; /*!< Incoming remote ICE candidates for new session */
struct ast_sockaddr ice_original_rtp_addr; /*!< rtp address that ICE started on first session */
unsigned int ice_num_components; /*!< The number of ICE components */
#endif
#ifdef HAVE_OPENSSL_SRTP
@ -417,6 +418,9 @@ struct ast_rtcp {
* own address every time
*/
char *local_addr_str;
enum ast_rtp_instance_rtcp type;
/* Buffer for frames created during RTCP interpretation */
unsigned char frame_buf[512 + AST_FRIENDLY_OFFSET];
};
struct rtp_red {
@ -658,6 +662,22 @@ static int ice_reset_session(struct ast_rtp_instance *instance)
pj_ice_sess_change_role(rtp->ice, role);
}
/* If we only have one component now, and we previously set up TURN for RTCP,
* we need to destroy that TURN socket.
*/
if (rtp->ice_num_components == 1 && rtp->turn_rtcp) {
struct timeval wait = ast_tvadd(ast_tvnow(), ast_samp2tv(TURN_STATE_WAIT_TIME, 1000));
struct timespec ts = { .tv_sec = wait.tv_sec, .tv_nsec = wait.tv_usec * 1000, };
ast_mutex_lock(&rtp->lock);
pj_turn_sock_destroy(rtp->turn_rtcp);
rtp->turn_state = PJ_TURN_STATE_NULL;
while (rtp->turn_state != PJ_TURN_STATE_DESTROYING) {
ast_cond_timedwait(&rtp->cond, &rtp->lock, &ts);
}
ast_mutex_unlock(&rtp->lock);
}
return res;
}
@ -773,11 +793,12 @@ static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
ast_log(LOG_WARNING, "No RTP candidates; skipping ICE checklist (%p)\n", instance);
}
if (!has_rtcp) {
/* If we're only dealing with one ICE component, then we don't care about the lack of RTCP candidates */
if (!has_rtcp && rtp->ice_num_components > 1) {
ast_log(LOG_WARNING, "No RTCP candidates; skipping ICE checklist (%p)\n", instance);
}
if (has_rtp && has_rtcp) {
if (has_rtp && (has_rtcp || rtp->ice_num_components == 1)) {
pj_status_t res = pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, cand_cnt, &candidates[0]);
char reason[80];
@ -1269,6 +1290,21 @@ static char *generate_random_string(char *buf, size_t size)
return buf;
}
static void ast_rtp_ice_change_components(struct ast_rtp_instance *instance, int num_components)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
/* Don't do anything if ICE is unsupported or if we're not changing the
* number of components
*/
if (!icesupport || !rtp->ice || rtp->ice_num_components == num_components) {
return;
}
rtp->ice_num_components = num_components;
ice_reset_session(instance);
}
/* ICE RTP Engine interface declaration */
static struct ast_rtp_engine_ice ast_rtp_ice = {
.set_authentication = ast_rtp_ice_set_authentication,
@ -1281,6 +1317,7 @@ static struct ast_rtp_engine_ice ast_rtp_ice = {
.ice_lite = ast_rtp_ice_lite,
.set_role = ast_rtp_ice_set_role,
.turn_request = ast_rtp_ice_turn_request,
.change_components = ast_rtp_ice_change_components,
};
#endif
@ -1540,6 +1577,7 @@ static int ast_rtp_dtls_active(struct ast_rtp_instance *instance)
static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
int rtcp_dtls_unique = (rtp->dtls.ssl != rtp->rtcp->dtls.ssl);
dtls_srtp_stop_timeout_timer(instance, rtp, 0);
@ -1557,7 +1595,7 @@ static void ast_rtp_dtls_stop(struct ast_rtp_instance *instance)
if (rtp->rtcp) {
dtls_srtp_stop_timeout_timer(instance, rtp, 1);
if (rtp->rtcp->dtls.ssl) {
if (rtp->rtcp->dtls.ssl && rtcp_dtls_unique) {
SSL_free(rtp->rtcp->dtls.ssl);
rtp->rtcp->dtls.ssl = NULL;
ast_mutex_destroy(&rtp->rtcp->dtls.lock);
@ -1785,7 +1823,7 @@ static void ast_rtp_on_ice_complete(pj_ice_sess *ice, pj_status_t status)
#ifdef HAVE_OPENSSL_SRTP
dtls_perform_handshake(instance, &rtp->dtls, 0);
if (rtp->rtcp) {
if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
}
#endif
@ -2025,7 +2063,7 @@ static int dtls_srtp_renegotiate(const void *data)
SSL_do_handshake(rtp->dtls.ssl);
dtls_srtp_check_pending(instance, rtp, 0);
if (rtp->rtcp && rtp->rtcp->dtls.ssl) {
if (rtp->rtcp && rtp->rtcp->dtls.ssl && rtp->rtcp->dtls.ssl != rtp->dtls.ssl) {
SSL_renegotiate(rtp->rtcp->dtls.ssl);
SSL_do_handshake(rtp->rtcp->dtls.ssl);
dtls_srtp_check_pending(instance, rtp, 1);
@ -2616,7 +2654,7 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad
passwd = pj_str(rtp->local_passwd);
/* Create an ICE session for ICE negotiation */
if (pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, 2,
if (pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, rtp->ice_num_components,
&ast_rtp_ice_sess_cb, &ufrag, &passwd, NULL, &rtp->ice) == PJ_SUCCESS) {
/* Make this available for the callbacks */
rtp->ice->user_data = instance;
@ -2625,9 +2663,10 @@ static int ice_create(struct ast_rtp_instance *instance, struct ast_sockaddr *ad
rtp_add_candidates_to_ice(instance, rtp, addr, port, AST_RTP_ICE_COMPONENT_RTP,
TRANSPORT_SOCKET_RTP);
/* Only add the RTCP candidates to ICE when replacing the session. New sessions
/* Only add the RTCP candidates to ICE when replacing the session and if
* the ICE session contains more than just an RTP component. New sessions
* handle this in a separate part of the setup phase */
if (replace && rtp->rtcp) {
if (replace && rtp->rtcp && rtp->ice_num_components > 1) {
rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us,
ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP,
TRANSPORT_SOCKET_RTCP);
@ -2712,6 +2751,7 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
#ifdef HAVE_PJPROJECT
/* Create an ICE session for ICE negotiation */
if (icesupport) {
rtp->ice_num_components = 2;
ast_debug(3, "Creating ICE session %s (%d) for RTP instance '%p'\n", ast_sockaddr_stringify(addr), x, instance);
if (ice_create(instance, addr, x, 0)) {
ast_log(LOG_NOTICE, "Failed to start ICE session\n");
@ -2721,7 +2761,6 @@ static int ast_rtp_new(struct ast_rtp_instance *instance,
}
}
#endif
/* Record any information we may need */
rtp->sched = sched;
@ -4150,63 +4189,21 @@ static void update_lost_stats(struct ast_rtp *rtp, unsigned int lost_packets)
rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current;
}
static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
static struct ast_frame *ast_rtcp_interpret(struct ast_rtp_instance *instance, const unsigned char *rtcpdata, size_t size, struct ast_sockaddr *addr)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
struct ast_sockaddr addr;
unsigned char rtcpdata[8192 + AST_FRIENDLY_OFFSET];
unsigned int *rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET);
int res, packetwords, position = 0;
unsigned int *rtcpheader = (unsigned int *)(rtcpdata);
int packetwords, position = 0;
int report_counter = 0;
struct ast_rtp_rtcp_report_block *report_block;
struct ast_frame *f = &ast_null_frame;
/* Read in RTCP data from the socket */
if ((res = rtcp_recvfrom(instance, rtcpdata + AST_FRIENDLY_OFFSET,
sizeof(rtcpdata) - AST_FRIENDLY_OFFSET,
0, &addr)) < 0) {
ast_assert(errno != EBADF);
if (errno != EAGAIN) {
ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n",
(errno) ? strerror(errno) : "Unspecified");
return NULL;
}
return &ast_null_frame;
}
/* If this was handled by the ICE session don't do anything further */
if (!res) {
return &ast_null_frame;
}
if (!*(rtcpdata + AST_FRIENDLY_OFFSET)) {
struct sockaddr_in addr_tmp;
struct ast_sockaddr addr_v4;
if (ast_sockaddr_is_ipv4(&addr)) {
ast_sockaddr_to_sin(&addr, &addr_tmp);
} else if (ast_sockaddr_ipv4_mapped(&addr, &addr_v4)) {
ast_debug(1, "Using IPv6 mapped address %s for STUN\n",
ast_sockaddr_stringify(&addr));
ast_sockaddr_to_sin(&addr_v4, &addr_tmp);
} else {
ast_debug(1, "Cannot do STUN for non IPv4 address %s\n",
ast_sockaddr_stringify(&addr));
return &ast_null_frame;
}
if ((ast_stun_handle_packet(rtp->rtcp->s, &addr_tmp, rtcpdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == AST_STUN_ACCEPT)) {
ast_sockaddr_from_sin(&addr, &addr_tmp);
ast_sockaddr_copy(&rtp->rtcp->them, &addr);
}
return &ast_null_frame;
}
packetwords = res / 4;
packetwords = size / 4;
if (ast_rtp_instance_get_prop(instance, AST_RTP_PROPERTY_NAT)) {
/* Send to whoever sent to us */
if (ast_sockaddr_cmp(&rtp->rtcp->them, &addr)) {
ast_sockaddr_copy(&rtp->rtcp->them, &addr);
if (ast_sockaddr_cmp(&rtp->rtcp->them, addr)) {
ast_sockaddr_copy(&rtp->rtcp->them, addr);
if (rtpdebug) {
ast_debug(0, "RTCP NAT: Got RTCP from other end. Now sending to address %s\n",
ast_sockaddr_stringify(&rtp->rtcp->them));
@ -4214,7 +4211,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
}
}
ast_debug(1, "Got RTCP report of %d bytes\n", res);
ast_debug(1, "Got RTCP report of %zu bytes\n", size);
while (position < packetwords) {
int i, pt, rc;
@ -4242,9 +4239,9 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
return &ast_null_frame;
}
if (rtcp_debug_test_addr(&addr)) {
if (rtcp_debug_test_addr(addr)) {
ast_verbose("\n\nGot RTCP from %s\n",
ast_sockaddr_stringify(&addr));
ast_sockaddr_stringify(addr));
ast_verbose("PT: %d(%s)\n", pt, (pt == RTCP_PT_SR) ? "Sender Report" :
(pt == RTCP_PT_RR) ? "Receiver Report" :
(pt == RTCP_PT_FUR) ? "H.261 FUR" : "Unknown");
@ -4267,7 +4264,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
(unsigned int)ntohl(rtcpheader[i + 1]),
&rtcp_report->sender_information.ntp_timestamp);
rtcp_report->sender_information.rtp_timestamp = ntohl(rtcpheader[i + 2]);
if (rtcp_debug_test_addr(&addr)) {
if (rtcp_debug_test_addr(addr)) {
ast_verbose("NTP timestamp: %u.%06u\n",
(unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_sec,
(unsigned int)rtcp_report->sender_information.ntp_timestamp.tv_usec);
@ -4299,7 +4296,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
report_block->dlsr = ntohl(rtcpheader[i + 5]);
if (report_block->lsr
&& update_rtt_stats(rtp, report_block->lsr, report_block->dlsr)
&& rtcp_debug_test_addr(&addr)) {
&& rtcp_debug_test_addr(addr)) {
struct timeval now;
unsigned int lsr_now, lsw, msw;
gettimeofday(&now, NULL);
@ -4316,7 +4313,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
update_lost_stats(rtp, report_block->lost_count.packets);
rtp->rtcp->reported_jitter_count++;
if (rtcp_debug_test_addr(&addr)) {
if (rtcp_debug_test_addr(addr)) {
ast_verbose(" Fraction lost: %d\n", report_block->lost_count.fraction);
ast_verbose(" Packets lost so far: %u\n", report_block->lost_count.packets);
ast_verbose(" Highest sequence number: %u\n", report_block->highest_seq_no & 0x0000ffff);
@ -4344,7 +4341,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
/* Return an AST_FRAME_RTCP frame with the ast_rtp_rtcp_report
* object as a its data */
rtp->f.frametype = AST_FRAME_RTCP;
rtp->f.data.ptr = rtp->rawdata + AST_FRIENDLY_OFFSET;
rtp->f.data.ptr = rtp->rtcp->frame_buf + AST_FRIENDLY_OFFSET;
memcpy(rtp->f.data.ptr, rtcp_report, sizeof(struct ast_rtp_rtcp_report));
rtp->f.datalen = sizeof(struct ast_rtp_rtcp_report);
if (rc > 0) {
@ -4367,7 +4364,7 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
case RTCP_PT_FUR:
/* Handle RTCP FIR as FUR */
case RTCP_PT_PSFB:
if (rtcp_debug_test_addr(&addr)) {
if (rtcp_debug_test_addr(addr)) {
ast_verbose("Received an RTCP Fast Update Request\n");
}
rtp->f.frametype = AST_FRAME_CONTROL;
@ -4379,13 +4376,13 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
f = &rtp->f;
break;
case RTCP_PT_SDES:
if (rtcp_debug_test_addr(&addr)) {
if (rtcp_debug_test_addr(addr)) {
ast_verbose("Received an SDES from %s\n",
ast_sockaddr_stringify(&rtp->rtcp->them));
}
break;
case RTCP_PT_BYE:
if (rtcp_debug_test_addr(&addr)) {
if (rtcp_debug_test_addr(addr)) {
ast_verbose("Received a BYE from %s\n",
ast_sockaddr_stringify(&rtp->rtcp->them));
}
@ -4400,6 +4397,58 @@ static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
rtp->rtcp->rtcp_info = 1;
return f;
}
static struct ast_frame *ast_rtcp_read(struct ast_rtp_instance *instance)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
struct ast_sockaddr addr;
unsigned char rtcpdata[8192 + AST_FRIENDLY_OFFSET];
unsigned char *read_area = rtcpdata + AST_FRIENDLY_OFFSET;
size_t read_area_size = sizeof(rtcpdata) - AST_FRIENDLY_OFFSET;
int res;
/* Read in RTCP data from the socket */
if ((res = rtcp_recvfrom(instance, read_area, read_area_size,
0, &addr)) < 0) {
ast_assert(errno != EBADF);
if (errno != EAGAIN) {
ast_log(LOG_WARNING, "RTCP Read error: %s. Hanging up.\n",
(errno) ? strerror(errno) : "Unspecified");
return NULL;
}
return &ast_null_frame;
}
/* If this was handled by the ICE session don't do anything further */
if (!res) {
return &ast_null_frame;
}
if (!*(read_area)) {
struct sockaddr_in addr_tmp;
struct ast_sockaddr addr_v4;
if (ast_sockaddr_is_ipv4(&addr)) {
ast_sockaddr_to_sin(&addr, &addr_tmp);
} else if (ast_sockaddr_ipv4_mapped(&addr, &addr_v4)) {
ast_debug(1, "Using IPv6 mapped address %s for STUN\n",
ast_sockaddr_stringify(&addr));
ast_sockaddr_to_sin(&addr_v4, &addr_tmp);
} else {
ast_debug(1, "Cannot do STUN for non IPv4 address %s\n",
ast_sockaddr_stringify(&addr));
return &ast_null_frame;
}
if ((ast_stun_handle_packet(rtp->rtcp->s, &addr_tmp, read_area, res, NULL, NULL) == AST_STUN_ACCEPT)) {
ast_sockaddr_from_sin(&addr, &addr_tmp);
ast_sockaddr_copy(&rtp->rtcp->them, &addr);
}
return &ast_null_frame;
}
return ast_rtcp_interpret(instance, read_area, read_area_size, &addr);
}
static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int *rtpheader, int len, int hdrlen)
@ -4507,19 +4556,54 @@ static int bridge_p2p_rtp_write(struct ast_rtp_instance *instance, unsigned int
return 0;
}
static int rtcp_mux(struct ast_rtp *rtp, const unsigned char *packet)
{
uint8_t version;
uint8_t pt;
uint8_t m;
if (!rtp->rtcp || rtp->rtcp->type != AST_RTP_INSTANCE_RTCP_MUX) {
return 0;
}
version = (packet[0] & 0XC0) >> 6;
if (version == 0) {
/* version 0 indicates this is a STUN packet and shouldn't
* be interpreted as a possible RTCP packet
*/
return 0;
}
/* The second octet of a packet will be one of the following:
* For RTP: The marker bit (1 bit) and the RTP payload type (7 bits)
* For RTCP: The payload type (8)
*
* RTP has a forbidden range of payload types (64-95) since these
* will conflict with RTCP payload numbers if the marker bit is set.
*/
m = packet[1] & 0x80;
pt = packet[1] & 0x7F;
if (m && pt >= 64 && pt <= 95) {
return 1;
}
return 0;
}
static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtcp)
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
struct ast_sockaddr addr;
int res, hdrlen = 12, version, payloadtype, padding, mark, ext, cc, prev_seqno;
unsigned int *rtpheader = (unsigned int*)(rtp->rawdata + AST_FRIENDLY_OFFSET), seqno, ssrc, timestamp;
unsigned char *read_area = rtp->rawdata + AST_FRIENDLY_OFFSET;
size_t read_area_size = sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET;
unsigned int *rtpheader = (unsigned int*)(read_area), seqno, ssrc, timestamp;
RAII_VAR(struct ast_rtp_payload_type *, payload, NULL, ao2_cleanup);
struct ast_sockaddr remote_address = { {0,} };
struct frame_list frames;
/* If this is actually RTCP let's hop on over and handle it */
if (rtcp) {
if (rtp->rtcp) {
if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
return ast_rtcp_read(instance);
}
return &ast_null_frame;
@ -4531,8 +4615,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
}
/* Actually read in the data from the socket */
if ((res = rtp_recvfrom(instance, rtp->rawdata + AST_FRIENDLY_OFFSET,
sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0,
if ((res = rtp_recvfrom(instance, read_area, read_area_size, 0,
&addr)) < 0) {
ast_assert(errno != EBADF);
if (errno != EAGAIN) {
@ -4548,12 +4631,17 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
return &ast_null_frame;
}
/* This could be a multiplexed RTCP packet. If so, be sure to interpret it correctly */
if (rtcp_mux(rtp, read_area)) {
return ast_rtcp_interpret(instance, read_area, read_area_size, &addr);
}
/* Make sure the data that was read in is actually enough to make up an RTP packet */
if (res < hdrlen) {
/* If this is a keepalive containing only nulls, don't bother with a warning */
int i;
for (i = 0; i < res; ++i) {
if (rtp->rawdata[AST_FRIENDLY_OFFSET + i] != '\0') {
if (read_area[i] != '\0') {
ast_log(LOG_WARNING, "RTP Read too short\n");
return &ast_null_frame;
}
@ -4580,7 +4668,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
ast_sockaddr_stringify(&addr));
return &ast_null_frame;
}
if ((ast_stun_handle_packet(rtp->s, &addr_tmp, rtp->rawdata + AST_FRIENDLY_OFFSET, res, NULL, NULL) == AST_STUN_ACCEPT) &&
if ((ast_stun_handle_packet(rtp->s, &addr_tmp, read_area, res, NULL, NULL) == AST_STUN_ACCEPT) &&
ast_sockaddr_isnull(&remote_address)) {
ast_sockaddr_from_sin(&addr, &addr_tmp);
ast_rtp_instance_set_remote_address(instance, &addr);
@ -4629,7 +4717,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
/* do not update the originally given address, but only the remote */
ast_rtp_instance_set_incoming_source_address(instance, &addr);
ast_sockaddr_copy(&remote_address, &addr);
if (rtp->rtcp) {
if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
ast_sockaddr_copy(&rtp->rtcp->them, &addr);
ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(&addr) + 1);
}
@ -4696,7 +4784,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
/* Remove any padding bytes that may be present */
if (padding) {
res -= rtp->rawdata[AST_FRIENDLY_OFFSET + res - 1];
res -= read_area[res - 1];
}
/* Skip over any CSRC fields */
@ -4770,11 +4858,11 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
* by passing the pointer to the frame list to it so that the method
* can append frames to the list as needed.
*/
process_dtmf_rfc2833(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark, &frames);
process_dtmf_rfc2833(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark, &frames);
} else if (payload->rtp_code == AST_RTP_CISCO_DTMF) {
f = process_dtmf_cisco(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
f = process_dtmf_cisco(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
} else if (payload->rtp_code == AST_RTP_CN) {
f = process_cn_rfc3389(instance, rtp->rawdata + AST_FRIENDLY_OFFSET + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
f = process_cn_rfc3389(instance, read_area + hdrlen, res - hdrlen, seqno, timestamp, &addr, payloadtype, mark);
} else {
ast_log(LOG_NOTICE, "Unknown RTP codec %d received from '%s'\n",
payloadtype,
@ -4830,7 +4918,7 @@ static struct ast_frame *ast_rtp_read(struct ast_rtp_instance *instance, int rtc
rtp->f.src = "RTP";
rtp->f.mallocd = 0;
rtp->f.datalen = res - hdrlen;
rtp->f.data.ptr = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET;
rtp->f.data.ptr = read_area + hdrlen;
rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET;
rtp->f.seqno = seqno;
@ -4941,19 +5029,29 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
if (value) {
struct ast_sockaddr local_addr;
if (rtp->rtcp) {
if (rtp->rtcp && rtp->rtcp->type == value) {
ast_debug(1, "Ignoring duplicate RTCP property on RTP instance '%p'\n", instance);
return;
}
/* Setup RTCP to be activated on the next RTP write */
if (!(rtp->rtcp = ast_calloc(1, sizeof(*rtp->rtcp)))) {
return;
if (!rtp->rtcp) {
rtp->rtcp = ast_calloc(1, sizeof(*rtp->rtcp));
if (!rtp->rtcp) {
return;
}
rtp->rtcp->s = -1;
rtp->rtcp->dtls.timeout_timer = -1;
rtp->rtcp->schedid = -1;
}
rtp->rtcp->type = value;
/* Grab the IP address and port we are going to use */
ast_rtp_instance_get_local_address(instance, &rtp->rtcp->us);
ast_sockaddr_set_port(&rtp->rtcp->us,
ast_sockaddr_port(&rtp->rtcp->us) + 1);
if (value == AST_RTP_INSTANCE_RTCP_STANDARD) {
ast_sockaddr_set_port(&rtp->rtcp->us,
ast_sockaddr_port(&rtp->rtcp->us) + 1);
}
ast_sockaddr_copy(&local_addr, &rtp->rtcp->us);
if (!ast_find_ourip(&local_addr, &rtp->rtcp->us, 0)) {
@ -4963,6 +5061,7 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
ast_sockaddr_copy(&local_addr, &rtp->rtcp->us);
}
ast_free(rtp->rtcp->local_addr_str);
rtp->rtcp->local_addr_str = ast_strdup(ast_sockaddr_stringify(&local_addr));
if (!rtp->rtcp->local_addr_str) {
ast_free(rtp->rtcp);
@ -4970,43 +5069,67 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
return;
}
if ((rtp->rtcp->s =
create_new_socket("RTCP",
ast_sockaddr_is_ipv4(&rtp->rtcp->us) ?
AF_INET :
ast_sockaddr_is_ipv6(&rtp->rtcp->us) ?
AF_INET6 : -1)) < 0) {
ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance);
ast_free(rtp->rtcp->local_addr_str);
ast_free(rtp->rtcp);
rtp->rtcp = NULL;
return;
}
if (value == AST_RTP_INSTANCE_RTCP_STANDARD) {
/* We're either setting up RTCP from scratch or
* switching from MUX. Either way, we won't have
* a socket set up, and we need to set it up
*/
if ((rtp->rtcp->s =
create_new_socket("RTCP",
ast_sockaddr_is_ipv4(&rtp->rtcp->us) ?
AF_INET :
ast_sockaddr_is_ipv6(&rtp->rtcp->us) ?
AF_INET6 : -1)) < 0) {
ast_debug(1, "Failed to create a new socket for RTCP on instance '%p'\n", instance);
ast_free(rtp->rtcp->local_addr_str);
ast_free(rtp->rtcp);
rtp->rtcp = NULL;
return;
}
/* Try to actually bind to the IP address and port we are going to use for RTCP, if this fails we have to bail out */
if (ast_bind(rtp->rtcp->s, &rtp->rtcp->us)) {
ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance);
close(rtp->rtcp->s);
ast_free(rtp->rtcp->local_addr_str);
ast_free(rtp->rtcp);
rtp->rtcp = NULL;
return;
/* Try to actually bind to the IP address and port we are going to use for RTCP, if this fails we have to bail out */
if (ast_bind(rtp->rtcp->s, &rtp->rtcp->us)) {
ast_debug(1, "Failed to setup RTCP on RTP instance '%p'\n", instance);
close(rtp->rtcp->s);
ast_free(rtp->rtcp->local_addr_str);
ast_free(rtp->rtcp);
rtp->rtcp = NULL;
return;
}
#ifdef HAVE_PJPROJECT
if (rtp->ice) {
rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP);
}
#endif
#ifdef HAVE_OPENSSL_SRTP
dtls_setup_rtcp(instance);
#endif
} else {
struct ast_sockaddr addr;
/* RTCPMUX uses the same socket as RTP. If we were previously using standard RTCP
* then close the socket we previously created.
*
* It may seem as though there is a possible race condition here where we might try
* to close the RTCP socket while it is being used to send data. However, this is not
* a problem in practice since setting and adjusting of RTCP properties happens prior
* to activating RTP. It is not until RTP is activated that timers start for RTCP
* transmission
*/
if (rtp->rtcp->s > -1) {
close(rtp->rtcp->s);
}
rtp->rtcp->s = rtp->s;
ast_rtp_instance_get_remote_address(instance, &addr);
ast_sockaddr_copy(&rtp->rtcp->them, &addr);
#ifdef HAVE_OPENSSL_SRTP
if (rtp->rtcp->dtls.ssl) {
SSL_free(rtp->rtcp->dtls.ssl);
}
rtp->rtcp->dtls.ssl = rtp->dtls.ssl;
#endif
}
ast_debug(1, "Setup RTCP on RTP instance '%p'\n", instance);
rtp->rtcp->schedid = -1;
#ifdef HAVE_PJPROJECT
if (rtp->ice) {
rtp_add_candidates_to_ice(instance, rtp, &rtp->rtcp->us, ast_sockaddr_port(&rtp->rtcp->us), AST_RTP_ICE_COMPONENT_RTCP, TRANSPORT_SOCKET_RTCP);
}
#endif
#ifdef HAVE_OPENSSL_SRTP
rtp->rtcp->dtls.timeout_timer = -1;
dtls_setup_rtcp(instance);
#endif
return;
} else {
if (rtp->rtcp) {
@ -5021,9 +5144,11 @@ static void ast_rtp_prop_set(struct ast_rtp_instance *instance, enum ast_rtp_pro
}
rtp->rtcp->schedid = -1;
}
close(rtp->rtcp->s);
if (rtp->rtcp->s > -1 && rtp->rtcp->s != rtp->s) {
close(rtp->rtcp->s);
}
#ifdef HAVE_OPENSSL_SRTP
if (rtp->rtcp->dtls.ssl) {
if (rtp->rtcp->dtls.ssl && rtp->rtcp->dtls.ssl != rtp->dtls.ssl) {
SSL_free(rtp->rtcp->dtls.ssl);
}
#endif
@ -5065,10 +5190,12 @@ static void ast_rtp_remote_address_set(struct ast_rtp_instance *instance, struct
ast_debug(1, "Setting RTCP address on RTP instance '%p'\n", instance);
ast_sockaddr_copy(&rtp->rtcp->them, addr);
if (!ast_sockaddr_isnull(addr)) {
ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(addr) + 1);
if (rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
ast_sockaddr_set_port(&rtp->rtcp->them, ast_sockaddr_port(addr) + 1);
/* Update the local RTCP address with what is being used */
ast_sockaddr_set_port(&local, ast_sockaddr_port(&local) + 1);
/* Update the local RTCP address with what is being used */
ast_sockaddr_set_port(&local, ast_sockaddr_port(&local) + 1);
}
ast_sockaddr_copy(&rtp->rtcp->us, &local);
ast_free(rtp->rtcp->local_addr_str);
@ -5356,7 +5483,7 @@ static int ast_rtp_activate(struct ast_rtp_instance *instance)
dtls_perform_handshake(instance, &rtp->dtls, 0);
if (rtp->rtcp) {
if (rtp->rtcp && rtp->rtcp->type == AST_RTP_INSTANCE_RTCP_STANDARD) {
dtls_perform_handshake(instance, &rtp->rtcp->dtls, 1);
}