mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-12 15:45:18 +00:00
Merge "SDP: Ensure SDPs "merge" properly."
This commit is contained in:
@@ -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
|
||||
*
|
||||
|
@@ -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 */
|
||||
|
@@ -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 */
|
||||
|
@@ -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
|
||||
|
15
main/codec.c
15
main/codec.c
@@ -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;
|
||||
|
435
main/sdp.c
435
main/sdp.c
@@ -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;
|
||||
}
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
1192
main/sdp_state.c
1192
main/sdp_state.c
File diff suppressed because it is too large
Load Diff
821
tests/test_sdp.c
Normal file
821
tests/test_sdp.c
Normal 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");
|
Reference in New Issue
Block a user