<p>George Joseph <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/13850">View Change</a></p><div style="white-space:pre-wrap">Approvals:
George Joseph: Looks good to me, approved; Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">bridging: Add better support for adding/removing streams.<br><br>This change adds support to bridge_softmix to allow the addition<br>and removal of additional video source streams. When such a change<br>occurs each participant is renegotiated as needed to reflect the<br>update. If another video source is added then each participant<br>gets another source. If a video source is removed then it is<br>removed from each participant. This functionality allows you to<br>have both your webcam and screenshare providing video if you<br>desire, or even more streams. Mapping has been changed to use<br>the topology index on the source channel as a unique identifier<br>for outgoing participant streams, this will never change and<br>provides an easy way to establish the mapping.<br><br>The bridge_simple and bridge_native_rtp modules have also been<br>updated to renegotiate when the stream topology of a party changes<br>allowing the same behavior to occur as added to bridge_softmix.<br>If a screen share is added then the opposite party is renegotiated.<br>If that screen share is removed then the opposite party is<br>renegotiated again.<br><br>Some additional fixes are also included in here. Stream state is<br>now conveyed in SDP so sendonly/recvonly/inactive streams can<br>be requested. Removed streams now also remove previous state<br>from themselves so consumers don't get confused.<br><br>ASTERISK-28733<br><br>Change-Id: I93f41fb41b85646bef71408111c17ccea30cb0c5<br>---<br>M bridges/bridge_native_rtp.c<br>M bridges/bridge_simple.c<br>M bridges/bridge_softmix.c<br>M include/asterisk/channel.h<br>M main/channel.c<br>M main/stream.c<br>M res/res_pjsip_sdp_rtp.c<br>M res/res_pjsip_session.c<br>8 files changed, 571 insertions(+), 150 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/bridges/bridge_native_rtp.c b/bridges/bridge_native_rtp.c</span><br><span>index 7fd4ae1..a6addf2 100644</span><br><span>--- a/bridges/bridge_native_rtp.c</span><br><span>+++ b/bridges/bridge_native_rtp.c</span><br><span>@@ -43,6 +43,7 @@</span><br><span> #include "asterisk/bridge_technology.h"</span><br><span> #include "asterisk/frame.h"</span><br><span> #include "asterisk/rtp_engine.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/stream.h"</span><br><span> </span><br><span> /*! \brief Internal structure which contains bridged RTP channel hook data */</span><br><span> struct native_rtp_framehook_data {</span><br><span>@@ -85,6 +86,28 @@</span><br><span> struct rtp_glue_data glue;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Forward declarations */</span><br><span style="color: hsl(120, 100%, 40%);">+static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);</span><br><span style="color: hsl(120, 100%, 40%);">+static void native_rtp_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);</span><br><span style="color: hsl(120, 100%, 40%);">+static void native_rtp_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);</span><br><span style="color: hsl(120, 100%, 40%);">+static void native_rtp_bridge_suspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);</span><br><span style="color: hsl(120, 100%, 40%);">+static int native_rtp_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame);</span><br><span style="color: hsl(120, 100%, 40%);">+static int native_rtp_bridge_compatible(struct ast_bridge *bridge);</span><br><span style="color: hsl(120, 100%, 40%);">+static void native_rtp_stream_topology_changed(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_bridge_technology native_rtp_bridge = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "native_rtp",</span><br><span style="color: hsl(120, 100%, 40%);">+ .capabilities = AST_BRIDGE_CAPABILITY_NATIVE,</span><br><span style="color: hsl(120, 100%, 40%);">+ .preference = AST_BRIDGE_PREFERENCE_BASE_NATIVE,</span><br><span style="color: hsl(120, 100%, 40%);">+ .join = native_rtp_bridge_join,</span><br><span style="color: hsl(120, 100%, 40%);">+ .unsuspend = native_rtp_bridge_unsuspend,</span><br><span style="color: hsl(120, 100%, 40%);">+ .leave = native_rtp_bridge_leave,</span><br><span style="color: hsl(120, 100%, 40%);">+ .suspend = native_rtp_bridge_suspend,</span><br><span style="color: hsl(120, 100%, 40%);">+ .write = native_rtp_bridge_write,</span><br><span style="color: hsl(120, 100%, 40%);">+ .compatible = native_rtp_bridge_compatible,</span><br><span style="color: hsl(120, 100%, 40%);">+ .stream_topology_changed = native_rtp_stream_topology_changed,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void rtp_glue_data_init(struct rtp_glue_data *glue)</span><br><span> {</span><br><span> glue->cb = NULL;</span><br><span>@@ -831,12 +854,124 @@</span><br><span> data->hook_data = NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_stream_topology *native_rtp_request_stream_topology_update(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *existing_topology,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *requested_topology)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format_cap *audio_formats = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *new_topology;</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%);">+ new_topology = ast_stream_topology_clone(requested_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!new_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%);">+ /* We find an existing stream with negotiated audio formats that we can place into</span><br><span style="color: hsl(120, 100%, 40%);">+ * any audio streams in the new topology to ensure that negotiation succeeds. Some</span><br><span style="color: hsl(120, 100%, 40%);">+ * endpoints incorrectly terminate the call if SDP negotiation fails.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < ast_stream_topology_get_count(existing_topology); ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stream = ast_stream_topology_get_stream(existing_topology, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_stream_get_type(stream) != AST_MEDIA_TYPE_AUDIO ||</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_get_state(stream) == AST_STREAM_STATE_REMOVED) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</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%);">+ audio_formats = ast_stream_get_formats(stream);</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 (audio_formats) {</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < ast_stream_topology_get_count(new_topology); ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stream = ast_stream_topology_get_stream(new_topology, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_stream_get_type(stream) != AST_MEDIA_TYPE_AUDIO ||</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_get_state(stream) == AST_STREAM_STATE_REMOVED) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</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_format_cap_append_from_cap(ast_stream_get_formats(stream), audio_formats,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_MEDIA_TYPE_AUDIO);</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%);">+ for (i = 0; i < ast_stream_topology_get_count(new_topology); ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stream = ast_stream_topology_get_stream(new_topology, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* For both recvonly and sendonly the stream state reflects our state, that is we</span><br><span style="color: hsl(120, 100%, 40%);">+ * are receiving only and we are sending only. Since we are renegotiating a remote</span><br><span style="color: hsl(120, 100%, 40%);">+ * party we need to swap this to reflect what we will be doing. That is, if we are</span><br><span style="color: hsl(120, 100%, 40%);">+ * receiving from Alice then we want to be sending to Bob, so swap recvonly to</span><br><span style="color: hsl(120, 100%, 40%);">+ * sendonly.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_stream_get_state(stream) == AST_STREAM_STATE_RECVONLY) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(stream, AST_STREAM_STATE_SENDONLY);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (ast_stream_get_state(stream) == AST_STREAM_STATE_SENDONLY) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(stream, AST_STREAM_STATE_RECVONLY);</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 new_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%);">+static void native_rtp_stream_topology_changed(struct ast_bridge *bridge,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_bridge_channel *bridge_channel)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *c0 = bridge_channel->chan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *c1 = AST_LIST_FIRST(&bridge->channels)->chan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *req_top;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *existing_top;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *new_top;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_bridge_channel_stream_map(bridge_channel);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_channel_get_stream_topology_change_source(bridge_channel->chan)</span><br><span style="color: hsl(120, 100%, 40%);">+ == &native_rtp_bridge) {</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 (c0 == c1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ c1 = AST_LIST_LAST(&bridge->channels)->chan;</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 (c0 == c1) {</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 a party renegotiates we want to renegotiate their counterpart to a matching</span><br><span style="color: hsl(120, 100%, 40%);">+ * topology.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_lock_both(c0, c1);</span><br><span style="color: hsl(120, 100%, 40%);">+ req_top = ast_channel_get_stream_topology(c0);</span><br><span style="color: hsl(120, 100%, 40%);">+ existing_top = ast_channel_get_stream_topology(c1);</span><br><span style="color: hsl(120, 100%, 40%);">+ new_top = native_rtp_request_stream_topology_update(existing_top, req_top);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(c0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(c1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!new_top) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Failure. We'll just have to live with the current topology. */</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_channel_request_stream_topology_change(c1, new_top, &native_rtp_bridge);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_free(new_top);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*!</span><br><span> * \internal</span><br><span> * \brief Called by the bridge core 'join' callback for each channel joining he bridge</span><br><span> */</span><br><span> static int native_rtp_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *req_top;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *existing_top;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *new_top;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *c0 = AST_LIST_FIRST(&bridge->channels)->chan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *c1 = AST_LIST_LAST(&bridge->channels)->chan;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> ast_debug(2, "Bridge '%s'. Channel '%s' is joining bridge tech\n",</span><br><span> bridge->uniqueid, ast_channel_name(bridge_channel->chan));</span><br><span> </span><br><span>@@ -858,6 +993,27 @@</span><br><span> return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (c0 != c1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* When both channels are joined we want to try to improve the experience by</span><br><span style="color: hsl(120, 100%, 40%);">+ * raising the number of streams so they match.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_lock_both(c0, c1);</span><br><span style="color: hsl(120, 100%, 40%);">+ req_top = ast_channel_get_stream_topology(c0);</span><br><span style="color: hsl(120, 100%, 40%);">+ existing_top = ast_channel_get_stream_topology(c1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_stream_topology_get_count(req_top) < ast_stream_topology_get_count(existing_top)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SWAP(req_top, existing_top);</span><br><span style="color: hsl(120, 100%, 40%);">+ SWAP(c0, c1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ new_top = native_rtp_request_stream_topology_update(existing_top, req_top);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(c0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(c1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (new_top) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_request_stream_topology_change(c1, new_top, &native_rtp_bridge);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_free(new_top);</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> native_rtp_bridge_start(bridge, NULL);</span><br><span> return 0;</span><br><span> }</span><br><span>@@ -939,18 +1095,6 @@</span><br><span> return defer;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static struct ast_bridge_technology native_rtp_bridge = {</span><br><span style="color: hsl(0, 100%, 40%);">- .name = "native_rtp",</span><br><span style="color: hsl(0, 100%, 40%);">- .capabilities = AST_BRIDGE_CAPABILITY_NATIVE,</span><br><span style="color: hsl(0, 100%, 40%);">- .preference = AST_BRIDGE_PREFERENCE_BASE_NATIVE,</span><br><span style="color: hsl(0, 100%, 40%);">- .join = native_rtp_bridge_join,</span><br><span style="color: hsl(0, 100%, 40%);">- .unsuspend = native_rtp_bridge_unsuspend,</span><br><span style="color: hsl(0, 100%, 40%);">- .leave = native_rtp_bridge_leave,</span><br><span style="color: hsl(0, 100%, 40%);">- .suspend = native_rtp_bridge_suspend,</span><br><span style="color: hsl(0, 100%, 40%);">- .write = native_rtp_bridge_write,</span><br><span style="color: hsl(0, 100%, 40%);">- .compatible = native_rtp_bridge_compatible,</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> static int unload_module(void)</span><br><span> {</span><br><span> ast_bridge_technology_unregister(&native_rtp_bridge);</span><br><span>diff --git a/bridges/bridge_simple.c b/bridges/bridge_simple.c</span><br><span>index 40f7ddc..545b3ad 100644</span><br><span>--- a/bridges/bridge_simple.c</span><br><span>+++ b/bridges/bridge_simple.c</span><br><span>@@ -46,63 +46,8 @@</span><br><span> </span><br><span> static void simple_bridge_stream_topology_changed(struct ast_bridge *bridge,</span><br><span> struct ast_bridge_channel *bridge_channel);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int simple_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_channel *c0 = AST_LIST_FIRST(&bridge->channels)->chan;</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_channel *c1 = AST_LIST_LAST(&bridge->channels)->chan;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- /*</span><br><span style="color: hsl(0, 100%, 40%);">- * If this is the first channel we can't make it compatible...</span><br><span style="color: hsl(0, 100%, 40%);">- * unless we make it compatible with itself. O.o</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">- if (c0 == c1) {</span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_channel_make_compatible(c0, c1)) {</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- /* Align stream topologies */</span><br><span style="color: hsl(0, 100%, 40%);">- simple_bridge_stream_topology_changed(bridge, NULL);</span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static int simple_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- const struct ast_control_t38_parameters *t38_parameters;</span><br><span style="color: hsl(0, 100%, 40%);">- int defer = 0;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (!ast_bridge_queue_everyone_else(bridge, bridge_channel, frame)) {</span><br><span style="color: hsl(0, 100%, 40%);">- /* This frame was successfully queued so no need to defer */</span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- /* Depending on the frame defer it so when the next channel joins it receives it */</span><br><span style="color: hsl(0, 100%, 40%);">- switch (frame->frametype) {</span><br><span style="color: hsl(0, 100%, 40%);">- case AST_FRAME_CONTROL:</span><br><span style="color: hsl(0, 100%, 40%);">- switch (frame->subclass.integer) {</span><br><span style="color: hsl(0, 100%, 40%);">- case AST_CONTROL_T38_PARAMETERS:</span><br><span style="color: hsl(0, 100%, 40%);">- t38_parameters = frame->data.ptr;</span><br><span style="color: hsl(0, 100%, 40%);">- switch (t38_parameters->request_response) {</span><br><span style="color: hsl(0, 100%, 40%);">- case AST_T38_REQUEST_NEGOTIATE:</span><br><span style="color: hsl(0, 100%, 40%);">- defer = -1;</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- default:</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- default:</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- default:</span><br><span style="color: hsl(0, 100%, 40%);">- break;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- return defer;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(120, 100%, 40%);">+static int simple_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);</span><br><span style="color: hsl(120, 100%, 40%);">+static int simple_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame);</span><br><span> </span><br><span> static struct ast_bridge_technology simple_bridge = {</span><br><span> .name = "simple_bridge",</span><br><span>@@ -157,52 +102,145 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < ast_stream_topology_get_count(new_topology); ++i) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stream = ast_stream_topology_get_stream(new_topology, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* For both recvonly and sendonly the stream state reflects our state, that is we</span><br><span style="color: hsl(120, 100%, 40%);">+ * are receiving only and we are sending only. Since we are renegotiating a remote</span><br><span style="color: hsl(120, 100%, 40%);">+ * party we need to swap this to reflect what we will be doing. That is, if we are</span><br><span style="color: hsl(120, 100%, 40%);">+ * receiving from Alice then we want to be sending to Bob, so swap recvonly to</span><br><span style="color: hsl(120, 100%, 40%);">+ * sendonly.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_stream_get_state(stream) == AST_STREAM_STATE_RECVONLY) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(stream, AST_STREAM_STATE_SENDONLY);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (ast_stream_get_state(stream) == AST_STREAM_STATE_SENDONLY) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(stream, AST_STREAM_STATE_RECVONLY);</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> return new_topology;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int simple_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *req_top;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *existing_top;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *new_top;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *c0 = AST_LIST_FIRST(&bridge->channels)->chan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *c1 = AST_LIST_LAST(&bridge->channels)->chan;</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 this is the first channel we can't make it compatible...</span><br><span style="color: hsl(120, 100%, 40%);">+ * unless we make it compatible with itself. O.o</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (c0 == c1) {</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%);">+ if (ast_channel_make_compatible(c0, c1)) {</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%);">+ /* When both channels are joined we want to try to improve the experience by</span><br><span style="color: hsl(120, 100%, 40%);">+ * raising the number of streams so they match.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_lock_both(c0, c1);</span><br><span style="color: hsl(120, 100%, 40%);">+ req_top = ast_channel_get_stream_topology(c0);</span><br><span style="color: hsl(120, 100%, 40%);">+ existing_top = ast_channel_get_stream_topology(c1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_stream_topology_get_count(req_top) < ast_stream_topology_get_count(existing_top)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SWAP(req_top, existing_top);</span><br><span style="color: hsl(120, 100%, 40%);">+ SWAP(c0, c1);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ new_top = simple_bridge_request_stream_topology_update(existing_top, req_top);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(c0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(c1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!new_top) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Failure. We'll just have to live with the current topology. */</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%);">+ ast_channel_request_stream_topology_change(c1, new_top, &simple_bridge);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_free(new_top);</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%);">+static int simple_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct ast_control_t38_parameters *t38_parameters;</span><br><span style="color: hsl(120, 100%, 40%);">+ int defer = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_bridge_queue_everyone_else(bridge, bridge_channel, frame)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* This frame was successfully queued so no need to defer */</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%);">+ /* Depending on the frame defer it so when the next channel joins it receives it */</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (frame->frametype) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case AST_FRAME_CONTROL:</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (frame->subclass.integer) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case AST_CONTROL_T38_PARAMETERS:</span><br><span style="color: hsl(120, 100%, 40%);">+ t38_parameters = frame->data.ptr;</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (t38_parameters->request_response) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case AST_T38_REQUEST_NEGOTIATE:</span><br><span style="color: hsl(120, 100%, 40%);">+ defer = -1;</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%);">+ 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%);">+ 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%);">+ return defer;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void simple_bridge_stream_topology_changed(struct ast_bridge *bridge,</span><br><span> struct ast_bridge_channel *bridge_channel)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_channel *req_chan;</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_channel *existing_chan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *c0 = bridge_channel->chan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *c1 = AST_LIST_FIRST(&bridge->channels)->chan;</span><br><span> struct ast_stream_topology *req_top;</span><br><span> struct ast_stream_topology *existing_top;</span><br><span> struct ast_stream_topology *new_top;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (bridge_channel) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_bridge_channel_stream_map(bridge_channel);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_bridge_channel_stream_map(bridge_channel);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_channel_get_stream_topology_change_source(bridge_channel->chan)</span><br><span style="color: hsl(0, 100%, 40%);">- == &simple_bridge) {</span><br><span style="color: hsl(0, 100%, 40%);">- return;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- req_chan = AST_LIST_FIRST(&bridge->channels)->chan;</span><br><span style="color: hsl(0, 100%, 40%);">- existing_chan = AST_LIST_LAST(&bridge->channels)->chan;</span><br><span style="color: hsl(0, 100%, 40%);">- if (req_chan == existing_chan) {</span><br><span style="color: hsl(0, 100%, 40%);">- /* Wait until both channels are in the bridge to align topologies. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_channel_get_stream_topology_change_source(bridge_channel->chan)</span><br><span style="color: hsl(120, 100%, 40%);">+ == &simple_bridge) {</span><br><span> return;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /* Align topologies according to size or first channel to join */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_channel_lock_both(req_chan, existing_chan);</span><br><span style="color: hsl(0, 100%, 40%);">- req_top = ast_channel_get_stream_topology(req_chan);</span><br><span style="color: hsl(0, 100%, 40%);">- existing_top = ast_channel_get_stream_topology(existing_chan);</span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_stream_topology_get_count(req_top) < ast_stream_topology_get_count(existing_top)) {</span><br><span style="color: hsl(0, 100%, 40%);">- SWAP(req_top, existing_top);</span><br><span style="color: hsl(0, 100%, 40%);">- SWAP(req_chan, existing_chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (c0 == c1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ c1 = AST_LIST_LAST(&bridge->channels)->chan;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (c0 == c1) {</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 a party renegotiates we want to renegotiate their counterpart to a matching</span><br><span style="color: hsl(120, 100%, 40%);">+ * topology.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_lock_both(c0, c1);</span><br><span style="color: hsl(120, 100%, 40%);">+ req_top = ast_channel_get_stream_topology(c0);</span><br><span style="color: hsl(120, 100%, 40%);">+ existing_top = ast_channel_get_stream_topology(c1);</span><br><span> new_top = simple_bridge_request_stream_topology_update(existing_top, req_top);</span><br><span style="color: hsl(0, 100%, 40%);">- ast_channel_unlock(req_chan);</span><br><span style="color: hsl(0, 100%, 40%);">- ast_channel_unlock(existing_chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(c0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(c1);</span><br><span> </span><br><span> if (!new_top) {</span><br><span> /* Failure. We'll just have to live with the current topology. */</span><br><span> return;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ast_channel_request_stream_topology_change(existing_chan, new_top, &simple_bridge);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_request_stream_topology_change(c1, new_top, &simple_bridge);</span><br><span> ast_stream_topology_free(new_top);</span><br><span> }</span><br><span> </span><br><span>diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c</span><br><span>index c24fa7a..e1c6734 100644</span><br><span>--- a/bridges/bridge_softmix.c</span><br><span>+++ b/bridges/bridge_softmix.c</span><br><span>@@ -462,12 +462,12 @@</span><br><span> *</span><br><span> * \param stream The stream to test</span><br><span> * \param source_channel_name The name of a source video channel to match</span><br><span style="color: hsl(0, 100%, 40%);">- * \param source_stream_name The name of the source video stream to match</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param source_channel_stream_position The position of the video on the source channel</span><br><span> * \retval 1 The stream is a video destination stream</span><br><span> * \retval 0 The stream is not a video destination stream</span><br><span> */</span><br><span> static int is_video_dest(const struct ast_stream *stream, const char *source_channel_name,</span><br><span style="color: hsl(0, 100%, 40%);">- const char *source_stream_name)</span><br><span style="color: hsl(120, 100%, 40%);">+ int source_channel_stream_position)</span><br><span> {</span><br><span> char *dest_video_name;</span><br><span> size_t dest_video_name_len;</span><br><span>@@ -480,17 +480,17 @@</span><br><span> dest_video_name_len = SOFTBRIDGE_VIDEO_DEST_LEN + 1;</span><br><span> if (!ast_strlen_zero(source_channel_name)) {</span><br><span> dest_video_name_len += strlen(source_channel_name) + 1;</span><br><span style="color: hsl(0, 100%, 40%);">- if (!ast_strlen_zero(source_stream_name)) {</span><br><span style="color: hsl(0, 100%, 40%);">- dest_video_name_len += strlen(source_stream_name) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (source_channel_stream_position != -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ dest_video_name_len += 11;</span><br><span> }</span><br><span> </span><br><span> dest_video_name = ast_alloca(dest_video_name_len);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!ast_strlen_zero(source_stream_name)) {</span><br><span style="color: hsl(0, 100%, 40%);">- /* We are looking for an exact stream name */</span><br><span style="color: hsl(0, 100%, 40%);">- snprintf(dest_video_name, dest_video_name_len, "%s%c%s%c%s",</span><br><span style="color: hsl(120, 100%, 40%);">+ if (source_channel_stream_position != -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* We are looking for an exact stream position */</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(dest_video_name, dest_video_name_len, "%s%c%s%c%d",</span><br><span> SOFTBRIDGE_VIDEO_DEST_PREFIX, SOFTBRIDGE_VIDEO_DEST_SEPARATOR,</span><br><span> source_channel_name, SOFTBRIDGE_VIDEO_DEST_SEPARATOR,</span><br><span style="color: hsl(0, 100%, 40%);">- source_stream_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ source_channel_stream_position);</span><br><span> return !strcmp(ast_stream_get_name(stream), dest_video_name);</span><br><span> }</span><br><span> snprintf(dest_video_name, dest_video_name_len, "%s%c%s",</span><br><span>@@ -503,46 +503,62 @@</span><br><span> return !strncmp(ast_stream_get_name(stream), dest_video_name, dest_video_name_len - 1);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int append_source_stream(struct ast_stream_topology *dest,</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *channel_name, const char *sdp_label,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream, int index)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *stream_clone_name = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream_clone;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* We use the stream topology index for the stream to uniquely identify and recognize it.</span><br><span style="color: hsl(120, 100%, 40%);">+ * This is guaranteed to remain the same across renegotiation of the source channel and</span><br><span style="color: hsl(120, 100%, 40%);">+ * ensures that the stream name is unique.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_asprintf(&stream_clone_name, "%s%c%s%c%d", SOFTBRIDGE_VIDEO_DEST_PREFIX,</span><br><span style="color: hsl(120, 100%, 40%);">+ SOFTBRIDGE_VIDEO_DEST_SEPARATOR, channel_name, SOFTBRIDGE_VIDEO_DEST_SEPARATOR,</span><br><span style="color: hsl(120, 100%, 40%);">+ index) < 0) {</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%);">+ stream_clone = ast_stream_clone(stream, stream_clone_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(stream_clone_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!stream_clone) {</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%);">+ /* Sends an "a:label" attribute in the SDP for participant event correlation */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(sdp_label)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_metadata(stream_clone, "SDP:LABEL", sdp_label);</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%);">+ /* We will be sending them a stream and not expecting anything in return */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(stream_clone, AST_STREAM_STATE_SENDONLY);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_stream_topology_append_stream(dest, stream_clone) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_free(stream_clone);</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%);">+ 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%);">+</span><br><span> static int append_source_streams(struct ast_stream_topology *dest,</span><br><span> const char *channel_name, const char *sdp_label,</span><br><span> const struct ast_stream_topology *source)</span><br><span> {</span><br><span> int i;</span><br><span style="color: hsl(0, 100%, 40%);">- const char *stream_identify;</span><br><span> </span><br><span> for (i = 0; i < ast_stream_topology_get_count(source); ++i) {</span><br><span> struct ast_stream *stream;</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_stream *stream_clone;</span><br><span style="color: hsl(0, 100%, 40%);">- char *stream_clone_name = NULL;</span><br><span> </span><br><span> stream = ast_stream_topology_get_stream(source, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (!is_video_source(stream)) {</span><br><span> continue;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- stream_identify = ast_stream_get_metadata(stream, "MSID:LABEL");</span><br><span style="color: hsl(0, 100%, 40%);">- if (!stream_identify) {</span><br><span style="color: hsl(0, 100%, 40%);">- stream_identify = ast_stream_get_name(stream);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_asprintf(&stream_clone_name, "%s_%s_%s", SOFTBRIDGE_VIDEO_DEST_PREFIX,</span><br><span style="color: hsl(0, 100%, 40%);">- channel_name, stream_identify) < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- stream_clone = ast_stream_clone(stream, stream_clone_name);</span><br><span style="color: hsl(0, 100%, 40%);">- ast_free(stream_clone_name);</span><br><span style="color: hsl(0, 100%, 40%);">- if (!stream_clone) {</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- /* Sends an "a:label" attribute in the SDP for participant event correlation */</span><br><span style="color: hsl(0, 100%, 40%);">- if (!ast_strlen_zero(sdp_label)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_stream_set_metadata(stream_clone, "SDP:LABEL", sdp_label);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_stream_topology_append_stream(dest, stream_clone) < 0) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_stream_free(stream_clone);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (append_source_stream(dest, channel_name, sdp_label, stream, i)) {</span><br><span> return -1;</span><br><span> }</span><br><span> }</span><br><span>@@ -752,7 +768,7 @@</span><br><span> </span><br><span> stream = ast_stream_topology_get_stream(topology, i);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (is_video_dest(stream, channel_name, NULL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (is_video_dest(stream, channel_name, -1)) {</span><br><span> ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED);</span><br><span> stream_removed = 1;</span><br><span> }</span><br><span>@@ -2135,13 +2151,13 @@</span><br><span> /*!</span><br><span> * \brief Map a source stream to all of its destination streams.</span><br><span> *</span><br><span style="color: hsl(0, 100%, 40%);">- * \param source_stream_name Name of the source stream</span><br><span> * \param source_channel_name Name of channel where the source stream originates</span><br><span> * \param bridge_stream_position The slot in the bridge where source video will come from</span><br><span> * \param participants The bridge_channels in the bridge</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param source_channel_stream_position The position of the stream on the source channel</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">-static void map_source_to_destinations(const char *source_stream_name, const char *source_channel_name,</span><br><span style="color: hsl(0, 100%, 40%);">- size_t bridge_stream_position, struct ast_bridge_channels_list *participants)</span><br><span style="color: hsl(120, 100%, 40%);">+static void map_source_to_destinations(const char *source_channel_name,</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t bridge_stream_position, struct ast_bridge_channels_list *participants, int source_channel_stream_position)</span><br><span> {</span><br><span> struct ast_bridge_channel *participant;</span><br><span> </span><br><span>@@ -2161,7 +2177,7 @@</span><br><span> struct ast_stream *stream;</span><br><span> </span><br><span> stream = ast_stream_topology_get_stream(topology, i);</span><br><span style="color: hsl(0, 100%, 40%);">- if (is_video_dest(stream, source_channel_name, source_stream_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (is_video_dest(stream, source_channel_name, source_channel_stream_position)) {</span><br><span> struct softmix_channel *sc = participant->tech_pvt;</span><br><span> </span><br><span> AST_VECTOR_REPLACE(&participant->stream_map.to_channel, bridge_stream_position, i);</span><br><span>@@ -2228,6 +2244,137 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void softmix_bridge_stream_sources_update(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct softmix_channel *sc)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int index;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *old_topology = sc->topology;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *new_topology = ast_channel_get_stream_topology(bridge_channel->chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ int removed_streams[MAX(ast_stream_topology_get_count(sc->topology), ast_stream_topology_get_count(new_topology))];</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t removed_streams_count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *added_streams;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_bridge_channels_list *participants = &bridge->channels;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_bridge_channel *participant;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ added_streams = ast_stream_topology_alloc();</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!added_streams) {</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%);">+ /* We go through the old topology comparing it to the new topology to determine what streams</span><br><span style="color: hsl(120, 100%, 40%);">+ * changed state. A state transition can result in the stream being considered a new source</span><br><span style="color: hsl(120, 100%, 40%);">+ * (for example it was removed and is now present) or being removed (a stream became inactive).</span><br><span style="color: hsl(120, 100%, 40%);">+ * Added streams are copied into a topology and added to each other participant while for</span><br><span style="color: hsl(120, 100%, 40%);">+ * removed streams we merely store their position and mark them as removed later.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (index = 0; index < ast_stream_topology_get_count(sc->topology) && index < ast_stream_topology_get_count(new_topology); ++index) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *old_stream = ast_stream_topology_get_stream(sc->topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *new_stream = ast_stream_topology_get_stream(new_topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Ignore all streams that don't carry video and streams that are strictly outgoing destination streams */</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((ast_stream_get_type(old_stream) != AST_MEDIA_TYPE_VIDEO && ast_stream_get_type(new_stream) != AST_MEDIA_TYPE_VIDEO) ||</span><br><span style="color: hsl(120, 100%, 40%);">+ !strncmp(ast_stream_get_name(old_stream), SOFTBRIDGE_VIDEO_DEST_PREFIX,</span><br><span style="color: hsl(120, 100%, 40%);">+ SOFTBRIDGE_VIDEO_DEST_LEN)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</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_stream_get_type(old_stream) == AST_MEDIA_TYPE_VIDEO && ast_stream_get_type(new_stream) != AST_MEDIA_TYPE_VIDEO) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If a stream renegotiates from video to non-video then we need to remove it as a source */</span><br><span style="color: hsl(120, 100%, 40%);">+ removed_streams[removed_streams_count++] = index;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (ast_stream_get_type(old_stream) != AST_MEDIA_TYPE_VIDEO && ast_stream_get_type(new_stream) == AST_MEDIA_TYPE_VIDEO) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_stream_get_state(new_stream) != AST_STREAM_STATE_REMOVED) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If a stream renegotiates from non-video to video in a non-removed state we need to add it as a source */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (append_source_stream(added_streams, ast_channel_name(bridge_channel->chan),</span><br><span style="color: hsl(120, 100%, 40%);">+ bridge->softmix.send_sdp_label ? ast_channel_uniqueid(bridge_channel->chan) : NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ new_stream, index)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</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%);">+ } else if (ast_stream_get_state(old_stream) != AST_STREAM_STATE_REMOVED &&</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_get_state(new_stream) != AST_STREAM_STATE_SENDRECV && ast_stream_get_state(new_stream) != AST_STREAM_STATE_RECVONLY) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If a stream renegotiates and is removed then we remove it */</span><br><span style="color: hsl(120, 100%, 40%);">+ removed_streams[removed_streams_count++] = index;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (ast_stream_get_state(old_stream) == AST_STREAM_STATE_REMOVED &&</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_get_state(new_stream) != AST_STREAM_STATE_INACTIVE && ast_stream_get_state(new_stream) != AST_STREAM_STATE_SENDONLY &&</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_get_state(new_stream) != AST_STREAM_STATE_REMOVED) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If a stream renegotiates and is added then we add it */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (append_source_stream(added_streams, ast_channel_name(bridge_channel->chan),</span><br><span style="color: hsl(120, 100%, 40%);">+ bridge->softmix.send_sdp_label ? ast_channel_uniqueid(bridge_channel->chan) : NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ new_stream, index)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Any newly added streams that did not take the position of a removed stream</span><br><span style="color: hsl(120, 100%, 40%);">+ * will be present at the end of the new topology. Since streams are never</span><br><span style="color: hsl(120, 100%, 40%);">+ * removed from the topology but merely marked as removed we can pick up where we</span><br><span style="color: hsl(120, 100%, 40%);">+ * left off when comparing the old and new topologies.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (; index < ast_stream_topology_get_count(new_topology); ++index) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream = ast_stream_topology_get_stream(new_topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!is_video_source(stream)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</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_source_stream(added_streams, ast_channel_name(bridge_channel->chan),</span><br><span style="color: hsl(120, 100%, 40%);">+ bridge->softmix.send_sdp_label ? ast_channel_uniqueid(bridge_channel->chan) : NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ stream, index)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</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%);">+ /* We always update the stored topology if we can to reflect what is currently negotiated */</span><br><span style="color: hsl(120, 100%, 40%);">+ sc->topology = ast_stream_topology_clone(new_topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!sc->topology) {</span><br><span style="color: hsl(120, 100%, 40%);">+ sc->topology = old_topology;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_free(old_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%);">+ /* If there are no removed sources and no added sources we don't need to renegotiate the</span><br><span style="color: hsl(120, 100%, 40%);">+ * other participants.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!removed_streams_count && !ast_stream_topology_get_count(added_streams)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</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%);">+ /* Go through each participant adding in the new streams and removing the old ones */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE(participants, participant, entry) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (participant == bridge_channel) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</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%);">+ sc = participant->tech_pvt;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* We add in all the new streams first so that they do not take the place</span><br><span style="color: hsl(120, 100%, 40%);">+ * of any of our removed streams, allowing the remote side to reset the state</span><br><span style="color: hsl(120, 100%, 40%);">+ * for each removed stream. */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (append_all_streams(sc->topology, added_streams)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto cleanup;</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%);">+ /* Then we go through and remove any ones that were removed */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (index = 0; removed_streams_count && index < ast_stream_topology_get_count(sc->topology); ++index) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream = ast_stream_topology_get_stream(sc->topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ int removed_stream;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (removed_stream = 0; removed_stream < removed_streams_count; ++removed_stream) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (is_video_dest(stream, ast_channel_name(bridge_channel->chan), removed_streams[removed_stream])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_request_stream_topology_change(participant->chan, sc->topology, 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%);">+cleanup:</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_free(added_streams);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*!</span><br><span> * \brief stream_topology_changed callback</span><br><span> *</span><br><span>@@ -2241,7 +2388,7 @@</span><br><span> static void softmix_bridge_stream_topology_changed(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)</span><br><span> {</span><br><span> struct softmix_bridge_data *softmix_data = bridge->tech_pvt;</span><br><span style="color: hsl(0, 100%, 40%);">- struct softmix_channel *sc;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct softmix_channel *sc = bridge_channel->tech_pvt;</span><br><span> struct ast_bridge_channel *participant;</span><br><span> struct ast_vector_int media_types;</span><br><span> int nths[AST_MEDIA_TYPE_END] = {0};</span><br><span>@@ -2258,6 +2405,10 @@</span><br><span> break;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_lock(bridge_channel->chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ softmix_bridge_stream_sources_update(bridge, bridge_channel, sc);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(bridge_channel->chan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> AST_VECTOR_INIT(&media_types, AST_MEDIA_TYPE_END);</span><br><span> </span><br><span> /* The bridge stream identifiers may change, so reset the mapping for them.</span><br><span>@@ -2307,7 +2458,6 @@</span><br><span> </span><br><span> for (i = 0; i < ast_stream_topology_get_count(topology); ++i) {</span><br><span> struct ast_stream *stream = ast_stream_topology_get_stream(topology, i);</span><br><span style="color: hsl(0, 100%, 40%);">- const char *stream_identify;</span><br><span> </span><br><span> if (is_video_source(stream)) {</span><br><span> AST_VECTOR_APPEND(&media_types, AST_MEDIA_TYPE_VIDEO);</span><br><span>@@ -2325,12 +2475,8 @@</span><br><span> ast_channel_unlock(participant->chan);</span><br><span> ast_bridge_channel_unlock(participant);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- stream_identify = ast_stream_get_metadata(stream, "MSID:LABEL");</span><br><span style="color: hsl(0, 100%, 40%);">- if (!stream_identify) {</span><br><span style="color: hsl(0, 100%, 40%);">- stream_identify = ast_stream_get_name(stream);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- map_source_to_destinations(stream_identify, ast_channel_name(participant->chan),</span><br><span style="color: hsl(0, 100%, 40%);">- AST_VECTOR_SIZE(&media_types) - 1, &bridge->channels);</span><br><span style="color: hsl(120, 100%, 40%);">+ map_source_to_destinations(ast_channel_name(participant->chan),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_SIZE(&media_types) - 1, &bridge->channels, i);</span><br><span> ast_bridge_channel_lock(participant);</span><br><span> ast_channel_lock(participant->chan);</span><br><span> } else if (ast_stream_get_type(stream) == AST_MEDIA_TYPE_VIDEO) {</span><br><span>@@ -2495,10 +2641,10 @@</span><br><span> { "alice_video", "vp8", AST_MEDIA_TYPE_VIDEO, },</span><br><span> };</span><br><span> static const struct stream_parameters alice_dest_stream = {</span><br><span style="color: hsl(0, 100%, 40%);">- "softbridge_dest_PJSIP/Bob-00000001_bob_video", "h264,vp8", AST_MEDIA_TYPE_VIDEO,</span><br><span style="color: hsl(120, 100%, 40%);">+ "softbridge_dest_PJSIP/Bob-00000001_1", "h264,vp8", AST_MEDIA_TYPE_VIDEO,</span><br><span> };</span><br><span> static const struct stream_parameters bob_dest_stream = {</span><br><span style="color: hsl(0, 100%, 40%);">- "softbridge_dest_PJSIP/Alice-00000000_alice_video", "vp8", AST_MEDIA_TYPE_VIDEO,</span><br><span style="color: hsl(120, 100%, 40%);">+ "softbridge_dest_PJSIP/Alice-00000000_1", "vp8", AST_MEDIA_TYPE_VIDEO,</span><br><span> };</span><br><span> struct ast_stream_topology *topology_alice = NULL;</span><br><span> struct ast_stream_topology *topology_bob = NULL;</span><br><span>@@ -2645,7 +2791,7 @@</span><br><span> goto end;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (is_video_dest(actual, removal_results[i].channel_name, NULL) &&</span><br><span style="color: hsl(120, 100%, 40%);">+ if (is_video_dest(actual, removal_results[i].channel_name, -1) &&</span><br><span> ast_stream_get_state(actual) != AST_STREAM_STATE_REMOVED) {</span><br><span> ast_test_status_update(test, "Removed stream %s does not have a state of removed\n", ast_stream_get_name(actual));</span><br><span> goto end;</span><br><span>diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h</span><br><span>index a0b7728..1013003 100644</span><br><span>--- a/include/asterisk/channel.h</span><br><span>+++ b/include/asterisk/channel.h</span><br><span>@@ -206,6 +206,12 @@</span><br><span> </span><br><span> struct ast_stream_topology;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Set as the change source reason when a channel stream topology has</span><br><span style="color: hsl(120, 100%, 40%);">+ * been changed externally as a result of the remote side renegotiating.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static const char ast_stream_topology_changed_external[] = "external";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \todo Add an explanation of an Asterisk generator</span><br><span> */</span><br><span> struct ast_generator {</span><br><span>@@ -5018,6 +5024,20 @@</span><br><span> int ast_channel_stream_topology_changed(struct ast_channel *chan, struct ast_stream_topology *topology);</span><br><span> </span><br><span> /*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Provide notice from a channel that the topology has changed on it as a result</span><br><span style="color: hsl(120, 100%, 40%);">+ * of the remote party renegotiating.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan The channel to provide notice from</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 failure</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note This interface is provided for channels to provide notice that a topology change</span><br><span style="color: hsl(120, 100%, 40%);">+ * has occurred as a result of a remote party renegotiating the stream topology.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_channel_stream_topology_changed_externally(struct ast_channel *chan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span> * \brief Retrieve the source that initiated the last stream topology change</span><br><span> *</span><br><span> * \param chan The channel</span><br><span>diff --git a/main/channel.c b/main/channel.c</span><br><span>index 4b77390..63ce64e 100644</span><br><span>--- a/main/channel.c</span><br><span>+++ b/main/channel.c</span><br><span>@@ -11051,6 +11051,25 @@</span><br><span> return ast_channel_tech(chan)->indicate(chan, AST_CONTROL_STREAM_TOPOLOGY_CHANGED, topology, sizeof(topology));</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_channel_stream_topology_changed_externally(struct ast_channel *chan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_STREAM_TOPOLOGY_CHANGED };</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(chan != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_channel_is_multistream(chan)) {</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%);">+ ast_channel_lock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_internal_set_stream_topology_change_source(chan, (void *)&ast_stream_topology_changed_external);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_queue_frame(chan, &f);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+</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> void ast_channel_set_flag(struct ast_channel *chan, unsigned int flag)</span><br><span> {</span><br><span> ast_channel_lock(chan);</span><br><span>diff --git a/main/stream.c b/main/stream.c</span><br><span>index 47415bf..626fa3a 100644</span><br><span>--- a/main/stream.c</span><br><span>+++ b/main/stream.c</span><br><span>@@ -96,8 +96,9 @@</span><br><span> struct ast_stream *ast_stream_alloc(const char *name, enum ast_media_type type)</span><br><span> {</span><br><span> struct ast_stream *stream;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t name_len = MAX(strlen(S_OR(name, "")), 7); /* Ensure there is enough room for 'removed' */</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- stream = ast_calloc(1, sizeof(*stream) + strlen(S_OR(name, "")) + 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ stream = ast_calloc(1, sizeof(*stream) + name_len + 1);</span><br><span> if (!stream) {</span><br><span> return NULL;</span><br><span> }</span><br><span>@@ -113,16 +114,16 @@</span><br><span> struct ast_stream *ast_stream_clone(const struct ast_stream *stream, const char *name)</span><br><span> {</span><br><span> struct ast_stream *new_stream;</span><br><span style="color: hsl(0, 100%, 40%);">- size_t stream_size;</span><br><span> const char *stream_name;</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t name_len;</span><br><span> </span><br><span> if (!stream) {</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span> stream_name = name ?: stream->name;</span><br><span style="color: hsl(0, 100%, 40%);">- stream_size = sizeof(*stream) + strlen(stream_name) + 1;</span><br><span style="color: hsl(0, 100%, 40%);">- new_stream = ast_calloc(1, stream_size);</span><br><span style="color: hsl(120, 100%, 40%);">+ name_len = MAX(strlen(stream_name), 7); /* Ensure there is enough room for 'removed' */</span><br><span style="color: hsl(120, 100%, 40%);">+ new_stream = ast_calloc(1, sizeof(*stream) + name_len + 1);</span><br><span> if (!new_stream) {</span><br><span> return NULL;</span><br><span> }</span><br><span>@@ -205,6 +206,19 @@</span><br><span> ast_assert(stream != NULL);</span><br><span> </span><br><span> stream->state = state;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* When a stream is set to removed that means that any previous data for it</span><br><span style="color: hsl(120, 100%, 40%);">+ * is no longer valid. We therefore change its name to removed and remove</span><br><span style="color: hsl(120, 100%, 40%);">+ * any old metadata associated with it.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (state == AST_STREAM_STATE_REMOVED) {</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(stream->name, "removed");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_variables_destroy(stream->metadata);</span><br><span style="color: hsl(120, 100%, 40%);">+ stream->metadata = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (stream->formats) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_remove_by_type(stream->formats, 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> }</span><br><span> </span><br><span> const char *ast_stream_state2str(enum ast_stream_state state)</span><br><span>diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c</span><br><span>index 8d9cece..5906211 100644</span><br><span>--- a/res/res_pjsip_sdp_rtp.c</span><br><span>+++ b/res/res_pjsip_sdp_rtp.c</span><br><span>@@ -1547,6 +1547,8 @@</span><br><span> static const pj_str_t STR_IP6 = { "IP6", 3};</span><br><span> static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };</span><br><span> static const pj_str_t STR_SENDONLY = { "sendonly", 8 };</span><br><span style="color: hsl(120, 100%, 40%);">+ static const pj_str_t STR_RECVONLY = { "recvonly", 8 };</span><br><span style="color: hsl(120, 100%, 40%);">+ static const pj_str_t STR_INACTIVE = { "inactive", 8 };</span><br><span> pjmedia_sdp_media *media;</span><br><span> const char *hostip = NULL;</span><br><span> struct ast_sockaddr addr;</span><br><span>@@ -1813,7 +1815,15 @@</span><br><span> </span><br><span> /* Add the sendrecv attribute - we purposely don't keep track because pjmedia-sdp will automatically change our offer for us */</span><br><span> attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);</span><br><span style="color: hsl(0, 100%, 40%);">- attr->name = !session_media->locally_held ? STR_SENDRECV : STR_SENDONLY;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (session_media->locally_held || ast_stream_get_state(stream) == AST_STREAM_STATE_SENDONLY) {</span><br><span style="color: hsl(120, 100%, 40%);">+ attr->name = STR_SENDONLY; /* Send sendonly to initate a local hold */</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (ast_stream_get_state(stream) == AST_STREAM_STATE_RECVONLY) {</span><br><span style="color: hsl(120, 100%, 40%);">+ attr->name = STR_RECVONLY; /* Stream has requested recvonly */</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (ast_stream_get_state(stream) == AST_STREAM_STATE_INACTIVE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ attr->name = STR_INACTIVE; /* Stream has requested inactive */</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ attr->name = STR_SENDRECV; /* No hold in either direction */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> media->attr[media->attr_count++] = attr;</span><br><span> </span><br><span> /* If we've got rtcp-mux enabled, add it unless we received an offer without it */</span><br><span>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c</span><br><span>index 1371909..504d6f1 100644</span><br><span>--- a/res/res_pjsip_session.c</span><br><span>+++ b/res/res_pjsip_session.c</span><br><span>@@ -945,7 +945,7 @@</span><br><span> {</span><br><span> int i;</span><br><span> struct ast_stream_topology *topology;</span><br><span style="color: hsl(0, 100%, 40%);">- unsigned int changed = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ unsigned int changed = 0; /* 0 = unchanged, 1 = new source, 2 = new topology */</span><br><span> </span><br><span> if (!session->pending_media_state->topology) {</span><br><span> if (session->active_media_state->topology) {</span><br><span>@@ -1057,6 +1057,14 @@</span><br><span> topology = ast_stream_topology_clone(session->pending_media_state->topology);</span><br><span> if (topology) {</span><br><span> ast_channel_set_stream_topology(session->channel, topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If this is a remotely done renegotiation that has changed the stream topology notify what is</span><br><span style="color: hsl(120, 100%, 40%);">+ * currently handling this channel.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pjmedia_sdp_neg_was_answer_remote(session->inv_session->neg) == PJ_FALSE &&</span><br><span style="color: hsl(120, 100%, 40%);">+ session->active_media_state && session->active_media_state->topology &&</span><br><span style="color: hsl(120, 100%, 40%);">+ !ast_stream_topology_equal(session->active_media_state->topology, topology)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ changed = 2;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> }</span><br><span> </span><br><span> /* Remove all current file descriptors from the channel */</span><br><span>@@ -1079,10 +1087,12 @@</span><br><span> </span><br><span> ast_channel_unlock(session->channel);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (changed) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (changed == 1) {</span><br><span> struct ast_frame f = { AST_FRAME_CONTROL, .subclass.integer = AST_CONTROL_STREAM_TOPOLOGY_SOURCE_CHANGED };</span><br><span> </span><br><span> ast_queue_frame(session->channel, &f);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (changed == 2) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_stream_topology_changed_externally(session->channel);</span><br><span> } else {</span><br><span> ast_queue_frame(session->channel, &ast_null_frame);</span><br><span> }</span><br><span>@@ -1875,6 +1885,7 @@</span><br><span> enum ast_media_type type;</span><br><span> struct ast_sip_session_media *session_media = NULL;</span><br><span> enum ast_sip_session_sdp_stream_defer res;</span><br><span style="color: hsl(120, 100%, 40%);">+ pjmedia_sdp_media *remote_stream = sdp->media[i];</span><br><span> </span><br><span> /* We need a null-terminated version of the media string */</span><br><span> ast_copy_pj_str(media, &sdp->media[i]->desc.media, sizeof(media));</span><br><span>@@ -1903,6 +1914,25 @@</span><br><span> return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* For backwards compatibility with the core default streams are always sendrecv */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_sip_session_is_pending_stream_default(session, stream)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pjmedia_sdp_media_find_attr2(remote_stream, "sendonly", NULL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Stream state reflects our state of a stream, so in the case of</span><br><span style="color: hsl(120, 100%, 40%);">+ * sendonly and recvonly we store the opposite since that is what ours</span><br><span style="color: hsl(120, 100%, 40%);">+ * is.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(stream, AST_STREAM_STATE_RECVONLY);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (pjmedia_sdp_media_find_attr2(remote_stream, "recvonly", NULL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(stream, AST_STREAM_STATE_SENDONLY);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (pjmedia_sdp_media_find_attr2(remote_stream, "inactive", NULL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(stream, AST_STREAM_STATE_INACTIVE);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(stream, AST_STREAM_STATE_SENDRECV);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(stream, AST_STREAM_STATE_SENDRECV);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (session_media->handler) {</span><br><span> handler = session_media->handler;</span><br><span> if (handler->defer_incoming_sdp_stream) {</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/13850">change 13850</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/+/13850"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: certified/16.8 </div>
<div style="display:none"> Gerrit-Change-Id: I93f41fb41b85646bef71408111c17ccea30cb0c5 </div>
<div style="display:none"> Gerrit-Change-Number: 13850 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Joshua Colp <jcolp@sangoma.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-MessageType: merged </div>