/*! \brief Helper function that creates an outgoing channel and returns it immediately */ static struct ast_channel *dial_transfer(struct ast_channel *caller, const char *exten, const char *context) { char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1]; struct ast_channel *chan; int cause; /* Fill the variable with the extension and context we want to call */ snprintf(destination, sizeof(destination), "%s@%s", exten, context); /* Now we request that chan_local prepare to call the destination */ if (!(chan = ast_request("Local", caller->nativeformats, caller, destination, &cause))) { return NULL; } /* Before we actually dial out let's inherit appropriate information. */ ast_channel_lock_both(caller, chan); ast_connected_line_copy_from_caller(&chan->connected, &caller->caller); ast_channel_inherit_variables(caller, chan); ast_channel_datastore_inherit(caller, chan); ast_channel_unlock(chan); ast_channel_unlock(caller); /* Since the above worked fine now we actually call it and return the channel */ if (ast_call(chan, destination, 0)) { ast_hangup(chan); return NULL; } return chan; }
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 Send a pvt in with no locks held and get all locks * * \note NO locks should be held prior to calling this function * \note The pvt must have a ref held before calling this function * \note if outchan or outowner is set != NULL after calling this function * those channels are locked and reffed. * \note Batman. */ static void awesome_locking(struct local_pvt *p, struct ast_channel **outchan, struct ast_channel **outowner) { struct ast_channel *chan = NULL; struct ast_channel *owner = NULL; for (;;) { ao2_lock(p); if (p->chan) { chan = p->chan; ast_channel_ref(chan); } if (p->owner) { owner = p->owner; ast_channel_ref(owner); } ao2_unlock(p); /* if we don't have both channels, then this is very easy */ if (!owner || !chan) { if (owner) { ast_channel_lock(owner); } else if(chan) { ast_channel_lock(chan); } ao2_lock(p); } else { /* lock both channels first, then get the pvt lock */ ast_channel_lock_both(chan, owner); ao2_lock(p); } /* Now that we have all the locks, validate that nothing changed */ if (p->owner != owner || p->chan != chan) { if (owner) { ast_channel_unlock(owner); owner = ast_channel_unref(owner); } if (chan) { ast_channel_unlock(chan); chan = ast_channel_unref(chan); } ao2_unlock(p); continue; } break; } *outowner = p->owner; *outchan = p->chan; }
/*! * \internal * \brief Helper function that creates an outgoing channel and returns it immediately. This function is nearly * identical to the dial_transfer function in bridge_basic.c, however it doesn't swap the * local channel and the channel that instigated the park. */ static struct ast_channel *park_local_transfer(struct ast_channel *parker, const char *context, const char *exten, struct transfer_channel_data *parked_channel_data) { char destination[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 1]; struct ast_channel *parkee; struct ast_channel *parkee_side_2; int cause; /* Fill the variable with the extension and context we want to call */ snprintf(destination, sizeof(destination), "%s@%s", exten, context); /* Now we request that chan_local prepare to call the destination */ parkee = ast_request("Local", ast_channel_nativeformats(parker), NULL, parker, destination, &cause); if (!parkee) { return NULL; } /* Before we actually dial out let's inherit appropriate information. */ ast_channel_lock_both(parker, parkee); ast_channel_req_accountcodes(parkee, parker, AST_CHANNEL_REQUESTOR_REPLACEMENT); ast_connected_line_copy_from_caller(ast_channel_connected(parkee), ast_channel_caller(parker)); ast_channel_inherit_variables(parker, parkee); ast_channel_datastore_inherit(parker, parkee); ast_channel_unlock(parker); parkee_side_2 = ast_local_get_peer(parkee); ast_assert(parkee_side_2 != NULL); ast_channel_unlock(parkee); /* We need to have the parker subscribe to the new local channel before hand. */ if (create_parked_subscription_full(parker, ast_channel_uniqueid(parkee_side_2), 1, parked_channel_data)) { ast_channel_unref(parkee_side_2); ast_hangup(parkee); return NULL; } ast_bridge_set_transfer_variables(parkee_side_2, ast_channel_name(parker), 0); ast_channel_unref(parkee_side_2); /* Since the above worked fine now we actually call it and return the channel */ if (ast_call(parkee, destination, 0)) { ast_hangup(parkee); return NULL; } return parkee; }
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; }
/*! * \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); }