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
This commit is contained in:
parent
7440fd0397
commit
8d1064eaaf
|
@ -521,7 +521,7 @@ static int compatible_formats_exist(struct ast_stream_topology *top, struct ast_
|
|||
struct ast_format_cap *cap_from_top;
|
||||
int res;
|
||||
|
||||
cap_from_top = ast_format_cap_from_stream_topology(top);
|
||||
cap_from_top = ast_stream_topology_get_formats(top);
|
||||
|
||||
if (!cap_from_top) {
|
||||
return 0;
|
||||
|
@ -581,7 +581,7 @@ static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int s
|
|||
ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, AST_MEDIA_TYPE_UNKNOWN);
|
||||
topology = ast_stream_topology_clone(session->endpoint->media.topology);
|
||||
} else {
|
||||
caps = ast_format_cap_from_stream_topology(session->pending_media_state->topology);
|
||||
caps = ast_stream_topology_get_formats(session->pending_media_state->topology);
|
||||
topology = ast_stream_topology_clone(session->pending_media_state->topology);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
Subject: Core
|
||||
Master-Only: True
|
||||
|
||||
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.
|
|
@ -0,0 +1,5 @@
|
|||
Subject: Core
|
||||
Master-Only: True
|
||||
|
||||
The ast_format_cap_from_stream_topology() function has been renamed
|
||||
to ast_stream_topology_get_formats().
|
|
@ -310,6 +310,17 @@ int ast_format_cap_has_type(const struct ast_format_cap *cap, enum ast_media_typ
|
|||
*/
|
||||
const char *ast_format_cap_get_names(const struct ast_format_cap *cap, struct ast_str **buf);
|
||||
|
||||
/*!
|
||||
* \brief Append the names of codecs of a set of formats to an ast_str buffer
|
||||
* \since 18
|
||||
*
|
||||
* \param cap The capabilities structure containing the formats
|
||||
* \param buf A \c ast_str buffer to append the names of the formats to
|
||||
*
|
||||
* \return The contents of the buffer in \c buf
|
||||
*/
|
||||
const char *ast_format_cap_append_names(const struct ast_format_cap *cap, struct ast_str **buf);
|
||||
|
||||
#ifndef AST_FORMAT_CAP_NAMES_LEN
|
||||
/*! Buffer size for callers of ast_format_cap_get_names to allocate. */
|
||||
#define AST_FORMAT_CAP_NAMES_LEN 384
|
||||
|
|
|
@ -29,6 +29,23 @@
|
|||
#include "asterisk/codec.h"
|
||||
#include "asterisk/vector.h"
|
||||
|
||||
/*!
|
||||
* \brief Internal macro to assist converting enums to strings
|
||||
* \internal
|
||||
* \since 18
|
||||
*
|
||||
* This macro checks that _value is in the bounds
|
||||
* of the enum/string map.
|
||||
*/
|
||||
#define _stream_maps_to_str(_mapname, _value) \
|
||||
({ \
|
||||
const char *_rtn = ""; \
|
||||
if (ARRAY_IN_BOUNDS(_value, _mapname)) { \
|
||||
_rtn = _mapname[_value]; \
|
||||
} \
|
||||
_rtn; \
|
||||
})
|
||||
|
||||
/*!
|
||||
* \brief Forward declaration for a stream, as it is opaque
|
||||
*/
|
||||
|
@ -75,8 +92,273 @@ enum ast_stream_state {
|
|||
* \brief Set when the stream is not sending OR receiving media
|
||||
*/
|
||||
AST_STREAM_STATE_INACTIVE,
|
||||
/*!
|
||||
* \brief Sentinel
|
||||
*/
|
||||
AST_STREAM_STATE_END
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Stream state enum to string map
|
||||
* \internal
|
||||
* \since 18
|
||||
*/
|
||||
extern const char *ast_stream_state_map[AST_STREAM_STATE_END];
|
||||
|
||||
/*!
|
||||
* \brief Safely get the name of a stream state
|
||||
* \since 18
|
||||
*
|
||||
* \param stream_state One of enum ast_stream_state
|
||||
* \returns A constant string with the name of the state or an empty string
|
||||
* if an invalid value was passed in.
|
||||
*/
|
||||
#define ast_stream_state_to_str(stream_state) _stream_maps_to_str(ast_stream_state_map, stream_state)
|
||||
|
||||
/*!
|
||||
* \brief Advanced Codec Negotiation Preferences
|
||||
* \since 18
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \brief The preference parameters themselves
|
||||
* \since 18
|
||||
*/
|
||||
enum ast_stream_codec_negotiation_params {
|
||||
CODEC_NEGOTIATION_PARAM_UNSPECIFIED = 0,
|
||||
/*! Which of the lists to "prefer" */
|
||||
CODEC_NEGOTIATION_PARAM_PREFER,
|
||||
/*! "operation" to perform */
|
||||
CODEC_NEGOTIATION_PARAM_OPERATION,
|
||||
/*! "keep" all or only first */
|
||||
CODEC_NEGOTIATION_PARAM_KEEP,
|
||||
/*! Allow or prevent "transcode" */
|
||||
CODEC_NEGOTIATION_PARAM_TRANSCODE,
|
||||
/*! Sentinel */
|
||||
CODEC_NEGOTIATION_PARAM_END,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief The "prefer" values
|
||||
* \since 18
|
||||
*/
|
||||
enum ast_stream_codec_negotiation_prefs_prefer_values {
|
||||
CODEC_NEGOTIATION_PREFER_UNSPECIFIED = 0,
|
||||
/*! Prefer the "pending" list */
|
||||
CODEC_NEGOTIATION_PREFER_PENDING,
|
||||
/*! Prefer the "configured" list */
|
||||
CODEC_NEGOTIATION_PREFER_CONFIGURED,
|
||||
/*! Sentinel */
|
||||
CODEC_NEGOTIATION_PREFER_END,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief The "operation" values
|
||||
* \since 18
|
||||
*/
|
||||
enum ast_stream_codec_negotiation_prefs_operation_values {
|
||||
CODEC_NEGOTIATION_OPERATION_UNSPECIFIED = 0,
|
||||
/*! "intersect": only those codecs that appear in both lists */
|
||||
CODEC_NEGOTIATION_OPERATION_INTERSECT,
|
||||
/*! "union": all codecs in both lists */
|
||||
CODEC_NEGOTIATION_OPERATION_UNION,
|
||||
/*! "only_preferred": only the codecs in the preferred list */
|
||||
CODEC_NEGOTIATION_OPERATION_ONLY_PREFERRED,
|
||||
/*! "only_nonpreferred": only the codecs in the non-preferred list */
|
||||
CODEC_NEGOTIATION_OPERATION_ONLY_NONPREFERRED,
|
||||
/*! Sentinel */
|
||||
CODEC_NEGOTIATION_OPERATION_END,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief The "keep" values
|
||||
* \since 18
|
||||
*/
|
||||
enum ast_stream_codec_negotiation_prefs_keep_values {
|
||||
CODEC_NEGOTIATION_KEEP_UNSPECIFIED = 0,
|
||||
/*! "keep" all codecs after performing the operation */
|
||||
CODEC_NEGOTIATION_KEEP_ALL,
|
||||
/*! "keep" only the first codec after performing the operation */
|
||||
CODEC_NEGOTIATION_KEEP_FIRST,
|
||||
/*! Sentinel */
|
||||
CODEC_NEGOTIATION_KEEP_END,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief The "transcode" values
|
||||
* \since 18
|
||||
*/
|
||||
enum ast_stream_codec_negotiation_prefs_transcode_values {
|
||||
CODEC_NEGOTIATION_TRANSCODE_UNSPECIFIED = 0,
|
||||
/*! "allow" transcoding */
|
||||
CODEC_NEGOTIATION_TRANSCODE_ALLOW,
|
||||
/*! "prevent" transcoding */
|
||||
CODEC_NEGOTIATION_TRANSCODE_PREVENT,
|
||||
/*! Sentinel */
|
||||
CODEC_NEGOTIATION_TRANSCODE_END,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Preference enum to string map
|
||||
* \internal
|
||||
* \since 18
|
||||
*/
|
||||
extern const char *ast_stream_codec_negotiation_params_map[CODEC_NEGOTIATION_PARAM_END];
|
||||
|
||||
/*!
|
||||
* \brief "prefer" enum to string map
|
||||
* \internal
|
||||
* \since 18
|
||||
*/
|
||||
extern const char *ast_stream_codec_negotiation_prefer_map[CODEC_NEGOTIATION_PREFER_END];
|
||||
|
||||
/*!
|
||||
* \brief "operation" enum to string map
|
||||
* \internal
|
||||
* \since 18
|
||||
*/
|
||||
extern const char *ast_stream_codec_negotiation_operation_map[CODEC_NEGOTIATION_OPERATION_END];
|
||||
|
||||
/*!
|
||||
* \brief "keep" enum to string map
|
||||
* \internal
|
||||
* \since 18
|
||||
*/
|
||||
extern const char *ast_stream_codec_negotiation_keep_map[CODEC_NEGOTIATION_KEEP_END];
|
||||
|
||||
/*!
|
||||
* \brief "transcode" state enum to string map
|
||||
* \internal
|
||||
* \since 18
|
||||
*/
|
||||
extern const char *ast_stream_codec_negotiation_transcode_map[CODEC_NEGOTIATION_TRANSCODE_END];
|
||||
|
||||
/*!
|
||||
* \brief Safely get the name of a preference parameter
|
||||
* \since 18
|
||||
*
|
||||
* \param value One of enum \ref ast_stream_codec_negotiation_params
|
||||
* \returns A constant string with the name of the preference or an empty string
|
||||
* if an invalid value was passed in.
|
||||
*/
|
||||
#define ast_stream_codec_param_to_str(value) _stream_maps_to_str(ast_stream_codec_negotiation_params_map, value)
|
||||
|
||||
/*!
|
||||
* \brief Safely get the name of a "prefer" parameter value
|
||||
* \since 18
|
||||
*
|
||||
* \param value One of enum \ref ast_stream_codec_negotiation_prefer_values
|
||||
* \returns A constant string with the name of the value or an empty string
|
||||
* if an invalid value was passed in.
|
||||
*/
|
||||
#define ast_stream_codec_prefer_to_str(value) _stream_maps_to_str(ast_stream_codec_negotiation_prefer_map, value)
|
||||
|
||||
/*!
|
||||
* \brief Safely get the name of an "operation" parameter value
|
||||
* \since 18
|
||||
*
|
||||
* \param value One of enum \ref ast_stream_codec_negotiation_operation_values
|
||||
* \returns A constant string with the name of the value or an empty string
|
||||
* if an invalid value was passed in.
|
||||
*/
|
||||
#define ast_stream_codec_operation_to_str(value) _stream_maps_to_str(ast_stream_codec_negotiation_operation_map, value)
|
||||
|
||||
/*!
|
||||
* \brief Safely get the name of a "keep" parameter value
|
||||
* \since 18
|
||||
*
|
||||
* \param value One of enum \ref ast_stream_codec_negotiation_keep_values
|
||||
* \returns A constant string with the name of the value or an empty string
|
||||
* if an invalid value was passed in.
|
||||
*/
|
||||
#define ast_stream_codec_keep_to_str(value) _stream_maps_to_str(ast_stream_codec_negotiation_keep_map, value)
|
||||
|
||||
/*!
|
||||
* \brief Safely get the name of a "transcode" parameter value
|
||||
* \since 18
|
||||
*
|
||||
* \param value One of enum \ref ast_stream_codec_negotiation_transcode_values
|
||||
* \returns A constant string with the name of the value or an empty string
|
||||
* if an invalid value was passed in.
|
||||
*/
|
||||
#define ast_stream_codec_transcode_to_str(value) _stream_maps_to_str(ast_stream_codec_negotiation_transcode_map, value)
|
||||
|
||||
/*!
|
||||
* \brief
|
||||
* \since 18
|
||||
*
|
||||
* The structure that makes up a codec negotiation preference object
|
||||
*/
|
||||
struct ast_stream_codec_negotiation_prefs {
|
||||
/*! Which codec list to prefer */
|
||||
enum ast_stream_codec_negotiation_prefs_prefer_values prefer;
|
||||
/*! The operation to perform on the lists */
|
||||
enum ast_stream_codec_negotiation_prefs_operation_values operation;
|
||||
/*! What to keep after the operation is performed */
|
||||
enum ast_stream_codec_negotiation_prefs_keep_values keep;
|
||||
/*! To allow or prevent transcoding */
|
||||
enum ast_stream_codec_negotiation_prefs_transcode_values transcode;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Define for allocating buffer space for to_str() functions
|
||||
* \since 18
|
||||
*/
|
||||
#define AST_STREAM_MAX_CODEC_PREFS_LENGTH (128)
|
||||
|
||||
/*!
|
||||
* \brief Return a string representing the codec preferences
|
||||
* \since 18
|
||||
*
|
||||
* This function can be used for debugging purposes but is also
|
||||
* used in pjsip_configuration as a sorcery parameter handler
|
||||
*
|
||||
* \param prefs A pointer to a ast_stream_codec_negotiation_prefs structure
|
||||
* \param buf A pointer to an ast_str* used for the output. See note below.
|
||||
*
|
||||
* \returns the contents of the ast_str as a const char *.
|
||||
*
|
||||
* \warning No attempt should ever be made to free the returned
|
||||
* char * and it should be dup'd if needed after the ast_str is freed.
|
||||
*
|
||||
* \note
|
||||
* buf can't be NULL but it CAN contain a NULL value. If so, a new
|
||||
* ast_str will be allocated and the value of buf updated with a pointer
|
||||
* to it. Whether the caller supplies the ast_str or it's allocated by
|
||||
* this function, it's the caller's responsibility to free it.
|
||||
*
|
||||
* Sample output:
|
||||
* "prefer: configured, operation: union, keep:all, transcode:prevent"
|
||||
*/
|
||||
const char *ast_stream_codec_prefs_to_str(const struct ast_stream_codec_negotiation_prefs *prefs,
|
||||
struct ast_str **buf);
|
||||
|
||||
/*!
|
||||
* \brief Parses a string representing the codec prefs into a ast_stream_codec_negotiation_pref structure
|
||||
* \since 18
|
||||
*
|
||||
* This function is mainly used by pjsip_configuration as a sorcery parameter handler.
|
||||
*
|
||||
* \param pref_string A string in the format described by ast_stream_codec_prefs_to_str().
|
||||
* \param prefs Pointer to a ast_stream_codec_negotiation_prefs structure to receive the parsed values.
|
||||
* \param error_message An optional ast_str** into which parsing errors will be placed.
|
||||
*
|
||||
* \retval 0 if success
|
||||
* \retval -1 if failed
|
||||
*
|
||||
* \details
|
||||
* Whitespace around the ':' and ',' separators is ignored and the parameters
|
||||
* can be specified in any order. Parameters missing in the input string
|
||||
* will have their values set to the appropriate *_UNSPECIFIED value and will not
|
||||
* be considered an error. It's up to the caller to decide whether set a default
|
||||
* value, return an error, etc.
|
||||
*
|
||||
* Sample input:
|
||||
* "prefer : configured , operation: union,keep:all, transcode:prevent"
|
||||
*/
|
||||
int ast_stream_codec_prefs_parse(const char *pref_string, struct ast_stream_codec_negotiation_prefs *prefs,
|
||||
struct ast_str **error_message);
|
||||
|
||||
/*!
|
||||
* \brief Create a new media stream representation
|
||||
*
|
||||
|
@ -166,6 +448,31 @@ void ast_stream_set_type(struct ast_stream *stream, enum ast_media_type type);
|
|||
*/
|
||||
const struct ast_format_cap *ast_stream_get_formats(const struct ast_stream *stream);
|
||||
|
||||
/*!
|
||||
* \brief Get a string representing the stream for debugging/display purposes
|
||||
* \since 18
|
||||
*
|
||||
* \param stream A stream
|
||||
* \param buf A pointer to an ast_str* used for the output.
|
||||
*
|
||||
* \retval "" (empty string) if either buf or *buf are NULL
|
||||
* \retval "(null stream)" if *stream was NULL
|
||||
* \retval <stream_representation> otherwise
|
||||
*
|
||||
* \warning No attempt should ever be made to free the returned
|
||||
* char * and it should be dup'd if needed after the ast_str is freed.
|
||||
*
|
||||
* \details
|
||||
*
|
||||
* Return format:
|
||||
* <name>:<media_type>:<stream_state> (formats)
|
||||
*
|
||||
* Sample return:
|
||||
* "audio:audio:sendrecv (ulaw,g722)"
|
||||
*
|
||||
*/
|
||||
const char *ast_stream_to_str(const struct ast_stream *stream, struct ast_str **buf);
|
||||
|
||||
/*!
|
||||
* \brief Get the count of the current negotiated formats of a stream
|
||||
*
|
||||
|
@ -310,6 +617,33 @@ struct ast_rtp_codecs *ast_stream_get_rtp_codecs(const struct ast_stream *stream
|
|||
*/
|
||||
void ast_stream_set_rtp_codecs(struct ast_stream *stream, struct ast_rtp_codecs *rtp_codecs);
|
||||
|
||||
/*!
|
||||
* \brief Create a resolved stream from 2 streams
|
||||
* \since 18
|
||||
*
|
||||
* \param pending_stream The "live" stream created from an SDP,
|
||||
* passed through the core, or used to create an SDP.
|
||||
* \param configured_stream The static stream used to validate the pending stream.
|
||||
* \param prefs A pointer to an ast_stream_codec_negotiation_prefs structure.
|
||||
* \param error_message If supplied, error messages will be appended.
|
||||
*
|
||||
* \details
|
||||
* The resulting stream will contain all of the attributes and metadata of the
|
||||
* pending stream but will contain only the formats that passed the validation
|
||||
* specified by the ast_stream_codec_negotiation_prefs structure. This may mean
|
||||
* that the stream's format_caps will be empty. It's up to the caller to determine
|
||||
* what to do with the stream in that case. I.E. Free it, set it to the
|
||||
* REMOVED state, etc. A stream will always be returned unless there was
|
||||
* some catastrophic allocation failure.
|
||||
*
|
||||
* \retval NULL if there was some allocation failure.
|
||||
* \retval A new, resolved stream.
|
||||
*
|
||||
*/
|
||||
struct ast_stream *ast_stream_create_resolved(struct ast_stream *pending_stream,
|
||||
struct ast_stream *configured_stream, struct ast_stream_codec_negotiation_prefs *prefs,
|
||||
struct ast_str **error_message);
|
||||
|
||||
/*!
|
||||
* \brief Create a stream topology
|
||||
*
|
||||
|
@ -386,6 +720,18 @@ int ast_stream_topology_append_stream(struct ast_stream_topology *topology,
|
|||
*/
|
||||
int ast_stream_topology_get_count(const struct ast_stream_topology *topology);
|
||||
|
||||
/*!
|
||||
* \brief Get the number of active (non-REMOVED) streams in a topology
|
||||
*
|
||||
* \param topology The topology of streams
|
||||
*
|
||||
* \return the number of active streams
|
||||
*
|
||||
* \since 18
|
||||
*/
|
||||
int ast_stream_topology_get_active_count(const struct ast_stream_topology *topology);
|
||||
|
||||
|
||||
/*!
|
||||
* \brief Get a specific stream from the topology
|
||||
*
|
||||
|
@ -471,16 +817,60 @@ struct ast_stream_topology *ast_stream_topology_create_from_format_cap(
|
|||
*
|
||||
* \param topology The topology of streams
|
||||
*
|
||||
* \retval non-NULL success
|
||||
* \retval non-NULL success (the resulting format caps must be unreffed by the caller)
|
||||
* \retval NULL failure
|
||||
*
|
||||
* \note The stream topology is NOT altered by this function.
|
||||
*
|
||||
* \since 15
|
||||
*/
|
||||
struct ast_format_cap *ast_format_cap_from_stream_topology(
|
||||
struct ast_format_cap *ast_stream_topology_get_formats(
|
||||
struct ast_stream_topology *topology);
|
||||
|
||||
/*!
|
||||
* \brief Get a string representing the topology for debugging/display purposes
|
||||
* \since 18
|
||||
*
|
||||
* \param topology A stream topology
|
||||
* \param buf A pointer to an ast_str* used for the output.
|
||||
*
|
||||
* \retval "" (empty string) if either buf or *buf are NULL
|
||||
* \retval "(null topology)" if *topology was NULL
|
||||
* \retval <topology_representation> otherwise
|
||||
*
|
||||
* \warning No attempt should ever be made to free the returned
|
||||
* char * and it should be dup'd if needed after the ast_str is freed.
|
||||
*
|
||||
* Return format:
|
||||
* <final>? <stream> ...
|
||||
*
|
||||
* Sample return:
|
||||
* "final <audio:audio:sendrecv (ulaw,g722)> <video:video:sendonly (h264)>"
|
||||
*
|
||||
*/
|
||||
const char *ast_stream_topology_to_str(const struct ast_stream_topology *topology, struct ast_str **buf);
|
||||
|
||||
/*!
|
||||
* \brief Create a format capabilities structure containing all the formats from all the streams
|
||||
* of a particular type in the topology.
|
||||
* \since 18
|
||||
*
|
||||
* \details
|
||||
* A helper function that, given a stream topology and a media type, creates a format
|
||||
* capabilities structure containing all formats from all active streams with the particular type.
|
||||
*
|
||||
* \param topology The topology of streams
|
||||
* \param type The media type
|
||||
*
|
||||
* \retval non-NULL success (the resulting format caps must be unreffed by the caller)
|
||||
* \retval NULL failure
|
||||
*
|
||||
* \note The stream topology is NOT altered by this function.
|
||||
*
|
||||
*/
|
||||
struct ast_format_cap *ast_stream_topology_get_formats_by_type(
|
||||
struct ast_stream_topology *topology, enum ast_media_type type);
|
||||
|
||||
/*!
|
||||
* \brief Gets the first active stream of a specific type from the topology
|
||||
*
|
||||
|
@ -534,4 +924,34 @@ int ast_stream_get_group(const struct ast_stream *stream);
|
|||
*/
|
||||
void ast_stream_set_group(struct ast_stream *stream, int group);
|
||||
|
||||
/*!
|
||||
* \brief Create a resolved stream topology from 2 topologies
|
||||
* \since 18
|
||||
*
|
||||
* \param pending_topology The "live" topology created from an SDP,
|
||||
* passed through the core, or used to create an SDP.
|
||||
* \param configured_topology The static topology used to validate the pending topology.
|
||||
* It MUST have only 1 stream per media type.
|
||||
* \param prefs A pointer to an ast_stream_codec_negotiation_prefs structure.
|
||||
* \param error_message If supplied, error messages will be appended.
|
||||
*
|
||||
* \details
|
||||
* The streams in the resolved topology will contain all of the attributes
|
||||
* of the corresponding stream from the pending topology. It's format_caps
|
||||
* however will contain only the formats that passed the validation
|
||||
* specified by the ast_stream_codec_negotiation_prefs structure. This may
|
||||
* mean that some of the streams format_caps will be empty. If that's the case,
|
||||
* the stream will be in a REMOVED state. With those rules in mind,
|
||||
* a resolved topology will always be returned (unless there's some catastrophic
|
||||
* allocation failure) and the resolved topology is guaranteed to have the same
|
||||
* number of streams, in the same order, as the pending topology.
|
||||
*
|
||||
* \retval NULL if there was some allocation failure.
|
||||
* \retval The joint topology.
|
||||
*/
|
||||
struct ast_stream_topology *ast_stream_topology_create_resolved(
|
||||
struct ast_stream_topology *pending_topology, struct ast_stream_topology *validation_topology,
|
||||
struct ast_stream_codec_negotiation_prefs *prefs,
|
||||
struct ast_str **error_message);
|
||||
|
||||
#endif /* _AST_STREAM_H */
|
||||
|
|
|
@ -6227,7 +6227,7 @@ static struct ast_channel *request_channel(const char *type, struct ast_format_c
|
|||
|
||||
if (!request_cap && topology) {
|
||||
/* Turn the request stream topology into capabilities */
|
||||
request_cap = tmp_converted_cap = ast_format_cap_from_stream_topology(topology);
|
||||
request_cap = tmp_converted_cap = ast_stream_topology_get_formats(topology);
|
||||
}
|
||||
|
||||
/* find the best audio format to use */
|
||||
|
|
|
@ -1054,7 +1054,7 @@ struct ast_unreal_pvt *ast_unreal_alloc_stream_topology(size_t size, ao2_destruc
|
|||
return NULL;
|
||||
}
|
||||
|
||||
unreal->reqcap = ast_format_cap_from_stream_topology(topology);
|
||||
unreal->reqcap = ast_stream_topology_get_formats(topology);
|
||||
if (!unreal->reqcap) {
|
||||
ao2_ref(unreal, -1);
|
||||
return NULL;
|
||||
|
|
|
@ -699,11 +699,19 @@ int ast_format_cap_identical(const struct ast_format_cap *cap1, const struct ast
|
|||
return internal_format_cap_identical(cap2, cap1);
|
||||
}
|
||||
|
||||
const char *ast_format_cap_get_names(const struct ast_format_cap *cap, struct ast_str **buf)
|
||||
static const char *__ast_format_cap_get_names(const struct ast_format_cap *cap, struct ast_str **buf, int append)
|
||||
{
|
||||
int i;
|
||||
|
||||
ast_str_set(buf, 0, "(");
|
||||
if (!buf || !*buf) {
|
||||
return "";
|
||||
}
|
||||
|
||||
if (append) {
|
||||
ast_str_append(buf, 0, "(");
|
||||
} else {
|
||||
ast_str_set(buf, 0, "(");
|
||||
}
|
||||
|
||||
if (!cap || !AST_VECTOR_SIZE(&cap->preference_order)) {
|
||||
ast_str_append(buf, 0, "nothing)");
|
||||
|
@ -725,6 +733,16 @@ const char *ast_format_cap_get_names(const struct ast_format_cap *cap, struct as
|
|||
return ast_str_buffer(*buf);
|
||||
}
|
||||
|
||||
const char *ast_format_cap_get_names(const struct ast_format_cap *cap, struct ast_str **buf)
|
||||
{
|
||||
return __ast_format_cap_get_names(cap, buf, 0);
|
||||
}
|
||||
|
||||
const char *ast_format_cap_append_names(const struct ast_format_cap *cap, struct ast_str **buf)
|
||||
{
|
||||
return __ast_format_cap_get_names(cap, buf, 1);
|
||||
}
|
||||
|
||||
int ast_format_cap_empty(const struct ast_format_cap *cap)
|
||||
{
|
||||
int count = ast_format_cap_count(cap);
|
||||
|
|
364
main/stream.c
364
main/stream.c
|
@ -44,6 +44,40 @@ struct ast_stream_metadata_entry {
|
|||
char name_value[0];
|
||||
};
|
||||
|
||||
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
|
||||
|
@ -91,6 +125,107 @@ struct ast_stream_topology {
|
|||
* \brief A vector of all the streams in this topology
|
||||
*/
|
||||
AST_VECTOR(, struct ast_stream *) streams;
|
||||
/*! 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)
|
||||
|
@ -165,6 +300,7 @@ void ast_stream_free(struct ast_stream *stream)
|
|||
}
|
||||
|
||||
ao2_cleanup(stream->formats);
|
||||
|
||||
ast_free(stream);
|
||||
}
|
||||
|
||||
|
@ -196,6 +332,26 @@ const struct ast_format_cap *ast_stream_get_formats(const struct ast_stream *str
|
|||
return stream->formats;
|
||||
}
|
||||
|
||||
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);
|
||||
|
@ -375,6 +531,108 @@ void ast_stream_set_rtp_codecs(struct ast_stream *stream, struct ast_rtp_codecs
|
|||
stream->rtp_codecs = rtp_codecs;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -502,6 +760,22 @@ int ast_stream_topology_get_count(const struct ast_stream_topology *topology)
|
|||
return AST_VECTOR_SIZE(&topology->streams);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -610,8 +884,8 @@ struct ast_stream_topology *ast_stream_topology_create_from_format_cap(
|
|||
return topology;
|
||||
}
|
||||
|
||||
struct ast_format_cap *ast_format_cap_from_stream_topology(
|
||||
struct ast_stream_topology *topology)
|
||||
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;
|
||||
|
@ -627,17 +901,51 @@ struct ast_format_cap *ast_format_cap_from_stream_topology(
|
|||
struct ast_stream *stream;
|
||||
|
||||
stream = AST_VECTOR_GET(&topology->streams, i);
|
||||
if (!stream->formats
|
||||
|| stream->state == AST_STREAM_STATE_REMOVED) {
|
||||
if (!stream->formats || stream->state == AST_STREAM_STATE_REMOVED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ast_format_cap_append_from_cap(caps, stream->formats, AST_MEDIA_TYPE_UNKNOWN);
|
||||
if (type == AST_MEDIA_TYPE_UNKNOWN || type == stream->type) {
|
||||
ast_format_cap_append_from_cap(caps, stream->formats, AST_MEDIA_TYPE_UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -704,6 +1012,50 @@ void ast_stream_topology_map(const struct ast_stream_topology *topology,
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
@ -2142,7 +2142,7 @@ AST_TEST_DEFINE(format_cap_from_stream_topology)
|
|||
return AST_TEST_FAIL;
|
||||
}
|
||||
|
||||
stream_caps = ast_format_cap_from_stream_topology(topology);
|
||||
stream_caps = ast_stream_topology_get_formats(topology);
|
||||
if (!stream_caps) {
|
||||
ast_test_status_update(test, "Failed to create a format capabilities from a stream topology\n");
|
||||
ast_stream_topology_free(topology);
|
||||
|
|
Loading…
Reference in New Issue