static void native_rtp_bridge_stop(struct ast_bridge *bridge, struct ast_channel *target) { struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels); struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels); enum ast_rtp_glue_result native_type; struct ast_rtp_glue *glue0, *glue1 = NULL; RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup); if (bc0 == bc1) { return; } ast_channel_lock_both(bc0->chan, bc1->chan); native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1); switch (native_type) { case AST_RTP_GLUE_RESULT_LOCAL: if (ast_rtp_instance_get_engine(instance0)->local_bridge) { ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, NULL); } if (instance1 && ast_rtp_instance_get_engine(instance1)->local_bridge) { ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, NULL); } ast_rtp_instance_set_bridged(instance0, NULL); if (instance1) { ast_rtp_instance_set_bridged(instance1, NULL); } break; case AST_RTP_GLUE_RESULT_REMOTE: if (target) { /* * If a target was provided, it is being put on hold and should expect to * receive media from Asterisk instead of what it was previously connected to. */ if (bc0->chan == target) { glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0); } else { glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0); } } break; case AST_RTP_GLUE_RESULT_FORBID: break; } if (!target && native_type != AST_RTP_GLUE_RESULT_FORBID) { glue0->update_peer(bc0->chan, NULL, NULL, NULL, NULL, 0); glue1->update_peer(bc1->chan, NULL, NULL, NULL, NULL, 0); } ast_debug(2, "Discontinued RTP bridging of '%s' and '%s' - media will flow through Asterisk core\n", ast_channel_name(bc0->chan), ast_channel_name(bc1->chan)); ast_channel_unlock(bc0->chan); ast_channel_unlock(bc1->chan); }
/*! \brief Write function for writing frames into the bridge */ static enum ast_bridge_write_result multiplexed_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame) { struct ast_bridge_channel *other; if (AST_LIST_FIRST(&bridge->channels) == AST_LIST_LAST(&bridge->channels)) { return AST_BRIDGE_WRITE_FAILED; } if (!(other = (AST_LIST_FIRST(&bridge->channels) == bridge_channel ? AST_LIST_LAST(&bridge->channels) : AST_LIST_FIRST(&bridge->channels)))) { return AST_BRIDGE_WRITE_FAILED; } if (other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) { ast_write(other->chan, frame); } return AST_BRIDGE_WRITE_SUCCESS; }
static int simple_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) { struct ast_channel *c0 = AST_LIST_FIRST(&bridge->channels)->chan; struct ast_channel *c1 = AST_LIST_LAST(&bridge->channels)->chan; /* * If this is the first channel we can't make it compatible... * unless we make it compatible with itself. O.o */ if (c0 == c1) { return 0; } return ast_channel_make_compatible(c0, c1); }
/*! \brief Join function which actually adds the channel into the array to be monitored */ static int multiplexed_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel) { struct ast_channel *c0 = AST_LIST_FIRST(&bridge->channels)->chan, *c1 = AST_LIST_LAST(&bridge->channels)->chan; struct multiplexed_thread *multiplexed_thread = bridge->bridge_pvt; ast_debug(1, "Adding channel '%s' to multiplexed thread '%p' for monitoring\n", bridge_channel->chan->name, multiplexed_thread); multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 1); /* If the second channel has not yet joined do not make things compatible */ if (c0 == c1) { return 0; } if (((c0->writeformat == c1->readformat) && (c0->readformat == c1->writeformat) && (c0->nativeformats == c1->nativeformats))) { return 0; } return ast_channel_make_compatible(c0, c1); }
static int native_rtp_bridge_compatible(struct ast_bridge *bridge) { struct ast_bridge_channel *bc0; struct ast_bridge_channel *bc1; int is_compatible; /* We require two channels before even considering native bridging */ if (bridge->num_channels != 2) { ast_debug(1, "Bridge '%s' can not use native RTP bridge as two channels are required\n", bridge->uniqueid); return 0; } bc0 = AST_LIST_FIRST(&bridge->channels); bc1 = AST_LIST_LAST(&bridge->channels); ast_channel_lock_both(bc0->chan, bc1->chan); is_compatible = native_rtp_bridge_compatible_check(bridge, bc0, bc1); ast_channel_unlock(bc0->chan); ast_channel_unlock(bc1->chan); return is_compatible; }
/*! \brief Attended transfer abort feature */ static int attended_abort_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt) { struct ast_bridge_channel *called_bridge_channel = NULL; /* It is possible (albeit unlikely) that the bridge channels list may change, so we have to ensure we do all of our magic while locked */ ao2_lock(bridge); if (AST_LIST_FIRST(&bridge->channels) != bridge_channel) { called_bridge_channel = AST_LIST_FIRST(&bridge->channels); } else { called_bridge_channel = AST_LIST_LAST(&bridge->channels); } /* Now we basically eject the other channel from the bridge. This will cause their thread to hang them up, and our own code to consider the transfer failed. */ if (called_bridge_channel) { ast_bridge_change_state(called_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP); } ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END); ao2_unlock(bridge); return 0; }
/*! * \internal * \brief Start 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 unheld. * * \note Bridge must be locked when calling this function. */ static void native_rtp_bridge_start(struct ast_bridge *bridge, struct ast_channel *target) { struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels); struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels); enum ast_rtp_glue_result native_type = AST_RTP_GLUE_RESULT_FORBID; struct ast_rtp_glue *glue0, *glue1; RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, tinstance0, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, tinstance1, NULL, ao2_cleanup); RAII_VAR(struct ast_format_cap *, cap0, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup); RAII_VAR(struct ast_format_cap *, cap1, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup); if (bc0 == bc1) { return; } ast_channel_lock_both(bc0->chan, bc1->chan); if (!bc0->suspended && !bc1->suspended) { native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1); } switch (native_type) { case AST_RTP_GLUE_RESULT_LOCAL: if (ast_rtp_instance_get_engine(instance0)->local_bridge) { ast_rtp_instance_get_engine(instance0)->local_bridge(instance0, instance1); } if (ast_rtp_instance_get_engine(instance1)->local_bridge) { ast_rtp_instance_get_engine(instance1)->local_bridge(instance1, instance0); } ast_rtp_instance_set_bridged(instance0, instance1); ast_rtp_instance_set_bridged(instance1, instance0); ast_verb(4, "Locally RTP bridged '%s' and '%s' in stack\n", ast_channel_name(bc0->chan), ast_channel_name(bc1->chan)); break; case AST_RTP_GLUE_RESULT_REMOTE: if (glue0->get_codec) { glue0->get_codec(bc0->chan, cap0); } if (glue1->get_codec) { glue1->get_codec(bc1->chan, cap1); } /* If we have a target, it's the channel that received the UNHOLD or UPDATE_RTP_PEER frame and was told to resume */ if (!target) { glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0); glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0); ast_verb(4, "Remotely bridged '%s' and '%s' - media will flow directly between them\n", ast_channel_name(bc0->chan), ast_channel_name(bc1->chan)); } else { /* * If a target was provided, it is the recipient of an unhold or an update and needs to have * its media redirected to fit the current remote bridging needs. The other channel is either * already set up to handle the new media path or will have its own set of updates independent * of this pass. */ if (bc0->chan == target) { glue0->update_peer(bc0->chan, instance1, vinstance1, tinstance1, cap1, 0); } else { glue1->update_peer(bc1->chan, instance0, vinstance0, tinstance0, cap0, 0); } } break; case AST_RTP_GLUE_RESULT_FORBID: break; } ast_channel_unlock(bc0->chan); ast_channel_unlock(bc1->chan); }
static int native_rtp_bridge_compatible(struct ast_bridge *bridge) { struct ast_bridge_channel *bc0 = AST_LIST_FIRST(&bridge->channels); struct ast_bridge_channel *bc1 = AST_LIST_LAST(&bridge->channels); enum ast_rtp_glue_result native_type; struct ast_rtp_glue *glue0, *glue1; RAII_VAR(struct ast_rtp_instance *, instance0, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, instance1, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, vinstance0, NULL, ao2_cleanup); RAII_VAR(struct ast_rtp_instance *, vinstance1, NULL, ao2_cleanup); RAII_VAR(struct ast_format_cap *, cap0, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup); RAII_VAR(struct ast_format_cap *, cap1, ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT), ao2_cleanup); int read_ptime0, read_ptime1, write_ptime0, write_ptime1; /* We require two channels before even considering native bridging */ if (bridge->num_channels != 2) { ast_debug(1, "Bridge '%s' can not use native RTP bridge as two channels are required\n", bridge->uniqueid); return 0; } if (!native_rtp_bridge_capable(bc0->chan)) { ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has features which prevent it\n", bridge->uniqueid, ast_channel_name(bc0->chan)); return 0; } if (!native_rtp_bridge_capable(bc1->chan)) { ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has features which prevent it\n", bridge->uniqueid, ast_channel_name(bc1->chan)); return 0; } if ((native_type = native_rtp_bridge_get(bc0->chan, bc1->chan, &glue0, &glue1, &instance0, &instance1, &vinstance0, &vinstance1)) == AST_RTP_GLUE_RESULT_FORBID) { ast_debug(1, "Bridge '%s' can not use native RTP bridge as it was forbidden while getting details\n", bridge->uniqueid); return 0; } if (ao2_container_count(bc0->features->dtmf_hooks) && ast_rtp_instance_dtmf_mode_get(instance0)) { ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n", bridge->uniqueid, ast_channel_name(bc0->chan)); return 0; } if (ao2_container_count(bc1->features->dtmf_hooks) && ast_rtp_instance_dtmf_mode_get(instance1)) { ast_debug(1, "Bridge '%s' can not use native RTP bridge as channel '%s' has DTMF hooks\n", bridge->uniqueid, ast_channel_name(bc1->chan)); return 0; } if ((native_type == AST_RTP_GLUE_RESULT_LOCAL) && ((ast_rtp_instance_get_engine(instance0)->local_bridge != ast_rtp_instance_get_engine(instance1)->local_bridge) || (ast_rtp_instance_get_engine(instance0)->dtmf_compatible && !ast_rtp_instance_get_engine(instance0)->dtmf_compatible(bc0->chan, instance0, bc1->chan, instance1)))) { ast_debug(1, "Bridge '%s' can not use local native RTP bridge as local bridge or DTMF is not compatible\n", bridge->uniqueid); return 0; } /* Make sure that codecs match */ if (glue0->get_codec) { glue0->get_codec(bc0->chan, cap0); } if (glue1->get_codec) { glue1->get_codec(bc1->chan, cap1); } if (ast_format_cap_count(cap0) != 0 && ast_format_cap_count(cap1) != 0 && !ast_format_cap_iscompatible(cap0, cap1)) { struct ast_str *codec_buf0 = ast_str_alloca(64); struct ast_str *codec_buf1 = ast_str_alloca(64); ast_debug(1, "Channel codec0 = %s is not codec1 = %s, cannot native bridge in RTP.\n", ast_format_cap_get_names(cap0, &codec_buf0), ast_format_cap_get_names(cap1, &codec_buf1)); return 0; } read_ptime0 = ast_format_cap_get_format_framing(cap0, ast_channel_rawreadformat(bc0->chan)); read_ptime1 = ast_format_cap_get_format_framing(cap1, ast_channel_rawreadformat(bc1->chan)); write_ptime0 = ast_format_cap_get_format_framing(cap0, ast_channel_rawwriteformat(bc0->chan)); write_ptime1 = ast_format_cap_get_format_framing(cap1, ast_channel_rawwriteformat(bc1->chan)); if (read_ptime0 != write_ptime1 || read_ptime1 != write_ptime0) { ast_debug(1, "Packetization differs between RTP streams (%d != %d or %d != %d). Cannot native bridge in RTP\n", read_ptime0, write_ptime1, read_ptime1, write_ptime0); return 0; } return 1; }