From: Andreas Eversberg Date: Sat, 28 Jul 2012 09:16:42 +0000 (+0200) Subject: Add conference mixing to LCR's internal bridge X-Git-Url: http://git.eversberg.eu/gitweb.cgi?p=lcr.git;a=commitdiff_plain;h=a698197188e22f42a5483bcb775f66522863d871;hp=ec40766edc4b55496be56d3123b266900e15dc29 Add conference mixing to LCR's internal bridge Bride allow now to forward between two paries or mix between three to N parties. --- diff --git a/gsm_bs.cpp b/gsm_bs.cpp index 3a9ab7d..ee4bf04 100644 --- a/gsm_bs.cpp +++ b/gsm_bs.cpp @@ -179,15 +179,24 @@ void Pgsm_bs::start_dtmf_ind(unsigned int msg_type, unsigned int callref, struct send_and_free_mncc(p_g_lcr_gsm, resp->msg_type, resp); if (p_g_rtp_bridge) { - class Port *remote = bridge_remote(); - - if (remote) { - struct lcr_msg *message; - - /* send dtmf information, because we bridge RTP directly */ - message = message_create(0, remote->p_serial, EPOINT_TO_PORT, MESSAGE_DTMF); - message->param.dtmf = mncc->keypad; - message_put(message); + /* if two members are bridged */ + if (p_bridge && p_bridge->first && p_bridge->first->next && !p_bridge->first->next->next) { + class Port *remote = NULL; + + /* select other member */ + if (p_bridge->first->port == this) + remote = p_bridge->first->next->port; + if (p_bridge->first->next->port == this) + remote = p_bridge->first->port; + + if (remote) { + struct lcr_msg *message; + + /* send dtmf information, because we bridge RTP directly */ + message = message_create(0, remote->p_serial, EPOINT_TO_PORT, MESSAGE_DTMF); + message->param.dtmf = mncc->keypad; + message_put(message); + } } } else { /* generate DTMF tones, since we do audio forwarding inside LCR */ diff --git a/joinpbx.cpp b/joinpbx.cpp index ea2651b..988ba24 100644 --- a/joinpbx.cpp +++ b/joinpbx.cpp @@ -368,11 +368,11 @@ void JoinPBX::bridge(void) /* * Bridge between port instances if: - * - two relations + * - two or more relations * - one or all are not mISDN */ message = message_create(j_serial, relation->epoint_id, JOIN_TO_EPOINT, MESSAGE_BRIDGE); - message->param.bridge_id = (relations==2 && !allmISDN) ? j_serial : 0; + message->param.bridge_id = (relations>=2 && !allmISDN) ? j_serial : 0; PDEBUG(DEBUG_JOIN, "join%u EP%u requests bridge=%u\n", j_serial, relation->epoint_id, message->param.bridge_id); message_put(message); diff --git a/port.cpp b/port.cpp index a05deca..98a644f 100644 --- a/port.cpp +++ b/port.cpp @@ -47,6 +47,9 @@ Functions: #include "main.h" +/* enable to test conference mixing, even if only two members are bridged */ +//#define TEST_CONFERENCE 1 + #define SHORT_MIN -32768 #define SHORT_MAX 32767 @@ -1172,6 +1175,8 @@ void Port::update_load(void) * bridge handling */ +int bridge_timeout(struct lcr_timer *timer, void *instance, int index); + static void remove_bridge(struct port_bridge *bridge, class Port *port) { struct port_bridge **temp = &p_bridge_first; @@ -1186,6 +1191,14 @@ static void remove_bridge(struct port_bridge *bridge, class Port *port) *memberp = member->next; FREE(member, sizeof(struct port_bridge_member)); memuse--; +#ifndef TEST_CONFERENCE + if (bridge->first && bridge->first->next && !bridge->first->next->next) { +#else + if (bridge->first && !bridge->first->next) { +#endif + PDEBUG(DEBUG_PORT, "bridge %u is no conference anymore\n", bridge->bridge_id); + del_timer(&bridge->timer); + } break; } memberp = &((*memberp)->next); @@ -1260,35 +1273,153 @@ void Port::bridge(unsigned int bridge_id) *memberp = (struct port_bridge_member *) MALLOC(sizeof(struct port_bridge_member)); memuse++; (*memberp)->port = this; + /* check if bridge becomes a conference */ +#ifndef TEST_CONFERENCE + if (p_bridge->first->next && p_bridge->first->next->next && !p_bridge->first->next->next->next) { + p_bridge->first->next->next->write_p = 0; + p_bridge->first->next->next->min_space = 0; + memset(p_bridge->first->next->next->buffer, silence, sizeof((*memberp)->buffer)); +#else + if (p_bridge->first->next && !p_bridge->first->next->next) { +#endif + p_bridge->first->next->write_p = 0; + p_bridge->first->next->min_space = 0; + memset(p_bridge->first->next->buffer, silence, sizeof((*memberp)->buffer)); + p_bridge->first->write_p = 0; + p_bridge->first->min_space = 0; + memset(p_bridge->first->buffer, silence, sizeof((*memberp)->buffer)); + memset(p_bridge->sum_buffer, 0, sizeof(p_bridge->sum_buffer)); + p_bridge->read_p = 0; + add_timer(&p_bridge->timer, bridge_timeout, p_bridge, 0); + schedule_timer(&p_bridge->timer, 0, 20000); /* 20 MS */ + p_bridge->sample_count = 0; + PDEBUG(DEBUG_PORT, "bridge %u became a conference\n", p_bridge->bridge_id); + } } -class Port *Port::bridge_remote(void) +/* send data to remote Port or add to sum buffer */ +int Port::bridge_tx(unsigned char *data, int len) { - class Port *remote = NULL; + int write_p, space; + struct port_bridge_member *member; + signed long *sum; + unsigned char *buf; - /* get remote port from bridge */ + /* less than two ports, so drop */ if (!p_bridge || !p_bridge->first || !p_bridge->first->next) - return NULL; - if (p_bridge->first->port == this) - remote = p_bridge->first->next->port; - if (p_bridge->first->next->port == this) - remote = p_bridge->first->port; + return -EIO; +#ifndef TEST_CONFERENCE + /* two ports, so bridge */ + if (!p_bridge->first->next->next) { + if (p_bridge->first->port == this) + return p_bridge->first->next->port->bridge_rx(data, len); + if (p_bridge->first->next->port == this) + return p_bridge->first->port->bridge_rx(data, len); + return -EINVAL; + } +#endif + /* more than two ports... */ + member = p_bridge->first; + while (member) { + if (member->port == this) + break; + member = member->next; + } + if (!member) + return -EINVAL; + write_p = member->write_p; + /* calculate space, so write pointer will not overrun (or reach) read pointer in ring buffer */ + space = (p_bridge->read_p - write_p - 1) & (BRIDGE_BUFFER - 1); + /* clip len, if it does not fit */ + if (space < len) + len = space; + /* apply audio samples to sum buffer */ + sum = p_bridge->sum_buffer; + buf = member->buffer; + while (len--) { + sum[write_p] += audio_law_to_s32[*data]; + buf[write_p] = *data++; + write_p = (write_p + 1) & (BRIDGE_BUFFER - 1); + } + /* raise write pointer */ + member->write_p = write_p; - return remote; + return 0; } -/* send data to remote Port */ -int Port::bridge_tx(unsigned char *data, int len) +int bridge_timeout(struct lcr_timer *timer, void *instance, int index) { - class Port *remote = bridge_remote(); + struct port_bridge *bridge = (struct port_bridge *)instance; + struct port_bridge_member *member = bridge->first; + unsigned long long timer_time; + signed long *sum, sample; + unsigned char buffer[160], *buf, *d; + int i, read_p, space; + + bridge->sample_count += 160; + + /* schedule exactly 20ms from last schedule */ + timer_time = timer->timeout.tv_sec * MICRO_SECONDS + timer->timeout.tv_usec; + timer_time += 20000; /* 20 MS */ + timer->timeout.tv_sec = timer_time / MICRO_SECONDS; + timer->timeout.tv_usec = timer_time % MICRO_SECONDS; + timer->active = 1; + + while (member) { + /* calculate transmit data */ + read_p = bridge->read_p; + sum = bridge->sum_buffer; + buf = member->buffer; + d = buffer; + for (i = 0; i < 160; i++) { + sample = sum[read_p]; + sample -= audio_law_to_s32[buf[read_p]]; + buf[read_p] = silence; + if (sample < SHORT_MIN) sample = SHORT_MIN; + if (sample > SHORT_MAX) sample = SHORT_MAX; + *d++ = audio_s16_to_law[sample & 0xffff]; + read_p = (read_p + 1) & (BRIDGE_BUFFER - 1); + } + /* send data */ + member->port->bridge_rx(buffer, 160); + /* raise write pointer, if read pointer would overrun them */ + space = ((member->write_p - bridge->read_p) & (BRIDGE_BUFFER - 1)) - 160; + if (space < 0) { + space = 0; + member->write_p = read_p; +// PDEBUG(DEBUG_PORT, "bridge %u member %d has buffer underrun\n", bridge->bridge_id, member->port->p_serial); + } + /* find minimum delay */ + if (space < member->min_space) + member->min_space = space; + /* check if we should reduce buffer */ + if (bridge->sample_count >= 8000*5) { + /* reduce buffer by minimum delay */ +// PDEBUG(DEBUG_PORT, "bridge %u member %d has min space of %d samples\n", bridge->bridge_id, member->port->p_serial, member->min_space); + member->write_p = (member->write_p - member->min_space) & (BRIDGE_BUFFER - 1); + member->min_space = 1000000; /* infinite */ + } + member = member->next; + } - if (!remote) - return -EINVAL; + /* clear sample data */ + read_p = bridge->read_p; + sum = bridge->sum_buffer; + for (i = 0; i < 160; i++) { + sum[read_p] = 0; + read_p = (read_p + 1) & (BRIDGE_BUFFER - 1); + } + + /* raise read pointer */ + bridge->read_p = read_p; -// printf("Traffic: %u -> %u (bridge %u)\n", p_serial, remote->p_serial, p_bridge->bridge_id); - return remote->bridge_rx(data, len); + if (bridge->sample_count >= 8000*5) + bridge->sample_count = 0; + + return 0; } + /* receive data from remote Port (dummy, needs to be inherited) */ int Port::bridge_rx(unsigned char *data, int len) { diff --git a/port.h b/port.h index 90b7239..5ecbbbb 100644 --- a/port.h +++ b/port.h @@ -149,9 +149,14 @@ struct port_settings { int no_seconds; }; +#define BRIDGE_BUFFER 4096 + struct port_bridge_member { struct port_bridge_member *next; class Port *port; + unsigned char buffer[BRIDGE_BUFFER]; + int write_p; /* points to write position in buffer */ + int min_space; /* minimum space to calculate how much delay can be removed */ }; /* port bridge instance */ @@ -159,6 +164,10 @@ struct port_bridge { struct port_bridge *next; /* next bridge node */ unsigned int bridge_id; /* unique ID to identify bridge */ struct port_bridge_member *first; /* list of ports that are bridged */ + signed long sum_buffer[BRIDGE_BUFFER]; + int read_p; /* points to read position in buffer */ + struct lcr_timer timer; /* clock to transmit sum data */ + int sample_count; /* counter of samples since last delay check */ }; extern struct port_bridge *p_bridge_first; @@ -208,7 +217,6 @@ class Port /* audio bridging */ struct port_bridge *p_bridge; /* linked to a port bridge or NULL */ void bridge(unsigned int bridge_id); /* join a bridge */ - class Port *bridge_remote(void); /* get remote port */ int bridge_tx(unsigned char *data, int len); /* used to transmit data to remote port */ virtual int bridge_rx(unsigned char *data, int len); /* function to be inherited, so data is received */