asterisk/main/stream.c

1072 lines
28 KiB
C
Raw Normal View History

/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 2017, Digium, Inc.
*
* Joshua Colp <jcolp@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.
*/
/*! \file
*
* \brief Media Stream API
*
* \author Joshua Colp <jcolp@digium.com>
*/
/*** MODULEINFO
<support_level>core</support_level>
***/
#include "asterisk.h"
#include "asterisk/logger.h"
#include "asterisk/stream.h"
#include "asterisk/strings.h"
#include "asterisk/format.h"
#include "asterisk/format_cap.h"
#include "asterisk/vector.h"
#include "asterisk/config.h"
#include "asterisk/rtp_engine.h"
struct ast_stream_metadata_entry {
size_t length;
int value_start;
char name_value[0];
};
Streams: Add features for Advanced Codec Negotiation The Streams API becomes the home for the core ACN capabilities. These include... * Parsing and formatting of codec negotation preferences. * Resolving pending streams and topologies with those configured using configured preferences. * Utility functions for creating string representations of streams, topologies, and negotiation preferences. For codec negotiation preferences: * Added ast_stream_codec_prefs_parse() which takes a string representation of codec negotiation preferences, which may come from a pjsip endpoint for example, and populates a ast_stream_codec_negotiation_prefs structure. * Added ast_stream_codec_prefs_to_str() which does the reverse. * Added many functions to parse individual parameter name and value strings to their respectrive enum values, and the reverse. For streams: * Added ast_stream_create_resolved() which takes a "live" stream and resolves it with a configured stream and the negotiation preferences to create a new stream. * Added ast_stream_to_str() which create a string representation of a stream suitable for debug or display purposes. For topology: * Added ast_stream_topology_create_resolved() which takes a "live" topology and resolves it, stream by stream, with a configured topology stream and the negotiation preferences to create a new topology. * Added ast_stream_topology_to_str() which create a string representation of a topology suitable for debug or display purposes. * Renamed ast_format_caps_from_topology() to ast_stream_topology_get_formats() to be more consistent with the existing ast_stream_get_formats(). Additional changes: * A new function ast_format_cap_append_names() appends the results to the ast_str buffer instead of replacing buffer contents. Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56
2020-06-26 16:14:58 +00:00
const char *ast_stream_codec_negotiation_params_map[] = {
[CODEC_NEGOTIATION_PARAM_UNSPECIFIED] = "unspecified",
[CODEC_NEGOTIATION_PARAM_PREFER] = "prefer",
[CODEC_NEGOTIATION_PARAM_OPERATION] = "operation",
[CODEC_NEGOTIATION_PARAM_KEEP] = "keep",
[CODEC_NEGOTIATION_PARAM_TRANSCODE] = "transcode",
};
const char *ast_stream_codec_negotiation_prefer_map[] = {
[CODEC_NEGOTIATION_PREFER_UNSPECIFIED] = "unspecified",
[CODEC_NEGOTIATION_PREFER_PENDING] = "pending",
[CODEC_NEGOTIATION_PREFER_CONFIGURED] = "configured",
};
const char *ast_stream_codec_negotiation_operation_map[] = {
[CODEC_NEGOTIATION_OPERATION_UNSPECIFIED] = "unspecified",
[CODEC_NEGOTIATION_OPERATION_INTERSECT] = "intersect",
[CODEC_NEGOTIATION_OPERATION_UNION] = "union",
[CODEC_NEGOTIATION_OPERATION_ONLY_PREFERRED] = "only_preferred",
[CODEC_NEGOTIATION_OPERATION_ONLY_NONPREFERRED] = "only_nonpreferred",
};
const char *ast_stream_codec_negotiation_keep_map[] = {
[CODEC_NEGOTIATION_KEEP_UNSPECIFIED] = "unspecified",
[CODEC_NEGOTIATION_KEEP_ALL] = "all",
[CODEC_NEGOTIATION_KEEP_FIRST] = "first",
};
const char *ast_stream_codec_negotiation_transcode_map[] = {
[CODEC_NEGOTIATION_TRANSCODE_UNSPECIFIED] = "unspecified",
[CODEC_NEGOTIATION_TRANSCODE_ALLOW] = "allow",
[CODEC_NEGOTIATION_TRANSCODE_PREVENT] = "prevent",
};
struct ast_stream {
/*!
* \brief The type of media the stream is handling
*/
enum ast_media_type type;
/*!
* \brief The position of the stream in the topology
*/
unsigned int position;
/*!
* \brief Current formats negotiated on the stream
*/
struct ast_format_cap *formats;
/*!
* \brief The current state of the stream
*/
enum ast_stream_state state;
/*!
* \brief Stream metadata vector
*/
struct ast_variable *metadata;
/*!
* \brief The group that the stream is part of
*/
int group;
/*!
* \brief The rtp_codecs used by the stream
*/
struct ast_rtp_codecs *rtp_codecs;
/*!
* \brief Name for the stream within the context of the channel it is on
*/
char name[0];
};
struct ast_stream_topology {
/*!
* \brief A vector of all the streams in this topology
*/
AST_VECTOR(, struct ast_stream *) streams;
Streams: Add features for Advanced Codec Negotiation The Streams API becomes the home for the core ACN capabilities. These include... * Parsing and formatting of codec negotation preferences. * Resolving pending streams and topologies with those configured using configured preferences. * Utility functions for creating string representations of streams, topologies, and negotiation preferences. For codec negotiation preferences: * Added ast_stream_codec_prefs_parse() which takes a string representation of codec negotiation preferences, which may come from a pjsip endpoint for example, and populates a ast_stream_codec_negotiation_prefs structure. * Added ast_stream_codec_prefs_to_str() which does the reverse. * Added many functions to parse individual parameter name and value strings to their respectrive enum values, and the reverse. For streams: * Added ast_stream_create_resolved() which takes a "live" stream and resolves it with a configured stream and the negotiation preferences to create a new stream. * Added ast_stream_to_str() which create a string representation of a stream suitable for debug or display purposes. For topology: * Added ast_stream_topology_create_resolved() which takes a "live" topology and resolves it, stream by stream, with a configured topology stream and the negotiation preferences to create a new topology. * Added ast_stream_topology_to_str() which create a string representation of a topology suitable for debug or display purposes. * Renamed ast_format_caps_from_topology() to ast_stream_topology_get_formats() to be more consistent with the existing ast_stream_get_formats(). Additional changes: * A new function ast_format_cap_append_names() appends the results to the ast_str buffer instead of replacing buffer contents. Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56
2020-06-26 16:14:58 +00:00
/*! Indicates that this topology should not have further operations applied to it. */
int final;
};
const char *ast_stream_codec_prefs_to_str(const struct ast_stream_codec_negotiation_prefs *prefs, struct ast_str **buf)
{
if (!prefs || !buf || !*buf) {
return "";
}
ast_str_append(buf, 0, "%s:%s, %s:%s, %s:%s, %s:%s",
ast_stream_codec_param_to_str(CODEC_NEGOTIATION_PARAM_PREFER),
ast_stream_codec_prefer_to_str(prefs->prefer),
ast_stream_codec_param_to_str(CODEC_NEGOTIATION_PARAM_OPERATION),
ast_stream_codec_operation_to_str(prefs->operation),
ast_stream_codec_param_to_str(CODEC_NEGOTIATION_PARAM_KEEP),
ast_stream_codec_keep_to_str(prefs->keep),
ast_stream_codec_param_to_str(CODEC_NEGOTIATION_PARAM_TRANSCODE),
ast_stream_codec_transcode_to_str(prefs->transcode)
);
return ast_str_buffer(*buf);
}
/*!
* \internal
* \brief Sets a codec prefs member based on a the preference name and string value
*
* \warning This macro will cause the calling function to return if a preference name
* is matched but the value isn't valid for this preference.
*/
#define set_pref_value(_name, _value, _prefs, _UC, _lc, _error_message) \
({ \
int _res = 0; \
if (strcmp(_name, ast_stream_codec_negotiation_params_map[CODEC_NEGOTIATION_PARAM_ ## _UC]) == 0) { \
int i; \
for (i = CODEC_NEGOTIATION_ ## _UC ## _UNSPECIFIED + 1; i < CODEC_NEGOTIATION_ ## _UC ## _END; i++) { \
if (strcmp(value, ast_stream_codec_negotiation_ ## _lc ## _map[i]) == 0) { \
prefs->_lc = i; \
} \
} \
if ( prefs->_lc == CODEC_NEGOTIATION_ ## _UC ## _UNSPECIFIED) { \
_res = -1; \
if (_error_message) { \
ast_str_append(_error_message, 0, "Codec preference '%s' has invalid value '%s'", name, value); \
} \
} \
} \
if (_res < 0) { \
return _res; \
} \
})
int ast_stream_codec_prefs_parse(const char *pref_string, struct ast_stream_codec_negotiation_prefs *prefs,
struct ast_str **error_message)
{
char *initial_value = ast_strdupa(pref_string);
char *current_value;
char *pref;
char *saveptr1;
char *saveptr2;
char *name;
char *value;
if (!prefs) {
return -1;
}
prefs->prefer = CODEC_NEGOTIATION_PREFER_UNSPECIFIED;
prefs->operation = CODEC_NEGOTIATION_OPERATION_UNSPECIFIED;
prefs->keep = CODEC_NEGOTIATION_KEEP_UNSPECIFIED;
prefs->transcode = CODEC_NEGOTIATION_TRANSCODE_UNSPECIFIED;
for (current_value = initial_value; (pref = strtok_r(current_value, ",", &saveptr1)) != NULL; ) {
name = strtok_r(pref, ": ", &saveptr2);
value = strtok_r(NULL, ": ", &saveptr2);
if (!name || !value) {
if (error_message) {
ast_str_append(error_message, 0, "Codec preference '%s' is invalid", pref);
}
return -1;
}
set_pref_value(name, value, prefs, OPERATION, operation, error_message);
set_pref_value(name, value, prefs, PREFER, prefer, error_message);
set_pref_value(name, value, prefs, KEEP, keep, error_message);
set_pref_value(name, value, prefs, TRANSCODE, transcode, error_message);
current_value = NULL;
}
return 0;
}
const char *ast_stream_state_map[] = {
[AST_STREAM_STATE_REMOVED] = "removed",
[AST_STREAM_STATE_SENDRECV] = "sendrecv",
[AST_STREAM_STATE_SENDONLY] = "sendonly",
[AST_STREAM_STATE_RECVONLY] = "recvonly",
[AST_STREAM_STATE_INACTIVE] = "inactive",
};
struct ast_stream *ast_stream_alloc(const char *name, enum ast_media_type type)
{
struct ast_stream *stream;
size_t name_len = MAX(strlen(S_OR(name, "")), 7); /* Ensure there is enough room for 'removed' */
stream = ast_calloc(1, sizeof(*stream) + name_len + 1);
if (!stream) {
return NULL;
}
stream->type = type;
stream->state = AST_STREAM_STATE_INACTIVE;
stream->group = -1;
strcpy(stream->name, S_OR(name, "")); /* Safe */
stream->formats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!stream->formats) {
ast_free(stream);
return NULL;
}
return stream;
}
Add primitive SFU support to bridge_softmix. This sets up the "plumbing" in bridge_softmix to be able to accommodate Asterisk asking as an SFU (selective forwarding unit) for conferences. The way this works is that whenever a channel enters or leaves a conference, all participants in the bridge get sent a stream topology change request. The topologies consist of the channels' original topology, along with video destination streams corresponding to each participants' source video streams. So for instance, if Alice, Bob, and Carol are in the conference, and each supplies one video stream, then the topologies for each would look like so: Alice: Audio, Source video(Alice), Destination Video(Bob), Destination video (Carol) Bob: Audio, Source video(Bob) Destination Video(Alice), Destination video (Carol) Carol: Audio, Source video(Carol) Destination Video(Alice), Destination video (Bob) This way, video that arrives from a source video stream can then be copied out to the destination video streams on the other participants' channels. Once the bridge gets told that a topology on a channel has changed, the bridge constructs a map in order to get the video frames routed to the proper destination streams. This is done using the bridge channel's stream_map. This change is bare-bones with regards to SFU support. Some key features are missing at this point: * Stream limits. This commit makes no effort to limit the number of streams on a specific channel. This means that if there were 50 video callers in a conference, bridge_softmix will happily send out topology change requests to every channel in the bridge, requesting 50+ streams. * Configuration. The plumbing has been added to bridge_softmix, but there has been nothing added as of yet to app_confbridge to enable SFU video mode. * Testing. Some functions included here have unit tests. However, the functionality as a whole has only been verified by hand-tracing the code. * Selectivenss. For a "selective" forwarding unit, this does not currently have any means of being selective. * Features. Presumably, someone might wish to only receive video from specific sources. There are no external-facing functions at the moment that allow for users to select who they receive video from. * Efficiency. The current scheme treats all video streams as being unidirectional. We could be re-using a source video stream as a desetnation, too. But to simplify things on this first round, I did it this way. Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
struct ast_stream *ast_stream_clone(const struct ast_stream *stream, const char *name)
{
struct ast_stream *new_stream;
Add primitive SFU support to bridge_softmix. This sets up the "plumbing" in bridge_softmix to be able to accommodate Asterisk asking as an SFU (selective forwarding unit) for conferences. The way this works is that whenever a channel enters or leaves a conference, all participants in the bridge get sent a stream topology change request. The topologies consist of the channels' original topology, along with video destination streams corresponding to each participants' source video streams. So for instance, if Alice, Bob, and Carol are in the conference, and each supplies one video stream, then the topologies for each would look like so: Alice: Audio, Source video(Alice), Destination Video(Bob), Destination video (Carol) Bob: Audio, Source video(Bob) Destination Video(Alice), Destination video (Carol) Carol: Audio, Source video(Carol) Destination Video(Alice), Destination video (Bob) This way, video that arrives from a source video stream can then be copied out to the destination video streams on the other participants' channels. Once the bridge gets told that a topology on a channel has changed, the bridge constructs a map in order to get the video frames routed to the proper destination streams. This is done using the bridge channel's stream_map. This change is bare-bones with regards to SFU support. Some key features are missing at this point: * Stream limits. This commit makes no effort to limit the number of streams on a specific channel. This means that if there were 50 video callers in a conference, bridge_softmix will happily send out topology change requests to every channel in the bridge, requesting 50+ streams. * Configuration. The plumbing has been added to bridge_softmix, but there has been nothing added as of yet to app_confbridge to enable SFU video mode. * Testing. Some functions included here have unit tests. However, the functionality as a whole has only been verified by hand-tracing the code. * Selectivenss. For a "selective" forwarding unit, this does not currently have any means of being selective. * Features. Presumably, someone might wish to only receive video from specific sources. There are no external-facing functions at the moment that allow for users to select who they receive video from. * Efficiency. The current scheme treats all video streams as being unidirectional. We could be re-using a source video stream as a desetnation, too. But to simplify things on this first round, I did it this way. Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
const char *stream_name;
size_t name_len;
if (!stream) {
return NULL;
}
Add primitive SFU support to bridge_softmix. This sets up the "plumbing" in bridge_softmix to be able to accommodate Asterisk asking as an SFU (selective forwarding unit) for conferences. The way this works is that whenever a channel enters or leaves a conference, all participants in the bridge get sent a stream topology change request. The topologies consist of the channels' original topology, along with video destination streams corresponding to each participants' source video streams. So for instance, if Alice, Bob, and Carol are in the conference, and each supplies one video stream, then the topologies for each would look like so: Alice: Audio, Source video(Alice), Destination Video(Bob), Destination video (Carol) Bob: Audio, Source video(Bob) Destination Video(Alice), Destination video (Carol) Carol: Audio, Source video(Carol) Destination Video(Alice), Destination video (Bob) This way, video that arrives from a source video stream can then be copied out to the destination video streams on the other participants' channels. Once the bridge gets told that a topology on a channel has changed, the bridge constructs a map in order to get the video frames routed to the proper destination streams. This is done using the bridge channel's stream_map. This change is bare-bones with regards to SFU support. Some key features are missing at this point: * Stream limits. This commit makes no effort to limit the number of streams on a specific channel. This means that if there were 50 video callers in a conference, bridge_softmix will happily send out topology change requests to every channel in the bridge, requesting 50+ streams. * Configuration. The plumbing has been added to bridge_softmix, but there has been nothing added as of yet to app_confbridge to enable SFU video mode. * Testing. Some functions included here have unit tests. However, the functionality as a whole has only been verified by hand-tracing the code. * Selectivenss. For a "selective" forwarding unit, this does not currently have any means of being selective. * Features. Presumably, someone might wish to only receive video from specific sources. There are no external-facing functions at the moment that allow for users to select who they receive video from. * Efficiency. The current scheme treats all video streams as being unidirectional. We could be re-using a source video stream as a desetnation, too. But to simplify things on this first round, I did it this way. Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
stream_name = name ?: stream->name;
name_len = MAX(strlen(stream_name), 7); /* Ensure there is enough room for 'removed' */
new_stream = ast_calloc(1, sizeof(*stream) + name_len + 1);
if (!new_stream) {
return NULL;
}
Add primitive SFU support to bridge_softmix. This sets up the "plumbing" in bridge_softmix to be able to accommodate Asterisk asking as an SFU (selective forwarding unit) for conferences. The way this works is that whenever a channel enters or leaves a conference, all participants in the bridge get sent a stream topology change request. The topologies consist of the channels' original topology, along with video destination streams corresponding to each participants' source video streams. So for instance, if Alice, Bob, and Carol are in the conference, and each supplies one video stream, then the topologies for each would look like so: Alice: Audio, Source video(Alice), Destination Video(Bob), Destination video (Carol) Bob: Audio, Source video(Bob) Destination Video(Alice), Destination video (Carol) Carol: Audio, Source video(Carol) Destination Video(Alice), Destination video (Bob) This way, video that arrives from a source video stream can then be copied out to the destination video streams on the other participants' channels. Once the bridge gets told that a topology on a channel has changed, the bridge constructs a map in order to get the video frames routed to the proper destination streams. This is done using the bridge channel's stream_map. This change is bare-bones with regards to SFU support. Some key features are missing at this point: * Stream limits. This commit makes no effort to limit the number of streams on a specific channel. This means that if there were 50 video callers in a conference, bridge_softmix will happily send out topology change requests to every channel in the bridge, requesting 50+ streams. * Configuration. The plumbing has been added to bridge_softmix, but there has been nothing added as of yet to app_confbridge to enable SFU video mode. * Testing. Some functions included here have unit tests. However, the functionality as a whole has only been verified by hand-tracing the code. * Selectivenss. For a "selective" forwarding unit, this does not currently have any means of being selective. * Features. Presumably, someone might wish to only receive video from specific sources. There are no external-facing functions at the moment that allow for users to select who they receive video from. * Efficiency. The current scheme treats all video streams as being unidirectional. We could be re-using a source video stream as a desetnation, too. But to simplify things on this first round, I did it this way. Change-Id: I7c44a829cc63acf8b596a337b2dc3c13898a6c4d
2017-05-05 16:56:34 +00:00
memcpy(new_stream, stream, sizeof(*new_stream));
strcpy(new_stream->name, stream_name); /* Safe */
new_stream->group = -1;
new_stream->formats = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!new_stream->formats) {
ast_free(new_stream);
return NULL;
}
ast_format_cap_append_from_cap(new_stream->formats, stream->formats, AST_MEDIA_TYPE_UNKNOWN);
new_stream->metadata = ast_stream_get_metadata_list(stream);
/* rtp_codecs aren't cloned */
return new_stream;
}
void ast_stream_free(struct ast_stream *stream)
{
if (!stream) {
return;
}
ast_variables_destroy(stream->metadata);
if (stream->rtp_codecs) {
ast_rtp_codecs_payloads_destroy(stream->rtp_codecs);
}
ao2_cleanup(stream->formats);
Streams: Add features for Advanced Codec Negotiation The Streams API becomes the home for the core ACN capabilities. These include... * Parsing and formatting of codec negotation preferences. * Resolving pending streams and topologies with those configured using configured preferences. * Utility functions for creating string representations of streams, topologies, and negotiation preferences. For codec negotiation preferences: * Added ast_stream_codec_prefs_parse() which takes a string representation of codec negotiation preferences, which may come from a pjsip endpoint for example, and populates a ast_stream_codec_negotiation_prefs structure. * Added ast_stream_codec_prefs_to_str() which does the reverse. * Added many functions to parse individual parameter name and value strings to their respectrive enum values, and the reverse. For streams: * Added ast_stream_create_resolved() which takes a "live" stream and resolves it with a configured stream and the negotiation preferences to create a new stream. * Added ast_stream_to_str() which create a string representation of a stream suitable for debug or display purposes. For topology: * Added ast_stream_topology_create_resolved() which takes a "live" topology and resolves it, stream by stream, with a configured topology stream and the negotiation preferences to create a new topology. * Added ast_stream_topology_to_str() which create a string representation of a topology suitable for debug or display purposes. * Renamed ast_format_caps_from_topology() to ast_stream_topology_get_formats() to be more consistent with the existing ast_stream_get_formats(). Additional changes: * A new function ast_format_cap_append_names() appends the results to the ast_str buffer instead of replacing buffer contents. Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56
2020-06-26 16:14:58 +00:00
ast_free(stream);
}
const char *ast_stream_get_name(const struct ast_stream *stream)
{
ast_assert(stream != NULL);
return stream->name;
}
enum ast_media_type ast_stream_get_type(const struct ast_stream *stream)
{
ast_assert(stream != NULL);
return stream->type;
}
void ast_stream_set_type(struct ast_stream *stream, enum ast_media_type type)
{
ast_assert(stream != NULL);
stream->type = type;
}
const struct ast_format_cap *ast_stream_get_formats(const struct ast_stream *stream)
{
ast_assert(stream != NULL);
return stream->formats;
}
Streams: Add features for Advanced Codec Negotiation The Streams API becomes the home for the core ACN capabilities. These include... * Parsing and formatting of codec negotation preferences. * Resolving pending streams and topologies with those configured using configured preferences. * Utility functions for creating string representations of streams, topologies, and negotiation preferences. For codec negotiation preferences: * Added ast_stream_codec_prefs_parse() which takes a string representation of codec negotiation preferences, which may come from a pjsip endpoint for example, and populates a ast_stream_codec_negotiation_prefs structure. * Added ast_stream_codec_prefs_to_str() which does the reverse. * Added many functions to parse individual parameter name and value strings to their respectrive enum values, and the reverse. For streams: * Added ast_stream_create_resolved() which takes a "live" stream and resolves it with a configured stream and the negotiation preferences to create a new stream. * Added ast_stream_to_str() which create a string representation of a stream suitable for debug or display purposes. For topology: * Added ast_stream_topology_create_resolved() which takes a "live" topology and resolves it, stream by stream, with a configured topology stream and the negotiation preferences to create a new topology. * Added ast_stream_topology_to_str() which create a string representation of a topology suitable for debug or display purposes. * Renamed ast_format_caps_from_topology() to ast_stream_topology_get_formats() to be more consistent with the existing ast_stream_get_formats(). Additional changes: * A new function ast_format_cap_append_names() appends the results to the ast_str buffer instead of replacing buffer contents. Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56
2020-06-26 16:14:58 +00:00
const char *ast_stream_to_str(const struct ast_stream *stream, struct ast_str **buf)
{
if (!buf || !*buf) {
return "";
}
if (!stream) {
ast_str_append(buf, 0, "(null stream)");
return ast_str_buffer(*buf);
}
ast_str_append(buf, 0, "%s:%s:%s ",
S_OR(stream->name, "noname"),
ast_codec_media_type2str(stream->type),
ast_stream_state_map[stream->state]);
ast_format_cap_append_names(stream->formats, buf);
return ast_str_buffer(*buf);
}
int ast_stream_get_format_count(const struct ast_stream *stream)
{
ast_assert(stream != NULL);
return stream->formats ? ast_format_cap_count(stream->formats) : 0;
}
void ast_stream_set_formats(struct ast_stream *stream, struct ast_format_cap *caps)
{
ast_assert(stream != NULL);
ao2_cleanup(stream->formats);
stream->formats = ao2_bump(caps);
}
enum ast_stream_state ast_stream_get_state(const struct ast_stream *stream)
{
ast_assert(stream != NULL);
return stream->state;
}
void ast_stream_set_state(struct ast_stream *stream, enum ast_stream_state state)
{
ast_assert(stream != NULL);
stream->state = state;
/* When a stream is set to removed that means that any previous data for it
* is no longer valid. We therefore change its name to removed and remove
* any old metadata associated with it.
*/
if (state == AST_STREAM_STATE_REMOVED) {
strcpy(stream->name, "removed");
ast_variables_destroy(stream->metadata);
stream->metadata = NULL;
if (stream->formats) {
ast_format_cap_remove_by_type(stream->formats, AST_MEDIA_TYPE_UNKNOWN);
}
}
}
const char *ast_stream_state2str(enum ast_stream_state state)
{
switch (state) {
case AST_STREAM_STATE_REMOVED:
return "removed";
case AST_STREAM_STATE_SENDRECV:
return "sendrecv";
case AST_STREAM_STATE_SENDONLY:
return "sendonly";
case AST_STREAM_STATE_RECVONLY:
return "recvonly";
case AST_STREAM_STATE_INACTIVE:
return "inactive";
default:
return "<unknown>";
}
}
SDP: Rework SDP offer/answer model and update capabilities merges. The SDP offer/answer model requires an answer to an offer before a new SDP can be processed. This allows our local SDP creation to be deferred until we know that we need to create an offer or an answer SDP. Once the local SDP is created it won't change until the SDP negotiation is restarted. An offer SDP in an initial SIP INVITE can receive more than one answer SDP. In this case, we need to merge each answer SDP with our original offer capabilities to get the currently negotiated capabilities. To satisfy this requirement means that we cannot update our proposed capabilities until the negotiations are restarted. Local topology updates from ast_sdp_state_update_local_topology() are merged together until the next offer SDP is created. These accumulated updates are then merged with the current negotiated capabilities to create the new proposed capabilities that the offer SDP is built. Local topology updates are merged in several passes to attempt to be smart about how streams from the system are matched with the previously negotiated stream slots. To allow for T.38 support when merging, type matching considers audio and image types to be equivalent. First streams are matched by stream name and type. Then streams are matched by stream type only. Any remaining unmatched existing streams are declined. Any new active streams are either backfilled into pre-merge declined slots or appended onto the end of the merged topology. Any excess new streams above the maximum supported number of streams are simply discarded. Remote topology negotiation merges depend if the topology is an offer or answer. An offer remote topology negotiation dictates the stream slot ordering and new streams can be added. A remote offer can do anything to the previously negotiated streams except reduce the number of stream slots. An answer remote topology negotiation is limited to what our offer requested. The answer can only decline streams, pick codecs from the offered list, or indicate the remote's stream hold state. I had originally kept the RTP instance if the remote offer SDP changed a stream type between audio and video since they both use RTP. However, I later removed this support in favor of simply creating a new RTP instance since the stream's purpose has to be changing anyway. Any RTP packets from the old stream type might cause mischief for the bridged peer. * Added ast_sdp_state_restart_negotiations() to restart the SDP offer/answer negotiations. We will thus know to create a new local SDP when it is time to create an offer or answer. * Removed ast_sdp_state_reset(). Save the current topology before starting T.38. To recover from T.38 simply update the local topology to the saved topology and restart the SDP negotiations to get the offer SDP renegotiating the previous configuration. * Allow initial topology for ast_sdp_state_alloc() to be NULL so an initial remote offer SDP can dictate the streams we start with. We can always update the local topology later if it turns out we need to offer SDP first because the remote chose to defer sending us a SDP. * Made the ast_sdp_state_alloc() initial topology limit to max_streams, limit to configured codecs, handle declined streams, and discard unsupported types. * Convert struct ast_sdp to ao2 object. Needed to easily save off a remote SDP to refer to later for various reasons such as generating declined m= lines in the local SDP. * Improve converting remote SDP streams to a topology including stream state. A stream state of AST_STREAM_STATE_REMOVED indicates the stream is declined/dead. * Improve merging streams to take into account the stream state. * Added query for remote hold state. * Added maximum streams allowed SDP config option. * Added ability to create new streams as needed. New streams are created with configured default audio, video, or image codecs depending on stream type. * Added global locally_held state along with a per stream local hold state. Historically, Asterisk only has a global locally held state because when the we put the remote on hold we do it for all active streams. * Added queries for a rejected offer and current SDP negotiation role. The rejected query allows the using module to know how to respond to a failed remote SDP set. Should the using module respond with a 488 Not Acceptable Here or 500 Internal Error to the offer SDP? * Moved sdp_state_capabilities.connection_address to ast_sdp_state. There seems no reason to keep it in the sdp_state_capabilities struct since it was only used by the ast_sdp_state.proposed_capabilities instance. * Callbacks are now available to allow the using module some customization of negotiated streams and to complete setting up streams for use. See the typedef doxygen for each callback for what is allowable and when they are called. * Added topology answerer modify callback. * Added topology pre and post apply callbacks. * Added topology offerer modify callback. * Added topology offerer configure callback. * Had to rework the unit tests because I changed how SDP topologies are merged. Replaced several unit tests with new negotiation tests. Change-Id: If07fe6d79fbdce33968a9401d41d908385043a06
2017-05-02 23:51:56 +00:00
enum ast_stream_state ast_stream_str2state(const char *str)
{
if (!strcmp("sendrecv", str)) {
return AST_STREAM_STATE_SENDRECV;
}
if (!strcmp("sendonly", str)) {
return AST_STREAM_STATE_SENDONLY;
}
if (!strcmp("recvonly", str)) {
return AST_STREAM_STATE_RECVONLY;
}
if (!strcmp("inactive", str)) {
return AST_STREAM_STATE_INACTIVE;
}
return AST_STREAM_STATE_REMOVED;
}
const char *ast_stream_get_metadata(const struct ast_stream *stream, const char *m_key)
{
struct ast_variable *v;
ast_assert_return(stream != NULL, NULL);
ast_assert_return(m_key != NULL, NULL);
for (v = stream->metadata; v; v = v->next) {
if (strcmp(v->name, m_key) == 0) {
return v->value;
}
}
return NULL;
}
struct ast_variable *ast_stream_get_metadata_list(const struct ast_stream *stream)
{
struct ast_variable *v;
struct ast_variable *vout = NULL;
ast_assert_return(stream != NULL, NULL);
for (v = stream->metadata; v; v = v->next) {
struct ast_variable *vt = ast_variable_new(v->name, v->value, "");
if (!vt) {
ast_variables_destroy(vout);
return NULL;
}
ast_variable_list_append(&vout, vt);
}
return vout;
}
int ast_stream_set_metadata(struct ast_stream *stream, const char *m_key, const char *value)
{
struct ast_variable *v;
struct ast_variable *prev;
ast_assert_return(stream != NULL, -1);
ast_assert_return(m_key != NULL, -1);
prev = NULL;
v = stream->metadata;
while(v) {
struct ast_variable *next = v->next;
if (strcmp(v->name, m_key) == 0) {
if (prev) {
prev->next = next;
} else {
stream->metadata = next;
}
ast_free(v);
break;
} else {
prev = v;
}
v = next;
}
if (!value) {
return 0;
}
v = ast_variable_new(m_key, value, "");
if (!v) {
return -1;
}
ast_variable_list_append(&stream->metadata, v);
return 0;
}
int ast_stream_get_position(const struct ast_stream *stream)
{
ast_assert(stream != NULL);
return stream->position;
}
struct ast_rtp_codecs *ast_stream_get_rtp_codecs(const struct ast_stream *stream)
{
ast_assert(stream != NULL);
return stream->rtp_codecs;
}
void ast_stream_set_rtp_codecs(struct ast_stream *stream, struct ast_rtp_codecs *rtp_codecs)
{
ast_assert(stream != NULL);
if (stream->rtp_codecs) {
ast_rtp_codecs_payloads_destroy(rtp_codecs);
}
stream->rtp_codecs = rtp_codecs;
}
Streams: Add features for Advanced Codec Negotiation The Streams API becomes the home for the core ACN capabilities. These include... * Parsing and formatting of codec negotation preferences. * Resolving pending streams and topologies with those configured using configured preferences. * Utility functions for creating string representations of streams, topologies, and negotiation preferences. For codec negotiation preferences: * Added ast_stream_codec_prefs_parse() which takes a string representation of codec negotiation preferences, which may come from a pjsip endpoint for example, and populates a ast_stream_codec_negotiation_prefs structure. * Added ast_stream_codec_prefs_to_str() which does the reverse. * Added many functions to parse individual parameter name and value strings to their respectrive enum values, and the reverse. For streams: * Added ast_stream_create_resolved() which takes a "live" stream and resolves it with a configured stream and the negotiation preferences to create a new stream. * Added ast_stream_to_str() which create a string representation of a stream suitable for debug or display purposes. For topology: * Added ast_stream_topology_create_resolved() which takes a "live" topology and resolves it, stream by stream, with a configured topology stream and the negotiation preferences to create a new topology. * Added ast_stream_topology_to_str() which create a string representation of a topology suitable for debug or display purposes. * Renamed ast_format_caps_from_topology() to ast_stream_topology_get_formats() to be more consistent with the existing ast_stream_get_formats(). Additional changes: * A new function ast_format_cap_append_names() appends the results to the ast_str buffer instead of replacing buffer contents. Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56
2020-06-26 16:14:58 +00:00
struct ast_stream *ast_stream_create_resolved(struct ast_stream *pending_stream,
struct ast_stream *validation_stream, struct ast_stream_codec_negotiation_prefs *prefs,
struct ast_str **error_message)
{
struct ast_format_cap *preferred_caps = NULL;
struct ast_format_cap *nonpreferred_caps = NULL;
struct ast_format_cap *joint_caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
struct ast_stream *joint_stream;
enum ast_media_type media_type = pending_stream ? pending_stream->type : AST_MEDIA_TYPE_UNKNOWN;
int res = 0;
if (!pending_stream || !validation_stream || !prefs || !joint_caps
|| media_type == AST_MEDIA_TYPE_UNKNOWN) {
if (error_message) {
ast_str_append(error_message, 0, "Invalid arguments");
}
ao2_cleanup(joint_caps);
return NULL;
}
if (prefs->prefer == CODEC_NEGOTIATION_PREFER_PENDING) {
preferred_caps = pending_stream->formats;
nonpreferred_caps = validation_stream->formats;
} else {
preferred_caps = validation_stream->formats;
nonpreferred_caps = pending_stream->formats;
}
ast_format_cap_set_framing(joint_caps, ast_format_cap_get_framing(pending_stream->formats));
switch(prefs->operation) {
case CODEC_NEGOTIATION_OPERATION_ONLY_PREFERRED:
res = ast_format_cap_append_from_cap(joint_caps, preferred_caps, media_type);
break;
case CODEC_NEGOTIATION_OPERATION_ONLY_NONPREFERRED:
res = ast_format_cap_append_from_cap(joint_caps, nonpreferred_caps, media_type);
break;
case CODEC_NEGOTIATION_OPERATION_INTERSECT:
res = ast_format_cap_get_compatible(preferred_caps, nonpreferred_caps, joint_caps);
break;
case CODEC_NEGOTIATION_OPERATION_UNION:
res = ast_format_cap_append_from_cap(joint_caps, preferred_caps, media_type);
if (res == 0) {
res = ast_format_cap_append_from_cap(joint_caps, nonpreferred_caps, media_type);
}
break;
default:
break;
}
if (res) {
if (error_message) {
ast_str_append(error_message, 0, "No common formats available for media type '%s' ",
ast_codec_media_type2str(pending_stream->type));
ast_format_cap_append_names(preferred_caps, error_message);
ast_str_append(error_message, 0, "<>");
ast_format_cap_append_names(nonpreferred_caps, error_message);
ast_str_append(error_message, 0, " with prefs: ");
ast_stream_codec_prefs_to_str(prefs, error_message);
}
ao2_cleanup(joint_caps);
return NULL;
}
if (!ast_format_cap_empty(joint_caps)) {
if (prefs->keep == CODEC_NEGOTIATION_KEEP_FIRST) {
struct ast_format *single = ast_format_cap_get_format(joint_caps, 0);
ast_format_cap_remove_by_type(joint_caps, AST_MEDIA_TYPE_UNKNOWN);
ast_format_cap_append(joint_caps, single, 0);
ao2_ref(single, -1);
}
}
joint_stream = ast_stream_clone(pending_stream, NULL);
if (!joint_stream) {
ao2_cleanup(joint_caps);
return NULL;
}
/* ref to joint_caps will be transferred to the stream */
ast_stream_set_formats(joint_stream, joint_caps);
if (TRACE_ATLEAST(1)) {
struct ast_str *buf = ast_str_create((AST_FORMAT_CAP_NAMES_LEN * 3) + AST_STREAM_MAX_CODEC_PREFS_LENGTH);
if (buf) {
ast_str_set(&buf, 0, "Resolved '%s' stream ", ast_codec_media_type2str(pending_stream->type));
ast_format_cap_append_names(preferred_caps, &buf);
ast_str_append(&buf, 0, "<>");
ast_format_cap_append_names(nonpreferred_caps, &buf);
ast_str_append(&buf, 0, " to ");
ast_format_cap_append_names(joint_caps, &buf);
ast_str_append(&buf, 0, " with prefs: ");
ast_stream_codec_prefs_to_str(prefs, &buf);
ast_trace(1, "%s\n", ast_str_buffer(buf));
ast_free(buf);
}
}
ao2_cleanup(joint_caps);
return joint_stream;
}
static void stream_topology_destroy(void *data)
{
struct ast_stream_topology *topology = data;
AST_VECTOR_CALLBACK_VOID(&topology->streams, ast_stream_free);
AST_VECTOR_FREE(&topology->streams);
}
#define TOPOLOGY_INITIAL_STREAM_COUNT 2
struct ast_stream_topology *ast_stream_topology_alloc(void)
{
struct ast_stream_topology *topology;
topology = ao2_alloc_options(sizeof(*topology), stream_topology_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
if (!topology) {
return NULL;
}
if (AST_VECTOR_INIT(&topology->streams, TOPOLOGY_INITIAL_STREAM_COUNT)) {
ao2_ref(topology, -1);
topology = NULL;
}
return topology;
}
struct ast_stream_topology *ast_stream_topology_clone(
const struct ast_stream_topology *topology)
{
struct ast_stream_topology *new_topology;
int i;
ast_assert(topology != NULL);
new_topology = ast_stream_topology_alloc();
if (!new_topology) {
return NULL;
}
for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {
struct ast_stream *existing = AST_VECTOR_GET(&topology->streams, i);
struct ast_stream *stream = ast_stream_clone(existing, NULL);
if (!stream || AST_VECTOR_APPEND(&new_topology->streams, stream)) {
ast_stream_free(stream);
ast_stream_topology_free(new_topology);
return NULL;
}
ast_stream_set_group(stream, ast_stream_get_group(existing));
}
return new_topology;
}
chan_pjsip: Add support for multiple streams of the same type. The stream topology (list of streams and order) is now stored with the configured PJSIP endpoints and used during the negotiation process. Media negotiation state information has been changed to be stored in a separate object. Two of these objects exist at any one time on a session. The active media state information is what was previously negotiated and the pending media state information is what the media state will become if negotiation succeeds. Streams and other state information is stored in this object using the index (or position) of each individual stream for easy lookup. The ability for a media type handler to specify a callback for writing has been added as well as the ability to add file descriptors with a callback which is invoked when data is available to be read on them. This allows media logic to live outside of the chan_pjsip module. Direct media has been changed so that only the first audio and video stream are directly connected. In the future once the RTP engine glue API has been updated to know about streams each individual stream can be directly connected as appropriate. Media negotiation itself will currently answer all the provided streams on an offer within configured limits and on an offer will use the topology created as a result of the disallow/allow codec lines. If a stream has been removed or declined we will now mark it as such within the resulting SDP. Applications can now also request that the stream topology change. If we are told to do so we will limit any provided formats to the ones configured on the endpoint and send a re-invite with the new topology. Two new configuration options have also been added to PJSIP endpoints: max_audio_streams: determines the maximum number of audio streams to offer/accept from an endpoint. Defaults to 1. max_video_streams: determines the maximum number of video streams to offer/accept from an endpoint. Defaults to 1. ASTERISK-27076 Change-Id: I8afd8dd2eb538806a39b887af0abd046266e14c7
2017-05-30 14:12:47 +00:00
int ast_stream_topology_equal(const struct ast_stream_topology *left,
const struct ast_stream_topology *right)
{
int index;
ast_assert(left != NULL);
ast_assert(right != NULL);
if (ast_stream_topology_get_count(left) != ast_stream_topology_get_count(right)) {
return 0;
}
for (index = 0; index < ast_stream_topology_get_count(left); ++index) {
const struct ast_stream *left_stream = ast_stream_topology_get_stream(left, index);
const struct ast_stream *right_stream = ast_stream_topology_get_stream(right, index);
if (ast_stream_get_type(left_stream) != ast_stream_get_type(right_stream)) {
return 0;
}
if (ast_stream_get_state(left_stream) != ast_stream_get_state(right_stream)) {
return 0;
}
if (!ast_stream_get_formats(left_stream) && ast_stream_get_formats(right_stream) &&
ast_format_cap_count(ast_stream_get_formats(right_stream))) {
/* A NULL format capabilities and an empty format capabilities are the same, as they have
* no formats inside. If one does though... they are not equal.
*/
return 0;
} else if (!ast_stream_get_formats(right_stream) && ast_stream_get_formats(left_stream) &&
ast_format_cap_count(ast_stream_get_formats(left_stream))) {
return 0;
} else if (ast_stream_get_formats(left_stream) && ast_stream_get_formats(right_stream) &&
!ast_format_cap_identical(ast_stream_get_formats(left_stream), ast_stream_get_formats(right_stream))) {
/* But if both are actually present we need to do an actual identical check. */
return 0;
}
if (strcmp(ast_stream_get_name(left_stream), ast_stream_get_name(right_stream))) {
return 0;
}
}
return 1;
}
void ast_stream_topology_free(struct ast_stream_topology *topology)
{
ao2_cleanup(topology);
}
int ast_stream_topology_append_stream(struct ast_stream_topology *topology, struct ast_stream *stream)
{
ast_assert(topology && stream);
if (AST_VECTOR_APPEND(&topology->streams, stream)) {
return -1;
}
stream->position = AST_VECTOR_SIZE(&topology->streams) - 1;
return AST_VECTOR_SIZE(&topology->streams) - 1;
}
int ast_stream_topology_get_count(const struct ast_stream_topology *topology)
{
ast_assert(topology != NULL);
return AST_VECTOR_SIZE(&topology->streams);
}
Streams: Add features for Advanced Codec Negotiation The Streams API becomes the home for the core ACN capabilities. These include... * Parsing and formatting of codec negotation preferences. * Resolving pending streams and topologies with those configured using configured preferences. * Utility functions for creating string representations of streams, topologies, and negotiation preferences. For codec negotiation preferences: * Added ast_stream_codec_prefs_parse() which takes a string representation of codec negotiation preferences, which may come from a pjsip endpoint for example, and populates a ast_stream_codec_negotiation_prefs structure. * Added ast_stream_codec_prefs_to_str() which does the reverse. * Added many functions to parse individual parameter name and value strings to their respectrive enum values, and the reverse. For streams: * Added ast_stream_create_resolved() which takes a "live" stream and resolves it with a configured stream and the negotiation preferences to create a new stream. * Added ast_stream_to_str() which create a string representation of a stream suitable for debug or display purposes. For topology: * Added ast_stream_topology_create_resolved() which takes a "live" topology and resolves it, stream by stream, with a configured topology stream and the negotiation preferences to create a new topology. * Added ast_stream_topology_to_str() which create a string representation of a topology suitable for debug or display purposes. * Renamed ast_format_caps_from_topology() to ast_stream_topology_get_formats() to be more consistent with the existing ast_stream_get_formats(). Additional changes: * A new function ast_format_cap_append_names() appends the results to the ast_str buffer instead of replacing buffer contents. Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56
2020-06-26 16:14:58 +00:00
int ast_stream_topology_get_active_count(const struct ast_stream_topology *topology)
{
int i;
int count = 0;
ast_assert(topology != NULL);
for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {
struct ast_stream *stream = AST_VECTOR_GET(&topology->streams, i);
if (stream->state != AST_STREAM_STATE_REMOVED) {
count++;
}
}
return count;
}
struct ast_stream *ast_stream_topology_get_stream(
const struct ast_stream_topology *topology, unsigned int stream_num)
{
ast_assert(topology != NULL);
return AST_VECTOR_GET(&topology->streams, stream_num);
}
int ast_stream_topology_set_stream(struct ast_stream_topology *topology,
unsigned int position, struct ast_stream *stream)
{
struct ast_stream *existing_stream;
ast_assert(topology && stream);
if (position > AST_VECTOR_SIZE(&topology->streams)) {
return -1;
}
if (position < AST_VECTOR_SIZE(&topology->streams)) {
existing_stream = AST_VECTOR_GET(&topology->streams, position);
ast_stream_free(existing_stream);
}
stream->position = position;
if (position == AST_VECTOR_SIZE(&topology->streams)) {
return AST_VECTOR_APPEND(&topology->streams, stream);
}
return AST_VECTOR_REPLACE(&topology->streams, position, stream);
}
int ast_stream_topology_del_stream(struct ast_stream_topology *topology,
unsigned int position)
{
struct ast_stream *stream;
ast_assert(topology != NULL);
if (AST_VECTOR_SIZE(&topology->streams) <= position) {
return -1;
}
stream = AST_VECTOR_REMOVE_ORDERED(&topology->streams, position);
ast_stream_free(stream);
/* Fix up higher stream position indices */
for (; position < AST_VECTOR_SIZE(&topology->streams); ++position) {
stream = AST_VECTOR_GET(&topology->streams, position);
stream->position = position;
}
return 0;
}
struct ast_stream_topology *ast_stream_topology_create_from_format_cap(
struct ast_format_cap *cap)
{
struct ast_stream_topology *topology;
enum ast_media_type type;
topology = ast_stream_topology_alloc();
if (!topology || !cap || !ast_format_cap_count(cap)) {
return topology;
}
for (type = AST_MEDIA_TYPE_UNKNOWN + 1; type < AST_MEDIA_TYPE_END; type++) {
struct ast_format_cap *new_cap;
struct ast_stream *stream;
if (!ast_format_cap_has_type(cap, type)) {
continue;
}
new_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!new_cap) {
ast_stream_topology_free(topology);
return NULL;
}
ast_format_cap_set_framing(new_cap, ast_format_cap_get_framing(cap));
if (ast_format_cap_append_from_cap(new_cap, cap, type)) {
ao2_cleanup(new_cap);
ast_stream_topology_free(topology);
return NULL;
}
stream = ast_stream_alloc(ast_codec_media_type2str(type), type);
if (!stream) {
ao2_cleanup(new_cap);
ast_stream_topology_free(topology);
return NULL;
}
ast_stream_set_formats(stream, new_cap);
ao2_ref(new_cap, -1);
stream->state = AST_STREAM_STATE_SENDRECV;
if (ast_stream_topology_append_stream(topology, stream) == -1) {
ast_stream_free(stream);
ast_stream_topology_free(topology);
return NULL;
}
}
return topology;
}
Streams: Add features for Advanced Codec Negotiation The Streams API becomes the home for the core ACN capabilities. These include... * Parsing and formatting of codec negotation preferences. * Resolving pending streams and topologies with those configured using configured preferences. * Utility functions for creating string representations of streams, topologies, and negotiation preferences. For codec negotiation preferences: * Added ast_stream_codec_prefs_parse() which takes a string representation of codec negotiation preferences, which may come from a pjsip endpoint for example, and populates a ast_stream_codec_negotiation_prefs structure. * Added ast_stream_codec_prefs_to_str() which does the reverse. * Added many functions to parse individual parameter name and value strings to their respectrive enum values, and the reverse. For streams: * Added ast_stream_create_resolved() which takes a "live" stream and resolves it with a configured stream and the negotiation preferences to create a new stream. * Added ast_stream_to_str() which create a string representation of a stream suitable for debug or display purposes. For topology: * Added ast_stream_topology_create_resolved() which takes a "live" topology and resolves it, stream by stream, with a configured topology stream and the negotiation preferences to create a new topology. * Added ast_stream_topology_to_str() which create a string representation of a topology suitable for debug or display purposes. * Renamed ast_format_caps_from_topology() to ast_stream_topology_get_formats() to be more consistent with the existing ast_stream_get_formats(). Additional changes: * A new function ast_format_cap_append_names() appends the results to the ast_str buffer instead of replacing buffer contents. Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56
2020-06-26 16:14:58 +00:00
struct ast_format_cap *ast_stream_topology_get_formats_by_type(
struct ast_stream_topology *topology, enum ast_media_type type)
{
struct ast_format_cap *caps;
int i;
ast_assert(topology != NULL);
caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
if (!caps) {
return NULL;
}
for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {
struct ast_stream *stream;
stream = AST_VECTOR_GET(&topology->streams, i);
Streams: Add features for Advanced Codec Negotiation The Streams API becomes the home for the core ACN capabilities. These include... * Parsing and formatting of codec negotation preferences. * Resolving pending streams and topologies with those configured using configured preferences. * Utility functions for creating string representations of streams, topologies, and negotiation preferences. For codec negotiation preferences: * Added ast_stream_codec_prefs_parse() which takes a string representation of codec negotiation preferences, which may come from a pjsip endpoint for example, and populates a ast_stream_codec_negotiation_prefs structure. * Added ast_stream_codec_prefs_to_str() which does the reverse. * Added many functions to parse individual parameter name and value strings to their respectrive enum values, and the reverse. For streams: * Added ast_stream_create_resolved() which takes a "live" stream and resolves it with a configured stream and the negotiation preferences to create a new stream. * Added ast_stream_to_str() which create a string representation of a stream suitable for debug or display purposes. For topology: * Added ast_stream_topology_create_resolved() which takes a "live" topology and resolves it, stream by stream, with a configured topology stream and the negotiation preferences to create a new topology. * Added ast_stream_topology_to_str() which create a string representation of a topology suitable for debug or display purposes. * Renamed ast_format_caps_from_topology() to ast_stream_topology_get_formats() to be more consistent with the existing ast_stream_get_formats(). Additional changes: * A new function ast_format_cap_append_names() appends the results to the ast_str buffer instead of replacing buffer contents. Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56
2020-06-26 16:14:58 +00:00
if (!stream->formats || stream->state == AST_STREAM_STATE_REMOVED) {
continue;
}
Streams: Add features for Advanced Codec Negotiation The Streams API becomes the home for the core ACN capabilities. These include... * Parsing and formatting of codec negotation preferences. * Resolving pending streams and topologies with those configured using configured preferences. * Utility functions for creating string representations of streams, topologies, and negotiation preferences. For codec negotiation preferences: * Added ast_stream_codec_prefs_parse() which takes a string representation of codec negotiation preferences, which may come from a pjsip endpoint for example, and populates a ast_stream_codec_negotiation_prefs structure. * Added ast_stream_codec_prefs_to_str() which does the reverse. * Added many functions to parse individual parameter name and value strings to their respectrive enum values, and the reverse. For streams: * Added ast_stream_create_resolved() which takes a "live" stream and resolves it with a configured stream and the negotiation preferences to create a new stream. * Added ast_stream_to_str() which create a string representation of a stream suitable for debug or display purposes. For topology: * Added ast_stream_topology_create_resolved() which takes a "live" topology and resolves it, stream by stream, with a configured topology stream and the negotiation preferences to create a new topology. * Added ast_stream_topology_to_str() which create a string representation of a topology suitable for debug or display purposes. * Renamed ast_format_caps_from_topology() to ast_stream_topology_get_formats() to be more consistent with the existing ast_stream_get_formats(). Additional changes: * A new function ast_format_cap_append_names() appends the results to the ast_str buffer instead of replacing buffer contents. Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56
2020-06-26 16:14:58 +00:00
if (type == AST_MEDIA_TYPE_UNKNOWN || type == stream->type) {
ast_format_cap_append_from_cap(caps, stream->formats, AST_MEDIA_TYPE_UNKNOWN);
}
}
return caps;
}
Streams: Add features for Advanced Codec Negotiation The Streams API becomes the home for the core ACN capabilities. These include... * Parsing and formatting of codec negotation preferences. * Resolving pending streams and topologies with those configured using configured preferences. * Utility functions for creating string representations of streams, topologies, and negotiation preferences. For codec negotiation preferences: * Added ast_stream_codec_prefs_parse() which takes a string representation of codec negotiation preferences, which may come from a pjsip endpoint for example, and populates a ast_stream_codec_negotiation_prefs structure. * Added ast_stream_codec_prefs_to_str() which does the reverse. * Added many functions to parse individual parameter name and value strings to their respectrive enum values, and the reverse. For streams: * Added ast_stream_create_resolved() which takes a "live" stream and resolves it with a configured stream and the negotiation preferences to create a new stream. * Added ast_stream_to_str() which create a string representation of a stream suitable for debug or display purposes. For topology: * Added ast_stream_topology_create_resolved() which takes a "live" topology and resolves it, stream by stream, with a configured topology stream and the negotiation preferences to create a new topology. * Added ast_stream_topology_to_str() which create a string representation of a topology suitable for debug or display purposes. * Renamed ast_format_caps_from_topology() to ast_stream_topology_get_formats() to be more consistent with the existing ast_stream_get_formats(). Additional changes: * A new function ast_format_cap_append_names() appends the results to the ast_str buffer instead of replacing buffer contents. Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56
2020-06-26 16:14:58 +00:00
struct ast_format_cap *ast_stream_topology_get_formats(
struct ast_stream_topology *topology)
{
return ast_stream_topology_get_formats_by_type(topology, AST_MEDIA_TYPE_UNKNOWN);
}
const char *ast_stream_topology_to_str(const struct ast_stream_topology *topology,
struct ast_str **buf)
{
int i;
if (!buf ||!*buf) {
return "";
}
if (!topology) {
ast_str_append(buf, 0, "(null topology)");
return ast_str_buffer(*buf);
}
ast_str_append(buf, 0, "%s", S_COR(topology->final, "final", ""));
for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {
struct ast_stream *stream;
stream = AST_VECTOR_GET(&topology->streams, i);
ast_str_append(buf, 0, " <");
ast_stream_to_str(stream, buf);
ast_str_append(buf, 0, ">");
}
return ast_str_buffer(*buf);
}
struct ast_stream *ast_stream_topology_get_first_stream_by_type(
const struct ast_stream_topology *topology,
enum ast_media_type type)
{
int i;
ast_assert(topology != NULL);
for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {
struct ast_stream *stream;
stream = AST_VECTOR_GET(&topology->streams, i);
if (stream->type == type
&& stream->state != AST_STREAM_STATE_REMOVED) {
return stream;
}
}
return NULL;
}
void ast_stream_topology_map(const struct ast_stream_topology *topology,
struct ast_vector_int *types, struct ast_vector_int *v0, struct ast_vector_int *v1)
{
int i;
int nths[AST_MEDIA_TYPE_END] = {0};
int size = ast_stream_topology_get_count(topology);
/*
* Clear out any old mappings and initialize the new ones
*/
AST_VECTOR_FREE(v0);
AST_VECTOR_FREE(v1);
/*
* Both vectors are sized to the topology. The media types vector is always
* guaranteed to be the size of the given topology or greater.
*/
AST_VECTOR_INIT(v0, size);
AST_VECTOR_INIT(v1, size);
for (i = 0; i < size; ++i) {
struct ast_stream *stream = ast_stream_topology_get_stream(topology, i);
enum ast_media_type type = ast_stream_get_type(stream);
int index = AST_VECTOR_GET_INDEX_NTH(types, ++nths[type],
type, AST_VECTOR_ELEM_DEFAULT_CMP);
if (index == -1) {
/*
* If a given type is not found for an index level then update the
* media types vector with that type. This keeps the media types
* vector always at the max topology size.
*/
AST_VECTOR_APPEND(types, type);
index = AST_VECTOR_SIZE(types) - 1;
}
/*
* The mapping is reflexive in the sense that if it maps in one direction
* then the reverse direction maps back to the other's index.
*/
AST_VECTOR_REPLACE(v0, i, index);
AST_VECTOR_REPLACE(v1, index, i);
}
}
Streams: Add features for Advanced Codec Negotiation The Streams API becomes the home for the core ACN capabilities. These include... * Parsing and formatting of codec negotation preferences. * Resolving pending streams and topologies with those configured using configured preferences. * Utility functions for creating string representations of streams, topologies, and negotiation preferences. For codec negotiation preferences: * Added ast_stream_codec_prefs_parse() which takes a string representation of codec negotiation preferences, which may come from a pjsip endpoint for example, and populates a ast_stream_codec_negotiation_prefs structure. * Added ast_stream_codec_prefs_to_str() which does the reverse. * Added many functions to parse individual parameter name and value strings to their respectrive enum values, and the reverse. For streams: * Added ast_stream_create_resolved() which takes a "live" stream and resolves it with a configured stream and the negotiation preferences to create a new stream. * Added ast_stream_to_str() which create a string representation of a stream suitable for debug or display purposes. For topology: * Added ast_stream_topology_create_resolved() which takes a "live" topology and resolves it, stream by stream, with a configured topology stream and the negotiation preferences to create a new topology. * Added ast_stream_topology_to_str() which create a string representation of a topology suitable for debug or display purposes. * Renamed ast_format_caps_from_topology() to ast_stream_topology_get_formats() to be more consistent with the existing ast_stream_get_formats(). Additional changes: * A new function ast_format_cap_append_names() appends the results to the ast_str buffer instead of replacing buffer contents. Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56
2020-06-26 16:14:58 +00:00
struct ast_stream_topology *ast_stream_topology_create_resolved(
struct ast_stream_topology *pending_topology, struct ast_stream_topology *configured_topology,
struct ast_stream_codec_negotiation_prefs *prefs, struct ast_str**error_message)
{
struct ast_stream_topology *joint_topology = ast_stream_topology_alloc();
int res = 0;
int i;
if (!pending_topology || !configured_topology || !joint_topology) {
ao2_cleanup(joint_topology);
return NULL;
}
for (i = 0; i < AST_VECTOR_SIZE(&pending_topology->streams); i++) {
struct ast_stream *pending_stream = AST_VECTOR_GET(&pending_topology->streams, i);
struct ast_stream *configured_stream =
ast_stream_topology_get_first_stream_by_type(configured_topology, pending_stream->type);
struct ast_stream *joint_stream;
if (!configured_stream) {
joint_stream = ast_stream_clone(pending_stream, NULL);
if (!joint_stream) {
ao2_cleanup(joint_topology);
return NULL;
}
ast_stream_set_state(joint_stream, AST_STREAM_STATE_REMOVED);
} else {
joint_stream = ast_stream_create_resolved(pending_stream, configured_stream, prefs, error_message);
if (ast_stream_get_format_count(joint_stream) == 0) {
ast_stream_set_state(joint_stream, AST_STREAM_STATE_REMOVED);
}
}
res = ast_stream_topology_append_stream(joint_topology, joint_stream);
if (res < 0) {
ast_stream_free(joint_stream);
ao2_cleanup(joint_topology);
return NULL;
}
}
return joint_topology;
}
int ast_stream_get_group(const struct ast_stream *stream)
{
ast_assert(stream != NULL);
return stream->group;
}
void ast_stream_set_group(struct ast_stream *stream, int group)
{
ast_assert(stream != NULL);
stream->group = group;
}