jolly has uploaded this change for review. (
https://gerrit.osmocom.org/c/osmo-mgw/+/33548
)
Change subject: ASCI: Support conference briding with 1..n connections
......................................................................
ASCI: Support conference briding with 1..n connections
An RTP bridge may have 1 or many endpoints. Each endpoint may receive,
send or echo audio back. Because transcoding is not supported, it must
be assumed that only connection receives RTP in a conference with more
than 2 connections.
A connection that receives RTP into the endpont will have "send" pointer
set to (one of) the other connection that shall send out RTP.
All connection that send RTP have "send_next" pointer set to the next
or first connection that sends RTP. They are linked in a ring.
Regular bridge with two connections: Each connection forwards audio
towards the other connection and only towards the other connection.
The "send_next" pointers are set to NULL or pointing to the other
endpoint.
connection A connection B
("sendrecv") ("sendrecv")
conn->send --------------------------->
<------------------------------------- conn->send
conn->send_next ---------------------->
<------------------------------------- conn->send_next
Conference bridge with three connections: Each connection forwards
audio to itself and also towards all other connection.
connection A connection B connection C
("confecho") ("confecho") ("confecho");
conn->send --\ conn->send --\ conn->send --\
<-----------/ <-----------/ <-----------/
conn->send_next -->
conn->send_next -->
<------------------------------------- conn->send_next
The same conference bridge but without echo: Each connection forwards
auto to all other connection but not to itself.
connection A connection B connection C
("confecho") ("confecho") ("confecho");
conn->send -------> conn->send ------->
<------------------------------------- conn->send
conn->send_next -->
conn->send_next -->
<------------------------------------- conn->send_next
Change-Id: Ic99a55ab5a3a6170e940403fadd52697e99f2f3a
---
M include/osmocom/mgcp/mgcp_trunk.h
M src/libosmo-mgcp/mgcp_endp.c
M src/libosmo-mgcp/mgcp_network.c
M src/libosmo-mgcp/mgcp_protocol.c
M src/libosmo-mgcp/mgcp_trunk.c
5 files changed, 190 insertions(+), 60 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-mgw refs/changes/48/33548/1
diff --git a/include/osmocom/mgcp/mgcp_trunk.h b/include/osmocom/mgcp/mgcp_trunk.h
index e608161..7dff2b5 100644
--- a/include/osmocom/mgcp/mgcp_trunk.h
+++ b/include/osmocom/mgcp/mgcp_trunk.h
@@ -79,6 +79,8 @@
struct mgcp_trunk *mgcp_trunk_by_name(const struct mgcp_config *cfg, const char
*epname);
int e1_trunk_nr_from_epname(unsigned int *trunk_nr, const char *epname);
struct mgcp_trunk *mgcp_trunk_by_line_num(const struct mgcp_config *cfg, unsigned int
num);
+int mgcp_trunk_connect(struct mgcp_endpoint *endp, struct mgcp_conn *exclude_conn);
+void mgcp_trunk_endp_update(struct mgcp_endpoint *endp);
/* The virtual trunk is always created on trunk id 0 for historical reasons,
* use this define constant as ID when allocating a virtual trunk. Other
diff --git a/src/libosmo-mgcp/mgcp_endp.c b/src/libosmo-mgcp/mgcp_endp.c
index 20088b7..4114cf9 100644
--- a/src/libosmo-mgcp/mgcp_endp.c
+++ b/src/libosmo-mgcp/mgcp_endp.c
@@ -629,7 +629,7 @@
/* Allocate resources */
switch (endp->trunk->trunk_type) {
case MGCP_TRUNK_VIRTUAL:
- /* No updating initaliziation required for virtual endpoints. */
+ mgcp_trunk_endp_update(endp);
break;
case MGCP_TRUNK_E1:
mgcp_e1_endp_update(endp);
diff --git a/src/libosmo-mgcp/mgcp_network.c b/src/libosmo-mgcp/mgcp_network.c
index 8850670..a03f697 100644
--- a/src/libosmo-mgcp/mgcp_network.c
+++ b/src/libosmo-mgcp/mgcp_network.c
@@ -1292,6 +1292,7 @@
struct mgcp_conn *conn_dst;
struct osmo_sockaddr *from_addr = mc->from_addr;
char ipbuf[INET6_ADDRSTRLEN];
+ int rc = 0;
/*! NOTE: This callback function implements the endpoint specific
* dispatch behaviour of an rtp bridge/proxy endpoint. It is assumed
@@ -1323,36 +1324,25 @@
return mgcp_conn_rtp_dispatch_rtp(conn_src, msg);
}
- /* Find a destination connection. */
- /* NOTE: This code path runs every time an RTP packet is received. The
- * function mgcp_find_dst_conn() we use to determine the detination
- * connection will iterate the connection list inside the endpoint.
- * Since list iterations are quite costly, we will figure out the
- * destination only once and use the optional private data pointer of
- * the connection to cache the destination connection pointer. */
- if (!conn->send) {
- conn_dst = mgcp_find_dst_conn(conn);
- conn->send = conn_dst;
- } else {
- conn_dst = conn->send;
+ /* Dispatch RTP packet to destination RTP connection(s).
+ * Only if reception is enabled and there is at least one destination,
+ * conn->send is set to that destination. TRP is forwarded to that
+ * destination. If there are multiple destinations (conference),
+ * conn->send_next points to the next destination. The loop is executed
+ * until data is sent to all destinations. If conn->send points to the
+ * receiving conn (this conn), data is also echoed back to the sender.
+ *
+ * The loop terminates if there is no other destination (conn_dst == NULL)
+ * or if it reaches the originating connection (conn_dst == conn)
+ * or if it reaches the destination where it started (conn_dst == conn->send).
+ */
+ if ((conn_dst = conn->send)) {
+ do {
+ rc = mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
+ conn_dst = conn_dst->send_next;
+ } while (conn_dst && conn_dst != conn && conn_dst != conn->send);
}
-
- /* There is no destination conn, stop here */
- if (!conn_dst) {
- LOGPCONN(conn, DRTP, LOGL_DEBUG,
- "no connection to forward an incoming RTP packet to\n");
- return -1;
- }
-
- /* The destination conn is not an RTP connection */
- if (conn_dst->type != MGCP_CONN_TYPE_RTP) {
- LOGPCONN(conn, DRTP, LOGL_ERROR,
- "unable to find suitable destination conn\n");
- return -1;
- }
-
- /* Dispatch RTP packet to destination RTP connection */
- return mgcp_conn_rtp_dispatch_rtp(&conn_dst->u.rtp, msg);
+ return rc;
}
/*! dispatch incoming RTP packet to E1 subslot, handle RTCP packets locally.
@@ -1397,18 +1387,8 @@
* \param[in] conn Connection that is about to be removed (ignored). */
void mgcp_cleanup_rtp_bridge_cb(struct mgcp_endpoint *endp, struct mgcp_conn *conn)
{
- struct mgcp_conn *conn_cleanup;
-
- /* In mgcp_dispatch_rtp_bridge_cb() we use conn->priv to cache the
- * pointer to the destination connection, so that we do not have
- * to go through the list every time an RTP packet arrives. To prevent
- * a use-after-free situation we invalidate this information for all
- * connections present when one connection is removed from the
- * endpoint. */
- llist_for_each_entry(conn_cleanup, &endp->conns, entry) {
- if (conn_cleanup->send == conn)
- conn_cleanup->send = NULL;
- }
+ /* Restructure connections with this connection excluded. */
+ mgcp_trunk_connect(endp, conn);
}
/*! cleanup an endpoint when a connection on an E1 endpoint is removed.
diff --git a/src/libosmo-mgcp/mgcp_protocol.c b/src/libosmo-mgcp/mgcp_protocol.c
index 6223ffa..c4149ab 100644
--- a/src/libosmo-mgcp/mgcp_protocol.c
+++ b/src/libosmo-mgcp/mgcp_protocol.c
@@ -966,24 +966,6 @@
return create_err_response(endp, endp, 517, "CRCX", pdata->trans);
}
- /* Check if we are able to accept the creation of another connection */
- if (llist_count(&endp->conns) >= endp->type->max_conns) {
- LOGPENDP(endp, DLMGCP, LOGL_ERROR,
- "CRCX: endpoint full, max. %i connections allowed!\n",
- endp->type->max_conns);
- if (trunk->force_realloc) {
- /* There is no more room for a connection, make some
- * room by blindly tossing the oldest of the two two
- * connections */
- mgcp_conn_free_oldest(endp);
- } else {
- /* There is no more room for a connection, leave
- * everything as it is and return with an error */
- rate_ctr_inc(rate_ctr_group_get_ctr(rate_ctrs, MGCP_CRCX_FAIL_LIMIT_EXCEEDED));
- return create_err_response(endp, endp, 540, "CRCX", pdata->trans);
- }
- }
-
/* Check if this endpoint already serves a call, if so, check if the
* callids match up so that we are sure that this is our call */
if (endp->callid && mgcp_verify_call_id(endp, callid)) {
diff --git a/src/libosmo-mgcp/mgcp_trunk.c b/src/libosmo-mgcp/mgcp_trunk.c
index 69750f8..da685e1 100644
--- a/src/libosmo-mgcp/mgcp_trunk.c
+++ b/src/libosmo-mgcp/mgcp_trunk.c
@@ -24,6 +24,7 @@
#include <osmocom/mgcp/mgcp.h>
#include <osmocom/mgcp/mgcp_protocol.h>
#include <osmocom/mgcp/mgcp_endp.h>
+#include <osmocom/mgcp/mgcp_conn.h>
#include <osmocom/mgcp/mgcp_trunk.h>
#include <osmocom/mgcp/mgcp_e1.h>
#include <osmocom/abis/e1_input.h>
@@ -306,3 +307,115 @@
return NULL;
}
+
+/* Interconnect payload of connections depending on their mode.
+ * Optionally exclude a connection that shall be removed from the endpoint.
+ *
+ * A connection that receives RTP into the endpont will have "send" pointer
+ * set to (one of) the other connection that shall send out RTP.
+ *
+ * All connection that send RTP have "send_next" pointer set to the next
+ * or first connection that sends RTP. They are linked in a ring.
+ *
+ * Regular bridge with two connections: Each connection forwards audio
+ * towards the other connection and only towards the other connection.
+ * The "send_next" pointers are pointing to the other endpoint.
+
+ * connection A connection B
+ * ("sendrecv") ("sendrecv")
+ * conn->send --------------------------->
+ * <------------------------------------- conn->send
+ * conn->send_next ---------------------->
+ * <------------------------------------- conn->send_next
+ *
+ * Conference bridge with three connections: Each connection forwards
+ * audio to itself and also towards all other connection.
+ *
+ * connection A connection B connection C
+ * ("confecho") ("confecho") ("confecho");
+ * conn->send --\ conn->send --\ conn->send --\
+ * <-----------/ <-----------/ <-----------/
+ * conn->send_next -->
+ * conn->send_next -->
+ * <------------------------------------- conn->send_next
+ *
+ * The same conference bridge but without echo: Each connection forwards
+ * auto to all other connection but not to itself.
+ *
+ * connection A connection B connection C
+ * ("confecho") ("confecho") ("confecho");
+ * conn->send -------> conn->send ------->
+ * <------------------------------------- conn->send
+ * conn->send_next -->
+ * conn->send_next -->
+ * <------------------------------------- conn->send_next
+ *
+ */
+int mgcp_trunk_connect(struct mgcp_endpoint *endp, struct mgcp_conn *exclude_conn)
+{
+ struct mgcp_conn *conn = NULL, *first_conn = NULL, *previous_conn, *send_conn;
+
+ /* Link all _sending_ connection in a circular linked list, if there is
+ * more than one sending endpoint. */
+ llist_for_each_entry(conn, &endp->conns, entry) {
+ if (conn == exclude_conn)
+ continue;
+ if (conn->type != MGCP_CONN_TYPE_RTP)
+ continue;
+ if (!(conn->mode & MGCP_CONN_SEND_ONLY))
+ continue;
+ if (!first_conn)
+ first_conn = conn;
+ else
+ previous_conn->send_next = conn;
+ previous_conn = conn;
+ }
+ if (first_conn && previous_conn != first_conn)
+ previous_conn->send_next = first_conn;
+
+ /* Link all _receiving_ connections to a sending connection, if any.
+ * If the receiving connection also gets looped back what it receives, it is linked to
itsef, so that the
+ * payload is also forwarded to itself. */
+ llist_for_each_entry(conn, &endp->conns, entry) {
+ if (conn == exclude_conn)
+ continue;
+ if (conn->type != MGCP_CONN_TYPE_RTP)
+ continue;
+ if (!(conn->mode & MGCP_CONN_RECV_ONLY))
+ continue;
+ /* If we are also sending to ourself, our connection is the first sending endpoint. */
+ if ((conn->mode & MGCP_CONN_LOOPBACK) && (conn->mode &
MGCP_CONN_SEND_ONLY))
+ conn->send = conn;
+ /* If we are not sending to ourself, select the next sending connection in the circular
list. */
+ else if (conn->send_next)
+ conn->send = conn->send_next;
+ /* I case we are not in the ciruclar list, search for the first node in that list. */
+ else {
+ llist_for_each_entry(send_conn, &endp->conns, entry) {
+ if ((send_conn->mode & MGCP_CONN_SEND_ONLY))
+ conn->send = send_conn;
+ }
+ }
+ }
+
+ /* Debug the result. */
+ LOGP(DLMGCP, LOGL_DEBUG, "Endpoint %s has the following interconnections:\n",
endp->name);
+ llist_for_each_entry(conn, &endp->conns, entry) {
+ LOGP(DLMGCP, LOGL_DEBUG, " -> Connection ID %s %s incoming payload.\n",
conn->id,
+ (conn->mode & MGCP_CONN_RECV_ONLY) ? "receives" :
"ignores");
+ send_conn = conn->send;
+ while (send_conn) {
+ LOGP(DLMGCP, LOGL_DEBUG, " Payload is forwarded to ID %s\n",
send_conn->id);
+ send_conn = send_conn->send_next;
+ if (send_conn == conn || send_conn == conn->send)
+ break;
+ }
+ }
+
+ return 0;
+}
+
+void mgcp_trunk_endp_update(struct mgcp_endpoint *endp)
+{
+ mgcp_trunk_connect(endp, NULL);
+}
--
To view, visit
https://gerrit.osmocom.org/c/osmo-mgw/+/33548
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-mgw
Gerrit-Branch: master
Gerrit-Change-Id: Ic99a55ab5a3a6170e940403fadd52697e99f2f3a
Gerrit-Change-Number: 33548
Gerrit-PatchSet: 1
Gerrit-Owner: jolly <andreas(a)eversberg.eu>
Gerrit-MessageType: newchange