Merge "SDP: Ensure SDPs "merge" properly."

This commit is contained in:
Joshua Colp
2017-04-27 05:38:07 -05:00
committed by Gerrit Code Review
10 changed files with 2152 additions and 482 deletions

View File

@@ -165,6 +165,17 @@ int ast_codec_get_max(void);
*/
const char *ast_codec_media_type2str(enum ast_media_type type);
/*!
* \brief Conversion function to take a media string and convert it to a media type
*
* \param media_type_str The media type string
*
* \retval The ast_media_type that corresponds to the string
*
* \since 15.0.0
*/
enum ast_media_type ast_media_type_from_str(const char *media_type_str);
/*!
* \brief Get the number of samples contained within a frame
*

View File

@@ -146,6 +146,22 @@ struct ast_sdp {
struct ast_sdp_m_lines *m_lines;
};
/*!
* \brief A structure representing an SDP rtpmap attribute
*/
struct ast_sdp_rtpmap {
/*! The RTP payload number for the rtpmap */
int payload;
/*! The Name of the codec */
char *encoding_name;
/*! The clock rate of the codec */
int clock_rate;
/*! Optional encoding parameters */
char *encoding_parameters;
/*! Area where strings are stored */
char buf[0];
};
/*!
* \brief Free an SDP Attribute
*
@@ -545,15 +561,88 @@ struct ast_sdp *ast_sdp_alloc(struct ast_sdp_o_line *o_line,
struct ast_sdp_t_line *t_line);
/*!
* \brief Create an SDP from an existing SDP State local topology
* \brief Find an attribute on the top-level SDP
*
* \param sdp_state SDP State
* \note This will not search within streams for the given attribute.
*
* \retval non-NULL Success
* \retval NULL Failure
* \param sdp The SDP in which to search
* \param attr_name The name of the attribute to search for
* \param payload Optional payload number to search for. If irrelevant, set to -1
*
* \since 15
* \retval NULL Could not find the given attribute
* \retval Non-NULL The attribute to find
*
* \since 15.0.0
*/
struct ast_sdp *ast_sdp_create_from_state(const struct ast_sdp_state *sdp_state);
struct ast_sdp_a_line *ast_sdp_find_attribute(const struct ast_sdp *sdp,
const char *attr_name, int payload);
/*!
* \brief Find an attribute on an SDP stream (m-line)
*
* \param sdp The SDP in which to search
* \param attr_name The name of the attribute to search for
* \param payload Optional payload number to search for. If irrelevant, set to -1
*
* \retval NULL Could not find the given attribute
* \retval Non-NULL The attribute to find
*
* \since 15.0.0
*/
struct ast_sdp_a_line *ast_sdp_m_find_attribute(const struct ast_sdp_m_line *m_line,
const char *attr_name, int payload);
/*!
* \brief Convert an SDP a_line into an rtpmap
*
* The returned value is heap-allocated and must be freed with
* ast_sdp_rtpmap_free()
*
* \param a_line The SDP a_line to convert
*
* \retval NULL Fail
* \retval non-NULL Success
*
* \since 15.0.0
*/
struct ast_sdp_rtpmap *ast_sdp_a_get_rtpmap(const struct ast_sdp_a_line *a_line);
/*!
* \brief Allocate a new SDP rtpmap
*
* \param payload The RTP payload number
* \param encoding_name The human-readable name for the codec
* \param clock_rate The rate of the codec, in cycles per second
* \param encoding_parameters Optional codec-specific parameters (such as number of channels)
*
* \retval NULL Fail
* \retval non-NULL Success
*
* \since 15.0.0
*/
struct ast_sdp_rtpmap *ast_sdp_rtpmap_alloc(int payload, const char *encoding_name,
int clock_rate, const char *encoding_parameters);
/*!
* \brief Free an SDP rtpmap
*
* \since 15.0.0
*/
void ast_sdp_rtpmap_free(struct ast_sdp_rtpmap *rtpmap);
/*!
* \brief Turn an SDP into a stream topology
*
* This traverses the m-lines of the SDP and creates a stream topology, with
* each m-line corresponding to a stream in the created topology.
*
* \param sdp The SDP to convert
*
* \retval NULL An error occurred when converting
* \retval non-NULL The generated stream topology
*
* \since 15.0.0
*/
struct ast_stream_topology *ast_get_topology_from_sdp(const struct ast_sdp *sdp);
#endif /* _SDP_PRIV_H */

View File

@@ -106,7 +106,7 @@ void ast_sdp_options_set_media_address(struct ast_sdp_options *options,
*
* \returns media_address
*/
const char *ast_sdp_options_get_media_address(struct ast_sdp_options *options);
const char *ast_sdp_options_get_media_address(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -126,7 +126,7 @@ void ast_sdp_options_set_sdpowner(struct ast_sdp_options *options,
*
* \returns sdpowner
*/
const char *ast_sdp_options_get_sdpowner(struct ast_sdp_options *options);
const char *ast_sdp_options_get_sdpowner(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -146,7 +146,7 @@ void ast_sdp_options_set_sdpsession(struct ast_sdp_options *options,
*
* \returns sdpsession
*/
const char *ast_sdp_options_get_sdpsession(struct ast_sdp_options *options);
const char *ast_sdp_options_get_sdpsession(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -166,7 +166,7 @@ void ast_sdp_options_set_rtp_engine(struct ast_sdp_options *options,
*
* \returns rtp_engine
*/
const char *ast_sdp_options_get_rtp_engine(struct ast_sdp_options *options);
const char *ast_sdp_options_get_rtp_engine(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -186,7 +186,7 @@ void ast_sdp_options_set_bind_rtp_to_media_address(struct ast_sdp_options *optio
*
* \returns bind_rtp_to_media_address
*/
unsigned int ast_sdp_options_get_bind_rtp_to_media_address(struct ast_sdp_options *options);
unsigned int ast_sdp_options_get_bind_rtp_to_media_address(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -206,7 +206,7 @@ void ast_sdp_options_set_rtp_symmetric(struct ast_sdp_options *options,
*
* \returns rtp_symmetric
*/
unsigned int ast_sdp_options_get_rtp_symmetric(struct ast_sdp_options *options);
unsigned int ast_sdp_options_get_rtp_symmetric(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -226,7 +226,7 @@ void ast_sdp_options_set_telephone_event(struct ast_sdp_options *options,
*
* \returns telephone_event
*/
unsigned int ast_sdp_options_get_telephone_event(struct ast_sdp_options *options);
unsigned int ast_sdp_options_get_telephone_event(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -246,7 +246,7 @@ void ast_sdp_options_set_rtp_ipv6(struct ast_sdp_options *options,
*
* \returns rtp_ipv6
*/
unsigned int ast_sdp_options_get_rtp_ipv6(struct ast_sdp_options *options);
unsigned int ast_sdp_options_get_rtp_ipv6(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -266,7 +266,7 @@ void ast_sdp_options_set_g726_non_standard(struct ast_sdp_options *options,
*
* \returns g726_non_standard
*/
unsigned int ast_sdp_options_get_g726_non_standard(struct ast_sdp_options *options);
unsigned int ast_sdp_options_get_g726_non_standard(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -286,7 +286,7 @@ void ast_sdp_options_set_tos_audio(struct ast_sdp_options *options,
*
* \returns tos_audio
*/
unsigned int ast_sdp_options_get_tos_audio(struct ast_sdp_options *options);
unsigned int ast_sdp_options_get_tos_audio(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -306,7 +306,7 @@ void ast_sdp_options_set_cos_audio(struct ast_sdp_options *options,
*
* \returns cos_audio
*/
unsigned int ast_sdp_options_get_cos_audio(struct ast_sdp_options *options);
unsigned int ast_sdp_options_get_cos_audio(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -326,7 +326,7 @@ void ast_sdp_options_set_tos_video(struct ast_sdp_options *options,
*
* \returns tos_video
*/
unsigned int ast_sdp_options_get_tos_video(struct ast_sdp_options *options);
unsigned int ast_sdp_options_get_tos_video(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -346,7 +346,7 @@ void ast_sdp_options_set_cos_video(struct ast_sdp_options *options,
*
* \returns cos_video
*/
unsigned int ast_sdp_options_get_cos_video(struct ast_sdp_options *options);
unsigned int ast_sdp_options_get_cos_video(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -366,7 +366,7 @@ void ast_sdp_options_set_ice(struct ast_sdp_options *options,
*
* \returns ice
*/
enum ast_sdp_options_ice ast_sdp_options_get_ice(struct ast_sdp_options *options);
enum ast_sdp_options_ice ast_sdp_options_get_ice(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -386,7 +386,7 @@ void ast_sdp_options_set_impl(struct ast_sdp_options *options,
*
* \returns impl
*/
enum ast_sdp_options_impl ast_sdp_options_get_impl(struct ast_sdp_options *options);
enum ast_sdp_options_impl ast_sdp_options_get_impl(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
@@ -406,6 +406,25 @@ void ast_sdp_options_set_encryption(struct ast_sdp_options *options,
*
* \returns encryption
*/
enum ast_sdp_options_encryption ast_sdp_options_get_encryption(struct ast_sdp_options *options);
enum ast_sdp_options_encryption ast_sdp_options_get_encryption(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
* \brief Get SDP Options RTCP MUX
*
* \param options SDP Options
*
* \returns Boolean indicating if RTCP MUX is enabled.
*/
unsigned int ast_sdp_options_get_rtcp_mux(const struct ast_sdp_options *options);
/*!
* \since 15.0.0
* \brief Set SDP Options RTCP MUX
*
* \param options SDP Options
* \param value Boolean that indicates if RTCP MUX should be enabled.
*/
void ast_sdp_options_set_rtcp_mux(struct ast_sdp_options *options, unsigned int value);
#endif /* _ASTERISK_SDP_OPTIONS_H */

View File

@@ -138,7 +138,7 @@ const void *ast_sdp_state_get_local_sdp_impl(struct ast_sdp_state *sdp_state);
*
* \since 15
*/
void ast_sdp_state_set_remote_sdp(struct ast_sdp_state *sdp_state, struct ast_sdp *sdp);
void ast_sdp_state_set_remote_sdp(struct ast_sdp_state *sdp_state, const struct ast_sdp *sdp);
/*!
* \brief Set the remote SDP from an Implementation

View File

@@ -376,6 +376,21 @@ const char *ast_codec_media_type2str(enum ast_media_type type)
}
}
enum ast_media_type ast_media_type_from_str(const char *media_type_str)
{
if (!strcasecmp(media_type_str, "audio")) {
return AST_MEDIA_TYPE_AUDIO;
} else if (!strcasecmp(media_type_str, "video")) {
return AST_MEDIA_TYPE_VIDEO;
} else if (!strcasecmp(media_type_str, "image")) {
return AST_MEDIA_TYPE_IMAGE;
} else if (!strcasecmp(media_type_str, "text")) {
return AST_MEDIA_TYPE_TEXT;
} else {
return AST_MEDIA_TYPE_UNKNOWN;
}
}
unsigned int ast_codec_samples_count(struct ast_frame *frame)
{
struct ast_codec *codec;

View File

@@ -500,201 +500,268 @@ int ast_sdp_m_add_format(struct ast_sdp_m_line *m_line, const struct ast_sdp_opt
return 0;
}
int ast_sdp_add_m_from_rtp_stream(struct ast_sdp *sdp, const struct ast_sdp_state *sdp_state,
const struct ast_sdp_options *options, int stream_index)
static struct ast_sdp_a_line *sdp_find_attribute_common(const struct ast_sdp_a_lines *a_lines,
const char *attr_name, int payload)
{
struct ast_stream *stream = ast_stream_topology_get_stream(ast_sdp_state_get_local_topology(sdp_state), stream_index);
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_rtp_instance *rtp = ast_sdp_state_get_rtp_instance(sdp_state, stream_index);
struct ast_sdp_a_line *a_line;
int i;
ast_assert(sdp && options && stream);
for (i = 0; i < AST_VECTOR_SIZE(a_lines); ++i) {
int a_line_payload;
media_type = ast_stream_get_type(stream);
if (ast_sdp_state_get_stream_connection_address(sdp_state, 0, &address_rtp)) {
return -1;
}
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);
a_line = AST_VECTOR_GET(a_lines, i);
if (strcmp(a_line->name, attr_name)) {
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 (payload >= 0) {
int sscanf_res;
sscanf_res = sscanf(a_line->value, "%30d", &a_line_payload);
if (sscanf_res == 1 && payload == a_line_payload) {
return a_line;
}
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(ast_sdp_state_get_locally_held(sdp_state, stream_index) ? "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;
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++) {
enum ast_media_type type = ast_stream_get_type(ast_stream_topology_get_stream(topology, stream_num));
if (type == AST_MEDIA_TYPE_AUDIO || type == AST_MEDIA_TYPE_VIDEO) {
if (ast_sdp_add_m_from_rtp_stream(sdp, sdp_state, options, stream_num)) {
goto error;
}
}
}
return sdp;
error:
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 a_line;
}
}
return NULL;
}
struct ast_sdp_a_line *ast_sdp_find_attribute(const struct ast_sdp *sdp,
const char *attr_name, int payload)
{
return sdp_find_attribute_common(sdp->a_lines, attr_name, payload);
}
struct ast_sdp_a_line *ast_sdp_m_find_attribute(const struct ast_sdp_m_line *m_line,
const char *attr_name, int payload)
{
return sdp_find_attribute_common(m_line->a_lines, attr_name, payload);
}
struct ast_sdp_rtpmap *ast_sdp_rtpmap_alloc(int payload, const char *encoding_name,
int clock_rate, const char *encoding_parameters)
{
struct ast_sdp_rtpmap *rtpmap;
char *buf_pos;
rtpmap = ast_calloc(1, sizeof(*rtpmap) + strlen(encoding_name) + strlen(encoding_parameters) + 2);
if (!rtpmap) {
return NULL;
}
rtpmap->payload = payload;
rtpmap->clock_rate = clock_rate;
buf_pos = rtpmap->buf;
COPY_STR_AND_ADVANCE(buf_pos, rtpmap->encoding_name, encoding_name);
COPY_STR_AND_ADVANCE(buf_pos, rtpmap->encoding_parameters, encoding_parameters);
return rtpmap;
}
void ast_sdp_rtpmap_free(struct ast_sdp_rtpmap *rtpmap)
{
ast_free(rtpmap);
}
struct ast_sdp_rtpmap *ast_sdp_a_get_rtpmap(const struct ast_sdp_a_line *a_line)
{
char *value_copy;
char *slash;
int payload;
char encoding_name[64];
int clock_rate;
char *encoding_parameters;
struct ast_sdp_rtpmap *rtpmap;
int clock_rate_len;
value_copy = ast_strip(ast_strdupa(a_line->value));
if (sscanf(value_copy, "%30d %63s", &payload, encoding_name) != 2) {
return NULL;
}
slash = strchr(encoding_name, '/');
if (!slash) {
return NULL;
}
*slash++ = '\0';
if (ast_strlen_zero(encoding_name)) {
return NULL;
}
if (sscanf(slash, "%30d%n", &clock_rate, &clock_rate_len) < 1) {
return NULL;
}
slash += clock_rate_len;
if (!ast_strlen_zero(slash)) {
if (*slash == '/') {
*slash++ = '\0';
encoding_parameters = slash;
if (ast_strlen_zero(encoding_parameters)) {
return NULL;
}
} else {
return NULL;
}
} else {
encoding_parameters = "";
}
rtpmap = ast_sdp_rtpmap_alloc(payload, encoding_name, clock_rate,
encoding_parameters);
return rtpmap;
}
/*!
* \brief Turn an SDP attribute into an sdp_rtpmap structure
*
* \param m_line The media section where this attribute was found.
* \param payload The RTP payload to find an rtpmap for
* \param[out] rtpmap The rtpmap to fill in.
* \return Zero if successful, otherwise less than zero
*/
static struct ast_sdp_rtpmap *sdp_payload_get_rtpmap(const struct ast_sdp_m_line *m_line, int payload)
{
struct ast_sdp_a_line *rtpmap_attr;
rtpmap_attr = ast_sdp_m_find_attribute(m_line, "rtpmap", payload);
if (!rtpmap_attr) {
return NULL;
}
return ast_sdp_a_get_rtpmap(rtpmap_attr);
}
/*!
* \brief Find and process fmtp attributes for a given payload
*
* \param m_line The stream on which to search for the fmtp attribute
* \param payload The specific fmtp attribute to search for
* \param codecs The current RTP codecs that have been built up
*/
static void process_fmtp(const struct ast_sdp_m_line *m_line, int payload,
struct ast_rtp_codecs *codecs)
{
struct ast_sdp_a_line *attr;
char *param;
char *param_start;
char *param_end;
size_t len;
struct ast_format *replace;
struct ast_format *format;
attr = ast_sdp_m_find_attribute(m_line, "fmtp", payload);
if (!attr) {
return;
}
/* Extract the "a=fmtp:%d %s" attribute parameter string after the payload type. */
param_start = ast_skip_nonblanks(attr->value);/* Skip payload type */
param_start = ast_skip_blanks(param_start);
param_end = ast_skip_nonblanks(param_start);
if (param_end == param_start) {
/* There is no parameter string */
return;
}
len = param_end - param_start;
param = ast_alloca(len + 1);
memcpy(param, param_start, len);
param[len] = '\0';
format = ast_rtp_codecs_get_payload_format(codecs, payload);
if (!format) {
return;
}
replace = ast_format_parse_sdp_fmtp(format, param);
if (replace) {
ast_rtp_codecs_payload_replace_format(codecs, payload, replace);
ao2_ref(replace, -1);
}
ao2_ref(format, -1);
}
/*!
* \brief Convert an SDP stream into an Asterisk stream
*
* Given an m-line from an SDP, convert it into an ast_stream structure.
* This takes formats, as well as clock-rate and fmtp attributes into account.
*
* \param m_line The SDP media section to convert
* \retval NULL An error occurred
* \retval non-NULL The converted stream
*/
static struct ast_stream *get_stream_from_m(const struct ast_sdp_m_line *m_line)
{
int i;
int non_ast_fmts;
struct ast_rtp_codecs codecs;
struct ast_format_cap *caps;
struct ast_stream *stream;
caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!caps) {
return NULL;
}
stream = ast_stream_alloc(m_line->type, ast_media_type_from_str(m_line->type));
if (!stream) {
ao2_ref(caps, -1);
return NULL;
}
ast_rtp_codecs_payloads_initialize(&codecs);
for (i = 0; i < ast_sdp_m_get_payload_count(m_line); ++i) {
struct ast_sdp_payload *payload_s;
struct ast_sdp_rtpmap *rtpmap;
int payload;
payload_s = ast_sdp_m_get_payload(m_line, i);
sscanf(payload_s->fmt, "%30d", &payload);
ast_rtp_codecs_payloads_set_m_type(&codecs, NULL, payload);
rtpmap = sdp_payload_get_rtpmap(m_line, payload);
if (!rtpmap) {
continue;
}
ast_rtp_codecs_payloads_set_rtpmap_type_rate(&codecs, NULL,
payload, m_line->type, rtpmap->encoding_name, 0,
rtpmap->clock_rate);
ast_sdp_rtpmap_free(rtpmap);
process_fmtp(m_line, payload, &codecs);
}
ast_rtp_codecs_payload_formats(&codecs, caps, &non_ast_fmts);
ast_stream_set_formats(stream, caps);
ao2_ref(caps, -1);
ast_rtp_codecs_payloads_destroy(&codecs);
return stream;
}
struct ast_stream_topology *ast_get_topology_from_sdp(const struct ast_sdp *sdp)
{
struct ast_stream_topology *topology;
int i;
topology = ast_stream_topology_alloc();
if (!topology) {
return NULL;
}
for (i = 0; i < ast_sdp_get_m_count(sdp); ++i) {
struct ast_stream *stream;
stream = get_stream_from_m(ast_sdp_get_m(sdp, i));
if (!stream) {
continue;
}
ast_stream_topology_append_stream(topology, stream);
}
return topology;
}

View File

@@ -36,7 +36,7 @@ void ast_sdp_options_set_##field(struct ast_sdp_options *options, const char *va
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) \
const char *ast_sdp_options_get_##field(const struct ast_sdp_options *options) \
{ \
ast_assert(options != NULL); \
return options->field; \
@@ -48,7 +48,7 @@ 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) \
type ast_sdp_options_get_##field(const struct ast_sdp_options *options) \
{ \
ast_assert(options != NULL); \
return options->field; \
@@ -64,6 +64,7 @@ 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, rtcp_mux);
DEFINE_GETTERS_SETTERS_FOR(unsigned int, tos_audio);
DEFINE_GETTERS_SETTERS_FOR(unsigned int, cos_audio);
DEFINE_GETTERS_SETTERS_FOR(unsigned int, tos_video);

View File

@@ -40,6 +40,7 @@ struct ast_sdp_options {
unsigned int rtp_ipv6 : 1;
unsigned int g726_non_standard : 1;
unsigned int locally_held : 1;
unsigned int rtcp_mux: 1;
};
struct {
unsigned int tos_audio;

File diff suppressed because it is too large Load Diff

821
tests/test_sdp.c Normal file
View File

@@ -0,0 +1,821 @@
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2017, Digium Inc.
*
* Mark Michelson <mmmichelson@digium.com>
*
* 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.
*/
/*** MODULEINFO
<depend>TEST_FRAMEWORK</depend>
<support_level>core</support_level>
***/
#include "asterisk.h"
#include "asterisk/test.h"
#include "asterisk/module.h"
#include "asterisk/sdp.h"
#include "asterisk/stream.h"
#include "asterisk/format.h"
#include "asterisk/format_cache.h"
#include "asterisk/format_cap.h"
static int validate_o_line(struct ast_test *test, const struct ast_sdp_o_line *o_line,
const char *sdpowner, const char *address_type, const char *address)
{
if (!o_line) {
return -1;
}
if (strcmp(o_line->username, sdpowner)) {
ast_test_status_update(test, "Expected o-line SDP owner %s but got %s\n",
sdpowner, o_line->username);
return -1;
}
if (strcmp(o_line->address_type, address_type)) {
ast_test_status_update(test, "Expected o-line SDP address type %s but got %s\n",
address_type, o_line->address_type);
return -1;
}
if (strcmp(o_line->address, address)) {
ast_test_status_update(test, "Expected o-line SDP address %s but got %s\n",
address, o_line->address);
return -1;
}
ast_test_status_update(test, "SDP o-line is as expected!\n");
return 0;
}
static int validate_c_line(struct ast_test *test, const struct ast_sdp_c_line *c_line,
const char *address_type, const char *address)
{
if (strcmp(c_line->address_type, address_type)) {
ast_test_status_update(test, "Expected c-line SDP address type %s but got %s\n",
address_type, c_line->address_type);
return -1;
}
if (strcmp(c_line->address, address)) {
ast_test_status_update(test, "Expected c-line SDP address %s but got %s\n",
address, c_line->address);
return -1;
}
ast_test_status_update(test, "SDP c-line is as expected!\n");
return 0;
}
static int validate_m_line(struct ast_test *test, const struct ast_sdp_m_line *m_line,
const char *media_type, int num_payloads)
{
if (strcmp(m_line->type, media_type)) {
ast_test_status_update(test, "Expected m-line media type %s but got %s\n",
media_type, m_line->type);
return -1;
}
if (ast_sdp_m_get_payload_count(m_line) != num_payloads) {
ast_test_status_update(test, "Expected m-line payload count %d but got %d\n",
num_payloads, ast_sdp_m_get_payload_count(m_line));
return -1;
}
ast_test_status_update(test, "SDP m-line is as expected\n");
return 0;
}
static int validate_rtpmap(struct ast_test *test, const struct ast_sdp_m_line *m_line,
const char *media_name)
{
struct ast_sdp_a_line *a_line;
int i;
for (i = 0; i < ast_sdp_m_get_a_count(m_line); ++i) {
struct ast_sdp_rtpmap *rtpmap;
int match;
a_line = ast_sdp_m_get_a(m_line, i);
if (strcmp(a_line->name, "rtpmap")) {
continue;
}
rtpmap = ast_sdp_a_get_rtpmap(a_line);
if (!rtpmap) {
return -1;
}
match = !strcmp(rtpmap->encoding_name, media_name);
ast_sdp_rtpmap_free(rtpmap);
if (match) {
return 0;
}
}
ast_test_status_update(test, "Could not find rtpmap with encoding name %s\n", media_name);
return -1;
}
AST_TEST_DEFINE(invalid_rtpmap)
{
/* a=rtpmap: is already assumed. This is the part after that */
static const char *invalids[] = {
"J PCMU/8000",
"0 PCMU:8000",
"0 PCMU/EIGHT-THOUSAND",
"0 PCMU/8000million/2",
"0 PCMU//2",
"0 /8000/2",
"0 PCMU/8000/",
"0 PCMU/8000million",
};
int i;
enum ast_test_result_state res = AST_TEST_PASS;
switch(cmd) {
case TEST_INIT:
info->name = "invalid_rtpmap";
info->category = "/main/sdp/";
info->summary = "Ensure invalid rtpmaps are rejected";
info->description =
"Try to convert several invalid rtpmap attributes. If\n"
"any succeeds, the test fails.";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
for (i = 0; i < ARRAY_LEN(invalids); ++i) {
struct ast_sdp_a_line *a_line;
struct ast_sdp_rtpmap *rtpmap;
a_line = ast_sdp_a_alloc("rtpmap", invalids[i]);
rtpmap = ast_sdp_a_get_rtpmap(a_line);
if (rtpmap) {
ast_test_status_update(test, "Invalid rtpmap '%s' was accepted as valid\n",
invalids[i]);
res = AST_TEST_FAIL;
}
ast_sdp_a_free(a_line);
ast_sdp_rtpmap_free(rtpmap);
}
return res;
}
AST_TEST_DEFINE(rtpmap)
{
static const char *valids[] = {
"0 PCMU/8000",
"107 opus/48000/2",
};
static int payloads[] = {
0,
107,
};
static const char *encoding_names[] = {
"PCMU",
"opus",
};
static int clock_rates[] = {
8000,
48000,
};
static const char *encoding_parameters[] = {
"",
"2",
};
int i;
enum ast_test_result_state res = AST_TEST_PASS;
switch(cmd) {
case TEST_INIT:
info->name = "rtpmap";
info->category = "/main/sdp/";
info->summary = "Ensure rtpmap attribute values are parsed correctly";
info->description =
"Parse several valid rtpmap attributes. Ensure that the parsed values\n"
"are what we expect";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
for (i = 0; i < ARRAY_LEN(valids); ++i) {
struct ast_sdp_a_line *a_line;
struct ast_sdp_rtpmap *rtpmap;
a_line = ast_sdp_a_alloc("rtpmap", valids[i]);
rtpmap = ast_sdp_a_get_rtpmap(a_line);
if (!rtpmap) {
ast_test_status_update(test, "Valid rtpmap '%s' was rejected as invalid\n",
valids[i]);
res = AST_TEST_FAIL;
continue;
}
if (rtpmap->payload != payloads[i]) {
ast_test_status_update(test, "RTPmap payload '%d' does not match expected '%d'\n",
rtpmap->payload, payloads[i]);
res = AST_TEST_FAIL;
}
if (strcmp(rtpmap->encoding_name, encoding_names[i])) {
ast_test_status_update(test, "RTPmap encoding_name '%s' does not match expected '%s'\n",
rtpmap->encoding_name, encoding_names[i]);
res = AST_TEST_FAIL;
}
if (rtpmap->clock_rate != clock_rates[i]) {
ast_test_status_update(test, "RTPmap clock rate '%d' does not match expected '%d'\n",
rtpmap->clock_rate, clock_rates[i]);
res = AST_TEST_FAIL;
}
if (strcmp(rtpmap->encoding_parameters, encoding_parameters[i])) {
ast_test_status_update(test, "RTPmap encoding_parameter '%s' does not match expected '%s'\n",
rtpmap->encoding_parameters, encoding_parameters[i]);
res = AST_TEST_FAIL;
}
ast_sdp_a_free(a_line);
ast_sdp_rtpmap_free(rtpmap);
}
return res;
}
AST_TEST_DEFINE(find_attr)
{
enum ast_test_result_state res = AST_TEST_PASS;
struct ast_sdp_m_line *m_line;
struct ast_sdp_a_line *a_line;
switch(cmd) {
case TEST_INIT:
info->name = "find_attr";
info->category = "/main/sdp/";
info->summary = "Ensure that finding attributes works as expected";
info->description =
"An SDP m-line is created, and two attributes are added.\n"
"We then attempt a series of attribute-finding calls that are expected to work\n"
"followed by a series of attribute-finding calls that are expected fo fail.";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
m_line = ast_sdp_m_alloc("audio", 666, 1, "RTP/AVP", NULL);
if (!m_line) {
res = AST_TEST_FAIL;
goto end;
}
a_line = ast_sdp_a_alloc("foo", "0 bar");
if (!a_line) {
res = AST_TEST_FAIL;
goto end;
}
ast_sdp_m_add_a(m_line, a_line);
a_line = ast_sdp_a_alloc("baz", "howdy");
if (!a_line) {
res = AST_TEST_FAIL;
goto end;
}
ast_sdp_m_add_a(m_line, a_line);
/* These should work */
a_line = ast_sdp_m_find_attribute(m_line, "foo", 0);
if (!a_line) {
ast_test_status_update(test, "Failed to find attribute 'foo' with payload '0'\n");
res = AST_TEST_FAIL;
}
a_line = ast_sdp_m_find_attribute(m_line, "foo", -1);
if (!a_line) {
ast_test_status_update(test, "Failed to find attribute 'foo' with unspecified payload\n");
res = AST_TEST_FAIL;
}
a_line = ast_sdp_m_find_attribute(m_line, "baz", -1);
if (!a_line) {
ast_test_status_update(test, "Failed to find attribute 'baz' with unspecified payload\n");
res = AST_TEST_FAIL;
}
/* These should fail */
a_line = ast_sdp_m_find_attribute(m_line, "foo", 1);
if (a_line) {
ast_test_status_update(test, "Found non-existent attribute 'foo' with payload '1'\n");
res = AST_TEST_FAIL;
}
a_line = ast_sdp_m_find_attribute(m_line, "baz", 0);
if (a_line) {
ast_test_status_update(test, "Found non-existent attribute 'baz' with payload '0'\n");
res = AST_TEST_FAIL;
}
a_line = ast_sdp_m_find_attribute(m_line, "wibble", 0);
if (a_line) {
ast_test_status_update(test, "Found non-existent attribute 'wibble' with payload '0'\n");
res = AST_TEST_FAIL;
}
a_line = ast_sdp_m_find_attribute(m_line, "wibble", -1);
if (a_line) {
ast_test_status_update(test, "Found non-existent attribute 'foo' with unspecified payload\n");
res = AST_TEST_FAIL;
}
end:
ast_sdp_m_free(m_line);
return res;
}
struct sdp_format {
enum ast_media_type type;
const char *formats;
};
static struct ast_sdp_state *build_sdp_state(int num_streams, const struct sdp_format *formats)
{
struct ast_stream_topology *topology = NULL;
struct ast_sdp_state *state = NULL;
struct ast_sdp_options *options;
int i;
options = ast_sdp_options_alloc();
if (!options) {
goto end;
}
ast_sdp_options_set_media_address(options, "127.0.0.1");
ast_sdp_options_set_sdpowner(options, "me");
ast_sdp_options_set_rtp_engine(options, "asterisk");
ast_sdp_options_set_impl(options, AST_SDP_IMPL_PJMEDIA);
topology = ast_stream_topology_alloc();
if (!topology) {
goto end;
}
for (i = 0; i < num_streams; ++i) {
RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);
struct ast_stream *stream;
caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!caps) {
goto end;
}
if (ast_format_cap_update_by_allow_disallow(caps, formats[i].formats, 1) < 0) {
goto end;
}
stream = ast_stream_alloc("sure_thing", formats[i].type);
if (!stream) {
goto end;
}
ast_stream_set_formats(stream, caps);
ast_stream_topology_append_stream(topology, stream);
}
state = ast_sdp_state_alloc(topology, options);
if (!state) {
goto end;
}
end:
ast_stream_topology_free(topology);
if (!state) {
ast_sdp_options_free(options);
}
return state;
}
AST_TEST_DEFINE(topology_to_sdp)
{
enum ast_test_result_state res = AST_TEST_FAIL;
struct ast_sdp_state *sdp_state = NULL;
const struct ast_sdp *sdp = NULL;
struct ast_sdp_m_line *m_line = NULL;
struct sdp_format formats[] = {
{ AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
{ AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
};
switch(cmd) {
case TEST_INIT:
info->name = "topology_to_sdp";
info->category = "/main/sdp/";
info->summary = "Convert a topology into an SDP";
info->description =
"Ensure SDPs get converted to expected stream topology";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
sdp_state = build_sdp_state(ARRAY_LEN(formats), formats);
if (!sdp_state) {
goto end;
}
sdp = ast_sdp_state_get_local_sdp(sdp_state);
if (!sdp) {
goto end;
}
if (validate_o_line(test, sdp->o_line, "me", "IP4", "127.0.0.1")) {
goto end;
}
if (validate_c_line(test, sdp->c_line, "IP4", "127.0.0.1")) {
goto end;
}
if (ast_sdp_get_m_count(sdp) != 2) {
ast_test_status_update(test, "Unexpected number of streams in generated SDP: %d\n",
ast_sdp_get_m_count(sdp));
goto end;
}
m_line = ast_sdp_get_m(sdp, 0);
if (validate_m_line(test, m_line, "audio", 4)) {
goto end;
}
if (validate_rtpmap(test, m_line, "PCMU")) {
goto end;
}
if (validate_rtpmap(test, m_line, "PCMA")) {
goto end;
}
if (validate_rtpmap(test, m_line, "G722")) {
goto end;
}
if (validate_rtpmap(test, m_line, "opus")) {
goto end;
}
m_line = ast_sdp_get_m(sdp, 1);
if (validate_m_line(test, m_line, "video", 2)) {
goto end;
}
if (validate_rtpmap(test, m_line, "VP8")) {
goto end;
}
if (validate_rtpmap(test, m_line, "H264")) {
goto end;
}
res = AST_TEST_PASS;
end:
ast_sdp_state_free(sdp_state);
return res;
}
static int validate_formats(struct ast_test *test, struct ast_stream_topology *topology, int index,
enum ast_media_type type, int format_count, const char **expected_formats)
{
struct ast_stream *stream;
struct ast_format_cap *caps;
struct ast_format *format;
int i;
stream = ast_stream_topology_get_stream(topology, index);
if (ast_stream_get_type(stream) != type) {
ast_test_status_update(test, "Unexpected stream type encountered\n");
return -1;
}
caps = ast_stream_get_formats(stream);
if (ast_format_cap_count(caps) != format_count) {
ast_test_status_update(test, "Unexpected format count '%d'. Expecting '%d'\n",
(int) ast_format_cap_count(caps), format_count);
return -1;
}
for (i = 0; i < ast_format_cap_count(caps); ++i) {
format = ast_format_cap_get_format(caps, i);
if (strcmp(ast_format_get_name(format), expected_formats[i])) {
ast_test_status_update(test, "Unexpected format '%s'at index %d. Expected '%s'\n",
ast_format_get_name(format), i, expected_formats[i]);
return -1;
}
}
return 0;
}
AST_TEST_DEFINE(sdp_to_topology)
{
enum ast_test_result_state res = AST_TEST_PASS;
struct ast_sdp_state *sdp_state;
const struct ast_sdp *sdp;
struct ast_stream_topology *topology = NULL;
struct sdp_format sdp_formats[] = {
{ AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
{ AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
};
static const char *expected_audio_formats[] = {
"ulaw",
"alaw",
"g722",
"opus",
};
static const char *expected_video_formats[] = {
"h264",
"vp8",
};
switch(cmd) {
case TEST_INIT:
info->name = "sdp_to_topology";
info->category = "/main/sdp/";
info->summary = "Convert an SDP into a topology";
info->description =
"Ensure SDPs get converted to expected stream topology";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
sdp_state = build_sdp_state(ARRAY_LEN(sdp_formats), sdp_formats);
if (!sdp_state) {
res = AST_TEST_FAIL;
goto end;
}
sdp = ast_sdp_state_get_local_sdp(sdp_state);
if (!sdp) {
res = AST_TEST_FAIL;
goto end;
}
topology = ast_get_topology_from_sdp(sdp);
if (ast_stream_topology_get_count(topology) != 2) {
ast_test_status_update(test, "Unexpected topology count '%d'. Expecting 2\n",
ast_stream_topology_get_count(topology));
res = AST_TEST_FAIL;
goto end;
}
if (validate_formats(test, topology, 0, AST_MEDIA_TYPE_AUDIO,
ARRAY_LEN(expected_audio_formats), expected_audio_formats)) {
res = AST_TEST_FAIL;
goto end;
}
if (validate_formats(test, topology, 1, AST_MEDIA_TYPE_VIDEO,
ARRAY_LEN(expected_video_formats), expected_video_formats)) {
res = AST_TEST_FAIL;
goto end;
}
end:
ast_sdp_state_free(sdp_state);
ast_stream_topology_free(topology);
return res;
}
static int validate_merged_sdp(struct ast_test *test, const struct ast_sdp *sdp)
{
struct ast_sdp_m_line *m_line;
if (!sdp) {
return -1;
}
m_line = ast_sdp_get_m(sdp, 0);
if (validate_m_line(test, m_line, "audio", 1)) {
return -1;
}
if (validate_rtpmap(test, m_line, "PCMU")) {
return -1;
}
/* The other audio formats should *NOT* be present */
if (!validate_rtpmap(test, m_line, "PCMA")) {
return -1;
}
if (!validate_rtpmap(test, m_line, "G722")) {
return -1;
}
if (!validate_rtpmap(test, m_line, "opus")) {
return -1;
}
m_line = ast_sdp_get_m(sdp, 1);
if (validate_m_line(test, m_line, "video", 1)) {
return -1;
}
if (validate_rtpmap(test, m_line, "VP8")) {
return -1;
}
if (!validate_rtpmap(test, m_line, "H264")) {
return -1;
}
return 0;
}
AST_TEST_DEFINE(sdp_merge_symmetric)
{
enum ast_test_result_state res = AST_TEST_PASS;
struct ast_sdp_state *sdp_state_offerer = NULL;
struct ast_sdp_state *sdp_state_answerer = NULL;
const struct ast_sdp *offerer_sdp;
const struct ast_sdp *answerer_sdp;
static const struct sdp_format offerer_formats[] = {
{ AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
{ AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
};
static const struct sdp_format answerer_formats[] = {
{ AST_MEDIA_TYPE_AUDIO, "ulaw" },
{ AST_MEDIA_TYPE_VIDEO, "vp8" },
};
switch(cmd) {
case TEST_INIT:
info->name = "sdp_merge_symmetric";
info->category = "/main/sdp/";
info->summary = "Merge two SDPs with symmetric stream types";
info->description =
"SDPs 1 and 2 each have one audio and one video stream (in that order).\n"
"SDP 1 offers to SDP 2, who answers. We ensure that both local SDPs have\n"
"the expected stream types and the expected formats";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
sdp_state_offerer = build_sdp_state(ARRAY_LEN(offerer_formats), offerer_formats);
if (!sdp_state_offerer) {
res = AST_TEST_FAIL;
goto end;
}
sdp_state_answerer = build_sdp_state(ARRAY_LEN(answerer_formats), answerer_formats);
if (!sdp_state_answerer) {
res = AST_TEST_FAIL;
goto end;
}
offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
if (!offerer_sdp) {
res = AST_TEST_FAIL;
goto end;
}
ast_sdp_state_set_remote_sdp(sdp_state_answerer, offerer_sdp);
answerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_answerer);
if (!answerer_sdp) {
res = AST_TEST_FAIL;
goto end;
}
ast_sdp_state_set_remote_sdp(sdp_state_offerer, answerer_sdp);
/* Get the offerer SDP again because it's now going to be the joint SDP */
offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
if (validate_merged_sdp(test, offerer_sdp)) {
res = AST_TEST_FAIL;
goto end;
}
if (validate_merged_sdp(test, answerer_sdp)) {
res = AST_TEST_FAIL;
goto end;
}
end:
ast_sdp_state_free(sdp_state_offerer);
ast_sdp_state_free(sdp_state_answerer);
return res;
}
AST_TEST_DEFINE(sdp_merge_crisscross)
{
enum ast_test_result_state res = AST_TEST_PASS;
struct ast_sdp_state *sdp_state_offerer = NULL;
struct ast_sdp_state *sdp_state_answerer = NULL;
const struct ast_sdp *offerer_sdp;
const struct ast_sdp *answerer_sdp;
static const struct sdp_format offerer_formats[] = {
{ AST_MEDIA_TYPE_AUDIO, "ulaw,alaw,g722,opus" },
{ AST_MEDIA_TYPE_VIDEO, "h264,vp8" },
};
static const struct sdp_format answerer_formats[] = {
{ AST_MEDIA_TYPE_VIDEO, "vp8" },
{ AST_MEDIA_TYPE_AUDIO, "ulaw" },
};
switch(cmd) {
case TEST_INIT:
info->name = "sdp_merge_crisscross";
info->category = "/main/sdp/";
info->summary = "Merge two SDPs with symmetric stream types";
info->description =
"SDPs 1 and 2 each have one audio and one video stream. However, SDP 1 and\n"
"2 natively have the formats in a different order.\n"
"SDP 1 offers to SDP 2, who answers. We ensure that both local SDPs have\n"
"the expected stream types and the expected formats. Since SDP 1 was the\n"
"offerer, the format order on SDP 1 should determine the order of formats in the SDPs";
return AST_TEST_NOT_RUN;
case TEST_EXECUTE:
break;
}
sdp_state_offerer = build_sdp_state(ARRAY_LEN(offerer_formats), offerer_formats);
if (!sdp_state_offerer) {
res = AST_TEST_FAIL;
goto end;
}
sdp_state_answerer = build_sdp_state(ARRAY_LEN(answerer_formats), answerer_formats);
if (!sdp_state_answerer) {
res = AST_TEST_FAIL;
goto end;
}
offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
if (!offerer_sdp) {
res = AST_TEST_FAIL;
goto end;
}
ast_sdp_state_set_remote_sdp(sdp_state_answerer, offerer_sdp);
answerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_answerer);
if (!answerer_sdp) {
res = AST_TEST_FAIL;
goto end;
}
ast_sdp_state_set_remote_sdp(sdp_state_offerer, answerer_sdp);
/* Get the offerer SDP again because it's now going to be the joint SDP */
offerer_sdp = ast_sdp_state_get_local_sdp(sdp_state_offerer);
if (validate_merged_sdp(test, offerer_sdp)) {
res = AST_TEST_FAIL;
goto end;
}
if (validate_merged_sdp(test, answerer_sdp)) {
res = AST_TEST_FAIL;
goto end;
}
end:
ast_sdp_state_free(sdp_state_offerer);
ast_sdp_state_free(sdp_state_answerer);
return res;
}
static int unload_module(void)
{
AST_TEST_UNREGISTER(invalid_rtpmap);
AST_TEST_UNREGISTER(rtpmap);
AST_TEST_UNREGISTER(find_attr);
AST_TEST_UNREGISTER(topology_to_sdp);
AST_TEST_UNREGISTER(sdp_to_topology);
AST_TEST_UNREGISTER(sdp_merge_symmetric);
AST_TEST_UNREGISTER(sdp_merge_crisscross);
return 0;
}
static int load_module(void)
{
AST_TEST_REGISTER(invalid_rtpmap);
AST_TEST_REGISTER(rtpmap);
AST_TEST_REGISTER(find_attr);
AST_TEST_REGISTER(topology_to_sdp);
AST_TEST_REGISTER(sdp_to_topology);
AST_TEST_REGISTER(sdp_merge_symmetric);
AST_TEST_REGISTER(sdp_merge_crisscross);
return AST_MODULE_LOAD_SUCCESS;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "SDP tests");