diff --git a/bridges/bridge_native_rtp.c b/bridges/bridge_native_rtp.c index 7775d41ec18316fa32146187ec8b3615b060e375..639b8946c69692f64e7c376e76c7e6fb6ba04320 100644 --- a/bridges/bridge_native_rtp.c +++ b/bridges/bridge_native_rtp.c @@ -183,10 +183,89 @@ static int native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel return 0; } -static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel *target) +/*! + * \internal + * \brief Given a bridge channel, get its RTP instance + * + * The returned ast_rtp_instance has its refcount bumped. + * + * \param bridge_channel Take a guess + * \retval NULL No RTP instance on this bridge channel + * \retval non-NULL The RTP instance on this bridge channel + */ +static struct ast_rtp_instance *bridge_channel_get_rtp_instance(struct ast_bridge_channel *bridge_channel) { - struct ast_bridge_channel *c0 = AST_LIST_FIRST(&bridge->channels); - struct ast_bridge_channel *c1 = AST_LIST_LAST(&bridge->channels); + struct ast_rtp_glue *glue; + struct ast_rtp_instance *instance; + + glue = ast_rtp_instance_get_glue(ast_channel_tech(bridge_channel->chan)->type); + if (!glue) { + return NULL; + } + + glue->get_rtp_info(bridge_channel->chan, &instance); + return instance; +} + +/*! + * \internal + * \brief Determine which two channels are bridged together + * + * Because of the nature of swapping, when the time comes for a channel to + * leave a native RTP bridge, it may be that there are more than two channels + * in the list of bridge channels. Therefore, it is important to correctly + * determine which two channels were bridged together. + * + * \param bridge The involved bridge + * \param leaving The bridge channel that is leaving the native RTP bridge + * \param[out] c0 The first bridged channel + * \param[out] c1 The second bridged channel + */ +static void find_bridged_channels(struct ast_bridge *bridge, struct ast_bridge_channel *leaving, + struct ast_bridge_channel **c0, struct ast_bridge_channel **c1) +{ + RAII_VAR(struct ast_rtp_instance *, leaving_instance, bridge_channel_get_rtp_instance(leaving), ao2_cleanup); + struct ast_bridge_channel *iter; + + if (!leaving_instance) { + return; + } + + AST_LIST_TRAVERSE(&bridge->channels, iter, entry) { + RAII_VAR(struct ast_rtp_instance *, instance, NULL, ao2_cleanup); + + if (iter == leaving) { + continue; + } + + instance = bridge_channel_get_rtp_instance(iter); + if (!instance) { + continue; + } + + if (instance == ast_rtp_instance_get_bridged(leaving_instance)) { + break; + } + } + *c0 = leaving; + *c1 = iter; + return; +} + +/*! + * \internal + * \brief Stop native RTP bridging of two channels + * + * \param bridge The bridge that had native RTP bridging happening on it + * \param target If remote RTP bridging, the channel that is placed on hold. + * \param leaving If this is called because a channel is leaving, this is the + * bridge channel that is leaving the bridge + */ +static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel *target, + struct ast_bridge_channel *leaving) +{ + struct ast_bridge_channel *c0 = NULL; + struct ast_bridge_channel *c1 = NULL; enum ast_rtp_glue_result native_type; struct ast_rtp_glue *glue0, *glue1 = NULL; RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup); @@ -194,7 +273,21 @@ static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup); - if (c0 == c1) { + if (bridge->num_channels == 2) { + c0 = AST_LIST_FIRST(&bridge->channels); + c1 = AST_LIST_LAST(&bridge->channels); + } else if (bridge->num_channels > 2) { + /* When a channel leaves a native RTP bridge, it is possible for + * more channels to exist in the bridge than when the RTP bridge + * was started. Thus we need to determine which two channels were + * bridged based on the leaving channel + */ + find_bridged_channels(bridge, leaving, &c0, &c1); + } else { + return; + } + + if (!c0 || !c1) { return; } @@ -254,7 +347,7 @@ static struct ast_frame *native_rtp_framehook(struct ast_channel *chan, struct a if (bridge) { if (f->subclass.integer == AST_CONTROL_HOLD) { - native_rtp_bridge_stop(bridge, chan); + native_rtp_bridge_stop(bridge, chan, NULL); } else if ((f->subclass.integer == AST_CONTROL_UNHOLD) || (f->subclass.integer == AST_CONTROL_UPDATE_RTP_PEER)) { native_rtp_bridge_start(bridge, chan); } @@ -420,7 +513,7 @@ static void native_rtp_bridge_leave(struct ast_bridge *bridge, struct ast_bridge { native_rtp_bridge_framehook_detach(bridge_channel); - native_rtp_bridge_stop(bridge, NULL); + native_rtp_bridge_stop(bridge, NULL, bridge_channel); } static int native_rtp_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) diff --git a/include/asterisk/bridge_technology.h b/include/asterisk/bridge_technology.h index 534eb541747943875a8ed2ac42bb404d8c6e70be..9c39f74c297253c707797697b635f1fed1664bac 100644 --- a/include/asterisk/bridge_technology.h +++ b/include/asterisk/bridge_technology.h @@ -113,6 +113,10 @@ struct ast_bridge_technology { * \brief Remove a channel from a bridging technology instance for a bridge. * * \note On entry, bridge is already locked. + * \note Do not make assumptions about the number of channels in the bridge when + * this callback is called. When a channel is swapped into a bridge for another + * channel, the leave callback is called after the new channel has been added to + * the bridge. */ void (*leave)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel); /*!