From 8470c2bdea89f1ed89d8a773d775de96ededf3fb Mon Sep 17 00:00:00 2001 From: George Joseph Date: Thu, 2 Mar 2017 16:11:06 -0700 Subject: RFC sdp: Initial SDP creation * Added additional fields to ast_sdp_options. * Re-organized ast_sdp. * Updated field names to correspond to RFC4566 terminology. * Created allocs/frees for SDP children. * Created getters/setters for SDP children where appropriate. * Added ast_sdp_create_from_state. * Refactored res_sdp_translator_pjmedia for changes. Change-Id: Iefbd877af7f5a4d3c74deead1bff8802661b0d48 --- main/rtp_engine.c | 6 +- main/sdp.c | 765 ++++++++++++++++++++++++++++++++++++++++++++++++++ main/sdp_options.c | 124 ++++---- main/sdp_private.h | 55 ++++ main/sdp_repr.c | 111 -------- main/sdp_state.c | 109 +++---- main/sdp_translator.c | 14 +- main/stream.c | 36 +++ 8 files changed, 978 insertions(+), 242 deletions(-) create mode 100644 main/sdp.c create mode 100644 main/sdp_private.h delete mode 100644 main/sdp_repr.c (limited to 'main') diff --git a/main/rtp_engine.c b/main/rtp_engine.c index a46546318..931f89d7c 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -1493,7 +1493,8 @@ int ast_rtp_codecs_find_payload_code(struct ast_rtp_codecs *codecs, int payload) return res; } -const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, struct ast_format *format, int code, enum ast_rtp_options options) +const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, + const struct ast_format *format, int code, enum ast_rtp_options options) { int i; const char *res = ""; @@ -1522,7 +1523,8 @@ const char *ast_rtp_lookup_mime_subtype2(const int asterisk_format, struct ast_f return res; } -unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, struct ast_format *format, int code) +unsigned int ast_rtp_lookup_sample_rate2(int asterisk_format, + const struct ast_format *format, int code) { unsigned int i; unsigned int res = 0; diff --git a/main/sdp.c b/main/sdp.c new file mode 100644 index 000000000..246763edf --- /dev/null +++ b/main/sdp.c @@ -0,0 +1,765 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2017, Digium, Inc. + * + * George Joseph + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + + +#include "asterisk.h" +#include "asterisk/utils.h" +#include "asterisk/netsock2.h" +#include "asterisk/codec.h" +#include "asterisk/format.h" +#include "asterisk/format_cap.h" +#include "asterisk/rtp_engine.h" +#include "asterisk/sdp_state.h" +#include "asterisk/sdp_options.h" +#include "asterisk/sdp_translator.h" +#include "asterisk/sdp.h" +#include "asterisk/vector.h" +#include "asterisk/utils.h" +#include "asterisk/stream.h" +#include "sdp_private.h" + +void ast_sdp_a_free(struct ast_sdp_a_line *a_line) +{ + ast_free(a_line); +} + +void ast_sdp_a_lines_free(struct ast_sdp_a_lines *a_lines) +{ + if (!a_lines) { + return; + } + + AST_VECTOR_CALLBACK_VOID(a_lines, ast_sdp_a_free); + AST_VECTOR_FREE(a_lines); + ast_free(a_lines); +} + +void ast_sdp_c_free(struct ast_sdp_c_line *c_line) +{ + ast_free(c_line); +} + +void ast_sdp_payload_free(struct ast_sdp_payload *payload) +{ + ast_free(payload); +} + +void ast_sdp_payloads_free(struct ast_sdp_payloads *payloads) +{ + if (!payloads) { + return; + } + + AST_VECTOR_CALLBACK_VOID(payloads, ast_sdp_payload_free); + AST_VECTOR_FREE(payloads); + ast_free(payloads); +} + +void ast_sdp_m_free(struct ast_sdp_m_line *m_line) +{ + if (!m_line) { + return; + } + + ast_sdp_a_lines_free(m_line->a_lines); + ast_sdp_payloads_free(m_line->payloads); + ast_sdp_c_free(m_line->c_line); + ast_free(m_line); +} + +void ast_sdp_m_lines_free(struct ast_sdp_m_lines *m_lines) +{ + if (!m_lines) { + return; + } + + AST_VECTOR_CALLBACK_VOID(m_lines, ast_sdp_m_free); + AST_VECTOR_FREE(m_lines); + ast_free(m_lines); +} + +void ast_sdp_o_free(struct ast_sdp_o_line *o_line) +{ + ast_free(o_line); +} + +void ast_sdp_s_free(struct ast_sdp_s_line *s_line) +{ + ast_free(s_line); +} + +void ast_sdp_t_free(struct ast_sdp_t_line *t_line) +{ + ast_free(t_line); +} + +void ast_sdp_free(struct ast_sdp *sdp) +{ + if (!sdp) { + return; + } + + ast_sdp_o_free(sdp->o_line); + ast_sdp_s_free(sdp->s_line); + ast_sdp_c_free(sdp->c_line); + ast_sdp_t_free(sdp->t_line); + ast_sdp_a_lines_free(sdp->a_lines); + ast_sdp_m_lines_free(sdp->m_lines); + ast_free(sdp); +} + +#define COPY_STR_AND_ADVANCE(p, dest, source) \ +({ \ + dest = p; \ + strcpy(dest, source); \ + p += (strlen(source) + 1); \ +}) + +struct ast_sdp_a_line *ast_sdp_a_alloc(const char *name, const char *value) +{ + struct ast_sdp_a_line *a_line; + size_t len; + char *p; + + ast_assert(!ast_strlen_zero(name)); + + if (ast_strlen_zero(value)) { + value = ""; + } + + len = sizeof(*a_line) + strlen(name) + strlen(value) + 2; + a_line = ast_calloc(1, len); + if (!a_line) { + return NULL; + } + + p = ((char *)a_line) + sizeof(*a_line); + + COPY_STR_AND_ADVANCE(p, a_line->name, name); + COPY_STR_AND_ADVANCE(p, a_line->value, value); + + return a_line; +} + +struct ast_sdp_c_line *ast_sdp_c_alloc(const char *address_type, const char *address) +{ + struct ast_sdp_c_line *c_line; + size_t len; + char *p; + + ast_assert(!ast_strlen_zero(address_type) && !ast_strlen_zero(address)); + + len = sizeof(*c_line) + strlen(address_type) + strlen(address) + 2; + c_line = ast_calloc(1, len); + if (!c_line) { + return NULL; + } + + p = ((char *)c_line) + sizeof(*c_line); + + COPY_STR_AND_ADVANCE(p, c_line->address_type, address_type); + COPY_STR_AND_ADVANCE(p, c_line->address, address); + + return c_line; +} + +struct ast_sdp_payload *ast_sdp_payload_alloc(const char *fmt) +{ + struct ast_sdp_payload *payload; + size_t len; + + ast_assert(!ast_strlen_zero(fmt)); + + len = sizeof(*payload) + strlen(fmt) + 1; + payload = ast_calloc(1, len); + if (!payload) { + return NULL; + } + + payload->fmt = ((char *)payload) + sizeof(*payload); + strcpy(payload->fmt, fmt); /* Safe */ + + return payload; +} + +struct ast_sdp_m_line *ast_sdp_m_alloc(const char *type, uint16_t port, + uint16_t port_count, const char *proto, struct ast_sdp_c_line *c_line) +{ + struct ast_sdp_m_line *m_line; + size_t len; + char *p; + + ast_assert(!ast_strlen_zero(type) && !ast_strlen_zero(proto)); + + len = sizeof(*m_line) + strlen(type) + strlen(proto) + 2; + m_line = ast_calloc(1, len); + if (!m_line) { + return NULL; + } + + m_line->a_lines = ast_calloc(1, sizeof(*m_line->a_lines)); + if (!m_line->a_lines) { + ast_sdp_m_free(m_line); + return NULL; + } + if (AST_VECTOR_INIT(m_line->a_lines, 20)) { + ast_sdp_m_free(m_line); + return NULL; + } + + m_line->payloads = ast_calloc(1, sizeof(*m_line->payloads)); + if (!m_line->payloads) { + ast_sdp_m_free(m_line); + return NULL; + } + if (AST_VECTOR_INIT(m_line->payloads, 20)) { + ast_sdp_m_free(m_line); + return NULL; + } + + p = ((char *)m_line) + sizeof(*m_line); + + COPY_STR_AND_ADVANCE(p, m_line->type, type); + COPY_STR_AND_ADVANCE(p, m_line->proto, proto); + m_line->port = port; + m_line->port_count = port_count; + m_line->c_line = c_line; + + return m_line; +} + +struct ast_sdp_s_line *ast_sdp_s_alloc(const char *session_name) +{ + struct ast_sdp_s_line *s_line; + size_t len; + + if (ast_strlen_zero(session_name)) { + session_name = " "; + } + + len = sizeof(*s_line) + strlen(session_name) + 1; + s_line = ast_calloc(1, len); + if (!s_line) { + return NULL; + } + + s_line->session_name = ((char *)s_line) + sizeof(*s_line); + strcpy(s_line->session_name, session_name); /* Safe */ + + return s_line; +} + +struct ast_sdp_t_line *ast_sdp_t_alloc(uint64_t start_time, uint64_t stop_time) +{ + struct ast_sdp_t_line *t_line; + + t_line = ast_calloc(1, sizeof(*t_line)); + if (!t_line) { + return NULL; + } + + t_line->start_time = start_time; + t_line->stop_time = stop_time; + + return t_line; +} + +struct ast_sdp_o_line *ast_sdp_o_alloc(const char *username, uint64_t session_id, + uint64_t session_version, const char *address_type, const char *address) +{ + struct ast_sdp_o_line *o_line; + size_t len; + char *p; + + ast_assert(!ast_strlen_zero(username) && !ast_strlen_zero(address_type) + && !ast_strlen_zero(address)); + + len = sizeof(*o_line) + strlen(username) + strlen(address_type) + strlen(address) + 3; + o_line = ast_calloc(1, len); + if (!o_line) { + return NULL; + } + + o_line->session_id = session_id; + o_line->session_version = session_version; + + p = ((char *)o_line) + sizeof(*o_line); + + COPY_STR_AND_ADVANCE(p, o_line->username, username); + COPY_STR_AND_ADVANCE(p, o_line->address_type, address_type); + COPY_STR_AND_ADVANCE(p, o_line->address, address); + + return o_line; +} + +struct ast_sdp *ast_sdp_alloc(struct ast_sdp_o_line *o_line, + struct ast_sdp_c_line *c_line, struct ast_sdp_s_line *s_line, + struct ast_sdp_t_line *t_line) +{ + struct ast_sdp *new_sdp; + + new_sdp = ast_calloc(1, sizeof *new_sdp); + if (!new_sdp) { + return NULL; + } + + new_sdp->a_lines = ast_calloc(1, sizeof(*new_sdp->a_lines)); + if (!new_sdp->a_lines) { + ast_sdp_free(new_sdp); + return NULL; + } + if (AST_VECTOR_INIT(new_sdp->a_lines, 20)) { + ast_sdp_free(new_sdp); + return NULL; + } + + new_sdp->m_lines = ast_calloc(1, sizeof(*new_sdp->m_lines)); + if (!new_sdp->m_lines) { + ast_sdp_free(new_sdp); + return NULL; + } + if (AST_VECTOR_INIT(new_sdp->m_lines, 20)) { + ast_sdp_free(new_sdp); + return NULL; + } + + new_sdp->o_line = o_line; + new_sdp->c_line = c_line; + new_sdp->s_line = s_line; + new_sdp->t_line = t_line; + + return new_sdp; +} + +int ast_sdp_add_a(struct ast_sdp *sdp, struct ast_sdp_a_line *a_line) +{ + ast_assert(sdp && a_line); + + return AST_VECTOR_APPEND(sdp->a_lines, a_line); +} + +int ast_sdp_get_a_count(const struct ast_sdp *sdp) +{ + ast_assert(sdp != NULL); + + return AST_VECTOR_SIZE(sdp->a_lines); +} + +struct ast_sdp_a_line *ast_sdp_get_a(const struct ast_sdp *sdp, int index) +{ + ast_assert(sdp != NULL); + + return AST_VECTOR_GET(sdp->a_lines, index); +} + +int ast_sdp_add_m(struct ast_sdp *sdp, struct ast_sdp_m_line *m_line) +{ + ast_assert(sdp && m_line); + + return AST_VECTOR_APPEND(sdp->m_lines, m_line); +} + +int ast_sdp_get_m_count(const struct ast_sdp *sdp) +{ + ast_assert(sdp != NULL); + + return AST_VECTOR_SIZE(sdp->m_lines); +} + +struct ast_sdp_m_line *ast_sdp_get_m(const struct ast_sdp *sdp, int index) +{ + ast_assert(sdp != NULL); + + return AST_VECTOR_GET(sdp->m_lines, index); +} + +int ast_sdp_m_add_a(struct ast_sdp_m_line *m_line, struct ast_sdp_a_line *a_line) +{ + ast_assert(m_line && a_line); + + return AST_VECTOR_APPEND(m_line->a_lines, a_line); +} + +int ast_sdp_m_get_a_count(const struct ast_sdp_m_line *m_line) +{ + ast_assert(m_line != NULL); + + return AST_VECTOR_SIZE(m_line->a_lines); +} + +struct ast_sdp_a_line *ast_sdp_m_get_a(const struct ast_sdp_m_line *m_line, int index) +{ + ast_assert(m_line != NULL); + + return AST_VECTOR_GET(m_line->a_lines, index); +} + +int ast_sdp_m_add_payload(struct ast_sdp_m_line *m_line, struct ast_sdp_payload *payload) +{ + ast_assert(m_line && payload); + + return AST_VECTOR_APPEND(m_line->payloads, payload); +} + +int ast_sdp_m_get_payload_count(const struct ast_sdp_m_line *m_line) +{ + ast_assert(m_line != NULL); + + return AST_VECTOR_SIZE(m_line->payloads); +} + +struct ast_sdp_payload *ast_sdp_m_get_payload(const struct ast_sdp_m_line *m_line, int index) +{ + ast_assert(m_line != NULL); + + return AST_VECTOR_GET(m_line->payloads, index); +} + +static int sdp_m_add_fmtp(struct ast_sdp_m_line *m_line, const struct ast_format *format, + int rtp_code) +{ + struct ast_str *fmtp0 = ast_str_alloca(256); + char *tmp; + + ast_format_generate_sdp_fmtp(format, rtp_code, &fmtp0); + if (ast_str_strlen(fmtp0) == 0) { + return -1; + } + + tmp = ast_str_buffer(fmtp0) + ast_str_strlen(fmtp0) - 1; + /* remove any carriage return line feeds */ + while (*tmp == '\r' || *tmp == '\n') --tmp; + *++tmp = '\0'; + + /* ast...generate gives us everything, just need value */ + tmp = strchr(ast_str_buffer(fmtp0), ':'); + if (tmp && tmp[1] != '\0') { + tmp++; + } else { + tmp = ast_str_buffer(fmtp0); + } + + ast_sdp_m_add_a(m_line, ast_sdp_a_alloc("fmtp", tmp)); + + return 0; +} + +static int sdp_m_add_rtpmap(struct ast_sdp_m_line *m_line, + const struct ast_sdp_options *options, int rtp_code, int asterisk_format, + const struct ast_format *format, int code) +{ + char tmp[64]; + const char *enc_name; + struct ast_sdp_payload *payload; + struct ast_sdp_a_line *a_line; + + snprintf(tmp, sizeof(tmp), "%d", rtp_code); + payload = ast_sdp_payload_alloc(tmp); + if (!payload || ast_sdp_m_add_payload(m_line, payload)) { + ast_sdp_payload_free(payload); + return -1; + } + + enc_name = ast_rtp_lookup_mime_subtype2(asterisk_format, format, code, + options->g726_non_standard ? AST_RTP_OPT_G726_NONSTANDARD : 0); + + snprintf(tmp, sizeof(tmp), "%d %s/%d%s%s", rtp_code, enc_name, + ast_rtp_lookup_sample_rate2(asterisk_format, format, code), + strcmp(enc_name, "opus") ? "" : "/", strcmp(enc_name, "opus") ? "" : "2"); + + a_line = ast_sdp_a_alloc("rtpmap", tmp); + if (!a_line || ast_sdp_m_add_a(m_line, a_line)) { + ast_sdp_a_free(a_line); + return -1; + } + + return 0; +} + +int ast_sdp_m_add_format(struct ast_sdp_m_line *m_line, const struct ast_sdp_options *options, + int rtp_code, int asterisk_format, const struct ast_format *format, int code) +{ + sdp_m_add_rtpmap(m_line, options, rtp_code, asterisk_format, format, code); + sdp_m_add_fmtp(m_line, format, rtp_code); + + return 0; +} + +/* TODO + * This isn't set anywhere yet. + */ +/*! \brief Scheduler for RTCP purposes */ +static struct ast_sched_context *sched; + +/*! \brief Internal function which creates an RTP instance */ +static struct ast_rtp_instance *create_rtp(const struct ast_sdp_options *options, + enum ast_media_type media_type) +{ + struct ast_rtp_instance *rtp; + struct ast_rtp_engine_ice *ice; + struct ast_sockaddr temp_media_address; + static struct ast_sockaddr address_rtp; + struct ast_sockaddr *media_address = &address_rtp; + + if (options->bind_rtp_to_media_address && !ast_strlen_zero(options->media_address)) { + ast_sockaddr_parse(&temp_media_address, options->media_address, 0); + media_address = &temp_media_address; + } else { + if (ast_check_ipv6()) { + ast_sockaddr_parse(&address_rtp, "::", 0); + } else { + ast_sockaddr_parse(&address_rtp, "0.0.0.0", 0); + } + } + + if (!(rtp = ast_rtp_instance_new(options->rtp_engine, sched, media_address, NULL))) { + ast_log(LOG_ERROR, "Unable to create RTP instance using RTP engine '%s'\n", + options->rtp_engine); + return NULL; + } + + ast_rtp_instance_set_prop(rtp, AST_RTP_PROPERTY_RTCP, 1); + ast_rtp_instance_set_prop(rtp, AST_RTP_PROPERTY_NAT, options->rtp_symmetric); + + if (options->ice == AST_SDP_ICE_DISABLED && (ice = ast_rtp_instance_get_ice(rtp))) { + ice->stop(rtp); + } + + if (options->telephone_event) { + ast_rtp_instance_dtmf_mode_set(rtp, AST_RTP_DTMF_MODE_RFC2833); + ast_rtp_instance_set_prop(rtp, AST_RTP_PROPERTY_DTMF, 1); + } + + if (media_type == AST_MEDIA_TYPE_AUDIO && + (options->tos_audio || options->cos_audio)) { + ast_rtp_instance_set_qos(rtp, options->tos_audio, + options->cos_audio, "SIP RTP Audio"); + } else if (media_type == AST_MEDIA_TYPE_VIDEO && + (options->tos_video || options->cos_video)) { + ast_rtp_instance_set_qos(rtp, options->tos_video, + options->cos_video, "SIP RTP Video"); + } + + ast_rtp_instance_set_last_rx(rtp, time(NULL)); + + return rtp; +} + +int ast_sdp_add_m_from_stream(struct ast_sdp *sdp, const struct ast_sdp_options *options, + struct ast_rtp_instance *rtp, const struct ast_stream *stream) +{ + struct ast_sdp_m_line *m_line; + struct ast_format_cap *caps; + int i; + int rtp_code; + int min_packet_size = 0; + int max_packet_size = 0; + enum ast_media_type media_type; + char tmp[64]; + struct ast_sockaddr address_rtp; + struct ast_sdp_a_line *a_line; + + + ast_assert(sdp && options && rtp && stream); + + media_type = ast_stream_get_type(stream); + ast_rtp_instance_get_local_address(rtp, &address_rtp); + + m_line = ast_sdp_m_alloc( + ast_codec_media_type2str(ast_stream_get_type(stream)), + ast_sockaddr_port(&address_rtp), 1, + options->encryption != AST_SDP_ENCRYPTION_DISABLED ? "RTP/SAVP" : "RTP/AVP", + NULL); + if (!m_line) { + return -1; + } + + caps = ast_stream_get_formats(stream); + + for (i = 0; i < ast_format_cap_count(caps); i++) { + struct ast_format *format = ast_format_cap_get_format(caps, i); + + if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(rtp), 1, format, 0)) == -1) { + ast_log(LOG_WARNING,"Unable to get rtp codec payload code for %s\n", ast_format_get_name(format)); + ao2_ref(format, -1); + continue; + } + + if (ast_sdp_m_add_format(m_line, options, rtp_code, 0, format, 0)) { + ast_sdp_m_free(m_line); + ao2_ref(format, -1); + return -1; + } + + if (ast_format_get_maximum_ms(format) && + ((ast_format_get_maximum_ms(format) < max_packet_size) || !max_packet_size)) { + max_packet_size = ast_format_get_maximum_ms(format); + } + + ao2_ref(format, -1); + } + + if (media_type != AST_MEDIA_TYPE_VIDEO) { + for (i = 1LL; i <= AST_RTP_MAX; i <<= 1) { + if (!(options->telephone_event & i)) { + continue; + } + + rtp_code = ast_rtp_codecs_payload_code( + ast_rtp_instance_get_codecs(rtp), 0, NULL, i); + + if (rtp_code == -1) { + continue; + } + + if (sdp_m_add_rtpmap(m_line, options, rtp_code, 0, NULL, i)) { + continue; + } + + if (i == AST_RTP_DTMF) { + snprintf(tmp, sizeof(tmp), "%d 0-16", rtp_code); + a_line = ast_sdp_a_alloc("fmtp", tmp); + if (!a_line || ast_sdp_m_add_a(m_line, a_line)) { + ast_sdp_a_free(a_line); + ast_sdp_m_free(m_line); + return -1; + } + } + } + } + + if (ast_sdp_m_get_a_count(m_line) == 0) { + return 0; + } + + /* If ptime is set add it as an attribute */ + min_packet_size = ast_rtp_codecs_get_framing(ast_rtp_instance_get_codecs(rtp)); + if (!min_packet_size) { + min_packet_size = ast_format_cap_get_framing(caps); + } + if (min_packet_size) { + snprintf(tmp, sizeof(tmp), "%d", min_packet_size); + + a_line = ast_sdp_a_alloc("ptime", tmp); + if (!a_line || ast_sdp_m_add_a(m_line, a_line)) { + ast_sdp_a_free(a_line); + ast_sdp_m_free(m_line); + return -1; + } + } + + if (max_packet_size) { + snprintf(tmp, sizeof(tmp), "%d", max_packet_size); + a_line = ast_sdp_a_alloc("maxptime", tmp); + if (!a_line || ast_sdp_m_add_a(m_line, a_line)) { + ast_sdp_a_free(a_line); + ast_sdp_m_free(m_line); + return -1; + } + } + + a_line = ast_sdp_a_alloc(options->locally_held ? "sendonly" : "sendrecv", ""); + if (!a_line || ast_sdp_m_add_a(m_line, a_line)) { + ast_sdp_a_free(a_line); + ast_sdp_m_free(m_line); + return -1; + } + + if (ast_sdp_add_m(sdp, m_line)) { + ast_sdp_m_free(m_line); + return -1; + } + + return 0; +} + +struct ast_sdp *ast_sdp_create_from_state(const struct ast_sdp_state *sdp_state) +{ + const struct ast_sdp_options *options; + RAII_VAR(struct ast_sdp *, sdp, NULL, ao2_cleanup); + const const struct ast_stream_topology *topology; + int stream_count; + int stream_num; + struct ast_sdp_o_line *o_line = NULL; + struct ast_sdp_c_line *c_line = NULL; + struct ast_sdp_s_line *s_line = NULL; + struct ast_sdp_t_line *t_line = NULL; + struct ast_rtp_instance *rtp = NULL; + char *address_type; + struct timeval tv = ast_tvnow(); + uint32_t t; + ast_assert(!!sdp_state); + + options = ast_sdp_state_get_options(sdp_state); + topology = ast_sdp_state_get_local_topology(sdp_state); + stream_count = ast_stream_topology_get_count(topology); + + t = tv.tv_sec + 2208988800UL; + address_type = (strchr(options->media_address, ':') ? "IP6" : "IP4"); + + o_line = ast_sdp_o_alloc(options->sdpowner, t, t, address_type, options->media_address); + if (!o_line) { + goto error; + } + c_line = ast_sdp_c_alloc(address_type, options->media_address); + if (!c_line) { + goto error; + } + + s_line = ast_sdp_s_alloc(options->sdpsession); + if (!s_line) { + goto error; + } + + sdp = ast_sdp_alloc(o_line, c_line, s_line, NULL); + if (!sdp) { + goto error; + } + + for (stream_num = 0; stream_num < stream_count; stream_num++) { + struct ast_stream *stream = ast_stream_topology_get_stream(topology, stream_num); + + rtp = create_rtp(options, ast_stream_get_type(stream)); + if (!rtp) { + goto error; + } + + ast_stream_set_data(stream, AST_STREAM_DATA_RTP_INSTANCE, + rtp, (ast_stream_data_free_fn)&ast_rtp_instance_destroy); + + if (ast_sdp_add_m_from_stream(sdp, options, rtp, stream)) { + goto error; + } + } + + return sdp; + +error: + ao2_cleanup(rtp); + if (sdp) { + ast_sdp_free(sdp); + } else { + ast_sdp_t_free(t_line); + ast_sdp_s_free(s_line); + ast_sdp_c_free(c_line); + ast_sdp_o_free(o_line); + } + + return NULL; +} + diff --git a/main/sdp_options.c b/main/sdp_options.c index e18dfa55a..ca076ac7b 100644 --- a/main/sdp_options.c +++ b/main/sdp_options.c @@ -21,23 +21,63 @@ #include "asterisk/utils.h" #include "asterisk/sdp_options.h" -struct ast_sdp_options { - enum ast_sdp_options_ice ice; - int telephone_event; - enum ast_sdp_options_repr repr; - enum ast_sdp_options_encryption encryption; -}; +#include "sdp_private.h" #define DEFAULT_ICE AST_SDP_ICE_DISABLED #define DEFAULT_TELEPHONE_EVENT 0 -#define DEFAULT_REPR AST_SDP_REPR_STRING +#define DEFAULT_IMPL AST_SDP_IMPL_STRING #define DEFAULT_ENCRYPTION AST_SDP_ENCRYPTION_DISABLED +#define DEFINE_STRINGFIELD_GETTERS_SETTERS_FOR(field, assert_on_null) \ +void ast_sdp_options_set_##field(struct ast_sdp_options *options, const char *value) \ +{ \ + ast_assert(options != NULL); \ + if ((assert_on_null)) ast_assert(!ast_strlen_zero(value)); \ + if (!strcmp(value, options->field)) return; \ + ast_string_field_set(options, field, value); \ +} \ +const char *ast_sdp_options_get_##field(struct ast_sdp_options *options) \ +{ \ + ast_assert(options != NULL); \ + return options->field; \ +} \ + +#define DEFINE_GETTERS_SETTERS_FOR(type, field) \ +void ast_sdp_options_set_##field(struct ast_sdp_options *options, type value) \ +{ \ + ast_assert(options != NULL); \ + options->field = value; \ +} \ +type ast_sdp_options_get_##field(struct ast_sdp_options *options) \ +{ \ + ast_assert(options != NULL); \ + return options->field; \ +} \ + +DEFINE_STRINGFIELD_GETTERS_SETTERS_FOR(media_address, 0); +DEFINE_STRINGFIELD_GETTERS_SETTERS_FOR(sdpowner, 0); +DEFINE_STRINGFIELD_GETTERS_SETTERS_FOR(sdpsession, 0); +DEFINE_STRINGFIELD_GETTERS_SETTERS_FOR(rtp_engine, 0); + +DEFINE_GETTERS_SETTERS_FOR(unsigned int, bind_rtp_to_media_address); +DEFINE_GETTERS_SETTERS_FOR(unsigned int, rtp_symmetric); +DEFINE_GETTERS_SETTERS_FOR(unsigned int, telephone_event); +DEFINE_GETTERS_SETTERS_FOR(unsigned int, rtp_ipv6); +DEFINE_GETTERS_SETTERS_FOR(unsigned int, g726_non_standard); +DEFINE_GETTERS_SETTERS_FOR(unsigned int, locally_held); +DEFINE_GETTERS_SETTERS_FOR(unsigned int, tos_audio); +DEFINE_GETTERS_SETTERS_FOR(unsigned int, cos_audio); +DEFINE_GETTERS_SETTERS_FOR(unsigned int, tos_video); +DEFINE_GETTERS_SETTERS_FOR(unsigned int, cos_video); +DEFINE_GETTERS_SETTERS_FOR(enum ast_sdp_options_ice, ice); +DEFINE_GETTERS_SETTERS_FOR(enum ast_sdp_options_impl, impl); +DEFINE_GETTERS_SETTERS_FOR(enum ast_sdp_options_encryption, encryption); + static void set_defaults(struct ast_sdp_options *options) { options->ice = DEFAULT_ICE; options->telephone_event = DEFAULT_TELEPHONE_EVENT; - options->repr = DEFAULT_REPR; + options->impl = DEFAULT_IMPL; options->encryption = DEFAULT_ENCRYPTION; } @@ -49,72 +89,18 @@ struct ast_sdp_options *ast_sdp_options_alloc(void) if (!options) { return NULL; } + + if (ast_string_field_init(options, 256)) { + ast_free(options); + return NULL; + } + set_defaults(options); return options; } void ast_sdp_options_free(struct ast_sdp_options *options) { + ast_string_field_free_memory(options); ast_free(options); } - -int ast_sdp_options_set_ice(struct ast_sdp_options *options, enum ast_sdp_options_ice ice_setting) -{ - ast_assert(options != NULL); - - options->ice = ice_setting; - return 0; -} - -enum ast_sdp_options_ice ast_sdp_options_get_ice(const struct ast_sdp_options *options) -{ - ast_assert(options != NULL); - - return options->ice; -} - -int ast_sdp_options_set_telephone_event(struct ast_sdp_options *options, int telephone_event_enabled) -{ - ast_assert(options != NULL); - - options->telephone_event = telephone_event_enabled; - return 0; -} - -int ast_sdp_options_get_telephone_event(const struct ast_sdp_options *options) -{ - ast_assert(options != NULL); - - return options->telephone_event; -} - -int ast_sdp_options_set_repr(struct ast_sdp_options *options, enum ast_sdp_options_repr repr) -{ - ast_assert(options != NULL); - - options->repr = repr; - return 0; -} - -enum ast_sdp_options_repr ast_sdp_options_get_repr(const struct ast_sdp_options *options) -{ - ast_assert(options != NULL); - - return options->repr; -} - -int ast_sdp_options_set_encryption(struct ast_sdp_options *options, - enum ast_sdp_options_encryption encryption) -{ - ast_assert(options != NULL); - - options->encryption = encryption; - return 0; -} - -enum ast_sdp_options_encryption ast_sdp_options_get_encryption(const struct ast_sdp_options *options) -{ - ast_assert(options != NULL); - - return options->encryption; -} diff --git a/main/sdp_private.h b/main/sdp_private.h new file mode 100644 index 000000000..45aaebf9a --- /dev/null +++ b/main/sdp_private.h @@ -0,0 +1,55 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2017, Digium, Inc. + * + * Mark Michelson + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +#ifndef _MAIN_SDP_PRIVATE_H +#define _MAIN_SDP_PRIVATE_H + +#include "asterisk/stringfields.h" +#include "asterisk/sdp_options.h" + +struct ast_sdp_options { + AST_DECLARE_STRING_FIELDS( + /*! Optional media address to use in SDP */ + AST_STRING_FIELD(media_address); + /*! SDP origin username */ + AST_STRING_FIELD(sdpowner); + /*! SDP session name */ + AST_STRING_FIELD(sdpsession); + /*! RTP Engine Name */ + AST_STRING_FIELD(rtp_engine); + ); + struct { + unsigned int bind_rtp_to_media_address : 1; + unsigned int rtp_symmetric : 1; + unsigned int telephone_event : 1; + unsigned int rtp_ipv6 : 1; + unsigned int g726_non_standard : 1; + unsigned int locally_held : 1; + }; + struct { + unsigned int tos_audio; + unsigned int cos_audio; + unsigned int tos_video; + unsigned int cos_video; + }; + enum ast_sdp_options_ice ice; + enum ast_sdp_options_impl impl; + enum ast_sdp_options_encryption encryption; +}; + +#endif /* _MAIN_SDP_PRIVATE_H */ diff --git a/main/sdp_repr.c b/main/sdp_repr.c deleted file mode 100644 index 6df243b0e..000000000 --- a/main/sdp_repr.c +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2017, Digium, Inc. - * - * Mark Michelson - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -#include "asterisk.h" -#include "asterisk/sdp_priv.h" -#include "asterisk/utils.h" - -struct ast_sdp *ast_sdp_alloc(void) -{ - struct ast_sdp *new_sdp; - - new_sdp = ast_calloc(1, sizeof *new_sdp); - return new_sdp; -} - -static void free_o_line(struct ast_sdp *dead) -{ - ast_free(dead->o_line.user); - ast_free(dead->o_line.family); - ast_free(dead->o_line.addr); -} - -static void free_s_line(struct ast_sdp *dead) -{ - ast_free(dead->s_line); -} - -static void free_c_line(struct ast_sdp_c_line *c_line) -{ - ast_free(c_line->family); - ast_free(c_line->addr); -} - -static void free_t_line(struct ast_sdp_t_line *t_line) -{ - return; -} - -static void free_a_line(struct ast_sdp_a_line *a_line) -{ - ast_free(a_line->name); - ast_free(a_line->value); -} - -static void free_a_lines(struct ast_sdp_a_line_vector *a_lines) -{ - int i; - - for (i = 0; i < AST_VECTOR_SIZE(a_lines); ++i) { - free_a_line(AST_VECTOR_GET_ADDR(a_lines, i)); - } - AST_VECTOR_FREE(a_lines); -} - -static void free_m_line(struct ast_sdp_m_line *m_line) -{ - int i; - - ast_free(m_line->type); - ast_free(m_line->profile); - free_c_line(&m_line->c_line); - - for (i = 0; i < AST_VECTOR_SIZE(&m_line->payloads); ++i) { - ast_free(AST_VECTOR_GET(&m_line->payloads, i)); - } - AST_VECTOR_FREE(&m_line->payloads); - - free_a_lines(&m_line->a_lines); -} - -static void free_m_lines(struct ast_sdp *dead) -{ - int i; - - for (i = 0; i < AST_VECTOR_SIZE(&dead->m_lines); ++i) { - free_m_line(AST_VECTOR_GET_ADDR(&dead->m_lines, i)); - } - - AST_VECTOR_FREE(&dead->m_lines); -} - -void ast_sdp_free(struct ast_sdp *dead) -{ - if (!dead) { - return; - } - - free_o_line(dead); - free_s_line(dead); - free_c_line(&dead->c_line); - free_t_line(&dead->t_line); - free_a_lines(&dead->a_lines); - free_m_lines(dead); - ast_free(dead); -} - diff --git a/main/sdp_state.c b/main/sdp_state.c index b478e7148..1b09ce16f 100644 --- a/main/sdp_state.c +++ b/main/sdp_state.c @@ -20,9 +20,10 @@ #include "asterisk/sdp_state.h" #include "asterisk/sdp_options.h" #include "asterisk/sdp_translator.h" -#include "asterisk/sdp_priv.h" #include "asterisk/vector.h" #include "asterisk/utils.h" + +#include "../include/asterisk/sdp.h" #include "asterisk/stream.h" enum ast_sdp_state_machine { @@ -77,13 +78,12 @@ struct ast_sdp_state { struct ast_sdp_options *options; /*! Translator that puts SDPs into the expected representation */ struct ast_sdp_translator *translator; - /*! RTP instance for each media stream */ - AST_VECTOR(, struct ast_rtp_instance *) rtp; /*! The current state machine state that we are in */ enum ast_sdp_state_machine state; }; -struct ast_sdp_state *ast_sdp_state_alloc(struct ast_stream_topology *streams, struct ast_sdp_options *options) +struct ast_sdp_state *ast_sdp_state_alloc(struct ast_stream_topology *streams, + struct ast_sdp_options *options) { struct ast_sdp_state *sdp_state; @@ -94,7 +94,7 @@ struct ast_sdp_state *ast_sdp_state_alloc(struct ast_stream_topology *streams, s sdp_state->options = options; - sdp_state->translator = ast_sdp_translator_new(ast_sdp_options_get_repr(sdp_state->options)); + sdp_state->translator = ast_sdp_translator_new(ast_sdp_options_get_impl(sdp_state->options)); if (!sdp_state->translator) { ast_sdp_state_free(sdp_state); return NULL; @@ -126,18 +126,23 @@ void ast_sdp_state_free(struct ast_sdp_state *sdp_state) ast_sdp_translator_free(sdp_state->translator); } -struct ast_rtp_instance *ast_sdp_state_get_rtp_instance(struct ast_sdp_state *sdp_state, int stream_index) +struct ast_rtp_instance *ast_sdp_state_get_rtp_instance( + const struct ast_sdp_state *sdp_state, int stream_index) { + struct ast_stream *stream; + ast_assert(sdp_state != NULL); - if (stream_index >= AST_VECTOR_SIZE(&sdp_state->rtp)) { + stream = ast_stream_topology_get_stream(sdp_state->local_capabilities, stream_index); + if (!stream) { return NULL; } - return AST_VECTOR_GET(&sdp_state->rtp, stream_index); + return (struct ast_rtp_instance *)ast_stream_get_data(stream, AST_STREAM_DATA_RTP_INSTANCE); } -struct ast_stream_topology *ast_sdp_state_get_joint_topology(struct ast_sdp_state *sdp_state) +const struct ast_stream_topology *ast_sdp_state_get_joint_topology( + const struct ast_sdp_state *sdp_state) { ast_assert(sdp_state != NULL); if (sdp_state->state == SDP_STATE_NEGOTIATED) { @@ -147,6 +152,23 @@ struct ast_stream_topology *ast_sdp_state_get_joint_topology(struct ast_sdp_stat } } +const struct ast_stream_topology *ast_sdp_state_get_local_topology( + const struct ast_sdp_state *sdp_state) +{ + ast_assert(sdp_state != NULL); + + return sdp_state->local_capabilities; +} + +const struct ast_sdp_options *ast_sdp_state_get_options( + const struct ast_sdp_state *sdp_state) +{ + ast_assert(sdp_state != NULL); + + return sdp_state->options; +} + +#if 0 static int merge_sdps(struct ast_sdp_state *sdp_state) { ast_assert(sdp_state->local_sdp != NULL); @@ -169,37 +191,38 @@ static int merge_sdps(struct ast_sdp_state *sdp_state) return 0; } +#endif -const void *ast_sdp_state_get_local(struct ast_sdp_state *sdp_state) +const struct ast_sdp *ast_sdp_state_get_local_sdp(struct ast_sdp_state *sdp_state) { - struct ast_sdp *sdp; - ast_assert(sdp_state != NULL); - /*TODO Create RTP instances based on local topology and SDP options (if not already created) */ - /*TODO Create local SDP based on local topology, SDP options, and RTP ports (if not already created) */ - - switch (sdp_state->state) { - case SDP_STATE_INITIAL: - sdp_state->state = SDP_STATE_OFFERER; - /* Fall through */ - case SDP_STATE_OFFERER: - default: - sdp = sdp_state->local_sdp; - break; - case SDP_STATE_ANSWERER: - sdp_state->state = SDP_STATE_NEGOTIATED; - merge_sdps(sdp_state); - /* Fall through */ - case SDP_STATE_NEGOTIATED: - sdp = sdp_state->joint_sdp; - break; + if (!sdp_state->local_sdp) { + sdp_state->local_sdp = ast_sdp_create_from_state(sdp_state); + } + + return sdp_state->local_sdp; +} + +const void *ast_sdp_state_get_local_sdp_impl(struct ast_sdp_state *sdp_state) +{ + const struct ast_sdp *sdp = ast_sdp_state_get_local_sdp(sdp_state); + + if (!sdp) { + return NULL; } return ast_sdp_translator_from_sdp(sdp_state->translator, sdp); } -int ast_sdp_state_set_remote(struct ast_sdp_state *sdp_state, void *remote) +void ast_sdp_state_set_remote_sdp(struct ast_sdp_state *sdp_state, struct ast_sdp *sdp) +{ + ast_assert(sdp_state != NULL); + + sdp_state->remote_sdp = sdp; +} + +int ast_sdp_state_set_remote_sdp_from_impl(struct ast_sdp_state *sdp_state, void *remote) { struct ast_sdp *sdp; @@ -210,29 +233,7 @@ int ast_sdp_state_set_remote(struct ast_sdp_state *sdp_state, void *remote) return -1; } - sdp_state->remote_sdp = remote; - /* TODO Convert the remote SDP into a topology and store that in - * sdp_state->remote_capabilities - */ - - switch (sdp_state->state) { - case SDP_STATE_ANSWERER: - default: - break; - case SDP_STATE_INITIAL: - sdp_state->state = SDP_STATE_ANSWERER; - break; - case SDP_STATE_OFFERER: - sdp_state->state = SDP_STATE_NEGOTIATED; - /* Fall through */ - case SDP_STATE_NEGOTIATED: - /* If state is already negotiated, and we receive a new - * remote SDP, we need to re-create the joint SDP and joint - * capabilities - */ - merge_sdps(sdp_state); - break; - } + sdp_state->remote_sdp = sdp; return 0; } diff --git a/main/sdp_translator.c b/main/sdp_translator.c index 5426ae954..abd0f6276 100644 --- a/main/sdp_translator.c +++ b/main/sdp_translator.c @@ -24,13 +24,13 @@ #include "asterisk/lock.h" AST_RWLOCK_DEFINE_STATIC(registered_ops_lock); -static struct ast_sdp_translator_ops *registered_ops[AST_SDP_REPR_END]; +static struct ast_sdp_translator_ops *registered_ops[AST_SDP_IMPL_END]; int ast_sdp_register_translator(struct ast_sdp_translator_ops *ops) { SCOPED_WRLOCK(lock, ®istered_ops_lock); - if (ops->repr >= AST_SDP_REPR_END) { + if (ops->repr >= AST_SDP_IMPL_END) { ast_log(LOG_ERROR, "SDP translator has unrecognized representation\n"); return -1; } @@ -49,14 +49,14 @@ void ast_sdp_unregister_translator(struct ast_sdp_translator_ops *ops) { SCOPED_WRLOCK(lock, ®istered_ops_lock); - if (ops->repr >= AST_SDP_REPR_END) { + if (ops->repr >= AST_SDP_IMPL_END) { return; } registered_ops[ops->repr] = NULL; } -struct ast_sdp_translator *ast_sdp_translator_new(enum ast_sdp_options_repr repr) +struct ast_sdp_translator *ast_sdp_translator_new(enum ast_sdp_options_impl repr) { struct ast_sdp_translator *translator; SCOPED_RDLOCK(lock, ®istered_ops_lock); @@ -88,12 +88,14 @@ void ast_sdp_translator_free(struct ast_sdp_translator *translator) ast_free(translator); } -struct ast_sdp *ast_sdp_translator_to_sdp(struct ast_sdp_translator *translator, void *native_sdp) +struct ast_sdp *ast_sdp_translator_to_sdp(struct ast_sdp_translator *translator, + void *native_sdp) { return translator->ops->to_sdp(native_sdp, translator->translator_priv); } -void *ast_sdp_translator_from_sdp(struct ast_sdp_translator *translator, struct ast_sdp *ast_sdp) +void *ast_sdp_translator_from_sdp(struct ast_sdp_translator *translator, + const struct ast_sdp *ast_sdp) { return translator->ops->from_sdp(ast_sdp, translator->translator_priv); } diff --git a/main/stream.c b/main/stream.c index 8bee2fdd0..9d36dbf25 100644 --- a/main/stream.c +++ b/main/stream.c @@ -56,6 +56,16 @@ struct ast_stream { */ enum ast_stream_state state; + /*! + * \brief Opaque stream data + */ + void *data[AST_STREAM_DATA_SLOT_MAX]; + + /*! + * \brief What to do with data when the stream is freed + */ + ast_stream_data_free_fn data_free_fn[AST_STREAM_DATA_SLOT_MAX]; + /*! * \brief Name for the stream within the context of the channel it is on */ @@ -110,10 +120,18 @@ struct ast_stream *ast_stream_clone(const struct ast_stream *stream) void ast_stream_free(struct ast_stream *stream) { + int i; + if (!stream) { return; } + for (i = 0; i < AST_STREAM_DATA_SLOT_MAX; i++) { + if (stream->data_free_fn[i]) { + stream->data_free_fn[i](stream->data[i]); + } + } + ao2_cleanup(stream->formats); ast_free(stream); } @@ -186,6 +204,24 @@ const char *ast_stream_state2str(enum ast_stream_state state) } } +void *ast_stream_get_data(struct ast_stream *stream, enum ast_stream_data_slot slot) +{ + ast_assert(stream != NULL); + + return stream->data[slot]; +} + +void *ast_stream_set_data(struct ast_stream *stream, enum ast_stream_data_slot slot, + void *data, ast_stream_data_free_fn data_free_fn) +{ + ast_assert(stream != NULL); + + stream->data[slot] = data; + stream->data_free_fn[slot] = data_free_fn; + + return data; +} + int ast_stream_get_position(const struct ast_stream *stream) { ast_assert(stream != NULL); -- cgit v1.2.3