<p>Friendly Automation <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/14577">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Joshua Colp: Looks good to me, but someone else must approve
Kevin Harwell: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved
Friendly Automation: Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">Streams: Add features for Advanced Codec Negotiation<br><br>The Streams API becomes the home for the core ACN capabilities.<br>These include...<br><br> * Parsing and formatting of codec negotation preferences.<br> * Resolving pending streams and topologies with those configured<br> using configured preferences.<br> * Utility functions for creating string representations of<br> streams, topologies, and negotiation preferences.<br><br>For codec negotiation preferences:<br> * Added ast_stream_codec_prefs_parse() which takes a string<br> representation of codec negotiation preferences, which<br> may come from a pjsip endpoint for example, and populates<br> a ast_stream_codec_negotiation_prefs structure.<br> * Added ast_stream_codec_prefs_to_str() which does the reverse.<br> * Added many functions to parse individual parameter name<br> and value strings to their respectrive enum values, and the<br> reverse.<br><br>For streams:<br> * Added ast_stream_create_resolved() which takes a "live" stream<br> and resolves it with a configured stream and the negotiation<br> preferences to create a new stream.<br> * Added ast_stream_to_str() which create a string representation<br> of a stream suitable for debug or display purposes.<br><br>For topology:<br> * Added ast_stream_topology_create_resolved() which takes a "live"<br> topology and resolves it, stream by stream, with a configured<br> topology stream and the negotiation preferences to create a new<br> topology.<br> * Added ast_stream_topology_to_str() which create a string<br> representation of a topology suitable for debug or display<br> purposes.<br> * Renamed ast_format_caps_from_topology() to<br> ast_stream_topology_get_formats() to be more consistent with<br> the existing ast_stream_get_formats().<br><br>Additional changes:<br> * A new function ast_format_cap_append_names() appends the results<br> to the ast_str buffer instead of replacing buffer contents.<br><br>Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56<br>---<br>M channels/chan_pjsip.c<br>A doc/CHANGES-staging/ACN_streams.txt<br>A doc/UPGRADE-staging/ACN_streams.txt<br>M include/asterisk/format_cap.h<br>M include/asterisk/stream.h<br>M main/channel.c<br>M main/core_unreal.c<br>M main/format_cap.c<br>M main/stream.c<br>M tests/test_stream.c<br>10 files changed, 865 insertions(+), 15 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c</span><br><span>index afaecdf..2fc98b4 100644</span><br><span>--- a/channels/chan_pjsip.c</span><br><span>+++ b/channels/chan_pjsip.c</span><br><span>@@ -521,7 +521,7 @@</span><br><span> struct ast_format_cap *cap_from_top;</span><br><span> int res;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- cap_from_top = ast_format_cap_from_stream_topology(top);</span><br><span style="color: hsl(120, 100%, 40%);">+ cap_from_top = ast_stream_topology_get_formats(top);</span><br><span> </span><br><span> if (!cap_from_top) {</span><br><span> return 0;</span><br><span>@@ -581,7 +581,7 @@</span><br><span> ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, AST_MEDIA_TYPE_UNKNOWN);</span><br><span> topology = ast_stream_topology_clone(session->endpoint->media.topology);</span><br><span> } else {</span><br><span style="color: hsl(0, 100%, 40%);">- caps = ast_format_cap_from_stream_topology(session->pending_media_state->topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ caps = ast_stream_topology_get_formats(session->pending_media_state->topology);</span><br><span> topology = ast_stream_topology_clone(session->pending_media_state->topology);</span><br><span> }</span><br><span> </span><br><span>diff --git a/doc/CHANGES-staging/ACN_streams.txt b/doc/CHANGES-staging/ACN_streams.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..6b54d71</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/ACN_streams.txt</span><br><span>@@ -0,0 +1,44 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: Core</span><br><span style="color: hsl(120, 100%, 40%);">+Master-Only: True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The Streams API becomes the home for the core ACN capabilities.</span><br><span style="color: hsl(120, 100%, 40%);">+These include...</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ * Parsing and formatting of codec negotation preferences.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Resolving pending streams and topologies with those configured</span><br><span style="color: hsl(120, 100%, 40%);">+ using configured preferences.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Utility functions for creating string representations of</span><br><span style="color: hsl(120, 100%, 40%);">+ streams, topologies, and negotiation preferences.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+For codec negotiation preferences:</span><br><span style="color: hsl(120, 100%, 40%);">+ * Added ast_stream_codec_prefs_parse() which takes a string</span><br><span style="color: hsl(120, 100%, 40%);">+ representation of codec negotiation preferences, which</span><br><span style="color: hsl(120, 100%, 40%);">+ may come from a pjsip endpoint for example, and populates</span><br><span style="color: hsl(120, 100%, 40%);">+ a ast_stream_codec_negotiation_prefs structure.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Added ast_stream_codec_prefs_to_str() which does the reverse.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Added many functions to parse individual parameter name</span><br><span style="color: hsl(120, 100%, 40%);">+ and value strings to their respectrive enum values, and the</span><br><span style="color: hsl(120, 100%, 40%);">+ reverse.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+For streams:</span><br><span style="color: hsl(120, 100%, 40%);">+ * Added ast_stream_create_resolved() which takes a "live" stream</span><br><span style="color: hsl(120, 100%, 40%);">+ and resolves it with a configured stream and the negotiation</span><br><span style="color: hsl(120, 100%, 40%);">+ preferences to create a new stream.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Added ast_stream_to_str() which create a string representation</span><br><span style="color: hsl(120, 100%, 40%);">+ of a stream suitable for debug or display purposes.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+For topology:</span><br><span style="color: hsl(120, 100%, 40%);">+ * Added ast_stream_topology_create_resolved() which takes a "live"</span><br><span style="color: hsl(120, 100%, 40%);">+ topology and resolves it, stream by stream, with a configured</span><br><span style="color: hsl(120, 100%, 40%);">+ topology stream and the negotiation preferences to create a new</span><br><span style="color: hsl(120, 100%, 40%);">+ topology.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Added ast_stream_topology_to_str() which create a string</span><br><span style="color: hsl(120, 100%, 40%);">+ representation of a topology suitable for debug or display</span><br><span style="color: hsl(120, 100%, 40%);">+ purposes.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Renamed ast_format_caps_from_topology() to</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_get_formats() to be more consistent with</span><br><span style="color: hsl(120, 100%, 40%);">+ the existing ast_stream_get_formats().</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Additional changes:</span><br><span style="color: hsl(120, 100%, 40%);">+ * A new function ast_format_cap_append_names() appends the results</span><br><span style="color: hsl(120, 100%, 40%);">+ to the ast_str buffer instead of replacing buffer contents.</span><br><span>diff --git a/doc/UPGRADE-staging/ACN_streams.txt b/doc/UPGRADE-staging/ACN_streams.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..400a6d2</span><br><span>--- /dev/null</span><br><span>+++ b/doc/UPGRADE-staging/ACN_streams.txt</span><br><span>@@ -0,0 +1,5 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: Core</span><br><span style="color: hsl(120, 100%, 40%);">+Master-Only: True</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+The ast_format_cap_from_stream_topology() function has been renamed</span><br><span style="color: hsl(120, 100%, 40%);">+to ast_stream_topology_get_formats().</span><br><span>diff --git a/include/asterisk/format_cap.h b/include/asterisk/format_cap.h</span><br><span>index 37021eb..e9796bb 100644</span><br><span>--- a/include/asterisk/format_cap.h</span><br><span>+++ b/include/asterisk/format_cap.h</span><br><span>@@ -310,6 +310,17 @@</span><br><span> */</span><br><span> const char *ast_format_cap_get_names(const struct ast_format_cap *cap, struct ast_str **buf);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Append the names of codecs of a set of formats to an ast_str buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param cap The capabilities structure containing the formats</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param buf A \c ast_str buffer to append the names of the formats to</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return The contents of the buffer in \c buf</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_format_cap_append_names(const struct ast_format_cap *cap, struct ast_str **buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #ifndef AST_FORMAT_CAP_NAMES_LEN</span><br><span> /*! Buffer size for callers of ast_format_cap_get_names to allocate. */</span><br><span> #define AST_FORMAT_CAP_NAMES_LEN 384</span><br><span>diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h</span><br><span>index bbeeacb..ca637be 100644</span><br><span>--- a/include/asterisk/stream.h</span><br><span>+++ b/include/asterisk/stream.h</span><br><span>@@ -30,6 +30,23 @@</span><br><span> #include "asterisk/vector.h"</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Internal macro to assist converting enums to strings</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This macro checks that _value is in the bounds</span><br><span style="color: hsl(120, 100%, 40%);">+ * of the enum/string map.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define _stream_maps_to_str(_mapname, _value) \</span><br><span style="color: hsl(120, 100%, 40%);">+({ \</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *_rtn = ""; \</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ARRAY_IN_BOUNDS(_value, _mapname)) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ _rtn = _mapname[_value]; \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+ _rtn; \</span><br><span style="color: hsl(120, 100%, 40%);">+})</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Forward declaration for a stream, as it is opaque</span><br><span> */</span><br><span> struct ast_stream;</span><br><span>@@ -75,9 +92,274 @@</span><br><span> * \brief Set when the stream is not sending OR receiving media</span><br><span> */</span><br><span> AST_STREAM_STATE_INACTIVE,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Sentinel</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STREAM_STATE_END</span><br><span> };</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Stream state enum to string map</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+extern const char *ast_stream_state_map[AST_STREAM_STATE_END];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Safely get the name of a stream state</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param stream_state One of enum ast_stream_state</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A constant string with the name of the state or an empty string</span><br><span style="color: hsl(120, 100%, 40%);">+ * if an invalid value was passed in.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define ast_stream_state_to_str(stream_state) _stream_maps_to_str(ast_stream_state_map, stream_state)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Advanced Codec Negotiation Preferences</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The preference parameters themselves</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+enum ast_stream_codec_negotiation_params {</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_PARAM_UNSPECIFIED = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Which of the lists to "prefer" */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_PARAM_PREFER,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! "operation" to perform */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_PARAM_OPERATION,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! "keep" all or only first */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_PARAM_KEEP,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Allow or prevent "transcode" */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_PARAM_TRANSCODE,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Sentinel */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_PARAM_END,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The "prefer" values</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+enum ast_stream_codec_negotiation_prefs_prefer_values {</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_PREFER_UNSPECIFIED = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Prefer the "pending" list */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_PREFER_PENDING,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Prefer the "configured" list */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_PREFER_CONFIGURED,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Sentinel */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_PREFER_END,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The "operation" values</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+enum ast_stream_codec_negotiation_prefs_operation_values {</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_OPERATION_UNSPECIFIED = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! "intersect": only those codecs that appear in both lists */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_OPERATION_INTERSECT,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! "union": all codecs in both lists */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_OPERATION_UNION,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! "only_preferred": only the codecs in the preferred list */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_OPERATION_ONLY_PREFERRED,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! "only_nonpreferred": only the codecs in the non-preferred list */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_OPERATION_ONLY_NONPREFERRED,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Sentinel */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_OPERATION_END,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The "keep" values</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+enum ast_stream_codec_negotiation_prefs_keep_values {</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_KEEP_UNSPECIFIED = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! "keep" all codecs after performing the operation */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_KEEP_ALL,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! "keep" only the first codec after performing the operation */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_KEEP_FIRST,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Sentinel */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_KEEP_END,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief The "transcode" values</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+enum ast_stream_codec_negotiation_prefs_transcode_values {</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_TRANSCODE_UNSPECIFIED = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! "allow" transcoding */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_TRANSCODE_ALLOW,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! "prevent" transcoding */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_TRANSCODE_PREVENT,</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Sentinel */</span><br><span style="color: hsl(120, 100%, 40%);">+ CODEC_NEGOTIATION_TRANSCODE_END,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Preference enum to string map</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+extern const char *ast_stream_codec_negotiation_params_map[CODEC_NEGOTIATION_PARAM_END];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief "prefer" enum to string map</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+extern const char *ast_stream_codec_negotiation_prefer_map[CODEC_NEGOTIATION_PREFER_END];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief "operation" enum to string map</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+extern const char *ast_stream_codec_negotiation_operation_map[CODEC_NEGOTIATION_OPERATION_END];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief "keep" enum to string map</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+extern const char *ast_stream_codec_negotiation_keep_map[CODEC_NEGOTIATION_KEEP_END];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief "transcode" state enum to string map</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+extern const char *ast_stream_codec_negotiation_transcode_map[CODEC_NEGOTIATION_TRANSCODE_END];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Safely get the name of a preference parameter</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param value One of enum \ref ast_stream_codec_negotiation_params</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A constant string with the name of the preference or an empty string</span><br><span style="color: hsl(120, 100%, 40%);">+ * if an invalid value was passed in.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define ast_stream_codec_param_to_str(value) _stream_maps_to_str(ast_stream_codec_negotiation_params_map, value)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Safely get the name of a "prefer" parameter value</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param value One of enum \ref ast_stream_codec_negotiation_prefer_values</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A constant string with the name of the value or an empty string</span><br><span style="color: hsl(120, 100%, 40%);">+ * if an invalid value was passed in.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define ast_stream_codec_prefer_to_str(value) _stream_maps_to_str(ast_stream_codec_negotiation_prefer_map, value)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Safely get the name of an "operation" parameter value</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param value One of enum \ref ast_stream_codec_negotiation_operation_values</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A constant string with the name of the value or an empty string</span><br><span style="color: hsl(120, 100%, 40%);">+ * if an invalid value was passed in.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define ast_stream_codec_operation_to_str(value) _stream_maps_to_str(ast_stream_codec_negotiation_operation_map, value)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Safely get the name of a "keep" parameter value</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param value One of enum \ref ast_stream_codec_negotiation_keep_values</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A constant string with the name of the value or an empty string</span><br><span style="color: hsl(120, 100%, 40%);">+ * if an invalid value was passed in.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define ast_stream_codec_keep_to_str(value) _stream_maps_to_str(ast_stream_codec_negotiation_keep_map, value)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Safely get the name of a "transcode" parameter value</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param value One of enum \ref ast_stream_codec_negotiation_transcode_values</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns A constant string with the name of the value or an empty string</span><br><span style="color: hsl(120, 100%, 40%);">+ * if an invalid value was passed in.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define ast_stream_codec_transcode_to_str(value) _stream_maps_to_str(ast_stream_codec_negotiation_transcode_map, value)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * The structure that makes up a codec negotiation preference object</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_stream_codec_negotiation_prefs {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Which codec list to prefer */</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_stream_codec_negotiation_prefs_prefer_values prefer;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! The operation to perform on the lists */</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_stream_codec_negotiation_prefs_operation_values operation;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! What to keep after the operation is performed */</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_stream_codec_negotiation_prefs_keep_values keep;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! To allow or prevent transcoding */</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_stream_codec_negotiation_prefs_transcode_values transcode;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Define for allocating buffer space for to_str() functions</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define AST_STREAM_MAX_CODEC_PREFS_LENGTH (128)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Return a string representing the codec preferences</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This function can be used for debugging purposes but is also</span><br><span style="color: hsl(120, 100%, 40%);">+ * used in pjsip_configuration as a sorcery parameter handler</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param prefs A pointer to a ast_stream_codec_negotiation_prefs structure</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param buf A pointer to an ast_str* used for the output. See note below.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns the contents of the ast_str as a const char *.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \warning No attempt should ever be made to free the returned</span><br><span style="color: hsl(120, 100%, 40%);">+ * char * and it should be dup'd if needed after the ast_str is freed.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note</span><br><span style="color: hsl(120, 100%, 40%);">+ * buf can't be NULL but it CAN contain a NULL value. If so, a new</span><br><span style="color: hsl(120, 100%, 40%);">+ * ast_str will be allocated and the value of buf updated with a pointer</span><br><span style="color: hsl(120, 100%, 40%);">+ * to it. Whether the caller supplies the ast_str or it's allocated by</span><br><span style="color: hsl(120, 100%, 40%);">+ * this function, it's the caller's responsibility to free it.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Sample output:</span><br><span style="color: hsl(120, 100%, 40%);">+ * "prefer: configured, operation: union, keep:all, transcode:prevent"</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_codec_prefs_to_str(const struct ast_stream_codec_negotiation_prefs *prefs,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str **buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Parses a string representing the codec prefs into a ast_stream_codec_negotiation_pref structure</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This function is mainly used by pjsip_configuration as a sorcery parameter handler.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param pref_string A string in the format described by ast_stream_codec_prefs_to_str().</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param prefs Pointer to a ast_stream_codec_negotiation_prefs structure to receive the parsed values.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param error_message An optional ast_str** into which parsing errors will be placed.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 if success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 if failed</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * Whitespace around the ':' and ',' separators is ignored and the parameters</span><br><span style="color: hsl(120, 100%, 40%);">+ * can be specified in any order. Parameters missing in the input string</span><br><span style="color: hsl(120, 100%, 40%);">+ * will have their values set to the appropriate *_UNSPECIFIED value and will not</span><br><span style="color: hsl(120, 100%, 40%);">+ * be considered an error. It's up to the caller to decide whether set a default</span><br><span style="color: hsl(120, 100%, 40%);">+ * value, return an error, etc.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Sample input:</span><br><span style="color: hsl(120, 100%, 40%);">+ * "prefer : configured , operation: union,keep:all, transcode:prevent"</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_stream_codec_prefs_parse(const char *pref_string, struct ast_stream_codec_negotiation_prefs *prefs,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str **error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Create a new media stream representation</span><br><span> *</span><br><span> * \param name A name for the stream</span><br><span>@@ -167,6 +449,31 @@</span><br><span> const struct ast_format_cap *ast_stream_get_formats(const struct ast_stream *stream);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get a string representing the stream for debugging/display purposes</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param stream A stream</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param buf A pointer to an ast_str* used for the output.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval "" (empty string) if either buf or *buf are NULL</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval "(null stream)" if *stream was NULL</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval <stream_representation> otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \warning No attempt should ever be made to free the returned</span><br><span style="color: hsl(120, 100%, 40%);">+ * char * and it should be dup'd if needed after the ast_str is freed.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Return format:</span><br><span style="color: hsl(120, 100%, 40%);">+ * <name>:<media_type>:<stream_state> (formats)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Sample return:</span><br><span style="color: hsl(120, 100%, 40%);">+ * "audio:audio:sendrecv (ulaw,g722)"</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_to_str(const struct ast_stream *stream, struct ast_str **buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Get the count of the current negotiated formats of a stream</span><br><span> *</span><br><span> * \param stream The media stream</span><br><span>@@ -311,6 +618,33 @@</span><br><span> void ast_stream_set_rtp_codecs(struct ast_stream *stream, struct ast_rtp_codecs *rtp_codecs);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create a resolved stream from 2 streams</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param pending_stream The "live" stream created from an SDP,</span><br><span style="color: hsl(120, 100%, 40%);">+ * passed through the core, or used to create an SDP.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param configured_stream The static stream used to validate the pending stream.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param prefs A pointer to an ast_stream_codec_negotiation_prefs structure.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param error_message If supplied, error messages will be appended.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * The resulting stream will contain all of the attributes and metadata of the</span><br><span style="color: hsl(120, 100%, 40%);">+ * pending stream but will contain only the formats that passed the validation</span><br><span style="color: hsl(120, 100%, 40%);">+ * specified by the ast_stream_codec_negotiation_prefs structure. This may mean</span><br><span style="color: hsl(120, 100%, 40%);">+ * that the stream's format_caps will be empty. It's up to the caller to determine</span><br><span style="color: hsl(120, 100%, 40%);">+ * what to do with the stream in that case. I.E. Free it, set it to the</span><br><span style="color: hsl(120, 100%, 40%);">+ * REMOVED state, etc. A stream will always be returned unless there was</span><br><span style="color: hsl(120, 100%, 40%);">+ * some catastrophic allocation failure.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL if there was some allocation failure.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval A new, resolved stream.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_stream *ast_stream_create_resolved(struct ast_stream *pending_stream,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *configured_stream, struct ast_stream_codec_negotiation_prefs *prefs,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str **error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Create a stream topology</span><br><span> *</span><br><span> * \retval non-NULL success</span><br><span>@@ -387,6 +721,18 @@</span><br><span> int ast_stream_topology_get_count(const struct ast_stream_topology *topology);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get the number of active (non-REMOVED) streams in a topology</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param topology The topology of streams</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \return the number of active streams</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_stream_topology_get_active_count(const struct ast_stream_topology *topology);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Get a specific stream from the topology</span><br><span> *</span><br><span> * \param topology The topology of streams</span><br><span>@@ -471,17 +817,61 @@</span><br><span> *</span><br><span> * \param topology The topology of streams</span><br><span> *</span><br><span style="color: hsl(0, 100%, 40%);">- * \retval non-NULL success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval non-NULL success (the resulting format caps must be unreffed by the caller)</span><br><span> * \retval NULL failure</span><br><span> *</span><br><span> * \note The stream topology is NOT altered by this function.</span><br><span> *</span><br><span> * \since 15</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">-struct ast_format_cap *ast_format_cap_from_stream_topology(</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_format_cap *ast_stream_topology_get_formats(</span><br><span> struct ast_stream_topology *topology);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get a string representing the topology for debugging/display purposes</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param topology A stream topology</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param buf A pointer to an ast_str* used for the output.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval "" (empty string) if either buf or *buf are NULL</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval "(null topology)" if *topology was NULL</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval <topology_representation> otherwise</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \warning No attempt should ever be made to free the returned</span><br><span style="color: hsl(120, 100%, 40%);">+ * char * and it should be dup'd if needed after the ast_str is freed.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Return format:</span><br><span style="color: hsl(120, 100%, 40%);">+ * <final>? <stream> ...</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Sample return:</span><br><span style="color: hsl(120, 100%, 40%);">+ * "final <audio:audio:sendrecv (ulaw,g722)> <video:video:sendonly (h264)>"</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_topology_to_str(const struct ast_stream_topology *topology, struct ast_str **buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create a format capabilities structure containing all the formats from all the streams</span><br><span style="color: hsl(120, 100%, 40%);">+ * of a particular type in the topology.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * A helper function that, given a stream topology and a media type, creates a format</span><br><span style="color: hsl(120, 100%, 40%);">+ * capabilities structure containing all formats from all active streams with the particular type.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param topology The topology of streams</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param type The media type</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval non-NULL success (the resulting format caps must be unreffed by the caller)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL failure</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note The stream topology is NOT altered by this function.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_format_cap *ast_stream_topology_get_formats_by_type(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *topology, enum ast_media_type type);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Gets the first active stream of a specific type from the topology</span><br><span> *</span><br><span> * \param topology The topology of streams</span><br><span>@@ -534,4 +924,34 @@</span><br><span> */</span><br><span> void ast_stream_set_group(struct ast_stream *stream, int group);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Create a resolved stream topology from 2 topologies</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 18</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param pending_topology The "live" topology created from an SDP,</span><br><span style="color: hsl(120, 100%, 40%);">+ * passed through the core, or used to create an SDP.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param configured_topology The static topology used to validate the pending topology.</span><br><span style="color: hsl(120, 100%, 40%);">+ * It MUST have only 1 stream per media type.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param prefs A pointer to an ast_stream_codec_negotiation_prefs structure.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param error_message If supplied, error messages will be appended.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \details</span><br><span style="color: hsl(120, 100%, 40%);">+ * The streams in the resolved topology will contain all of the attributes</span><br><span style="color: hsl(120, 100%, 40%);">+ * of the corresponding stream from the pending topology. It's format_caps</span><br><span style="color: hsl(120, 100%, 40%);">+ * however will contain only the formats that passed the validation</span><br><span style="color: hsl(120, 100%, 40%);">+ * specified by the ast_stream_codec_negotiation_prefs structure. This may</span><br><span style="color: hsl(120, 100%, 40%);">+ * mean that some of the streams format_caps will be empty. If that's the case,</span><br><span style="color: hsl(120, 100%, 40%);">+ * the stream will be in a REMOVED state. With those rules in mind,</span><br><span style="color: hsl(120, 100%, 40%);">+ * a resolved topology will always be returned (unless there's some catastrophic</span><br><span style="color: hsl(120, 100%, 40%);">+ * allocation failure) and the resolved topology is guaranteed to have the same</span><br><span style="color: hsl(120, 100%, 40%);">+ * number of streams, in the same order, as the pending topology.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL if there was some allocation failure.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval The joint topology.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_stream_topology *ast_stream_topology_create_resolved(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *pending_topology, struct ast_stream_topology *validation_topology,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_codec_negotiation_prefs *prefs,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str **error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #endif /* _AST_STREAM_H */</span><br><span>diff --git a/main/channel.c b/main/channel.c</span><br><span>index 7424b81..40d7724 100644</span><br><span>--- a/main/channel.c</span><br><span>+++ b/main/channel.c</span><br><span>@@ -6227,7 +6227,7 @@</span><br><span> </span><br><span> if (!request_cap && topology) {</span><br><span> /* Turn the request stream topology into capabilities */</span><br><span style="color: hsl(0, 100%, 40%);">- request_cap = tmp_converted_cap = ast_format_cap_from_stream_topology(topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ request_cap = tmp_converted_cap = ast_stream_topology_get_formats(topology);</span><br><span> }</span><br><span> </span><br><span> /* find the best audio format to use */</span><br><span>diff --git a/main/core_unreal.c b/main/core_unreal.c</span><br><span>index bff1c3e..16414e6 100644</span><br><span>--- a/main/core_unreal.c</span><br><span>+++ b/main/core_unreal.c</span><br><span>@@ -1054,7 +1054,7 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- unreal->reqcap = ast_format_cap_from_stream_topology(topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ unreal->reqcap = ast_stream_topology_get_formats(topology);</span><br><span> if (!unreal->reqcap) {</span><br><span> ao2_ref(unreal, -1);</span><br><span> return NULL;</span><br><span>diff --git a/main/format_cap.c b/main/format_cap.c</span><br><span>index ca60557..a97fd6d 100644</span><br><span>--- a/main/format_cap.c</span><br><span>+++ b/main/format_cap.c</span><br><span>@@ -699,11 +699,19 @@</span><br><span> return internal_format_cap_identical(cap2, cap1);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-const char *ast_format_cap_get_names(const struct ast_format_cap *cap, struct ast_str **buf)</span><br><span style="color: hsl(120, 100%, 40%);">+static const char *__ast_format_cap_get_names(const struct ast_format_cap *cap, struct ast_str **buf, int append)</span><br><span> {</span><br><span> int i;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ast_str_set(buf, 0, "(");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!buf || !*buf) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (append) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(buf, 0, "(");</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(buf, 0, "(");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> </span><br><span> if (!cap || !AST_VECTOR_SIZE(&cap->preference_order)) {</span><br><span> ast_str_append(buf, 0, "nothing)");</span><br><span>@@ -725,6 +733,16 @@</span><br><span> return ast_str_buffer(*buf);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_format_cap_get_names(const struct ast_format_cap *cap, struct ast_str **buf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return __ast_format_cap_get_names(cap, buf, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_format_cap_append_names(const struct ast_format_cap *cap, struct ast_str **buf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return __ast_format_cap_get_names(cap, buf, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_format_cap_empty(const struct ast_format_cap *cap)</span><br><span> {</span><br><span> int count = ast_format_cap_count(cap);</span><br><span>diff --git a/main/stream.c b/main/stream.c</span><br><span>index c5aa5f8..a21177d 100644</span><br><span>--- a/main/stream.c</span><br><span>+++ b/main/stream.c</span><br><span>@@ -44,6 +44,40 @@</span><br><span> char name_value[0];</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_codec_negotiation_params_map[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_PARAM_UNSPECIFIED] = "unspecified",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_PARAM_PREFER] = "prefer",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_PARAM_OPERATION] = "operation",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_PARAM_KEEP] = "keep",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_PARAM_TRANSCODE] = "transcode",</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_codec_negotiation_prefer_map[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_PREFER_UNSPECIFIED] = "unspecified",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_PREFER_PENDING] = "pending",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_PREFER_CONFIGURED] = "configured",</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_codec_negotiation_operation_map[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_OPERATION_UNSPECIFIED] = "unspecified",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_OPERATION_INTERSECT] = "intersect",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_OPERATION_UNION] = "union",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_OPERATION_ONLY_PREFERRED] = "only_preferred",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_OPERATION_ONLY_NONPREFERRED] = "only_nonpreferred",</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_codec_negotiation_keep_map[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_KEEP_UNSPECIFIED] = "unspecified",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_KEEP_ALL] = "all",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_KEEP_FIRST] = "first",</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_codec_negotiation_transcode_map[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_TRANSCODE_UNSPECIFIED] = "unspecified",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_TRANSCODE_ALLOW] = "allow",</span><br><span style="color: hsl(120, 100%, 40%);">+ [CODEC_NEGOTIATION_TRANSCODE_PREVENT] = "prevent",</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct ast_stream {</span><br><span> /*!</span><br><span> * \brief The type of media the stream is handling</span><br><span>@@ -91,6 +125,107 @@</span><br><span> * \brief A vector of all the streams in this topology</span><br><span> */</span><br><span> AST_VECTOR(, struct ast_stream *) streams;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Indicates that this topology should not have further operations applied to it. */</span><br><span style="color: hsl(120, 100%, 40%);">+ int final;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_codec_prefs_to_str(const struct ast_stream_codec_negotiation_prefs *prefs, struct ast_str **buf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!prefs || !buf || !*buf) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(buf, 0, "%s:%s, %s:%s, %s:%s, %s:%s",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_codec_param_to_str(CODEC_NEGOTIATION_PARAM_PREFER),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_codec_prefer_to_str(prefs->prefer),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_codec_param_to_str(CODEC_NEGOTIATION_PARAM_OPERATION),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_codec_operation_to_str(prefs->operation),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_codec_param_to_str(CODEC_NEGOTIATION_PARAM_KEEP),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_codec_keep_to_str(prefs->keep),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_codec_param_to_str(CODEC_NEGOTIATION_PARAM_TRANSCODE),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_codec_transcode_to_str(prefs->transcode)</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_str_buffer(*buf);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Sets a codec prefs member based on a the preference name and string value</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \warning This macro will cause the calling function to return if a preference name</span><br><span style="color: hsl(120, 100%, 40%);">+ * is matched but the value isn't valid for this preference.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define set_pref_value(_name, _value, _prefs, _UC, _lc, _error_message) \</span><br><span style="color: hsl(120, 100%, 40%);">+({ \</span><br><span style="color: hsl(120, 100%, 40%);">+ int _res = 0; \</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(_name, ast_stream_codec_negotiation_params_map[CODEC_NEGOTIATION_PARAM_ ## _UC]) == 0) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ int i; \</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = CODEC_NEGOTIATION_ ## _UC ## _UNSPECIFIED + 1; i < CODEC_NEGOTIATION_ ## _UC ## _END; i++) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(value, ast_stream_codec_negotiation_ ## _lc ## _map[i]) == 0) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ prefs->_lc = i; \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+ if ( prefs->_lc == CODEC_NEGOTIATION_ ## _UC ## _UNSPECIFIED) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ _res = -1; \</span><br><span style="color: hsl(120, 100%, 40%);">+ if (_error_message) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(_error_message, 0, "Codec preference '%s' has invalid value '%s'", name, value); \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+ if (_res < 0) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ return _res; \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+})</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_stream_codec_prefs_parse(const char *pref_string, struct ast_stream_codec_negotiation_prefs *prefs,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str **error_message)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *initial_value = ast_strdupa(pref_string);</span><br><span style="color: hsl(120, 100%, 40%);">+ char *current_value;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *pref;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *saveptr1;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *saveptr2;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *value;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!prefs) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ prefs->prefer = CODEC_NEGOTIATION_PREFER_UNSPECIFIED;</span><br><span style="color: hsl(120, 100%, 40%);">+ prefs->operation = CODEC_NEGOTIATION_OPERATION_UNSPECIFIED;</span><br><span style="color: hsl(120, 100%, 40%);">+ prefs->keep = CODEC_NEGOTIATION_KEEP_UNSPECIFIED;</span><br><span style="color: hsl(120, 100%, 40%);">+ prefs->transcode = CODEC_NEGOTIATION_TRANSCODE_UNSPECIFIED;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (current_value = initial_value; (pref = strtok_r(current_value, ",", &saveptr1)) != NULL; ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ name = strtok_r(pref, ": ", &saveptr2);</span><br><span style="color: hsl(120, 100%, 40%);">+ value = strtok_r(NULL, ": ", &saveptr2);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!name || !value) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (error_message) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(error_message, 0, "Codec preference '%s' is invalid", pref);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ set_pref_value(name, value, prefs, OPERATION, operation, error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+ set_pref_value(name, value, prefs, PREFER, prefer, error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+ set_pref_value(name, value, prefs, KEEP, keep, error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+ set_pref_value(name, value, prefs, TRANSCODE, transcode, error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ current_value = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_state_map[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+ [AST_STREAM_STATE_REMOVED] = "removed",</span><br><span style="color: hsl(120, 100%, 40%);">+ [AST_STREAM_STATE_SENDRECV] = "sendrecv",</span><br><span style="color: hsl(120, 100%, 40%);">+ [AST_STREAM_STATE_SENDONLY] = "sendonly",</span><br><span style="color: hsl(120, 100%, 40%);">+ [AST_STREAM_STATE_RECVONLY] = "recvonly",</span><br><span style="color: hsl(120, 100%, 40%);">+ [AST_STREAM_STATE_INACTIVE] = "inactive",</span><br><span> };</span><br><span> </span><br><span> struct ast_stream *ast_stream_alloc(const char *name, enum ast_media_type type)</span><br><span>@@ -165,6 +300,7 @@</span><br><span> }</span><br><span> </span><br><span> ao2_cleanup(stream->formats);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ast_free(stream);</span><br><span> }</span><br><span> </span><br><span>@@ -196,6 +332,26 @@</span><br><span> return stream->formats;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_to_str(const struct ast_stream *stream, struct ast_str **buf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!buf || !*buf) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!stream) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(buf, 0, "(null stream)");</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_str_buffer(*buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(buf, 0, "%s:%s:%s ",</span><br><span style="color: hsl(120, 100%, 40%);">+ S_OR(stream->name, "noname"),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_codec_media_type2str(stream->type),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_state_map[stream->state]);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_append_names(stream->formats, buf);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_str_buffer(*buf);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_stream_get_format_count(const struct ast_stream *stream)</span><br><span> {</span><br><span> ast_assert(stream != NULL);</span><br><span>@@ -375,6 +531,108 @@</span><br><span> stream->rtp_codecs = rtp_codecs;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_stream *ast_stream_create_resolved(struct ast_stream *pending_stream,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *validation_stream, struct ast_stream_codec_negotiation_prefs *prefs,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str **error_message)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format_cap *preferred_caps = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format_cap *nonpreferred_caps = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format_cap *joint_caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *joint_stream;</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_media_type media_type = pending_stream ? pending_stream->type : AST_MEDIA_TYPE_UNKNOWN;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!pending_stream || !validation_stream || !prefs || !joint_caps</span><br><span style="color: hsl(120, 100%, 40%);">+ || media_type == AST_MEDIA_TYPE_UNKNOWN) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (error_message) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(error_message, 0, "Invalid arguments");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(joint_caps);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (prefs->prefer == CODEC_NEGOTIATION_PREFER_PENDING) {</span><br><span style="color: hsl(120, 100%, 40%);">+ preferred_caps = pending_stream->formats;</span><br><span style="color: hsl(120, 100%, 40%);">+ nonpreferred_caps = validation_stream->formats;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ preferred_caps = validation_stream->formats;</span><br><span style="color: hsl(120, 100%, 40%);">+ nonpreferred_caps = pending_stream->formats;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_set_framing(joint_caps, ast_format_cap_get_framing(pending_stream->formats));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch(prefs->operation) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case CODEC_NEGOTIATION_OPERATION_ONLY_PREFERRED:</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_format_cap_append_from_cap(joint_caps, preferred_caps, media_type);</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CODEC_NEGOTIATION_OPERATION_ONLY_NONPREFERRED:</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_format_cap_append_from_cap(joint_caps, nonpreferred_caps, media_type);</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CODEC_NEGOTIATION_OPERATION_INTERSECT:</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_format_cap_get_compatible(preferred_caps, nonpreferred_caps, joint_caps);</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CODEC_NEGOTIATION_OPERATION_UNION:</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_format_cap_append_from_cap(joint_caps, preferred_caps, media_type);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_format_cap_append_from_cap(joint_caps, nonpreferred_caps, media_type);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ default:</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (error_message) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(error_message, 0, "No common formats available for media type '%s' ",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_codec_media_type2str(pending_stream->type));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_append_names(preferred_caps, error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(error_message, 0, "<>");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_append_names(nonpreferred_caps, error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(error_message, 0, " with prefs: ");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_codec_prefs_to_str(prefs, error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(joint_caps);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_format_cap_empty(joint_caps)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (prefs->keep == CODEC_NEGOTIATION_KEEP_FIRST) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format *single = ast_format_cap_get_format(joint_caps, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_remove_by_type(joint_caps, AST_MEDIA_TYPE_UNKNOWN);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_append(joint_caps, single, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(single, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ joint_stream = ast_stream_clone(pending_stream, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!joint_stream) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(joint_caps);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* ref to joint_caps will be transferred to the stream */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_formats(joint_stream, joint_caps);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (TRACE_ATLEAST(1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *buf = ast_str_create((AST_FORMAT_CAP_NAMES_LEN * 3) + AST_STREAM_MAX_CODEC_PREFS_LENGTH);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (buf) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_set(&buf, 0, "Resolved '%s' stream ", ast_codec_media_type2str(pending_stream->type));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_append_names(preferred_caps, &buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&buf, 0, "<>");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_append_names(nonpreferred_caps, &buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&buf, 0, " to ");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_append_names(joint_caps, &buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&buf, 0, " with prefs: ");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_codec_prefs_to_str(prefs, &buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(1, "%s\n", ast_str_buffer(buf));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(joint_caps);</span><br><span style="color: hsl(120, 100%, 40%);">+ return joint_stream;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void stream_topology_destroy(void *data)</span><br><span> {</span><br><span> struct ast_stream_topology *topology = data;</span><br><span>@@ -502,6 +760,22 @@</span><br><span> return AST_VECTOR_SIZE(&topology->streams);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_stream_topology_get_active_count(const struct ast_stream_topology *topology)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+ int count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(topology != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream = AST_VECTOR_GET(&topology->streams, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (stream->state != AST_STREAM_STATE_REMOVED) {</span><br><span style="color: hsl(120, 100%, 40%);">+ count++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return count;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct ast_stream *ast_stream_topology_get_stream(</span><br><span> const struct ast_stream_topology *topology, unsigned int stream_num)</span><br><span> {</span><br><span>@@ -610,8 +884,8 @@</span><br><span> return topology;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-struct ast_format_cap *ast_format_cap_from_stream_topology(</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_stream_topology *topology)</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_format_cap *ast_stream_topology_get_formats_by_type(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *topology, enum ast_media_type type)</span><br><span> {</span><br><span> struct ast_format_cap *caps;</span><br><span> int i;</span><br><span>@@ -627,17 +901,51 @@</span><br><span> struct ast_stream *stream;</span><br><span> </span><br><span> stream = AST_VECTOR_GET(&topology->streams, i);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!stream->formats</span><br><span style="color: hsl(0, 100%, 40%);">- || stream->state == AST_STREAM_STATE_REMOVED) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!stream->formats || stream->state == AST_STREAM_STATE_REMOVED) {</span><br><span> continue;</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- ast_format_cap_append_from_cap(caps, stream->formats, AST_MEDIA_TYPE_UNKNOWN);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (type == AST_MEDIA_TYPE_UNKNOWN || type == stream->type) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_append_from_cap(caps, stream->formats, AST_MEDIA_TYPE_UNKNOWN);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> }</span><br><span> </span><br><span> return caps;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_format_cap *ast_stream_topology_get_formats(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *topology)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_stream_topology_get_formats_by_type(topology, AST_MEDIA_TYPE_UNKNOWN);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *ast_stream_topology_to_str(const struct ast_stream_topology *topology,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str **buf)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!buf ||!*buf) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!topology) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(buf, 0, "(null topology)");</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_str_buffer(*buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(buf, 0, "%s", S_COR(topology->final, "final", ""));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ stream = AST_VECTOR_GET(&topology->streams, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(buf, 0, " <");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_to_str(stream, buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(buf, 0, ">");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_str_buffer(*buf);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> struct ast_stream *ast_stream_topology_get_first_stream_by_type(</span><br><span> const struct ast_stream_topology *topology,</span><br><span> enum ast_media_type type)</span><br><span>@@ -704,6 +1012,50 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_stream_topology *ast_stream_topology_create_resolved(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *pending_topology, struct ast_stream_topology *configured_topology,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_codec_negotiation_prefs *prefs, struct ast_str**error_message)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *joint_topology = ast_stream_topology_alloc();</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!pending_topology || !configured_topology || !joint_topology) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(joint_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&pending_topology->streams); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *pending_stream = AST_VECTOR_GET(&pending_topology->streams, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *configured_stream =</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_get_first_stream_by_type(configured_topology, pending_stream->type);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *joint_stream;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!configured_stream) {</span><br><span style="color: hsl(120, 100%, 40%);">+ joint_stream = ast_stream_clone(pending_stream, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!joint_stream) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(joint_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(joint_stream, AST_STREAM_STATE_REMOVED);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ joint_stream = ast_stream_create_resolved(pending_stream, configured_stream, prefs, error_message);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_stream_get_format_count(joint_stream) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(joint_stream, AST_STREAM_STATE_REMOVED);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_stream_topology_append_stream(joint_topology, joint_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_free(joint_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(joint_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return joint_topology;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_stream_get_group(const struct ast_stream *stream)</span><br><span> {</span><br><span> ast_assert(stream != NULL);</span><br><span>diff --git a/tests/test_stream.c b/tests/test_stream.c</span><br><span>index 50f0ccb..1333e2e 100644</span><br><span>--- a/tests/test_stream.c</span><br><span>+++ b/tests/test_stream.c</span><br><span>@@ -2142,7 +2142,7 @@</span><br><span> return AST_TEST_FAIL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- stream_caps = ast_format_cap_from_stream_topology(topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ stream_caps = ast_stream_topology_get_formats(topology);</span><br><span> if (!stream_caps) {</span><br><span> ast_test_status_update(test, "Failed to create a format capabilities from a stream topology\n");</span><br><span> ast_stream_topology_free(topology);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/14577">change 14577</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/14577"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I2df77dedd0c72c52deb6e329effe057a8e06cd56 </div>
<div style="display:none"> Gerrit-Change-Number: 14577 </div>
<div style="display:none"> Gerrit-PatchSet: 5 </div>
<div style="display:none"> Gerrit-Owner: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>