diff options
Diffstat (limited to 'res/res_pjsip_sdp_rtp.c')
-rw-r--r-- | res/res_pjsip_sdp_rtp.c | 239 |
1 files changed, 139 insertions, 100 deletions
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index c5a673aa4..03fef40cf 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -51,6 +51,8 @@ #include "asterisk/sdp_srtp.h" #include "asterisk/dsp.h" #include "asterisk/linkedlists.h" /* for AST_LIST_NEXT */ +#include "asterisk/stream.h" +#include "asterisk/format_cache.h" #include "asterisk/res_pjsip.h" #include "asterisk/res_pjsip_session.h" @@ -62,48 +64,7 @@ static struct ast_sched_context *sched; static struct ast_sockaddr address_rtp; static const char STR_AUDIO[] = "audio"; -static const int FD_AUDIO = 0; - static const char STR_VIDEO[] = "video"; -static const int FD_VIDEO = 2; - -/*! \brief Retrieves an ast_format_type based on the given stream_type */ -static enum ast_media_type stream_to_media_type(const char *stream_type) -{ - if (!strcasecmp(stream_type, STR_AUDIO)) { - return AST_MEDIA_TYPE_AUDIO; - } else if (!strcasecmp(stream_type, STR_VIDEO)) { - return AST_MEDIA_TYPE_VIDEO; - } - - return 0; -} - -/*! \brief Get the starting descriptor for a media type */ -static int media_type_to_fdno(enum ast_media_type media_type) -{ - switch (media_type) { - case AST_MEDIA_TYPE_AUDIO: return FD_AUDIO; - case AST_MEDIA_TYPE_VIDEO: return FD_VIDEO; - case AST_MEDIA_TYPE_TEXT: - case AST_MEDIA_TYPE_UNKNOWN: - case AST_MEDIA_TYPE_IMAGE: - case AST_MEDIA_TYPE_END: break; - } - return -1; -} - -/*! \brief Remove all other cap types but the one given */ -static void format_cap_only_type(struct ast_format_cap *caps, enum ast_media_type media_type) -{ - int i = 0; - while (i <= AST_MEDIA_TYPE_TEXT) { - if (i != media_type && i != AST_MEDIA_TYPE_UNKNOWN) { - ast_format_cap_remove_by_type(caps, i); - } - i += 1; - } -} static int send_keepalive(const void *data) { @@ -253,11 +214,11 @@ static int create_rtp(struct ast_sip_session *session, struct ast_sip_session_me ast_rtp_instance_dtmf_mode_set(session_media->rtp, AST_RTP_DTMF_MODE_INBAND); } - if (!strcmp(session_media->stream_type, STR_AUDIO) && + if (session_media->type == AST_MEDIA_TYPE_AUDIO && (session->endpoint->media.tos_audio || session->endpoint->media.cos_audio)) { ast_rtp_instance_set_qos(session_media->rtp, session->endpoint->media.tos_audio, session->endpoint->media.cos_audio, "SIP RTP Audio"); - } else if (!strcmp(session_media->stream_type, STR_VIDEO) && + } else if (session_media->type == AST_MEDIA_TYPE_VIDEO && (session->endpoint->media.tos_video || session->endpoint->media.cos_video)) { ast_rtp_instance_set_qos(session_media->rtp, session->endpoint->media.tos_video, session->endpoint->media.cos_video, "SIP RTP Video"); @@ -347,12 +308,13 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp static int set_caps(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_media *stream, - int is_offer) + int is_offer, struct ast_stream *asterisk_stream) { RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup); RAII_VAR(struct ast_format_cap *, peer, NULL, ao2_cleanup); RAII_VAR(struct ast_format_cap *, joint, NULL, ao2_cleanup); - enum ast_media_type media_type = stream_to_media_type(session_media->stream_type); + RAII_VAR(struct ast_format_cap *, endpoint_caps, NULL, ao2_cleanup); + enum ast_media_type media_type = session_media->type; struct ast_rtp_codecs codecs = AST_RTP_CODECS_NULL_INIT; int fmts = 0; int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) && @@ -362,14 +324,14 @@ static int set_caps(struct ast_sip_session *session, if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) || !(peer = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) || !(joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { - ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", session_media->stream_type); + ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", + ast_codec_media_type2str(session_media->type)); return -1; } /* get the endpoint capabilities */ if (direct_media_enabled) { ast_format_cap_get_compatible(session->endpoint->media.codecs, session->direct_media_cap, caps); - format_cap_only_type(caps, media_type); } else { ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, media_type); } @@ -386,7 +348,7 @@ static int set_caps(struct ast_sip_session *session, ast_rtp_codecs_payloads_destroy(&codecs); ast_log(LOG_NOTICE, "No joint capabilities for '%s' media stream between our configuration(%s) and incoming SDP(%s)\n", - session_media->stream_type, + ast_codec_media_type2str(session_media->type), ast_format_cap_get_names(caps, &usbuf), ast_format_cap_get_names(peer, &thembuf)); return -1; @@ -402,9 +364,9 @@ static int set_caps(struct ast_sip_session *session, ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session_media->rtp), session_media->rtp); - ast_format_cap_append_from_cap(session->req_caps, joint, AST_MEDIA_TYPE_UNKNOWN); + ast_stream_set_formats(asterisk_stream, joint); - if (session->channel) { + if (session->channel && ast_sip_session_is_pending_stream_default(session, asterisk_stream)) { ast_channel_lock(session->channel); ast_format_cap_remove_by_type(caps, AST_MEDIA_TYPE_UNKNOWN); ast_format_cap_append_from_cap(caps, ast_channel_nativeformats(session->channel), @@ -968,24 +930,21 @@ static void set_ice_components(struct ast_sip_session *session, struct ast_sip_s } /*! \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) +static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, + struct ast_sip_session_media *session_media, const pjmedia_sdp_session *sdp, + int index, struct ast_stream *asterisk_stream) { char host[NI_MAXHOST]; RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); - enum ast_media_type media_type = stream_to_media_type(session_media->stream_type); + pjmedia_sdp_media *stream = sdp->media[index]; + enum ast_media_type media_type = session_media->type; enum ast_sip_session_media_encryption encryption = AST_SIP_MEDIA_ENCRYPT_NONE; int res; - /* If port is 0, ignore this media stream */ - if (!stream->desc.port) { - ast_debug(3, "Media stream '%s' is already declined\n", session_media->stream_type); - return 0; - } - /* If no type formats have been configured reject this stream */ if (!ast_format_cap_has_type(session->endpoint->media.codecs, media_type)) { - ast_debug(3, "Endpoint has no codecs for media type '%s', declining stream\n", session_media->stream_type); + ast_debug(3, "Endpoint has no codecs for media type '%s', declining stream\n", + ast_codec_media_type2str(session_media->type)); return 0; } @@ -1040,7 +999,7 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct pj_strdup(session->inv_session->pool, &session_media->transport, &stream->desc.transport); } - if (set_caps(session, session_media, stream, 1)) { + if (set_caps(session, session_media, stream, 1, asterisk_stream)) { return 0; } return 1; @@ -1161,9 +1120,10 @@ static int add_crypto_to_stream(struct ast_sip_session *session, /*! \brief Function which creates an outgoing stream */ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, - struct pjmedia_sdp_session *sdp) + struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_session *remote, struct ast_stream *stream) { pj_pool_t *pool = session->inv_session->pool_prov; + static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 }; static const pj_str_t STR_IN = { "IN", 2 }; static const pj_str_t STR_IP4 = { "IP4", 3}; static const pj_str_t STR_IP6 = { "IP6", 3}; @@ -1180,33 +1140,60 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as int min_packet_size = 0, max_packet_size = 0; int rtp_code; RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup); - enum ast_media_type media_type = stream_to_media_type(session_media->stream_type); - int use_override_prefs = ast_format_cap_count(session->req_caps); + enum ast_media_type media_type = session_media->type; int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) && ast_format_cap_count(session->direct_media_cap); - if ((use_override_prefs && !ast_format_cap_has_type(session->req_caps, media_type)) || - (!use_override_prefs && !ast_format_cap_has_type(session->endpoint->media.codecs, media_type))) { - /* If no type formats are configured don't add a stream */ - return 0; - } else if (!session_media->rtp && create_rtp(session, session_media)) { + media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media)); + if (!media) { return -1; } + pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(session_media->type)); - set_ice_components(session, session_media); - enable_rtcp(session, session_media, NULL); + /* If this is a removed (or declined) stream OR if no formats exist then construct a minimal stream in SDP */ + if (ast_stream_get_state(stream) == AST_STREAM_STATE_REMOVED || !ast_stream_get_formats(stream) || + !ast_format_cap_count(ast_stream_get_formats(stream))) { + media->desc.port = 0; + media->desc.port_count = 1; + + if (remote) { + pjmedia_sdp_media *remote_media = remote->media[ast_stream_get_position(stream)]; + int index; + + media->desc.transport = remote_media->desc.transport; + + /* Preserve existing behavior by copying the formats provided from the offer */ + for (index = 0; index < remote_media->desc.fmt_count; ++index) { + media->desc.fmt[index] = remote_media->desc.fmt[index]; + } + media->desc.fmt_count = remote_media->desc.fmt_count; + } else { + /* This is actually an offer so put a dummy payload in that is ignored and sane transport */ + media->desc.transport = STR_RTP_AVP; + pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], "32"); + } + + sdp->media[sdp->media_count++] = media; + ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED); + + return 1; + } - if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) || - !(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) { + if (!session_media->rtp && create_rtp(session, session_media)) { return -1; } + set_ice_components(session, session_media); + enable_rtcp(session, session_media, NULL); + + /* Crypto has to be added before setting the media transport so that SRTP is properly + * set up according to the configuration. This ends up changing the media transport. + */ if (add_crypto_to_stream(session, session_media, pool, media)) { return -1; } - media->desc.media = pj_str(session_media->stream_type); if (pj_strlen(&session_media->transport)) { /* If a transport has already been specified use it */ media->desc.transport = session_media->transport; @@ -1219,6 +1206,11 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as session->endpoint->media.rtp.force_avp)); } + media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)); + if (!media->conn) { + return -1; + } + /* Add connection level details */ if (direct_media_enabled) { hostip = ast_sockaddr_stringify_fmt(&session_media->direct_media_addr, AST_SOCKADDR_STR_ADDR); @@ -1229,7 +1221,8 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as } if (ast_strlen_zero(hostip)) { - ast_log(LOG_ERROR, "No local host IP available for stream %s\n", session_media->stream_type); + ast_log(LOG_ERROR, "No local host IP available for stream %s\n", + ast_codec_media_type2str(session_media->type)); return -1; } @@ -1247,25 +1240,23 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as } } + /* Add ICE attributes and candidates */ + add_ice_to_stream(session, session_media, pool, media); + ast_rtp_instance_get_local_address(session_media->rtp, &addr); media->desc.port = direct_media_enabled ? ast_sockaddr_port(&session_media->direct_media_addr) : (pj_uint16_t) ast_sockaddr_port(&addr); media->desc.port_count = 1; - /* Add ICE attributes and candidates */ - add_ice_to_stream(session, session_media, pool, media); - if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { - ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", session_media->stream_type); + ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", + ast_codec_media_type2str(session_media->type)); return -1; } if (direct_media_enabled) { ast_format_cap_get_compatible(session->endpoint->media.codecs, session->direct_media_cap, caps); - } else if (!ast_format_cap_count(session->req_caps) || - !ast_format_cap_iscompatible(session->req_caps, session->endpoint->media.codecs)) { - ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, media_type); } else { - ast_format_cap_append_from_cap(caps, session->req_caps, media_type); + ast_format_cap_append_from_cap(caps, ast_stream_get_formats(stream), media_type); } for (index = 0; index < ast_format_cap_count(caps); ++index) { @@ -1302,7 +1293,8 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as } /* Add non-codec formats */ - if (media_type != AST_MEDIA_TYPE_VIDEO && media->desc.fmt_count < PJMEDIA_MAX_SDP_FMT) { + if (ast_sip_session_is_pending_stream_default(session, stream) && media_type != AST_MEDIA_TYPE_VIDEO + && media->desc.fmt_count < PJMEDIA_MAX_SDP_FMT) { for (index = 1LL; index <= AST_RTP_MAX; index <<= 1) { if (!(noncodec & index)) { continue; @@ -1368,23 +1360,65 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as return 1; } -static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, - const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream, - const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream) +static struct ast_frame *media_session_rtp_read_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media) +{ + struct ast_frame *f; + + if (!session_media->rtp) { + return &ast_null_frame; + } + + f = ast_rtp_instance_read(session_media->rtp, 0); + if (!f) { + return NULL; + } + + ast_rtp_instance_set_last_rx(session_media->rtp, time(NULL)); + + return f; +} + +static struct ast_frame *media_session_rtcp_read_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media) +{ + struct ast_frame *f; + + if (!session_media->rtp) { + return &ast_null_frame; + } + + f = ast_rtp_instance_read(session_media->rtp, 1); + if (!f) { + return NULL; + } + + ast_rtp_instance_set_last_rx(session_media->rtp, time(NULL)); + + return f; +} + +static int media_session_rtp_write_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct ast_frame *frame) +{ + if (!session_media->rtp) { + return 0; + } + + return ast_rtp_instance_write(session_media->rtp, frame); +} + +static int apply_negotiated_sdp_stream(struct ast_sip_session *session, + struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, + const struct pjmedia_sdp_session *remote, int index, struct ast_stream *asterisk_stream) { RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); - enum ast_media_type media_type = stream_to_media_type(session_media->stream_type); + struct pjmedia_sdp_media *remote_stream = remote->media[index]; + enum ast_media_type media_type = session_media->type; char host[NI_MAXHOST]; - int fdno, res; + int res; if (!session->channel) { return 1; } - if (!local_stream->desc.port || !remote_stream->desc.port) { - return 1; - } - /* Ensure incoming transport is compatible with the endpoint's configuration */ if (!session->endpoint->media.rtp.use_received_transport && check_endpoint_media_transport(session->endpoint, remote_stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) { @@ -1424,21 +1458,26 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a /* Apply connection information to the RTP instance */ ast_sockaddr_set_port(addrs, remote_stream->desc.port); ast_rtp_instance_set_remote_address(session_media->rtp, addrs); - if (set_caps(session, session_media, remote_stream, 0)) { + if (set_caps(session, session_media, remote_stream, 0, asterisk_stream)) { return 1; } - if ((fdno = media_type_to_fdno(media_type)) < 0) { - return -1; - } - ast_channel_set_fd(session->channel, fdno, ast_rtp_instance_fd(session_media->rtp, 0)); + ast_sip_session_media_set_write_callback(session, session_media, media_session_rtp_write_callback); + ast_sip_session_media_add_read_callback(session, session_media, ast_rtp_instance_fd(session_media->rtp, 0), + media_session_rtp_read_callback); 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)); + ast_sip_session_media_add_read_callback(session, session_media, ast_rtp_instance_fd(session_media->rtp, 1), + media_session_rtcp_read_callback); } /* If ICE support is enabled find all the needed attributes */ process_ice_attributes(session, session_media, remote, remote_stream); + /* Set the channel uniqueid on the RTP instance now that it is becoming active */ + ast_channel_lock(session->channel); + ast_rtp_instance_set_channel_id(session_media->rtp, ast_channel_uniqueid(session->channel)); + ast_channel_unlock(session->channel); + /* Ensure the RTP instance is active */ ast_rtp_instance_activate(session_media->rtp); @@ -1476,7 +1515,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, struct a 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) { + session_media->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 |