<p>George Joseph <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/14912">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Joshua Colp: Looks good to me, but someone else must approve
George Joseph: Looks good to me, approved; Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_pjsip_session: Handle multi-stream re-invites better<br><br>When both Asterisk and a UA send re-invites at the same time, both<br>send 491 "Transaction in progress" responses to each other and back<br>off a specified amount of time before retrying. When Asterisk<br>prepares to send its re-invite, it sets up the session's pending<br>media state with the new topology it wants, then sends the<br>re-invite. Unfortunately, when it received the re-invite from the<br>UA, it partially processed the media in the re-invite and reset<br>the pending media state before sending the 491 losing the state it<br>set in its own re-invite.<br><br>Asterisk also was not tracking re-invites received while an existing<br>re-invite was queued resulting in sending stale SDP with missing<br>or duplicated streams, or no re-invite at all because we erroneously<br>determined that a re-invite wasn't needed.<br><br>There was also an issue in bridge_softmix where we were using a stream<br>from the wrong topology to determine if a stream was added. This also<br>caused us to erroneously determine that a re-invite wasn't needed.<br><br>Regardless of how the delayed re-invite was triggered, we need to<br>reconcile the topology that was active at the time the delayed<br>request was queued, the pending topology of the queued request,<br>and the topology currently active on the session. To do this we<br>need a topology resolver AND we need to make stream named unique<br>so we can accurately tell what a stream has been added or removed<br>and if we can re-use a slot in the topology.<br><br>Summary of changes:<br><br> * bridge_softmix:<br> * We no longer reset the stream name to "removed" in<br> remove_all_original_streams(). That was causing multiple streams<br> to have the same name and wrecked the checks for duplicate streams.<br><br> * softmix_bridge_stream_sources_update() was checking the old_stream<br> to see if it had the softmix prefix and not considering the stream<br> as "new" if it did. If the stream in that slot has something in it<br> because another re-invite happened, then that slot in old might<br> have a softmix stream but the same stream in new might actually<br> be a new one. Now we check the new_stream's name instead of<br> the old_stream's.<br><br> * stream:<br> * Instead of using plain media type name ("audio", "video", etc) as<br> the default stream name, we now append the stream position to it<br> to make it unique. We need to do this so we can distinguish multiple<br> streams of the same type from each other.<br><br> * When we set a stream's state to REMOVED, we no longer reset its<br> name to "removed" or destroy its metadata. Again, we need to<br> do this so we can distinguish multiple streams of the same<br> type from each other.<br><br> * res_pjsip_session:<br> * Added resolve_refresh_media_states() that takes in 3 media states<br> and creates an up-to-date pending media state that includes the changes<br> that might have happened while a delayed session refresh was in the<br> delayed queue.<br><br> * Added is_media_state_valid() that checks the consistency of<br> a media state and returns a true/false value. A valid state has:<br> * The same number of stream entries as media session entries.<br> Some media session entries can be NULL however.<br> * No duplicate streams.<br> * A valid stream for each non-NULL media session.<br> * A stream that matches each media session's stream_num<br> and media type.<br><br> * Updated handle_incoming_sdp() to set the stream name to include the<br> stream position number in the name to make it unique.<br><br> * Updated the ast_sip_session_delayed_request structure to include both<br> the pending and active media states and updated the associated delay<br> functions to process them.<br><br> * Updated sip_session_refresh() to accept both the pending and active<br> media states that were in effect when the request was originally queued<br> and to pass them on should the request need to be delayed again.<br><br> * Updated sip_session_refresh() to call resolve_refresh_media_states()<br> and substitute its results for the pending state passed in.<br><br> * Updated sip_session_refresh() with additional debugging.<br><br> * Updated session_reinvite_on_rx_request() to simply return PJ_FALSE<br> to pjproject if a transaction is in progress. This stops us from<br> creating a partial pending media state that would be invalid later on.<br><br> * Updated reschedule_reinvite() to clone both the current pending and<br> active media states and pass them to delay_request() so the resolver<br> can tell what the original intention of the re-invite was.<br><br> * Added a large unit test for the resolver.<br><br>ASTERISK-29014<br><br>Change-Id: Id3440972943c611a15f652c6c569fa0e4536bfcb<br>---<br>M bridges/bridge_softmix.c<br>M include/asterisk/stream.h<br>M main/stream.c<br>M res/res_pjsip_session.c<br>4 files changed, 1,253 insertions(+), 122 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c</span><br><span>index 817f8b2..931cf62 100644</span><br><span>--- a/bridges/bridge_softmix.c</span><br><span>+++ b/bridges/bridge_softmix.c</span><br><span>@@ -1081,11 +1081,7 @@</span><br><span> if (!strcmp(ast_stream_get_name(stream), ast_stream_get_name(original_stream))) {</span><br><span> struct ast_stream *removed;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /* Since the participant is still going to be in the bridge we</span><br><span style="color: hsl(0, 100%, 40%);">- * change the name so that routing does not attempt to route video</span><br><span style="color: hsl(0, 100%, 40%);">- * to this stream.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">- removed = ast_stream_clone(stream, "removed");</span><br><span style="color: hsl(120, 100%, 40%);">+ removed = ast_stream_clone(stream, NULL);</span><br><span> if (!removed) {</span><br><span> return -1;</span><br><span> }</span><br><span>@@ -2272,7 +2268,7 @@</span><br><span> </span><br><span> /* Ignore all streams that don't carry video and streams that are strictly outgoing destination streams */</span><br><span> 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(0, 100%, 40%);">- !strncmp(ast_stream_get_name(old_stream), SOFTBRIDGE_VIDEO_DEST_PREFIX,</span><br><span style="color: hsl(120, 100%, 40%);">+ !strncmp(ast_stream_get_name(new_stream), SOFTBRIDGE_VIDEO_DEST_PREFIX,</span><br><span> SOFTBRIDGE_VIDEO_DEST_LEN)) {</span><br><span> continue;</span><br><span> }</span><br><span>diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h</span><br><span>index 205feeb..387c5b3 100644</span><br><span>--- a/include/asterisk/stream.h</span><br><span>+++ b/include/asterisk/stream.h</span><br><span>@@ -718,6 +718,8 @@</span><br><span> * \returns the position of the stream in the topology (-1 on error)</span><br><span> *</span><br><span> * \since 15</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note If the stream's name is empty, it'll be set to <stream_type>-<position></span><br><span> */</span><br><span> int ast_stream_topology_append_stream(struct ast_stream_topology *topology,</span><br><span> struct ast_stream *stream);</span><br><span>@@ -775,6 +777,8 @@</span><br><span> * the first unused position. You can't set positions beyond that.</span><br><span> *</span><br><span> * \since 15</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \note If the stream's name is empty, it'll be set to <stream_type>-<position></span><br><span> */</span><br><span> int ast_stream_topology_set_stream(struct ast_stream_topology *topology,</span><br><span> unsigned int position, struct ast_stream *stream);</span><br><span>diff --git a/main/stream.c b/main/stream.c</span><br><span>index 859018d..e464c5e 100644</span><br><span>--- a/main/stream.c</span><br><span>+++ b/main/stream.c</span><br><span>@@ -228,10 +228,12 @@</span><br><span> [AST_STREAM_STATE_INACTIVE] = "inactive",</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#define MIN_STREAM_NAME_LEN 16</span><br><span style="color: hsl(120, 100%, 40%);">+</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(0, 100%, 40%);">- size_t name_len = MAX(strlen(S_OR(name, "")), 7); /* Ensure there is enough room for 'removed' */</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t name_len = MAX(strlen(S_OR(name, "")), MIN_STREAM_NAME_LEN); /* Ensure there is enough room for 'removed' or a type-position */</span><br><span> </span><br><span> stream = ast_calloc(1, sizeof(*stream) + name_len + 1);</span><br><span> if (!stream) {</span><br><span>@@ -263,7 +265,7 @@</span><br><span> }</span><br><span> </span><br><span> stream_name = name ?: stream->name;</span><br><span style="color: hsl(0, 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%);">+ name_len = MAX(strlen(S_OR(stream_name, "")), MIN_STREAM_NAME_LEN); /* Ensure there is enough room for 'removed' or a type-position */</span><br><span> new_stream = ast_calloc(1, sizeof(*stream) + name_len + 1);</span><br><span> if (!new_stream) {</span><br><span> return NULL;</span><br><span>@@ -381,18 +383,6 @@</span><br><span> </span><br><span> stream->state = state;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- /* When a stream is set to removed that means that any previous data for it</span><br><span style="color: hsl(0, 100%, 40%);">- * is no longer valid. We therefore change its name to removed and remove</span><br><span style="color: hsl(0, 100%, 40%);">- * any old metadata associated with it.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">- if (state == AST_STREAM_STATE_REMOVED) {</span><br><span style="color: hsl(0, 100%, 40%);">- strcpy(stream->name, "removed");</span><br><span style="color: hsl(0, 100%, 40%);">- ast_variables_destroy(stream->metadata);</span><br><span style="color: hsl(0, 100%, 40%);">- stream->metadata = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">- if (stream->formats) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_format_cap_remove_by_type(stream->formats, AST_MEDIA_TYPE_UNKNOWN);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span> }</span><br><span> </span><br><span> const char *ast_stream_state2str(enum ast_stream_state state)</span><br><span>@@ -765,6 +755,10 @@</span><br><span> </span><br><span> stream->position = AST_VECTOR_SIZE(&topology->streams) - 1;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(stream->name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(stream->name, MIN_STREAM_NAME_LEN, "%s-%d", ast_codec_media_type2str(stream->type), stream->position);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> return AST_VECTOR_SIZE(&topology->streams) - 1;</span><br><span> }</span><br><span> </span><br><span>@@ -821,6 +815,10 @@</span><br><span> return AST_VECTOR_APPEND(&topology->streams, stream);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(stream->name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(stream->name, MIN_STREAM_NAME_LEN, "%s-%d", ast_codec_media_type2str(stream->type), stream->position);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> return AST_VECTOR_REPLACE(&topology->streams, position, stream);</span><br><span> }</span><br><span> </span><br><span>@@ -879,7 +877,7 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- stream = ast_stream_alloc(ast_codec_media_type2str(type), type);</span><br><span style="color: hsl(120, 100%, 40%);">+ stream = ast_stream_alloc(NULL, type);</span><br><span> if (!stream) {</span><br><span> ao2_cleanup(new_cap);</span><br><span> ast_stream_topology_free(topology);</span><br><span>@@ -894,6 +892,8 @@</span><br><span> ast_stream_topology_free(topology);</span><br><span> return NULL;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(stream->name, MIN_STREAM_NAME_LEN, "%s-%d", ast_codec_media_type2str(stream->type), stream->position);</span><br><span> }</span><br><span> </span><br><span> return topology;</span><br><span>diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c</span><br><span>index f07ee38..0385a96 100644</span><br><span>--- a/res/res_pjsip_session.c</span><br><span>+++ b/res/res_pjsip_session.c</span><br><span>@@ -76,7 +76,8 @@</span><br><span> ast_sip_session_sdp_creation_cb on_sdp_creation,</span><br><span> ast_sip_session_response_cb on_response,</span><br><span> enum ast_sip_session_refresh_method method, int generate_new_sdp,</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_sip_session_media_state *media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *pending_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *active_media_state,</span><br><span> int queued);</span><br><span> </span><br><span> /*! \brief NAT hook for modifying outgoing messages with SDP */</span><br><span>@@ -493,7 +494,10 @@</span><br><span> * is the case we simply return it.</span><br><span> */</span><br><span> if (position < AST_VECTOR_SIZE(&media_state->sessions)) {</span><br><span style="color: hsl(0, 100%, 40%);">- return AST_VECTOR_GET(&media_state->sessions, position);</span><br><span style="color: hsl(120, 100%, 40%);">+ session_media = AST_VECTOR_GET(&media_state->sessions, position);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (session_media) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return session_media;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> }</span><br><span> </span><br><span> /* Determine if we can reuse the session media from the active media state if present */</span><br><span>@@ -777,27 +781,38 @@</span><br><span> }</span><br><span> if (!stream) {</span><br><span> struct ast_stream *existing_stream = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *stream_name = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *stream_label = NULL;</span><br><span> </span><br><span> if (session->active_media_state->topology &&</span><br><span> (i < ast_stream_topology_get_count(session->active_media_state->topology))) {</span><br><span> existing_stream = ast_stream_topology_get_stream(session->active_media_state->topology, i);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_stream_get_state(existing_stream) != AST_STREAM_STATE_REMOVED) {</span><br><span style="color: hsl(120, 100%, 40%);">+ stream_name = (char *)ast_stream_get_name(existing_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ stream_label = ast_stream_get_metadata(existing_stream, "SDP:LABEL");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- stream = ast_stream_alloc(existing_stream ? ast_stream_get_name(existing_stream) : ast_codec_media_type2str(type), type);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(stream_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_asprintf(&stream_name, "%s-%d", ast_codec_media_type2str(type), i) < 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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ stream = ast_stream_alloc(stream_name, type);</span><br><span> if (!stream) {</span><br><span> SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create stream\n");</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(stream_label)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_metadata(stream, "SDP:LABEL", stream_label);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (ast_stream_topology_set_stream(session->pending_media_state->topology, i, stream)) {</span><br><span> ast_stream_free(stream);</span><br><span> SCOPE_EXIT_RTN_VALUE(-1, "Couldn't set stream\n");</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- if (existing_stream) {</span><br><span style="color: hsl(0, 100%, 40%);">- const char *stream_label = ast_stream_get_metadata(existing_stream, "SDP:LABEL");</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">- if (!ast_strlen_zero(stream_label)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_stream_set_metadata(stream, "SDP:LABEL", stream_label);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span> </span><br><span> /* For backwards compatibility with the core the default audio stream is always sendrecv */</span><br><span> if (!ast_sip_session_is_pending_stream_default(session, stream) || strcmp(media, "audio")) {</span><br><span>@@ -1288,7 +1303,9 @@</span><br><span> /*! Whether to generate new SDP */</span><br><span> int generate_new_sdp;</span><br><span> /*! Requested media state for the SDP */</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_sip_session_media_state *media_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *pending_media_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! Active media state at the time of the original request */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *active_media_state;</span><br><span> AST_LIST_ENTRY(ast_sip_session_delayed_request) next;</span><br><span> };</span><br><span> </span><br><span>@@ -1298,7 +1315,8 @@</span><br><span> ast_sip_session_sdp_creation_cb on_sdp_creation,</span><br><span> ast_sip_session_response_cb on_response,</span><br><span> int generate_new_sdp,</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_sip_session_media_state *media_state)</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *pending_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *active_media_state)</span><br><span> {</span><br><span> struct ast_sip_session_delayed_request *delay = ast_calloc(1, sizeof(*delay));</span><br><span> </span><br><span>@@ -1310,13 +1328,15 @@</span><br><span> delay->on_sdp_creation = on_sdp_creation;</span><br><span> delay->on_response = on_response;</span><br><span> delay->generate_new_sdp = generate_new_sdp;</span><br><span style="color: hsl(0, 100%, 40%);">- delay->media_state = media_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ delay->pending_media_state = pending_media_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ delay->active_media_state = active_media_state;</span><br><span> return delay;</span><br><span> }</span><br><span> </span><br><span> static void delayed_request_free(struct ast_sip_session_delayed_request *delay)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sip_session_media_state_free(delay->media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(delay->pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(delay->active_media_state);</span><br><span> ast_free(delay);</span><br><span> }</span><br><span> </span><br><span>@@ -1341,16 +1361,20 @@</span><br><span> case DELAYED_METHOD_INVITE:</span><br><span> res = sip_session_refresh(session, delay->on_request_creation,</span><br><span> delay->on_sdp_creation, delay->on_response,</span><br><span style="color: hsl(0, 100%, 40%);">- AST_SIP_SESSION_REFRESH_METHOD_INVITE, delay->generate_new_sdp, delay->media_state, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_SIP_SESSION_REFRESH_METHOD_INVITE, delay->generate_new_sdp, delay->pending_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ delay->active_media_state, 1);</span><br><span> /* Ownership of media state transitions to ast_sip_session_refresh */</span><br><span style="color: hsl(0, 100%, 40%);">- delay->media_state = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ delay->pending_media_state = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ delay->active_media_state = NULL;</span><br><span> return res;</span><br><span> case DELAYED_METHOD_UPDATE:</span><br><span> res = sip_session_refresh(session, delay->on_request_creation,</span><br><span> delay->on_sdp_creation, delay->on_response,</span><br><span style="color: hsl(0, 100%, 40%);">- AST_SIP_SESSION_REFRESH_METHOD_UPDATE, delay->generate_new_sdp, delay->media_state, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_SIP_SESSION_REFRESH_METHOD_UPDATE, delay->generate_new_sdp, delay->pending_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ delay->active_media_state, 1);</span><br><span> /* Ownership of media state transitions to ast_sip_session_refresh */</span><br><span style="color: hsl(0, 100%, 40%);">- delay->media_state = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ delay->pending_media_state = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ delay->active_media_state = NULL;</span><br><span> return res;</span><br><span> case DELAYED_METHOD_BYE:</span><br><span> ast_sip_session_terminate(session, 0);</span><br><span>@@ -1521,17 +1545,21 @@</span><br><span> ast_sip_session_response_cb on_response,</span><br><span> int generate_new_sdp,</span><br><span> enum delayed_method method,</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_sip_session_media_state *media_state)</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *pending_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *active_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ int queue_head)</span><br><span> {</span><br><span> struct ast_sip_session_delayed_request *delay = delayed_request_alloc(method,</span><br><span style="color: hsl(0, 100%, 40%);">- on_request, on_sdp_creation, on_response, generate_new_sdp, media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ on_request, on_sdp_creation, on_response, generate_new_sdp, pending_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ active_media_state);</span><br><span> </span><br><span> if (!delay) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sip_session_media_state_free(media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span> return -1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (method == DELAYED_METHOD_BYE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (method == DELAYED_METHOD_BYE || queue_head) {</span><br><span> /* Send BYE as early as possible */</span><br><span> AST_LIST_INSERT_HEAD(&session->delayed_requests, delay, next);</span><br><span> } else {</span><br><span>@@ -1649,57 +1677,521 @@</span><br><span> }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Helper macros for merging and validating media states</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define STREAM_REMOVED(_stream) (ast_stream_get_state(_stream) == AST_STREAM_STATE_REMOVED)</span><br><span style="color: hsl(120, 100%, 40%);">+#define STATE_REMOVED(_stream_state) (_stream_state == AST_STREAM_STATE_REMOVED)</span><br><span style="color: hsl(120, 100%, 40%);">+#define STATE_NONE(_stream_state) (_stream_state == AST_STREAM_STATE_END)</span><br><span style="color: hsl(120, 100%, 40%);">+#define GET_STREAM_SAFE(_topology, _i) (_i < ast_stream_topology_get_count(_topology) ? ast_stream_topology_get_stream(_topology, _i) : NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+#define GET_STREAM_STATE_SAFE(_stream) (_stream ? ast_stream_get_state(_stream) : AST_STREAM_STATE_END)</span><br><span style="color: hsl(120, 100%, 40%);">+#define GET_STREAM_NAME_SAFE(_stream) (_stream ? ast_stream_get_name(_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%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Validate a media state</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param state Media state</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 1 The media state is valid</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 The media state is NOT valid</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 is_media_state_valid(const char *session_name, struct ast_sip_session_media_state *state)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ int stream_count = ast_stream_topology_get_count(state->topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ int session_count = AST_VECTOR_SIZE(&state->sessions);</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_ENTER(3, "%s: Topology: %s\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_tmp(256, ast_stream_topology_to_str(state->topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (session_count != stream_count) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(0, "%s: %d media sessions but %d streams\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+ session_count, stream_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%);">+ for (i = 0; i < stream_count; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media *media = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream = ast_stream_topology_get_stream(state->topology, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *stream_name = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int j;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_ENTER(4, "%s: Checking stream %s\n", session_name, ast_str_tmp(128, ast_stream_to_str(stream, &STR_TMP)));</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%);">+ SCOPE_EXIT_EXPR(goto end, "%s: stream %d is null\n", session_name, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ stream_name = ast_stream_get_name(stream);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (j = 0; j < stream_count; j++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *possible_dup = ast_stream_topology_get_stream(state->topology, j);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (j == i || !possible_dup) {</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%);">+ if (!STREAM_REMOVED(stream) && ast_strings_equal(stream_name, GET_STREAM_NAME_SAFE(possible_dup))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(goto end, "%s: stream %i %s is duplicated to %d\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+ i, stream_name, j);</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%);">+ media = AST_VECTOR_GET(&state->sessions, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!media) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: media %d is null\n", session_name, 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%);">+ for (j = 0; j < session_count; j++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media *possible_dup = AST_VECTOR_GET(&state->sessions, j);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (j == i || !possible_dup) {</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%);">+ if (!ast_strlen_zero(media->label) && !ast_strlen_zero(possible_dup->label)</span><br><span style="color: hsl(120, 100%, 40%);">+ && ast_strings_equal(media->label, possible_dup->label)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(goto end, "%s: media %d %s is duplicated to %d\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+ i, media->label, j);</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 (media->stream_num != i) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(goto end, "%s: media %d has stream_num %d\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+ i, media->stream_num);</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 (media->type != ast_stream_get_type(stream)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(goto end, "%s: media %d has type %s but stream has type %s\n", stream_name,</span><br><span style="color: hsl(120, 100%, 40%);">+ i, ast_codec_media_type2str(media->type), ast_codec_media_type2str(ast_stream_get_type(stream)));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT("%s: Done with stream %s\n", session_name, ast_str_tmp(128, ast_stream_to_str(stream, &STR_TMP)));</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 = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+end:</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(res, "%s: %s\n", session_name, res ? "Valid" : "NOT Valid");</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 Merge media states for a delayed session refresh</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param session_name For log messages</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param delayed_pending_state The pending media state at the time the resuest was queued</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param delayed_active_state The active media state at the time the resuest was queued</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param current_active_state The current active media state</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param run_validation Whether to run validation on the resulting media state or not</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns New merged topology or NULL if there's an error</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 struct ast_sip_session_media_state *resolve_refresh_media_states(</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *delayed_pending_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *delayed_active_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *current_active_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ int run_post_validation)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ RAII_VAR(struct ast_sip_session_media_state *, new_pending_state, NULL, ast_sip_session_media_state_free);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *returned_media_state = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *delayed_pending = delayed_pending_state->topology;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *delayed_active = delayed_active_state->topology;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *current_active = current_active_state->topology;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream_topology *new_pending = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int i;</span><br><span style="color: hsl(120, 100%, 40%);">+ int max_stream_count;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_ENTER(2, "%s: DP: %s DA: %s CA: %s\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_tmp(256, ast_stream_topology_to_str(delayed_pending, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_tmp(256, ast_stream_topology_to_str(delayed_active, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_tmp(256, ast_stream_topology_to_str(current_active, &STR_TMP))</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%);">+ max_stream_count = MAX(ast_stream_topology_get_count(delayed_pending),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_get_count(delayed_active));</span><br><span style="color: hsl(120, 100%, 40%);">+ max_stream_count = MAX(max_stream_count, ast_stream_topology_get_count(current_active));</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%);">+ * The new_pending_state is always based on the currently negotiated state because</span><br><span style="color: hsl(120, 100%, 40%);">+ * the stream ordering in its topology must be preserved.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ new_pending_state = ast_sip_session_media_state_clone(current_active_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!new_pending_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "%s: Couldn't clone current_active_state to new_pending_state\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ new_pending = new_pending_state->topology;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < max_stream_count; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *dp_stream = GET_STREAM_SAFE(delayed_pending, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *da_stream = GET_STREAM_SAFE(delayed_active, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *ca_stream = GET_STREAM_SAFE(current_active, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *np_stream = GET_STREAM_SAFE(new_pending, i);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *found_da_stream = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *found_np_stream = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_stream_state dp_state = GET_STREAM_STATE_SAFE(dp_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_stream_state da_state = GET_STREAM_STATE_SAFE(da_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_stream_state ca_state = GET_STREAM_STATE_SAFE(ca_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_stream_state np_state = GET_STREAM_STATE_SAFE(np_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_stream_state found_da_state = AST_STREAM_STATE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_stream_state found_np_state = AST_STREAM_STATE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *da_name = GET_STREAM_NAME_SAFE(da_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *dp_name = GET_STREAM_NAME_SAFE(dp_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *ca_name = GET_STREAM_NAME_SAFE(ca_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *np_name = GET_STREAM_NAME_SAFE(np_stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *found_da_name __attribute__((unused)) = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *found_np_name __attribute__((unused)) = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ int found_da_slot __attribute__((unused)) = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ int found_np_slot = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ int removed_np_slot = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ int j;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_ENTER(3, "%s: slot: %d DP: %s DA: %s CA: %s\n", session_name, i,</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_tmp(128, ast_stream_to_str(dp_stream, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_tmp(128, ast_stream_to_str(da_stream, &STR_TMP)),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_tmp(128, ast_stream_to_str(ca_stream, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (STATE_NONE(da_state) && STATE_NONE(dp_state) && STATE_NONE(ca_state)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(break, "%s: All gone\n", session_name);</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%);">+ * Simple cases are handled first to avoid having to search the NP and DA</span><br><span style="color: hsl(120, 100%, 40%);">+ * topologies for streams with the same name but not in the same position.</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 (STATE_NONE(dp_state) && !STATE_NONE(da_state)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * The slot in the delayed pending topology can't be empty if the delayed</span><br><span style="color: hsl(120, 100%, 40%);">+ * active topology has a stream there. Streams can't just go away. They</span><br><span style="color: hsl(120, 100%, 40%);">+ * can be reused or marked "removed" but they can't go away.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_WARNING, "%s: DP slot is empty but DA is not\n", session_name);</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 (STATE_NONE(dp_state)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * The current active topology can certainly have streams that weren't</span><br><span style="color: hsl(120, 100%, 40%);">+ * in existence when the delayed request was queued. In this case,</span><br><span style="color: hsl(120, 100%, 40%);">+ * no action is needed since we already copied the current active topology</span><br><span style="color: hsl(120, 100%, 40%);">+ * to the new pending one.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: No DP stream so use CA stream as is\n", session_name);</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_strings_equal(dp_name, da_name) && ast_strings_equal(da_name, ca_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * The delayed pending stream in this slot matches by name, the streams</span><br><span style="color: hsl(120, 100%, 40%);">+ * in the same slot in the other two topologies. Easy case.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Same stream in all 3 states\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dp_state == da_state && da_state == ca_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* All the same state, no need to update. */</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: All in the same state so nothing to do\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (da_state != ca_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Something set the CA state between the time this request was queued</span><br><span style="color: hsl(120, 100%, 40%);">+ * and now. The CA state wins so we don't do anything.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: Ignoring request to change state from %s to %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ session_name, ast_stream_state2str(ca_state), ast_stream_state2str(dp_state));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dp_state != da_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* DP needs to update the state */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(np_stream, dp_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: Changed NP stream state from %s to %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ session_name, ast_stream_state2str(ca_state), ast_stream_state2str(dp_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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * We're done with the simple cases. For the rest, we need to identify if the</span><br><span style="color: hsl(120, 100%, 40%);">+ * DP stream we're trying to take action on is already in the other topologies</span><br><span style="color: hsl(120, 100%, 40%);">+ * possibly in a different slot. To do that, if the stream in the DA or CA slots</span><br><span style="color: hsl(120, 100%, 40%);">+ * doesn't match the current DP stream, we need to iterate over the topology</span><br><span style="color: hsl(120, 100%, 40%);">+ * looking for a stream with the same name.</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%);">+ * Since we already copied all of the CA streams to the NP topology, we'll use it</span><br><span style="color: hsl(120, 100%, 40%);">+ * instead of CA because we'll be updating the NP as we go.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strings_equal(dp_name, np_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * The NP stream in this slot doesn't have the same name as the DP stream</span><br><span style="color: hsl(120, 100%, 40%);">+ * so we need to see if it's in another NP slot. We're not going to stop</span><br><span style="color: hsl(120, 100%, 40%);">+ * when we find a matching stream because we also want to find the first</span><br><span style="color: hsl(120, 100%, 40%);">+ * removed removed slot, if any, so we can re-use this slot. We'll break</span><br><span style="color: hsl(120, 100%, 40%);">+ * early if we find both before we reach the end.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Checking if DP is already in NP somewhere\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ for (j = 0; j < ast_stream_topology_get_count(new_pending); j++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *possible_existing = ast_stream_topology_get_stream(new_pending, j);</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *possible_existing_name = GET_STREAM_NAME_SAFE(possible_existing);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Checking %s against %s\n", session_name, dp_name, possible_existing_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (found_np_slot == -1 && ast_strings_equal(dp_name, possible_existing_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Pending stream %s slot %d is in NP slot %d\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+ dp_name, i, j);</span><br><span style="color: hsl(120, 100%, 40%);">+ found_np_slot = j;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_np_stream = possible_existing;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_np_state = ast_stream_get_state(possible_existing);</span><br><span style="color: hsl(120, 100%, 40%);">+ found_np_name = ast_stream_get_name(possible_existing);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (STREAM_REMOVED(possible_existing) && removed_np_slot == -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ removed_np_slot = j;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (removed_np_slot >= 0 && found_np_slot >= 0) {</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%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Makes the subsequent code easier */</span><br><span style="color: hsl(120, 100%, 40%);">+ found_np_slot = i;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_np_stream = np_stream;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_np_state = np_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_np_name = np_name;</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_strings_equal(dp_name, da_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * The DA stream in this slot doesn't have the same name as the DP stream</span><br><span style="color: hsl(120, 100%, 40%);">+ * so we need to see if it's in another DA slot. In real life, the DA stream</span><br><span style="color: hsl(120, 100%, 40%);">+ * in this slot could have a different name but there shouldn't be a case</span><br><span style="color: hsl(120, 100%, 40%);">+ * where the DP stream is another slot in the DA topology. Just in case though.</span><br><span style="color: hsl(120, 100%, 40%);">+ * We don't care about removed slots in the DA topology.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Checking if DP is already in DA somewhere\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ for (j = 0; j < ast_stream_topology_get_count(delayed_active); j++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *possible_existing = ast_stream_topology_get_stream(delayed_active, j);</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *possible_existing_name = GET_STREAM_NAME_SAFE(possible_existing);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Checking %s against %s\n", session_name, dp_name, possible_existing_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strings_equal(dp_name, possible_existing_name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Pending stream %s slot %d is already in delayed active slot %d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ session_name, dp_name, i, j);</span><br><span style="color: hsl(120, 100%, 40%);">+ found_da_slot = j;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_da_stream = possible_existing;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_da_state = ast_stream_get_state(possible_existing);</span><br><span style="color: hsl(120, 100%, 40%);">+ found_da_name = ast_stream_get_name(possible_existing);</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%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Makes the subsequent code easier */</span><br><span style="color: hsl(120, 100%, 40%);">+ found_da_slot = i;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_da_stream = da_stream;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_da_state = da_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_da_name = da_name;</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_trace(-1, "%s: Found NP slot: %d Found removed NP slot: %d Found DA slot: %d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ session_name, found_np_slot, removed_np_slot, found_da_slot);</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%);">+ * Now we know whether the DP stream is new or changing state and we know if the DP</span><br><span style="color: hsl(120, 100%, 40%);">+ * stream exists in the other topologies and if so, where in those topologies it exists.</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 (!found_da_stream) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * The DP stream isn't in the DA topology which would imply that the intention of the</span><br><span style="color: hsl(120, 100%, 40%);">+ * request was to add the stream, not change its state. It's possible though that</span><br><span style="color: hsl(120, 100%, 40%);">+ * the stream was added by another request between the time this request was queued</span><br><span style="color: hsl(120, 100%, 40%);">+ * and now so we need to check the CA topology as well.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: There was no corresponding DA stream so the request was to add a stream\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (found_np_stream) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * We found it in the CA topology. Since the intention was to add it</span><br><span style="color: hsl(120, 100%, 40%);">+ * and it's already there, there's nothing to do.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: New stream requested but it's already in CA\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* OK, it's not in either which would again imply that the intention of the</span><br><span style="color: hsl(120, 100%, 40%);">+ * request was to add the stream.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: There was no corresponding NP stream\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (STATE_REMOVED(dp_state)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * How can DP request to remove a stream that doesn't seem to exist anythere?</span><br><span style="color: hsl(120, 100%, 40%);">+ * It's not. It's possible that the stream was already removed and the slot</span><br><span style="color: hsl(120, 100%, 40%);">+ * reused in the CA topology, but it would still have to exist in the DA</span><br><span style="color: hsl(120, 100%, 40%);">+ * topology. Bail.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR,</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s: Attempting to remove stream %d:%s but it doesn't exist anywhere.\n", session_name, i, dp_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * We're now sure we want to add the the stream. Since we can re-use</span><br><span style="color: hsl(120, 100%, 40%);">+ * slots in the CA topology that have streams marked as "removed", we</span><br><span style="color: hsl(120, 100%, 40%);">+ * use the slot we saved in removed_np_slot if it exists.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Checking for open slot\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (removed_np_slot >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media *old_media = AST_VECTOR_GET(&new_pending_state->sessions, removed_np_slot);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = ast_stream_topology_set_stream(new_pending, removed_np_slot, ast_stream_clone(dp_stream, NULL));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_WARNING, "%s: Couldn't set stream in new topology\n", session_name);</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%);">+ * Since we're reusing the removed_np_slot slot for something else, we need</span><br><span style="color: hsl(120, 100%, 40%);">+ * to free and remove any session media already in it.</span><br><span style="color: hsl(120, 100%, 40%);">+ * ast_stream_topology_set_stream() took care of freeing the old stream.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_VECTOR_REPLACE(&new_pending_state->sessions, removed_np_slot, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_WARNING, "%s: Couldn't replace media session\n", session_name);</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(old_media);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: Replaced removed stream in slot %d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ session_name, removed_np_slot);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ int new_slot = ast_stream_topology_append_stream(new_pending, ast_stream_clone(dp_stream, NULL));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (new_slot < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_WARNING, "%s: Couldn't append stream in new topology\n", session_name);</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_VECTOR_REPLACE(&new_pending_state->sessions, new_slot, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_WARNING, "%s: Couldn't replace media session\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: Appended new stream to slot %d\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ session_name, new_slot);</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%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * The DP stream exists in the DA topology so it's a change of some sort.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: There was a corresponding DA stream so the request was to change/remove a stream\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (dp_state == found_da_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* No change? Let's see if it's in CA */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!found_np_stream) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /*</span><br><span style="color: hsl(120, 100%, 40%);">+ * The DP and DA state are the same which would imply that the stream</span><br><span style="color: hsl(120, 100%, 40%);">+ * already exists but it's not in the CA topology. It's possible that</span><br><span style="color: hsl(120, 100%, 40%);">+ * between the time this request was queued and now the stream was removed</span><br><span style="color: hsl(120, 100%, 40%);">+ * from the CA topology and the slot used for something else. Nothing</span><br><span style="color: hsl(120, 100%, 40%);">+ * we can do here.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: Stream doesn't exist in CA so nothing to do\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (dp_state == found_np_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: States are the same all around so nothing to do\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: Something changed the CA state so we're going to leave it as is\n", session_name);</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%);">+ /* We have a state change. */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Requesting state change to %s\n", session_name, ast_stream_state2str(dp_state));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!found_np_stream) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: Stream doesn't exist in CA so nothing to do\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (da_state == found_np_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_set_state(found_np_stream, dp_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: Changed NP stream state from %s to %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ session_name, ast_stream_state2str(found_np_state), ast_stream_state2str(dp_state));</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: Something changed the CA state so we're going to leave it as is\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ session_name);</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%);">+ SCOPE_EXIT("%s: Done with slot %d\n", session_name, 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%);">+ ast_trace(-1, "%s: Resetting default media states\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_MEDIA_TYPE_END; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int j;</span><br><span style="color: hsl(120, 100%, 40%);">+ new_pending_state->default_session[i] = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ for (j = 0; j < AST_VECTOR_SIZE(&new_pending_state->sessions); j++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media *media = AST_VECTOR_GET(&new_pending_state->sessions, j);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream = ast_stream_topology_get_stream(new_pending_state->topology, j);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (media && media->type == i && !STREAM_REMOVED(stream)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ new_pending_state->default_session[i] = media;</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%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (run_post_validation) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Running post-validation\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!is_media_state_valid(session_name, new_pending_state)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(NULL, LOG_ERROR, "State not consistent\n");</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%);">+ * We need to move the new pending state to another variable and set new_pending_state to NULL</span><br><span style="color: hsl(120, 100%, 40%);">+ * so RAII_VAR doesn't free it.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ returned_media_state = new_pending_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ new_pending_state = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(returned_media_state, "%s: NP: %s\n", session_name,</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_tmp(256, ast_stream_topology_to_str(new_pending, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int sip_session_refresh(struct ast_sip_session *session,</span><br><span> ast_sip_session_request_creation_cb on_request_creation,</span><br><span> ast_sip_session_sdp_creation_cb on_sdp_creation,</span><br><span> ast_sip_session_response_cb on_response,</span><br><span> enum ast_sip_session_refresh_method method, int generate_new_sdp,</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_sip_session_media_state *media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *pending_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *active_media_state,</span><br><span> int queued)</span><br><span> {</span><br><span> pjsip_inv_session *inv_session = session->inv_session;</span><br><span> pjmedia_sdp_session *new_sdp = NULL;</span><br><span> pjsip_tx_data *tdata;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_ENTER(3, "%s: New SDP? %s Queued? %s DP: %s DA: %s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+ generate_new_sdp ? "yes" : "no", queued ? "yes" : "no",</span><br><span style="color: hsl(120, 100%, 40%);">+ pending_media_state ? ast_str_tmp(256, ast_stream_topology_to_str(pending_media_state->topology, &STR_TMP)) : "none",</span><br><span style="color: hsl(120, 100%, 40%);">+ active_media_state ? ast_str_tmp(256, ast_stream_topology_to_str(active_media_state->topology, &STR_TMP)) : "none");</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (media_state && (!media_state->topology || !generate_new_sdp)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sip_session_media_state_free(media_state);</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pending_media_state && (!pending_media_state->topology || !generate_new_sdp)) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(-1, "%s: Not sending reinvite because %s%s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+ pending_media_state->topology == NULL ? "pending topology is null " : "",</span><br><span style="color: hsl(120, 100%, 40%);">+ !generate_new_sdp ? "generate_new_sdp is false" : "");</span><br><span> }</span><br><span> </span><br><span> if (inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {</span><br><span> /* Don't try to do anything with a hung-up call */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(3, "Not sending reinvite to %s because of disconnected state...\n",</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sorcery_object_get_id(session->endpoint));</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sip_session_media_state_free(media_state);</span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(0, "%s: Not sending reinvite because of disconnected state\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session));</span><br><span> }</span><br><span> </span><br><span> /* If the dialog has not yet been established we have to defer until it has */</span><br><span> if (inv_session->dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(3, "Delay sending request to %s because dialog has not been established...\n",</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sorcery_object_get_id(session->endpoint));</span><br><span style="color: hsl(0, 100%, 40%);">- return delay_request(session, on_request_creation, on_sdp_creation, on_response,</span><br><span style="color: hsl(120, 100%, 40%);">+ res = delay_request(session, on_request_creation, on_sdp_creation, on_response,</span><br><span> generate_new_sdp,</span><br><span> method == AST_SIP_SESSION_REFRESH_METHOD_INVITE</span><br><span> ? DELAYED_METHOD_INVITE : DELAYED_METHOD_UPDATE,</span><br><span style="color: hsl(0, 100%, 40%);">- media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ pending_media_state, active_media_state ? active_media_state : ast_sip_session_media_state_clone(session->active_media_state), queued);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(res, "%s: Delay sending reinvite because dialog has not been established\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session));</span><br><span> }</span><br><span> </span><br><span> if (method == AST_SIP_SESSION_REFRESH_METHOD_INVITE) {</span><br><span> if (inv_session->invite_tsx) {</span><br><span> /* We can't send a reinvite yet, so delay it */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(3, "Delay sending reinvite to %s because of outstanding transaction...\n",</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sorcery_object_get_id(session->endpoint));</span><br><span style="color: hsl(0, 100%, 40%);">- return delay_request(session, on_request_creation, on_sdp_creation,</span><br><span style="color: hsl(0, 100%, 40%);">- on_response, generate_new_sdp, DELAYED_METHOD_INVITE, media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = delay_request(session, on_request_creation, on_sdp_creation,</span><br><span style="color: hsl(120, 100%, 40%);">+ on_response, generate_new_sdp, DELAYED_METHOD_INVITE, pending_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ active_media_state ? active_media_state : ast_sip_session_media_state_clone(session->active_media_state), queued);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(res, "%s: Delay sending reinvite because of outstanding transaction\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session));</span><br><span> } else if (inv_session->state != PJSIP_INV_STATE_CONFIRMED) {</span><br><span> /* Initial INVITE transaction failed to progress us to a confirmed state</span><br><span> * which means re-invites are not possible</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(3, "Not sending reinvite to %s because not in confirmed state...\n",</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sorcery_object_get_id(session->endpoint));</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sip_session_media_state_free(media_state);</span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(0, "%s: Not sending reinvite because not in confirmed state\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session));</span><br><span> }</span><br><span> }</span><br><span> </span><br><span>@@ -1708,19 +2200,21 @@</span><br><span> if (inv_session->neg</span><br><span> && pjmedia_sdp_neg_get_state(inv_session->neg)</span><br><span> != PJMEDIA_SDP_NEG_STATE_DONE) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(3, "Delay session refresh with new SDP to %s because SDP negotiation is not yet done...\n",</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sorcery_object_get_id(session->endpoint));</span><br><span style="color: hsl(0, 100%, 40%);">- return delay_request(session, on_request_creation, on_sdp_creation,</span><br><span style="color: hsl(120, 100%, 40%);">+ res = delay_request(session, on_request_creation, on_sdp_creation,</span><br><span> on_response, generate_new_sdp,</span><br><span> method == AST_SIP_SESSION_REFRESH_METHOD_INVITE</span><br><span style="color: hsl(0, 100%, 40%);">- ? DELAYED_METHOD_INVITE : DELAYED_METHOD_UPDATE, media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ? DELAYED_METHOD_INVITE : DELAYED_METHOD_UPDATE, pending_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ active_media_state ? active_media_state : ast_sip_session_media_state_clone(session->active_media_state), queued);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(res, "%s: Delay session refresh with new SDP because SDP negotiation is not yet done\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session));</span><br><span> }</span><br><span> </span><br><span> /* If an explicitly requested media state has been provided use it instead of any pending one */</span><br><span style="color: hsl(0, 100%, 40%);">- if (media_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (pending_media_state) {</span><br><span> int index;</span><br><span> int type_streams[AST_MEDIA_TYPE_END] = {0};</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_stream *stream;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Pending media state exists\n", ast_sip_session_get_name(session));</span><br><span> </span><br><span> /* Media state conveys a desired media state, so if there are outstanding</span><br><span> * delayed requests we need to ensure we go into the queue and not jump</span><br><span>@@ -1728,12 +2222,35 @@</span><br><span> * order.</span><br><span> */</span><br><span> if (!queued && !AST_LIST_EMPTY(&session->delayed_requests)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(3, "Delay sending reinvite to %s because of outstanding requests...\n",</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sorcery_object_get_id(session->endpoint));</span><br><span style="color: hsl(0, 100%, 40%);">- return delay_request(session, on_request_creation, on_sdp_creation,</span><br><span style="color: hsl(120, 100%, 40%);">+ res = delay_request(session, on_request_creation, on_sdp_creation,</span><br><span> on_response, generate_new_sdp,</span><br><span> method == AST_SIP_SESSION_REFRESH_METHOD_INVITE</span><br><span style="color: hsl(0, 100%, 40%);">- ? DELAYED_METHOD_INVITE : DELAYED_METHOD_UPDATE, media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ? DELAYED_METHOD_INVITE : DELAYED_METHOD_UPDATE, pending_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ active_media_state ? active_media_state : ast_sip_session_media_state_clone(session->active_media_state), queued);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(res, "%s: Delay sending reinvite because of outstanding requests\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session));</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 (active_media_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *new_pending_state;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Active media state exists\n", ast_sip_session_get_name(session));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: DP: %s\n", ast_sip_session_get_name(session), ast_str_tmp(256, ast_stream_topology_to_str(pending_media_state->topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: DA: %s\n", ast_sip_session_get_name(session), ast_str_tmp(256, ast_stream_topology_to_str(active_media_state->topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: CP: %s\n", ast_sip_session_get_name(session), ast_str_tmp(256, ast_stream_topology_to_str(session->pending_media_state->topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: CA: %s\n", ast_sip_session_get_name(session), ast_str_tmp(256, ast_stream_topology_to_str(session->active_media_state->topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ new_pending_state = resolve_refresh_media_states(ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+ pending_media_state, active_media_state, session->active_media_state, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (new_pending_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: NP: %s\n", ast_sip_session_get_name(session), ast_str_tmp(256, ast_stream_topology_to_str(new_pending_state->topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ pending_media_state = new_pending_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_reset(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s: Unable to merge media states\n", ast_sip_session_get_name(session));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> }</span><br><span> </span><br><span> /* Prune the media state so the number of streams fit within the configured limits - we do it here</span><br><span>@@ -1741,39 +2258,49 @@</span><br><span> * of the SDP when producing it we'd be in trouble. We also enforce formats here for media types that</span><br><span> * are configurable on the endpoint.</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">- for (index = 0; index < ast_stream_topology_get_count(media_state->topology); ++index) {</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_stream *existing_stream = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Pruning and checking formats of streams\n", ast_sip_session_get_name(session));</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- stream = ast_stream_topology_get_stream(media_state->topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ for (index = 0; index < ast_stream_topology_get_count(pending_media_state->topology); ++index) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *existing_stream = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream = ast_stream_topology_get_stream(pending_media_state->topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_ENTER(4, "%s: Checking stream %s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_get_name(stream));</span><br><span> </span><br><span> if (session->active_media_state->topology &&</span><br><span> index < ast_stream_topology_get_count(session->active_media_state->topology)) {</span><br><span> existing_stream = ast_stream_topology_get_stream(session->active_media_state->topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Found existing stream %s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_get_name(existing_stream));</span><br><span> }</span><br><span> </span><br><span> if (is_stream_limitation_reached(ast_stream_get_type(stream), session->endpoint, type_streams)) {</span><br><span style="color: hsl(0, 100%, 40%);">- if (index < AST_VECTOR_SIZE(&media_state->sessions)) {</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_sip_session_media *session_media = AST_VECTOR_GET(&media_state->sessions, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (index < AST_VECTOR_SIZE(&pending_media_state->sessions)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media *session_media = AST_VECTOR_GET(&pending_media_state->sessions, index);</span><br><span> </span><br><span> ao2_cleanup(session_media);</span><br><span style="color: hsl(0, 100%, 40%);">- AST_VECTOR_REMOVE(&media_state->sessions, index, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_REMOVE(&pending_media_state->sessions, index, 1);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ast_stream_topology_del_stream(media_state->topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_del_stream(pending_media_state->topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Dropped overlimit stream %s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_get_name(stream));</span><br><span> </span><br><span> /* A stream has potentially moved into our spot so we need to jump back so we process it */</span><br><span> index -= 1;</span><br><span style="color: hsl(0, 100%, 40%);">- continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue);</span><br><span> }</span><br><span> </span><br><span> /* No need to do anything with stream if it's media state is removed */</span><br><span> if (ast_stream_get_state(stream) == AST_STREAM_STATE_REMOVED) {</span><br><span> /* If there is no existing stream we can just not have this stream in the topology at all. */</span><br><span> if (!existing_stream) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_stream_topology_del_stream(media_state->topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: Dropped removed stream %s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_get_name(stream));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_del_stream(pending_media_state->topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* TODO: Do we need to remove the corresponding media state? */</span><br><span> index -= 1;</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue);</span><br><span> }</span><br><span> </span><br><span> /* Enforce the configured allowed codecs on audio and video streams */</span><br><span>@@ -1783,8 +2310,10 @@</span><br><span> </span><br><span> joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span> if (!joint_cap) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sip_session_media_state_free(media_state);</span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_EXPR(goto end, LOG_ERROR, "%s: Unable to alloc format caps\n", ast_sip_session_get_name(session));</span><br><span> }</span><br><span> ast_format_cap_get_compatible(ast_stream_get_formats(stream), session->endpoint->media.codecs, joint_cap);</span><br><span> if (!ast_format_cap_count(joint_cap)) {</span><br><span>@@ -1794,9 +2323,10 @@</span><br><span> /* If there is no existing stream we can just not have this stream in the topology</span><br><span> * at all.</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">- ast_stream_topology_del_stream(media_state->topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_del_stream(pending_media_state->topology, index);</span><br><span> index -= 1;</span><br><span style="color: hsl(0, 100%, 40%);">- continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: Dropped incompatible stream %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session), ast_stream_get_name(stream));</span><br><span> } else if (ast_stream_get_state(stream) != ast_stream_get_state(existing_stream) ||</span><br><span> strcmp(ast_stream_get_name(stream), ast_stream_get_name(existing_stream))) {</span><br><span> /* If the underlying stream is a different type or different name then we have to</span><br><span>@@ -1804,7 +2334,8 @@</span><br><span> * is preserved.</span><br><span> */</span><br><span> ast_stream_set_state(stream, AST_STREAM_STATE_REMOVED);</span><br><span style="color: hsl(0, 100%, 40%);">- continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_EXPR(continue, "%s: Dropped incompatible stream %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session), ast_stream_get_name(stream));</span><br><span> } else {</span><br><span> /* However if the stream is otherwise remaining the same we can keep the formats</span><br><span> * that exist on it already which allows media to continue to flow. We don't modify</span><br><span>@@ -1819,6 +2350,8 @@</span><br><span> }</span><br><span> </span><br><span> ++type_streams[ast_stream_get_type(stream)];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT();</span><br><span> }</span><br><span> </span><br><span> if (session->active_media_state->topology) {</span><br><span>@@ -1827,84 +2360,103 @@</span><br><span> * streams than are currently present we fill in the topology to match the current number of streams</span><br><span> * that are active.</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">- for (index = ast_stream_topology_get_count(media_state->topology);</span><br><span style="color: hsl(0, 100%, 40%);">- index < ast_stream_topology_get_count(session->active_media_state->topology); ++index) {</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_stream *cloned;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- stream = ast_stream_topology_get_stream(session->active_media_state->topology, index);</span><br><span style="color: hsl(0, 100%, 40%);">- ast_assert(stream != NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ for (index = ast_stream_topology_get_count(pending_media_state->topology);</span><br><span style="color: hsl(120, 100%, 40%);">+ index < ast_stream_topology_get_count(session->active_media_state->topology); ++index) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream = ast_stream_topology_get_stream(session->active_media_state->topology, index);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *cloned;</span><br><span style="color: hsl(120, 100%, 40%);">+ int position;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_ENTER(4, "%s: Stream %s not in pending\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_get_name(stream));</span><br><span> </span><br><span> cloned = ast_stream_clone(stream, NULL);</span><br><span> if (!cloned) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sip_session_media_state_free(media_state);</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_EXPR(goto end, LOG_ERROR, "%s: Unable to clone stream %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session), ast_stream_get_name(stream));</span><br><span> }</span><br><span> </span><br><span> ast_stream_set_state(cloned, AST_STREAM_STATE_REMOVED);</span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_stream_topology_append_stream(media_state->topology, cloned) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ position = ast_stream_topology_append_stream(pending_media_state->topology, cloned);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (position < 0) {</span><br><span> ast_stream_free(cloned);</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sip_session_media_state_free(media_state);</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ res = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_EXPR(goto end, LOG_ERROR, "%s: Unable to append cloned stream\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session));</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT("%s: Appended empty stream in position %d to make counts match\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session), position);</span><br><span> }</span><br><span> </span><br><span> /* If the resulting media state matches the existing active state don't bother doing a session refresh */</span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_stream_topology_equal(session->active_media_state->topology, media_state->topology)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sip_session_media_state_free(media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_stream_topology_equal(session->active_media_state->topology, pending_media_state->topology)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: CA: %s\n", ast_sip_session_get_name(session), ast_str_tmp(256, ast_stream_topology_to_str(session->active_media_state->topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "%s: NP: %s\n", ast_sip_session_get_name(session), ast_str_tmp(256, ast_stream_topology_to_str(pending_media_state->topology, &STR_TMP)));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span> /* For external consumers we return 0 to say success, but internally for</span><br><span> * send_delayed_request we return a separate value to indicate that this</span><br><span> * session refresh would be redundant so we didn't send it</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">- return queued ? 1 : 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(queued ? 1 : 0, "%s: Topologies are equal. Not sending re-invite\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session));</span><br><span> }</span><br><span> }</span><br><span> </span><br><span> ast_sip_session_media_state_free(session->pending_media_state);</span><br><span style="color: hsl(0, 100%, 40%);">- session->pending_media_state = media_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ session->pending_media_state = pending_media_state;</span><br><span> }</span><br><span> </span><br><span> new_sdp = generate_session_refresh_sdp(session);</span><br><span> if (!new_sdp) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_log(LOG_ERROR, "Failed to generate session refresh SDP. Not sending session refresh\n");</span><br><span> ast_sip_session_media_state_reset(session->pending_media_state);</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s: Failed to generate session refresh SDP. Not sending session refresh\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_get_name(session));</span><br><span> }</span><br><span> if (on_sdp_creation) {</span><br><span> if (on_sdp_creation(session, new_sdp)) {</span><br><span> ast_sip_session_media_state_reset(session->pending_media_state);</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s: on_sdp_creation failed\n", ast_sip_session_get_name(session));</span><br><span> }</span><br><span> }</span><br><span> }</span><br><span> </span><br><span> if (method == AST_SIP_SESSION_REFRESH_METHOD_INVITE) {</span><br><span> if (pjsip_inv_reinvite(inv_session, NULL, new_sdp, &tdata)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_log(LOG_WARNING, "Failed to create reinvite properly.\n");</span><br><span> if (generate_new_sdp) {</span><br><span> ast_sip_session_media_state_reset(session->pending_media_state);</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s: Failed to create reinvite properly\n", ast_sip_session_get_name(session));</span><br><span> }</span><br><span> } else if (pjsip_inv_update(inv_session, NULL, new_sdp, &tdata)) {</span><br><span style="color: hsl(0, 100%, 40%);">- ast_log(LOG_WARNING, "Failed to create UPDATE properly.\n");</span><br><span> if (generate_new_sdp) {</span><br><span> ast_sip_session_media_state_reset(session->pending_media_state);</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s: Failed to create UPDATE properly\n", ast_sip_session_get_name(session));</span><br><span> }</span><br><span> if (on_request_creation) {</span><br><span> if (on_request_creation(session, tdata)) {</span><br><span> if (generate_new_sdp) {</span><br><span> ast_sip_session_media_state_reset(session->pending_media_state);</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s: on_request_creation failed.\n", ast_sip_session_get_name(session));</span><br><span> }</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(3, "Sending session refresh SDP via %s to %s\n",</span><br><span style="color: hsl(0, 100%, 40%);">- method == AST_SIP_SESSION_REFRESH_METHOD_INVITE ? "re-INVITE" : "UPDATE",</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sorcery_object_get_id(session->endpoint));</span><br><span> ast_sip_session_send_request_with_cb(session, tdata, on_response);</span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+end:</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(res, "%s: Sending session refresh SDP via %s\n", ast_sip_session_get_name(session),</span><br><span style="color: hsl(120, 100%, 40%);">+ method == AST_SIP_SESSION_REFRESH_METHOD_INVITE ? "re-INVITE" : "UPDATE");</span><br><span> }</span><br><span> </span><br><span> int ast_sip_session_refresh(struct ast_sip_session *session,</span><br><span>@@ -1915,9 +2467,8 @@</span><br><span> struct ast_sip_session_media_state *media_state)</span><br><span> {</span><br><span> return sip_session_refresh(session, on_request_creation, on_sdp_creation,</span><br><span style="color: hsl(0, 100%, 40%);">- on_response, method, generate_new_sdp, media_state, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ on_response, method, generate_new_sdp, media_state, NULL, 0);</span><br><span> }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span> int ast_sip_session_regenerate_answer(struct ast_sip_session *session,</span><br><span> ast_sip_session_sdp_creation_cb on_sdp_creation)</span><br><span> {</span><br><span>@@ -2130,6 +2681,11 @@</span><br><span> return PJ_FALSE;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ if (session->inv_session->invite_tsx) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* There's a transaction in progress so bail now and let pjproject send 491 */</span><br><span style="color: hsl(120, 100%, 40%);">+ return PJ_FALSE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (session->deferred_reinvite) {</span><br><span> pj_str_t key, deferred_key;</span><br><span> pjsip_tx_data *tdata;</span><br><span>@@ -2898,7 +3454,7 @@</span><br><span> /* If this is delayed the only thing that will happen is a BYE request so we don't</span><br><span> * actually need to store the response code for when it happens.</span><br><span> */</span><br><span style="color: hsl(0, 100%, 40%);">- delay_request(session, NULL, NULL, NULL, 0, DELAYED_METHOD_BYE, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ delay_request(session, NULL, NULL, NULL, 0, DELAYED_METHOD_BYE, NULL, NULL, 1);</span><br><span> break;</span><br><span> }</span><br><span> /* Fall through */</span><br><span>@@ -3570,13 +4126,32 @@</span><br><span> {</span><br><span> pjsip_inv_session *inv = session->inv_session;</span><br><span> pj_time_val tv;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *pending_media_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *active_media_state;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *session_name = ast_sip_session_get_name(session);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- ast_debug(3, "Endpoint '%s(%s)' re-INVITE collision.\n",</span><br><span style="color: hsl(0, 100%, 40%);">- ast_sorcery_object_get_id(session->endpoint),</span><br><span style="color: hsl(0, 100%, 40%);">- session->channel ? ast_channel_name(session->channel) : "");</span><br><span style="color: hsl(0, 100%, 40%);">- if (delay_request(session, NULL, NULL, on_response, 1, DELAYED_METHOD_INVITE, NULL)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(3, "%s re-INVITE collision.\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ pending_media_state = ast_sip_session_media_state_clone(session->pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!pending_media_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "%s: Failed to clone pending media state\n", session_name);</span><br><span> return;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ active_media_state = ast_sip_session_media_state_clone(session->active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!active_media_state) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "%s: Failed to clone active media state\n", session_name);</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 (delay_request(session, NULL, NULL, on_response, 1, DELAYED_METHOD_INVITE, pending_media_state,</span><br><span style="color: hsl(120, 100%, 40%);">+ active_media_state, 1)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(pending_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(active_media_state);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "%s: Failed to add delayed request\n", session_name);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> if (pj_timer_entry_running(&session->rescheduled_reinvite)) {</span><br><span> /* Timer already running. Something weird is going on. */</span><br><span> ast_debug(1, "Endpoint '%s(%s)' re-INVITE collision while timer running!!!\n",</span><br><span>@@ -4709,6 +5284,557 @@</span><br><span> ast_sip_mod_data_set(tdata->pool, tdata->mod_data, session_module.id, MOD_DATA_NAT_HOOK, nat_hook);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_stream *test_stream_alloc(const char *name, enum ast_media_type type, enum ast_stream_state state)</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ stream = ast_stream_alloc(name, type);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!stream) {</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(stream, state);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 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%);">+static struct ast_sip_session_media *test_media_add(</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state *media_state, const char *name, enum ast_media_type type,</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_stream_state state, int position)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media *session_media = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_stream *stream = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ stream = test_stream_alloc(name, type, state);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!stream) {</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 (position >= 0 && position < ast_stream_topology_get_count(media_state->topology)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_stream_topology_set_stream(media_state->topology, position, stream);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ position = ast_stream_topology_append_stream(media_state->topology, 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%);">+ session_media = ao2_alloc_options(sizeof(*session_media), session_media_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!session_media) {</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%);">+ session_media->keepalive_sched_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ session_media->timeout_sched_id = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ session_media->type = type;</span><br><span style="color: hsl(120, 100%, 40%);">+ session_media->stream_num = position;</span><br><span style="color: hsl(120, 100%, 40%);">+ session_media->bundle_group = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(session_media->label, name);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_REPLACE(&media_state->sessions, position, session_media)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(session_media, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</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 this stream will be active in some way and it is the first of this type then consider this the default media session to match */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!media_state->default_session[type] && ast_stream_get_state(ast_stream_topology_get_stream(media_state->topology, position)) != AST_STREAM_STATE_REMOVED) {</span><br><span style="color: hsl(120, 100%, 40%);">+ media_state->default_session[type] = session_media;</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 session_media;</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 test_is_media_session_equal(struct ast_sip_session_media *left, struct ast_sip_session_media *right)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (left == right) {</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%);">+ if (!left) {</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%);">+ if (!right) {</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%);">+ return memcmp(left, right, sizeof(*left)) == 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 test_is_media_state_equal(struct ast_sip_session_media_state *left, struct ast_sip_session_media_state *right,</span><br><span style="color: hsl(120, 100%, 40%);">+ int assert_on_failure)</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%);">+ SCOPE_ENTER(2);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (left == right) {</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(1, "equal\n");</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 (!(left && right)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(!assert_on_failure);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(0, "one is null: left: %p right: %p\n", left, right);</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_topology_equal(left->topology, right->topology)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(!assert_on_failure);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(0, "topologies differ\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_SIZE(&left->sessions) != AST_VECTOR_SIZE(&right->sessions)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(!assert_on_failure);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(0, "session vector sizes different: left %lu != right %lu\n", AST_VECTOR_SIZE(&left->sessions),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_SIZE(&right->sessions));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_VECTOR_SIZE(&left->read_callbacks) != AST_VECTOR_SIZE(&right->read_callbacks)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(!assert_on_failure);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(0, "read_callback vector sizes different: left %lu != right %lu\n", AST_VECTOR_SIZE(&left->read_callbacks),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_SIZE(&right->read_callbacks));</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(&left->sessions) ; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!test_is_media_session_equal(AST_VECTOR_GET(&left->sessions, i), AST_VECTOR_GET(&right->sessions, i))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(!assert_on_failure);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(0, "Media session %d different\n", 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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_VECTOR_SIZE(&left->read_callbacks) ; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (memcmp(AST_VECTOR_GET_ADDR(&left->read_callbacks, i),</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_VECTOR_GET_ADDR(&right->read_callbacks, i),</span><br><span style="color: hsl(120, 100%, 40%);">+ sizeof(struct ast_sip_session_media_read_callback_state)) != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(!assert_on_failure);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(0, "read_callback %d different\n", 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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < AST_MEDIA_TYPE_END; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(left->default_session[i] && right->default_session[i])) {</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%);">+ if (!left->default_session[i] || !right->default_session[i]</span><br><span style="color: hsl(120, 100%, 40%);">+ || left->default_session[i]->stream_num != right->default_session[i]->stream_num) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_assert(!assert_on_failure);</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(0, "Default media session %d different. Left: %s Right: %s\n", i,</span><br><span style="color: hsl(120, 100%, 40%);">+ left->default_session[i] ? left->default_session[i]->label : "null",</span><br><span style="color: hsl(120, 100%, 40%);">+ right->default_session[i] ? right->default_session[i]->label : "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%);">+ SCOPE_EXIT_RTN_VALUE(1, "equal\n");</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_TEST_DEFINE(test_resolve_refresh_media_states)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+#define FREE_STATE() \</span><br><span style="color: hsl(120, 100%, 40%);">+({ \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(new_pending_state); \</span><br><span style="color: hsl(120, 100%, 40%);">+ new_pending_state = NULL; \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(delayed_pending_state); \</span><br><span style="color: hsl(120, 100%, 40%);">+ delayed_pending_state = NULL; \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(delayed_active_state); \</span><br><span style="color: hsl(120, 100%, 40%);">+ delayed_active_state = NULL; \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(current_active_state); \</span><br><span style="color: hsl(120, 100%, 40%);">+ current_active_state = NULL; \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sip_session_media_state_free(expected_pending_state); \</span><br><span style="color: hsl(120, 100%, 40%);">+ expected_pending_state = 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%);">+#define RESET_STATE(__num) \</span><br><span style="color: hsl(120, 100%, 40%);">+({ \</span><br><span style="color: hsl(120, 100%, 40%);">+ testnum=__num; \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "Test %d\n", testnum); \</span><br><span style="color: hsl(120, 100%, 40%);">+ test_failed = 0; \</span><br><span style="color: hsl(120, 100%, 40%);">+ delayed_pending_state = ast_sip_session_media_state_alloc(); \</span><br><span style="color: hsl(120, 100%, 40%);">+ delayed_pending_state->topology = ast_stream_topology_alloc(); \</span><br><span style="color: hsl(120, 100%, 40%);">+ delayed_active_state = ast_sip_session_media_state_alloc(); \</span><br><span style="color: hsl(120, 100%, 40%);">+ delayed_active_state->topology = ast_stream_topology_alloc(); \</span><br><span style="color: hsl(120, 100%, 40%);">+ current_active_state = ast_sip_session_media_state_alloc(); \</span><br><span style="color: hsl(120, 100%, 40%);">+ current_active_state->topology = ast_stream_topology_alloc(); \</span><br><span style="color: hsl(120, 100%, 40%);">+ expected_pending_state = ast_sip_session_media_state_alloc(); \</span><br><span style="color: hsl(120, 100%, 40%);">+ expected_pending_state->topology = ast_stream_topology_alloc(); \</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%);">+#define CHECKER() \</span><br><span style="color: hsl(120, 100%, 40%);">+({ \</span><br><span style="color: hsl(120, 100%, 40%);">+ new_pending_state = resolve_refresh_media_states("unittest", delayed_pending_state, delayed_active_state, current_active_state, 1); \</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!test_is_media_state_equal(new_pending_state, expected_pending_state, 0)) { \</span><br><span style="color: hsl(120, 100%, 40%);">+ res = AST_TEST_FAIL; \</span><br><span style="color: hsl(120, 100%, 40%);">+ test_failed = 1; \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "da: %s\n", ast_str_tmp(256, ast_stream_topology_to_str(delayed_active_state->topology, &STR_TMP))); \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "dp: %s\n", ast_str_tmp(256, ast_stream_topology_to_str(delayed_pending_state->topology, &STR_TMP))); \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "ca: %s\n", ast_str_tmp(256, ast_stream_topology_to_str(current_active_state->topology, &STR_TMP))); \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "ep: %s\n", ast_str_tmp(256, ast_stream_topology_to_str(expected_pending_state->topology, &STR_TMP))); \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "np: %s\n", ast_str_tmp(256, ast_stream_topology_to_str(new_pending_state->topology, &STR_TMP))); \</span><br><span style="color: hsl(120, 100%, 40%);">+ } \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_test_status_update(test, "Test %d %s\n", testnum, test_failed ? "FAILED" : "passed"); \</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_trace(-1, "Test %d %s\n", testnum, test_failed ? "FAILED" : "passed"); \</span><br><span style="color: hsl(120, 100%, 40%);">+ test_failed = 0; \</span><br><span style="color: hsl(120, 100%, 40%);">+ FREE_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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state * delayed_pending_state = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state * delayed_active_state = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state * current_active_state = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state * new_pending_state = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sip_session_media_state * expected_pending_state = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ enum ast_test_result_state res = AST_TEST_PASS;</span><br><span style="color: hsl(120, 100%, 40%);">+ int test_failed = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int testnum = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_ENTER(1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ info->name = "merge_refresh_topologies";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->category = "/res/res_pjsip_session/";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->summary = "Test merging of delayed request topologies";</span><br><span style="color: hsl(120, 100%, 40%);">+ info->description = "Test merging of delayed request topologies";</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(AST_TEST_NOT_RUN);</span><br><span style="color: hsl(120, 100%, 40%);">+ case TEST_EXECUTE:</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%);">+ RESET_STATE(1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(2);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(3);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(4);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(5);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(6);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(7);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo6", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo6", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(8);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(9);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(10);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_REMOVED, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(11);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "myvideo3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(12);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "292-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "296-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "292-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "296-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "297-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "294-5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "292-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "296-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "290-3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "297-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "292-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "296-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "290-3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "297-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "294-5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(13);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "293-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "292-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "294-3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "295-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "296-5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "293-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "292-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "294-3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "295-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "296-5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "298-7", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "293-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "292-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "294-3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "295-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "296-5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "290-6", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "293-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "292-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "294-3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "295-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "296-5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "290-6", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "298-7", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(14);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "298-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "297-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "298-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "294-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "295-5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "298-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "297-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "291-3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "294-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "298-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "297-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "291-3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "294-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "295-5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ RESET_STATE(15);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "298-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_active_state, "297-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "298-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDONLY, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "294-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(delayed_pending_state, "295-5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "297-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "291-3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "294-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(current_active_state, "298-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "audio", AST_MEDIA_TYPE_AUDIO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "297-2", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "291-3", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "294-4", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "298-1", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDONLY, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ test_media_add(expected_pending_state, "295-5", AST_MEDIA_TYPE_VIDEO, AST_STREAM_STATE_SENDRECV, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ CHECKER();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ SCOPE_EXIT_RTN_VALUE(res);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* TEST_FRAMEWORK */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int load_module(void)</span><br><span> {</span><br><span> pjsip_endpoint *endpt;</span><br><span>@@ -4737,12 +5863,17 @@</span><br><span> ast_sip_register_service(&outbound_invite_auth_module);</span><br><span> </span><br><span> ast_module_shutdown_ref(ast_module_info->self);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_REGISTER(test_resolve_refresh_media_states);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> return AST_MODULE_LOAD_SUCCESS;</span><br><span> }</span><br><span> </span><br><span> static int unload_module(void)</span><br><span> {</span><br><span style="color: hsl(120, 100%, 40%);">+#ifdef TEST_FRAMEWORK</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_TEST_UNREGISTER(test_resolve_refresh_media_states);</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span> ast_sip_unregister_service(&outbound_invite_auth_module);</span><br><span> ast_sip_unregister_service(&session_reinvite_module);</span><br><span> ast_sip_unregister_service(&session_module);</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/14912">change 14912</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/+/14912"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 18 </div>
<div style="display:none"> Gerrit-Change-Id: Id3440972943c611a15f652c6c569fa0e4536bfcb </div>
<div style="display:none"> Gerrit-Change-Number: 14912 </div>
<div style="display:none"> Gerrit-PatchSet: 3 </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-MessageType: merged </div>