]> git.ipfire.org Git - thirdparty/asterisk.git/commitdiff
bridge_channel: Don't settle owed events on an optimization. 99/2299/2
authorRichard Mudgett <rmudgett@digium.com>
Sat, 20 Feb 2016 01:06:14 +0000 (19:06 -0600)
committerRichard Mudgett <rmudgett@digium.com>
Mon, 29 Feb 2016 18:50:19 +0000 (12:50 -0600)
Local channel optimization could cause DTMF digits to be duplicated.
Pending DTMF end events would be posted to a bridge when the local channel
optimizes out and is replaced by the channel further down the chain.  When
the real digit ends, the channel would get another DTMF end posted to the
bridge.

A -- LocalA;1/n -- LocalA;2/n -- LocalB;1 -- LocalB;2 -- B

1) LocalA has the /n flag to prevent optimization.
2) B is sending DTMF to A through the local channel chain.
3) When LocalB optimizes out it can move B to the position of LocalB;1
4) Without this patch, when B swaps with LocalB;1 then LocalB;1 would
settle an owed DTMF end to the bridge toward LocalA;2.
5) When B finally ends its DTMF it sends the DTMF end down the chain.
6) Without this patch, A would hear the DTMF digit end when LocalB
optimizes out and when B ends the original digit.

ASTERISK-25582

Change-Id: I1bbd28b8b399c0fb54985a5747f330a4cd2aa251

include/asterisk/bridge_channel_internal.h
main/bridge.c
main/bridge_channel.c

index e3fb73d7e13e0a76fc45dfb70e20fb79cb3ad39b..7f7d5a88b14583204331175cf690e3dcaccf5c8c 100644 (file)
@@ -84,7 +84,7 @@ struct ast_bridge_channel *bridge_channel_internal_alloc(struct ast_bridge *brid
 
 /*!
  * \internal
- * \brief Clear owed events by the channel to the original bridge.
+ * \brief Settle owed events by the channel to the original bridge.
  * \since 12.0.0
  *
  * \param orig_bridge Original bridge the channel was in before leaving.
@@ -116,6 +116,27 @@ void bridge_channel_settle_owed_events(struct ast_bridge *orig_bridge, struct as
  */
 int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel);
 
+/*!
+ * \internal
+ * \brief Push the bridge channel into its specified bridge.
+ * \since 13.8.0
+ *
+ * \param bridge_channel Channel to push.
+ * \param optimized non-zero if the push with swap is for an optimization.
+ *
+ * \note A ref is not held by bridge_channel->swap when calling because the
+ * push with swap happens immediately.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.  The channel did not get pushed.
+ *
+ * \note On failure the caller must call
+ * ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
+ */
+int bridge_channel_internal_push_full(struct ast_bridge_channel *bridge_channel, int optimized);
+
 /*!
  * \internal
  * \brief Pull the bridge channel out of its current bridge.
index b5c59514c5b2c5ed83930faf3adb2799b9c768df..9d8d807f817992547d18fb13bd28f88f2bd08cf3 100644 (file)
@@ -2187,7 +2187,7 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri
 
        bridge_channel_moving(bridge_channel, orig_bridge, dst_bridge);
 
-       if (bridge_channel_internal_push(bridge_channel)) {
+       if (bridge_channel_internal_push_full(bridge_channel, optimized)) {
                /* Try to put the channel back into the original bridge. */
                ast_bridge_features_remove(bridge_channel->features,
                        AST_BRIDGE_HOOK_REMOVE_ON_PULL);
@@ -2200,7 +2200,6 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri
                                        AST_BRIDGE_HOOK_REMOVE_ON_PULL);
                                ast_bridge_channel_leave_bridge(bridge_channel,
                                        BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, bridge_channel->bridge->cause);
-                               bridge_channel_settle_owed_events(orig_bridge, bridge_channel);
                        }
                } else {
                        ast_bridge_channel_leave_bridge(bridge_channel,
@@ -2208,7 +2207,7 @@ int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri
                        bridge_channel_settle_owed_events(orig_bridge, bridge_channel);
                }
                res = -1;
-       } else {
+       } else if (!optimized) {
                bridge_channel_settle_owed_events(orig_bridge, bridge_channel);
        }
 
index 3874e50ff6deb54592a65f0459de6c23c54910e9..fa15b0f71523a5200d5430c755da1b539b3ce6ee 100644 (file)
@@ -675,6 +675,22 @@ static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel,
        return 0;
 }
 
+/*!
+ * \internal
+ * \brief Cancel owed events by the channel to the bridge.
+ * \since 13.8.0
+ *
+ * \param bridge_channel Channel that owes events to the bridge.
+ *
+ * \note On entry, the bridge_channel->bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_cancel_owed_events(struct ast_bridge_channel *bridge_channel)
+{
+       bridge_channel->owed.dtmf_digit = '\0';
+}
+
 void bridge_channel_settle_owed_events(struct ast_bridge *orig_bridge, struct ast_bridge_channel *bridge_channel)
 {
        if (bridge_channel->owed.dtmf_digit) {
@@ -2037,7 +2053,7 @@ void bridge_channel_internal_pull(struct ast_bridge_channel *bridge_channel)
        ast_bridge_publish_leave(bridge, bridge_channel->chan);
 }
 
-int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
+int bridge_channel_internal_push_full(struct ast_bridge_channel *bridge_channel, int optimized)
 {
        struct ast_bridge *bridge = bridge_channel->bridge;
        struct ast_bridge_channel *swap;
@@ -2073,6 +2089,9 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
                /* This flag is cleared so the act of this channel leaving does not cause it to dissolve if need be */
                ast_clear_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY);
 
+               if (optimized) {
+                       bridge_channel_cancel_owed_events(swap);
+               }
                ast_bridge_channel_leave_bridge(swap, BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, 0);
                bridge_channel_internal_pull(swap);
 
@@ -2112,6 +2131,11 @@ int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
        return 0;
 }
 
+int bridge_channel_internal_push(struct ast_bridge_channel *bridge_channel)
+{
+       return bridge_channel_internal_push_full(bridge_channel, 0);
+}
+
 /*!
  * \internal
  * \brief Handle bridge channel control frame action.