Updated Libs

This commit is contained in:
Andreas Eversberg 2021-11-07 07:28:12 +01:00
parent e1dcbb89db
commit 68874b3b22
16 changed files with 965 additions and 151 deletions

299
src/isdn/ph_socket.c Normal file
View File

@ -0,0 +1,299 @@
/* PH-socket, a lightweight ISDN physical layer interface
*
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <time.h>
#include "../libdebug/debug.h"
#include "ph_socket.h"
#define CHAN s->name
int ph_socket_init(ph_socket_t *s, void *priv, const char *socket_name, int server)
{
int rc, flags;
memset(s, 0, sizeof(*s));
s->name = socket_name;
s->priv = priv;
s->tx_list_tail = &s->tx_list;
memset(&s->sock_address, 0, sizeof(s->sock_address));
s->sock_address.sun_family = AF_UNIX;
strncpy(s->sock_address.sun_path, socket_name, sizeof(s->sock_address.sun_path) - 1);
if (server) {
unlink(s->name);
rc = socket(PF_UNIX, SOCK_STREAM, 0);
if (rc < 0) {
PDEBUG_CHAN(DPH, DEBUG_ERROR, "Failed to create UNIX socket.\n");
return rc;
}
s->listen = rc;
rc = bind(s->listen, (struct sockaddr *)(&s->sock_address), SUN_LEN(&s->sock_address));
if (rc < 0) {
PDEBUG_CHAN(DPH, DEBUG_ERROR, "Failed to bind UNIX socket with path '%s' (errno = %d (%s)).\n", s->name, errno, strerror(errno));
return rc;
}
rc = listen(s->listen, 1);
if (rc < 0) {
PDEBUG_CHAN(DPH, DEBUG_ERROR, "Failed to listen to UNIX socket with path '%s' (errno = %d (%s)).\n", s->name, errno, strerror(errno));
return rc;
}
/* set nonblocking io */
flags = fcntl(s->listen, F_GETFL);
flags |= O_NONBLOCK;
fcntl(s->listen, F_SETFL, flags);
}
PDEBUG_CHAN(DPH, DEBUG_INFO, "Created PH-socket at '%s'.\n", s->name);
return 0;
}
static void close_connection(ph_socket_t *s)
{
struct socket_msg_list *ml;
uint8_t disable = PH_CTRL_DISABLE;
if (s->connect <= 0)
return;
PDEBUG_CHAN(DPH, DEBUG_INFO, "Connection from PH-socket closed.\n");
/* indicate loss of socket connection */
ph_socket_rx_msg(s, 0, (s->listen > 0) ? PH_PRIM_CTRL_REQ : PH_PRIM_CTRL_IND, &disable, 1);
close(s->connect);
s->connect = 0;
while ((ml = s->tx_list)) {
s->tx_list = ml->next;
free(ml);
}
s->tx_list_tail = &s->tx_list;
if (s->rx_msg) {
free(s->rx_msg);
s->rx_msg = NULL;
}
/* set timer, so that retry is delayed */
static struct timespec tv;
clock_gettime(CLOCK_REALTIME, &tv);
double now = (double)tv.tv_sec + (double)tv.tv_nsec / 1000000000.0;
s->connect_timer = now + SOCKET_RETRY_TIMER;
}
void ph_socket_exit(ph_socket_t *s)
{
PDEBUG_CHAN(DPH, DEBUG_INFO, "Destroyed PH-socket.\n");
close_connection(s);
if (s->listen > 0) {
unlink(s->name);
close(s->listen);
s->listen = 0;
}
}
int ph_socket_work(ph_socket_t *s)
{
uint8_t enable = PH_CTRL_ENABLE;
int rc, flags;
int work = 0;
if (s->listen > 0) {
struct sockaddr_un sock_address;
socklen_t sock_len = sizeof(sock_address);
/* see if there is an incoming connection */
rc = accept(s->listen, (struct sockaddr *)&sock_address, &sock_len);
if (rc > 0) {
if (s->connect > 0) {
PDEBUG_CHAN(DPH, DEBUG_ERROR, "Rejecting incoming connection, because we already have a client connected!\n");
close(rc);
} else {
PDEBUG_CHAN(DPH, DEBUG_INFO, "Connection from PH-socket client.\n");
s->connect = rc;
/* set nonblocking io */
flags = fcntl(s->connect, F_GETFL);
flags |= O_NONBLOCK;
fcntl(s->connect, F_SETFL, flags);
/* reset rx buffer */
s->rx_header_index = 0;
s->rx_data_index = 0;
/* indicate established socket connection */
ph_socket_rx_msg(s, 0, PH_PRIM_CTRL_REQ, &enable, 1);
}
work = 1;
}
} else {
/* open connection, if closed */
if (s->connect <= 0) {
static struct timespec tv;
clock_gettime(CLOCK_REALTIME, &tv);
double now = (double)tv.tv_sec + (double)tv.tv_nsec / 1000000000.0;
if (s->connect_timer < now) {
PDEBUG_CHAN(DPH, DEBUG_DEBUG, "Trying to connect to PH-socket server.\n");
rc = socket(PF_UNIX, SOCK_STREAM, 0);
if (rc < 0) {
PDEBUG_CHAN(DPH, DEBUG_ERROR, "Failed to create UNIX socket.\n");
s->connect_timer = now + SOCKET_RETRY_TIMER;
return 0;
}
s->connect = rc;
/* connect */
rc = connect(s->connect, (struct sockaddr *)&s->sock_address, SUN_LEN(&s->sock_address));
if (rc < 0 && errno != EAGAIN) {
if (!s->connect_failed)
PDEBUG_CHAN(DPH, DEBUG_NOTICE, "Failed to connect UNIX socket, retrying...\n");
close(s->connect);
s->connect_failed = 1;
s->connect = 0;
s->connect_timer = now + SOCKET_RETRY_TIMER;
return 0;
}
s->connect_failed = 0;
PDEBUG_CHAN(DPH, DEBUG_INFO, "Connection to PH-socket server.\n");
/* set nonblocking io */
flags = fcntl(s->connect, F_GETFL);
flags |= O_NONBLOCK;
fcntl(s->connect, F_SETFL, flags);
/* reset rx buffer */
s->rx_header_index = 0;
s->rx_data_index = 0;
/* indicate established socket connection */
ph_socket_rx_msg(s, 0, PH_PRIM_CTRL_IND, &enable, 1);
}
}
}
/* handle connection, if any */
if (s->connect > 0) {
if (!s->rx_msg)
s->rx_msg = calloc(1, sizeof(*s->rx_msg));
rx_again:
if (s->rx_header_index < (int)sizeof(s->rx_msg->msg.header)) {
/* read header until complete */
rc = recv(s->connect, ((uint8_t *)&s->rx_msg->msg.header) + s->rx_header_index, sizeof(s->rx_msg->msg.header) - s->rx_header_index, 0);
if (rc > 0) {
s->rx_header_index += rc;
work = 1;
goto rx_again;
} else if (rc == 0 || errno != EAGAIN) {
close_connection(s);
work = 1;
}
} else if (s->rx_data_index < s->rx_msg->msg.header.length) {
/* read data until complete */
rc = recv(s->connect, s->rx_msg->msg.data + s->rx_data_index, s->rx_msg->msg.header.length - s->rx_data_index, 0);
if (rc > 0) {
s->rx_data_index += rc;
work = 1;
goto rx_again;
} else if (rc == 0 || errno != EAGAIN) {
close_connection(s);
work = 1;
}
} else {
/* process and free message */
if (s->rx_msg->msg.header.prim != PH_PRIM_DATA_REQ
&& s->rx_msg->msg.header.prim != PH_PRIM_DATA_IND) {
PDEBUG_CHAN(DPH, DEBUG_DEBUG, "message 0x%02x channel %d from socket\n", s->rx_msg->msg.header.prim, s->rx_msg->msg.header.channel);
if (s->rx_msg->msg.header.length)
PDEBUG_CHAN(DPH, DEBUG_DEBUG, " -> data:%s\n", debug_hex(s->rx_msg->msg.data, s->rx_msg->msg.header.length));
}
ph_socket_rx_msg(s, s->rx_msg->msg.header.channel, s->rx_msg->msg.header.prim, s->rx_msg->msg.data, s->rx_msg->msg.header.length);
free(s->rx_msg);
s->rx_msg = NULL;
/* reset rx buffer */
s->rx_header_index = 0;
s->rx_data_index = 0;
}
tx_again:
if (s->tx_list) {
/* some frame in tx list, so try sending it */
rc = send(s->connect, ((uint8_t *)&s->tx_list->msg.header), sizeof(s->tx_list->msg.header) + s->tx_list->msg.header.length, 0);
if (rc > 0) {
struct socket_msg_list *ml;
if (rc != (int)sizeof(s->tx_list->msg.header) + s->tx_list->msg.header.length) {
PDEBUG_CHAN(DPH, DEBUG_ERROR, "Short write, please fix handling!\n");
}
/* remove list entry */
ml = s->tx_list;
s->tx_list = ml->next;
if (s->tx_list == NULL)
s->tx_list_tail = &s->tx_list;
free(ml);
work = 1;
goto tx_again;
} else if (rc == 0 || errno != EAGAIN) {
close_connection(s);
work = 1;
}
}
}
return work;
}
void ph_socket_tx_msg(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length)
{
struct socket_msg_list *tx_msg;
if (prim != PH_PRIM_DATA_REQ
&& prim != PH_PRIM_DATA_IND) {
PDEBUG_CHAN(DPH, DEBUG_DEBUG, "message 0x%02x channel %d to socket\n", prim, channel);
if (length)
PDEBUG_CHAN(DPH, DEBUG_DEBUG, " -> data:%s\n", debug_hex(data, length));
}
if (length > (int)sizeof(tx_msg->msg.data)) {
PDEBUG_CHAN(DPH, DEBUG_NOTICE, "Frame from HDLC process too large for socket, dropping!\n");
return;
}
if (s->connect <= 0) {
PDEBUG_CHAN(DPH, DEBUG_NOTICE, "Dropping message for socket, socket is closed!\n");
return;
}
tx_msg = calloc(1, sizeof(*tx_msg));
tx_msg->msg.header.channel = channel;
tx_msg->msg.header.prim = prim;
if (length) {
tx_msg->msg.header.length = length;
memcpy(tx_msg->msg.data, data, length);
}
/* move message to list */
*s->tx_list_tail = tx_msg;
s->tx_list_tail = &tx_msg->next;
}

110
src/isdn/ph_socket.h Normal file
View File

@ -0,0 +1,110 @@
#include <sys/un.h>
/*
* Procedure:
*
* If socket connection is establised, a PH_PRIM_CTRL_REQ message with
* PH_CTRL_ENABLE information is received by the socket server user.
* If the socket connection is lost, a PH_PRIM_CTRL_REQ message with
* PH_CTRL_DISABLE information is received by the user.
*
* If socket connection is establised, a PH_PRIM_CTRL_IND message with
* PH_CTRL_ENABLE information is received by the socket client user.
* If the socket connection is lost, a PH_PRIM_CTRL_IND message with
* PH_CTRL_DISABLE information is received by the user.
*
* The socket server should enable or disable interface depending on
* the PH_CTRL_ENABLE / PH_CTRL_DISABLE information.
*
* The socket client user shall keep track of last PH_PRIM_ACT_IND /
* PH_PRIM_DEACT_IND message and treat a PH_PRIM_CTRL_IND message with
* PH_CTRL_DISABLE information as a deactivation of all channels that
* were activated. Also it shall reject every PH_RIM_ACT_REQ with a
* PH_PRIM_DACT_IND, if the socket is currently unavailable.
*
* PH_PRIM_CTRL_REQ and PH_PRIM_CTRL_IND messages with PH_CTRL_ENABLE
* and PH_CTRL_DISABLE informations are not assoicated with a channel
* number. The socket sender shall set it to 0, the receiver shall
* ignore it.
*
* A missing MODE in PH_PRIM_ACT_REQ is interepreted as default:
* HDLC on D-channel, TRANS on B-channel.
*
* Each packet on the socket shall have the follwoing header:
* uint8_t channel;
* uint8_t prim;
* uint16_t length;
*
* The length shall be in host's endian on UN sockets and in network
* endian on TCP sockets and not being transmitted on UDP sockets.
*
* 0 to 65535 bytes shall follow the header, depending on the length
* information field.
*/
/* all primitives */
#define PH_PRIM_DATA_REQ 0x00 /* any data sent to channel from upper layer */
#define PH_PRIM_DATA_IND 0x01 /* any data received from channel to upper layer */
#define PH_PRIM_DATA_CNF 0x02 /* confirm data sent to channel */
#define PH_PRIM_CTRL_REQ 0x04 /* implementation specific requests towards interface */
#define PH_PRIM_CTRL_IND 0x05 /* implementation specific indications from interface */
#define PH_PRIM_ACT_REQ 0x08 /* activation request of channel, mode is given as payload */
#define PH_PRIM_ACT_IND 0x09 /* activation indication that the channel is now active */
#define PH_PRIM_DACT_REQ 0x0c /* deactivation request of channel */
#define PH_PRIM_DACT_IND 0x0d /* deactivation indication that the channel is now inactive */
/* one byte sent activation request */
#define PH_MODE_TRANS 0x00 /* raw data is sent via B-channel */
#define PH_MODE_HDLC 0x01 /* HDLC transcoding is performed via B-channel */
/* one byte sent with control messages */
#define PH_CTRL_DISABLE 0x00 /* disable (block) interface, when socket is disconnected */
#define PH_CTRL_ENABLE 0x01 /* enable (unblock) interface, when socket is connected */
#define PH_CTRL_LOOP_DISABLE 0x04 /* disable loopback */
#define PH_CTRL_LOOP1_ENABLE 0x05 /* enable LT transceier loopback */
#define PH_CTRL_LOOP2_ENABLE 0x06 /* enable NT transceier loopback */
#define PH_CTRL_LOOP_ERROR 0x10 /* frame error report (loopback test) */
#define PH_CTRL_VIOLATION_LT 0x11 /* code violation received by LT */
#define PH_CTRL_VIOLATION_NT 0x12 /* code violation received by NT */
struct socket_msg {
struct {
uint8_t channel;
uint8_t prim;
uint16_t length;
} header;
uint8_t data[65536];
} __attribute__((packed));
struct socket_msg_list {
struct socket_msg_list *next;
struct socket_msg msg;
};
#define SOCKET_RETRY_TIMER 2.0
typedef struct ph_socket {
const char *name;
void *priv;
struct sockaddr_un sock_address;
int listen; /* socket to listen to incoming connections */
int connect; /* socket of incoming connection */
int connect_failed; /* used to print a failure only once */
double connect_timer; /* time when to connect again */
struct socket_msg_list *tx_list, **tx_list_tail;
struct socket_msg_list *rx_msg;
int rx_header_index;
int rx_data_index;
} ph_socket_t;
int ph_socket_init(ph_socket_t *s, void *priv, const char *socket_name, int server);
void ph_socket_exit(ph_socket_t *s);
void ph_socket_tx_msg(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length);
void ph_socket_rx_msg(ph_socket_t *s, int channel, uint8_t prim, uint8_t *data, int length);
int ph_socket_work(ph_socket_t *s);

View File

@ -57,6 +57,7 @@ struct debug_cat {
{ "jollycom", "\033[1;34m" },
{ "eurosignal", "\033[1;34m" },
{ "pocsag", "\033[1;34m" },
{ "5-ton-folge", "\033[1;34m" },
{ "frame", "\033[0;36m" },
{ "call", "\033[0;37m" },
{ "cc", "\033[1;32m" },
@ -89,12 +90,16 @@ struct debug_cat {
{ "dss1", "\033[1;34m" },
{ "sip", "\033[1;35m" },
{ "telephone", "\033[1;34m" },
{ "uk0", "\033[1;34m" },
{ "ph", "\033[0;33m" },
{ "dcf77", "\033[1;34m" },
{ "jitter", "\033[0;36m" },
{ NULL, NULL }
};
int debuglevel = DEBUG_INFO;
int debug_date = 0;
uint64_t debug_mask = ~0;
uint64_t debug_mask[2] = { ~0, ~0 };
extern int num_kanal;
void (*clear_console_text)(void) = NULL;
@ -105,6 +110,25 @@ int debug_limit_scroll = 0;
static int lock_initialized = 0;
static pthread_mutex_t debug_mutex;
void lock_debug(void)
{
int rc;
if (!lock_initialized) {
rc = pthread_mutex_init(&debug_mutex, NULL);
if (rc == 0)
lock_initialized = 1;
}
if (lock_initialized)
pthread_mutex_lock(&debug_mutex);
}
void unlock_debug(void)
{
if (lock_initialized)
pthread_mutex_unlock(&debug_mutex);
}
void get_win_size(int *w, int *h)
{
struct winsize win;
@ -127,22 +151,15 @@ void _printdebug(const char *file, const char __attribute__((unused)) *function,
int s = sizeof(buffer) - 1;
const char *p;
va_list args;
int w, h;
int rc;
int w, h = 0; // make GCC happy
if (debuglevel > level)
return;
if (!(debug_mask & ((uint64_t)1 << cat)))
if (!(debug_mask[cat >> 6] & ((uint64_t)1 << (cat & 63))))
return;
if (!lock_initialized) {
rc = pthread_mutex_init(&debug_mutex, NULL);
if (rc == 0)
lock_initialized = 1;
}
if (lock_initialized)
pthread_mutex_lock(&debug_mutex);
lock_debug();
buffer[sizeof(buffer) - 1] = '\0';
@ -174,15 +191,14 @@ void _printdebug(const char *file, const char __attribute__((unused)) *function,
printf("%04d-%02d-%02d %02d:%02d:%02d.%03d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 10000.0));
}
printf("%s%s:%4d %s: %s\033[0;39m", debug_cat[cat].color, file, line, debug_level[level], buffer);
printf("%s%s %4d %s-%s: %s\033[0;39m", debug_cat[cat].color, file, line, debug_cat[cat].name, debug_level[level], buffer);
if (debug_limit_scroll)
printf("\0337\033[%d;%dr\0338", 1, h);
if (print_console_text)
print_console_text();
fflush(stdout);
if (lock_initialized)
pthread_mutex_unlock(&debug_mutex);
unlock_debug();
}
const char *debug_amplitude(double level)
@ -274,7 +290,7 @@ int parse_debug_opt(const char *optarg)
return -EINVAL;
}
if (dstring)
debug_mask = 0;
memset(debug_mask, 0, sizeof(debug_mask));
while((p = strsep(&dstring, ","))) {
for (i = 0; debug_cat[i].name; i++) {
if (!strcasecmp(p, debug_cat[i].name))
@ -285,7 +301,7 @@ int parse_debug_opt(const char *optarg)
free(dup);
return -EINVAL;
}
debug_mask |= ((uint64_t)1 << i);
debug_mask[i >> 6] |= ((uint64_t)1 << (i & 63));
}
free(dup);

View File

@ -19,38 +19,47 @@
#define DJOLLY 12
#define DEURO 13
#define DPOCSAG 14
#define DFRAME 15
#define DCALL 16
#define DCC 17
#define DDB 18
#define DTRANS 19
#define DDMS 20
#define DSMS 21
#define DSDR 22
#define DUHD 23
#define DSOAPY 24
#define DWAVE 25
#define DRADIO 26
#define DAM791X 27
#define DUART 28
#define DDEVICE 29
#define DDATENKLO 30
#define DZEIT 31
#define DSIM1 32
#define DSIM2 33
#define DSIMI 34
#define DSIM7 35
#define DMTP2 36
#define DMTP3 37
#define DMUP 38
#define DROUTER 39
#define DSTDERR 40
#define DSS5 41
#define DISDN 42
#define DMISDN 43
#define DDSS1 44
#define DSIP 45
#define DTEL 46
#define DFUENF 15
#define DFRAME 16
#define DCALL 17
#define DCC 18
#define DDB 19
#define DTRANS 20
#define DDMS 21
#define DSMS 22
#define DSDR 23
#define DUHD 24
#define DSOAPY 25
#define DWAVE 26
#define DRADIO 27
#define DAM791X 28
#define DUART 29
#define DDEVICE 30
#define DDATENKLO 31
#define DZEIT 32
#define DSIM1 33
#define DSIM2 34
#define DSIMI 35
#define DSIM7 36
#define DMTP2 37
#define DMTP3 38
#define DMUP 39
#define DROUTER 40
#define DSTDERR 41
#define DSS5 42
#define DISDN 43
#define DMISDN 44
#define DDSS1 45
#define DSIP 46
#define DTEL 47
#define DUK0 48
#define DPH 49
#define DDCF77 50
#define DJITTER 51
//NOTE: increment mask array, if 127 is exceeded
void lock_debug(void);
void unlock_debug(void);
void get_win_size(int *w, int *h);

View File

@ -1,6 +1,6 @@
/* Jitter buffering functions
*
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
@ -17,6 +17,59 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* How does it work:
*
* Storing:
*
* Each saved frame is sorted into the list of packages by their sequence
* number.
*
* The first packet will be stored with a delay of minimum jitter window size.
*
* Packets with the same sequence are dropped.
*
* Late packets cause jitter window to shift into the past (allowing more
* delay). Minimum jitter window size is added also, to prevent subsequent
* packets from beeing late too.
*
* Early packts that exceed maximum jitter window size cause jitter
* window to shift into the future.
*
* If no sequence is provided (autosequence), the sequence number is generated
* by a counter. Also the timestamp is generated by counting the length of each
* frame.
*
* If ssrc changes, the buffer is reset.
*
*
* Playout:
*
* The caller of the playout function can request any length of samples from
* the packet list. The packt's time stamp and the jitter window time stamp
* indicate what portion of a packet is already provided to the caller.
* Complete packet, sent to the caller, are removed.
*
* Missing packets are interpolated by repeating last 20ms of audio (optional)
* or by inserting zeroes (sample size > 1 byte) or by inserting 0xff (sample
* size = 1).
*
* Optionally the constant delay will be measured continuously and lowered to
* minimum window size. (adaptive jitter buffer window)
*
* Note that the delay is measured with time stamp of frame, no matter what
* the length is. Length is an extra delay, but not considered here.
*
*
* Unocking:
*
* If the buffer is created or reset, the buffer is locked, so no packets are
* stored. When the playout routine is called, the buffer is unlocked. This
* prevents from filling the buffer before playout is performed, which would
* cause high delay.
*
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
@ -26,35 +79,96 @@
#include "../libdebug/debug.h"
#include "jitter.h"
#define INITIAL_DELAY_INTERVAL 0.5
#define REPEAT_DELAY_INTERVAL 2.0
#define EXTRA_BUFFER 0.020 // 20 ms
/* uncomment to enable heavy debugging */
//#define HEAVY_DEBUG
static int unnamed_count = 1;
/* create jitter buffer */
int jitter_create(jitter_t *jitter, int length)
int jitter_create(jitter_t *jb, const char *name, double samplerate, int sample_size, double target_window_duration, double max_window_duration, uint32_t window_flags)
{
memset(jitter, 0, sizeof(*jitter));
jitter->spl = malloc(length * sizeof(sample_t));
if (!jitter->spl) {
PDEBUG(DDSP, DEBUG_ERROR, "No memory for jitter buffer.\n");
return -ENOMEM;
int rc = 0;
memset(jb, 0, sizeof(*jb));
jb->sample_duration = 1.0 / samplerate;
jb->sample_size = sample_size;
jb->target_window_size = (int)(samplerate * target_window_duration);
jb->max_window_size = (int)(samplerate * max_window_duration);
jb->window_flags = window_flags;
jb->delay_value = -1;
jb->extra_size = (int)(EXTRA_BUFFER * samplerate);
jb->extra_samples = calloc(sample_size, jb->extra_size);
if (!jb->extra_samples) {
PDEBUG(DJITTER, DEBUG_ERROR, "No memory for frame.\n");
rc = -ENOMEM;
goto error;
}
jitter->len = length;
jitter_reset(jitter);
return 0;
/* optionally give a string to be show with the debug */
if (name && *name)
snprintf(jb->name, sizeof(jb->name) - 1, "(%s) ", name);
else
snprintf(jb->name, sizeof(jb->name) - 1, "(unnamed %d) ", unnamed_count++);
jitter_reset(jb);
PDEBUG(DJITTER, DEBUG_DEBUG, "%sCreated jitter buffer.\n", jb->name);
error:
if (rc)
jitter_destroy(jb);
return rc;
}
void jitter_reset(jitter_t *jitter)
/* reset jitter buffer */
void jitter_reset(jitter_t *jb)
{
memset(jitter->spl, 0, jitter->len * sizeof(sample_t));
jitter_frame_t *jf, *temp;
/* put write pointer ahead by half of the buffer length */
jitter->inptr = jitter->len / 2;
if (!jb->window_valid)
return;
PDEBUG(DJITTER, DEBUG_DEBUG, "%sReset jitter buffer.\n", jb->name);
/* jitter buffer locked */
jb->unlocked = 0;
/* window becomes invalid */
jb->window_valid = 0;
/* remove all pending frames */
jf = jb->frame_list;
while(jf) {
temp = jf;
jf = jf->next;
free(temp);
}
jb->frame_list = NULL;
/* clear extrapolation buffer */
if (jb->extra_samples) {
if (jb->sample_size == 1)
memset(jb->extra_samples, 0xff, jb->sample_size * jb->extra_size);
else
memset(jb->extra_samples, 0, jb->sample_size * jb->extra_size);
}
jb->extra_index = 0;
}
void jitter_destroy(jitter_t *jitter)
void jitter_destroy(jitter_t *jb)
{
if (jitter->spl) {
free(jitter->spl);
jitter->spl = NULL;
jitter_reset(jb);
PDEBUG(DJITTER, DEBUG_DEBUG, "%sDestroying jitter buffer.\n", jb->name);
if (jb->extra_samples) {
free(jb->extra_samples);
jb->extra_samples = NULL;
}
}
@ -62,64 +176,220 @@ void jitter_destroy(jitter_t *jitter)
*
* stop if buffer is completely filled
*/
void jitter_save(jitter_t *jb, sample_t *samples, int length)
void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
{
sample_t *spl;
int inptr, outptr, len, space;
int i;
jitter_frame_t *jf, **jfp;
int16_t offset_sequence;
int32_t offset_timestamp;
spl = jb->spl;
inptr = jb->inptr;
outptr = jb->outptr;
len = jb->len;
space = (outptr - inptr + len - 1) % len;
/* ignore frames until the buffer is unlocked by jitter_load() */
if (!jb->unlocked)
return;
if (space < length)
length = space;
for (i = 0; i < length; i++) {
spl[inptr++] = *samples++;
if (inptr == len)
inptr = 0;
/* omit frames with no data */
if (length < 1)
return;
/* generate sequence and timestamp automatically, if enabled */
if (!has_sequence) {
#ifdef DEBUG_JITTER
PDEBUG(DJITTER, DEBUG_DEBUG, "%sSave frame of %d samples (no seqence).\n", jb->name, length);
#endif
sequence = jb->next_sequence;
jb->next_sequence++;
timestamp = jb->next_timestamp;
jb->next_timestamp += length;
ssrc = jb->window_ssrc;
} else {
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%sSave frame of %d samples (seqence=%u timestamp=%u ssrc=0x%02x).\n", jb->name, length, sequence, timestamp, ssrc);
#endif
jb->next_sequence = sequence + 1;
jb->next_timestamp = timestamp + length;
}
jb->inptr = inptr;
/* find location where to put frame into the list, depending on sequence number */
jfp = &jb->frame_list;
while(*jfp) {
offset_sequence = (int16_t)(sequence - (*jfp)->sequence);
/* found double entry */
if (offset_sequence == 0) {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Dropping double packet (sequence = %d)\n", jb->name, sequence);
return;
}
/* offset is negative, so we found the position to insert frame */
if (offset_sequence < 0)
break;
jfp = &((*jfp)->next);
}
/* first packet (with this ssrc) sets window size to target_window_size */
if (!jb->window_valid || jb->window_ssrc != ssrc) {
if (!jb->window_valid)
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Initial frame after init or reset.\n", jb->name);
else
PDEBUG(DJITTER, DEBUG_DEBUG, "%s SSRC changed.\n", jb->name);
jitter_reset(jb);
jb->window_timestamp = timestamp - (uint32_t)(jb->target_window_size);
jb->window_valid = 1;
jb->window_ssrc = ssrc;
jb->delay_counter = 0.0;
jb->delay_interval = INITIAL_DELAY_INTERVAL;
jb->delay_value = -1;
}
offset_timestamp = timestamp - jb->window_timestamp;
/* measure delay */
if (offset_timestamp > jb->delay_value)
jb->delay_value = offset_timestamp;
/* if frame is too early (delay ceases), shift window to the future */
if (offset_timestamp > jb->max_window_size) {
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too early: Shift jitter buffer to the future, and add target window size. (offset_timestamp(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
/* shift window so frame fits to the start of window + target delay */
jb->window_timestamp = timestamp - (uint32_t)(jb->target_window_size);
} else {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too early: Shift jitter buffer to the future. (offset_timestamp(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
/* shift window so it fits to the end of window */
jb->window_timestamp = timestamp - jb->max_window_size;
}
}
/* is frame is too late, shift window to the past. */
if (offset_timestamp < 0) {
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too late: Shift jitter buffer to the past, and add target window size. (offset_timestamp(%d) < 0)\n", jb->name, offset_timestamp);
/* shift window so frame fits to the start of window + target delay */
jb->window_timestamp = timestamp - (uint32_t)(jb->target_window_size);
} else {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too late: Shift jitter buffer to the past. (offset_timestamp(%d) < 0)\n", jb->name, offset_timestamp);
/* shift window so frame fits to the start of window */
jb->window_timestamp = timestamp;
}
}
/* insert or append frame */
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Store frame\n", jb->name);
#endif
jf = malloc(sizeof(*jf) + length * jb->sample_size);
if (!jf) {
PDEBUG(DJITTER, DEBUG_ERROR, "No memory for frame.\n");
return;
}
memset(jf, 0, sizeof(*jf)); // note: clear header only
jf->sequence = sequence;
jf->timestamp = timestamp;
memcpy(jf->samples, samples, length * jb->sample_size);
jf->length = length;
jf->next = *jfp;
*jfp = jf;
}
/* get audio from jitterbuffer
*/
void jitter_load(jitter_t *jb, sample_t *samples, int length)
void jitter_load(jitter_t *jb, void *samples, int length)
{
sample_t *spl;
int inptr, outptr, len, fill;
int i, ii;
jitter_frame_t *jf;
int32_t count, count2, index;
spl = jb->spl;
inptr = jb->inptr;
outptr = jb->outptr;
len = jb->len;
fill = (inptr - outptr + len) % len;
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%sLoad chunk of %d samples.\n", jb->name, length);
#endif
if (fill < length)
ii = fill;
else
ii = length;
/* now unlock jitter buffer */
jb->unlocked = 1;
/* fill what we got */
for (i = 0; i < ii; i++) {
*samples++ = spl[outptr++];
if (outptr == len)
outptr = 0;
}
/* on underrun, fill with silence */
for (; i < length; i++) {
*samples++ = 0;
jb->delay_counter += jb->sample_duration * (double)length;
if (jb->delay_counter >= jb->delay_interval) {
if (jb->delay_value >= 0)
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Statistics: target_delay=%.0fms max_delay=%.0fms current_delay=%.0fms\n", jb->name, (double)jb->target_window_size * jb->sample_duration * 1000.0, (double)jb->max_window_size * jb->sample_duration * 1000.0, (double)jb->delay_value * jb->sample_duration * 1000.0);
/* delay reduction, if greater than target jitter window size */
if ((jb->window_flags & JITTER_FLAG_LATENCY) && jb->delay_value > jb->target_window_size) {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Reducing current delay of %.0fms that is greater than target jitter window size of %.0fms.\n", jb->name, (double)jb->delay_value * jb->sample_duration * 1000.0, (double)jb->target_window_size * jb->sample_duration * 1000.0);
jb->window_timestamp += (uint32_t)(jb->delay_value - jb->target_window_size);
}
jb->delay_counter -= jb->delay_interval;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
jb->delay_value = -1;
}
jb->outptr = outptr;
}
void jitter_clear(jitter_t *jb)
{
jb->inptr = jb->outptr = 0;
/* process all frames until output buffer is loaded */
while (length) {
/* always get frame with the lowest sequence number (1st frame) */
jf = jb->frame_list;
if (jf) {
count = jf->timestamp - jb->window_timestamp;
if (count > length)
count = length;
} else
count = length;
/* if there is no frame or we have not reached frame's time stamp, extrapolate */
if (count > 0) {
#ifdef HEAVY_DEBUG
if (jf)
PDEBUG(DJITTER, DEBUG_DEBUG, "%s There is a frame ahead in buffer after %d samples. Interpolating gap.\n", jb->name, jf->timestamp - jb->window_timestamp);
else
PDEBUG(DJITTER, DEBUG_DEBUG, "%s There is no frame ahead in buffer. Interpolating gap.\n", jb->name);
#endif
/* extrapolate by playing the extrapolation buffer */
while (count) {
count2 = count;
if (count2 > jb->extra_size - jb->extra_index)
count2 = jb->extra_size - jb->extra_index;
memcpy(samples, (uint8_t *)jb->extra_samples + jb->extra_index * jb->sample_size, count2 * jb->sample_size);
jb->extra_index += count2;
if (jb->extra_index == jb->extra_size)
jb->extra_index = 0;
samples = (uint8_t *)samples + count2 * jb->sample_size;
length -= count2;
jb->window_timestamp += count2;
count -= count2;
}
if (length == 0)
return;
}
/* copy samples from frame (what is not in the past) */
index = jb->window_timestamp - jf->timestamp;
while (index < jf->length) {
/* use the lowest value of 'playout length' or 'remaining packet length' */
count = length;
if (jf->length - index < count)
count = jf->length - index;
/* if extrapolation is used, limit count to what we can store into buffer */
if (jb->extra_samples && jb->extra_size - jb->extra_index < count)
count = jb->extra_size - jb->extra_index;
/* copy samples from packet to play out, increment sample pointer and decrement length */
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Copy data (offset=%u count=%u) from frame (sequence=%u timestamp=%u length=%u).\n", jb->name, index, count, jf->sequence, jf->timestamp, jf->length);
#endif
memcpy(samples, (uint8_t *)jf->samples + index * jb->sample_size, count * jb->sample_size);
samples = (uint8_t *)samples + count * jb->sample_size;
length -= count;
/* copy frame data to extrapolation buffer also, increment index */
if ((jb->window_flags & JITTER_FLAG_REPEAT)) {
memcpy((uint8_t *)jb->extra_samples + jb->extra_index * jb->sample_size, (uint8_t *)jf->samples + index * jb->sample_size, count * jb->sample_size);
jb->extra_index += count;
if (jb->extra_index == jb->extra_size)
jb->extra_index = 0;
}
/* increment time stamp */
jb->window_timestamp += count;
index += count;
/* if there was enough to play out, we are done */
if (length == 0)
return;
}
/* free frame, because all samples are now in the past */
jb->frame_list = jf->next;
free(jf);
/* now go for next loop, in case there is still date to play out */
}
}

View File

@ -1,14 +1,58 @@
#define JITTER_FLAG_NONE 0 // no flags at all
#define JITTER_FLAG_LATENCY (1 << 0) // keep latency close to target_window_duration
#define JITTER_FLAG_REPEAT (1 << 1) // repeat audio to extrapolate gaps
/* window settings for low latency audio and extrapolation of gaps */
#define JITTER_AUDIO 0.050, 1.000, JITTER_FLAG_LATENCY | JITTER_FLAG_REPEAT
/* window settings for analog data (fax/modem) or digial data (HDLC) */
#define JITTER_DATA 0.100, 0.200, JITTER_FLAG_NONE
typedef struct jitter_frame {
struct jitter_frame *next;
uint16_t sequence;
uint32_t timestamp;
int length;
uint8_t samples[0];
} jitter_frame_t;
typedef struct jitter {
sample_t *spl; /* pointer to sample buffer */
int len; /* buffer size: number of samples */
int inptr, outptr; /* write pointer and read pointer */
char name[64];
/* sample properties */
int sample_size;
double sample_duration;
/* automatic sequence generation */
uint16_t next_sequence;
uint32_t next_timestamp;
/* window properties */
int unlocked;
uint32_t window_flags;
int target_window_size;
int max_window_size;
int window_valid;
uint32_t window_ssrc;
uint32_t window_timestamp;
/* reduction of delay */
double delay_interval;
double delay_counter;
int32_t delay_value;
/* extrapolation */
int extra_size;
int extra_index;
void *extra_samples;
/* list of frames */
jitter_frame_t *frame_list;
} jitter_t;
int jitter_create(jitter_t *jitter, int length);
void jitter_reset(jitter_t *jitter);
void jitter_destroy(jitter_t *jitter);
void jitter_save(jitter_t *jb, sample_t *samples, int length);
void jitter_load(jitter_t *jb, sample_t *samples, int length);
void jitter_clear(jitter_t *jb);
int jitter_create(jitter_t *jb, const char *name, double samplerate, int sample_size, double target_window_duration, double max_window_duration, uint32_t window_flags);
void jitter_reset(jitter_t *jb);
void jitter_destroy(jitter_t *jb);
void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint16_t sequence, uint32_t timestamp, uint32_t ssrc);
void jitter_load(jitter_t *jb, void *samples, int length);

View File

@ -22,7 +22,7 @@
#include "message.h"
#include "cause.h"
/* stolen from freeswitch */
/* stolen from freeswitch, did some corrections */
/* map sip responses to QSIG cause codes ala RFC4497 section 8.4.4 */
static uint8_t status2isdn_cause(uint16_t status)
{
@ -36,10 +36,9 @@ static uint8_t status2isdn_cause(uint16_t status)
case 603:
return 21; //SWITCH_CAUSE_CALL_REJECTED;
case 404:
return 1; //SWITCH_CAUSE_UNALLOCATED_NUMBER;
case 485:
case 604:
return 3; //SWITCH_CAUSE_NO_ROUTE_DESTINATION;
return 1; //SWITCH_CAUSE_UNALLOCATED_NUMBER;
case 408:
case 504:
return 102; //SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
@ -55,7 +54,7 @@ static uint8_t status2isdn_cause(uint16_t status)
case 513:
return 127; //SWITCH_CAUSE_INTERWORKING;
case 480:
return 180; //SWITCH_CAUSE_NO_USER_RESPONSE;
return 18; //SWITCH_CAUSE_NO_USER_RESPONSE;
case 400:
case 481:
case 500:
@ -68,7 +67,7 @@ static uint8_t status2isdn_cause(uint16_t status)
return 28; //SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
case 488:
case 606:
return 88; //SWITCH_CAUSE_INCOMPATIBLE_DESTINATION;
return 65; //SWITCH_CAUSE_BERER_CAPABILITY_NOT_IMPLEMENTED;
case 502:
return 38; //SWITCH_CAUSE_NETWORK_OUT_OF_ORDER;
case 405:
@ -81,7 +80,7 @@ static uint8_t status2isdn_cause(uint16_t status)
case 483:
return 25; //SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR;
case 487:
return 31; //??? SWITCH_CAUSE_ORIGINATOR_CANCEL;
return 31; //??? SWITCH_CAUSE_ORIGINATOR_CANCEL; (not specified)
default:
return 31; //SWITCH_CAUSE_NORMAL_UNSPECIFIED;
}

View File

@ -1072,6 +1072,36 @@ void osmo_cc_ul_msg(void *priv, uint32_t callref, osmo_cc_msg_t *msg)
osmo_cc_msg_list_enqueue(&call->sock_queue, msg, call->callref);
}
static void osmo_cc_help_name(void)
{
printf("Name options:\n\n");
printf("name <name>\n");
printf("Allows to override endpoint name given by application.\n");
}
static int osmo_cc_set_name(osmo_cc_endpoint_t *ep, const char *text)
{
if (!strncasecmp(text, "name", 4)) {
text += 4;
/* remove spaces after keyword */
while (*text) {
if (*text > 32)
break;
text++;
}
} else {
PDEBUG(DCC, DEBUG_ERROR, "Invalid name definition '%s'\n", text);
return -EINVAL;
}
free((char *)ep->local_name);
ep->local_name = strdup(text);
return 0;
}
static void osmo_cc_help_address(void)
{
printf("Address options:\n\n");
@ -1080,6 +1110,8 @@ static void osmo_cc_help_address(void)
printf("local [<IPv6 address>]:<port>\n");
printf("remote <IPv4 address>:<port>\n");
printf("remote [<IPv6 address>]:<port>\n\n");
printf("remote auto\n\n");
printf("remote none\n\n");
printf("These options can be used to define local and remote IP and port for the socket\n");
printf("interface. Note that IPv6 addresses must be enclosed by '[' and ']'.\n\n");
@ -1090,6 +1122,9 @@ static void osmo_cc_help_address(void)
printf("If no remote address is given, the local IP is used. If the local port is %d,\n", OSMO_CC_DEFAULT_PORT);
printf("the remote port will be %d. If not, the remote port will be %d. This way it is\n", OSMO_CC_DEFAULT_PORT + 1, OSMO_CC_DEFAULT_PORT);
printf("possible to link two interfaces without any IP configuration required.\n\n");
printf("Use 'remote auto' to enable and 'remote none' to disable. This can be useful to\n");
printf("override application default.\n\n");
}
static int osmo_cc_set_address(osmo_cc_endpoint_t *ep, const char *text)
@ -1124,6 +1159,11 @@ static int osmo_cc_set_address(osmo_cc_endpoint_t *ep, const char *text)
ep->remote_auto = 1;
return 0;
}
if (!strcasecmp(text, "none")) {
PDEBUG(DCC, DEBUG_DEBUG, "disable automatic remote peer selection\n");
ep->remote_auto = 0;
return 0;
}
ep->remote_auto = 0;
address_p = &ep->remote_address;
host_p = &ep->remote_host;
@ -1220,6 +1260,7 @@ static int osmo_cc_set_rtp(osmo_cc_endpoint_t *ep, const char *text)
return -EINVAL;
}
from = from * 10 + *text - '0';
text++;
}
/* remove spaces after keyword */
@ -1235,7 +1276,8 @@ static int osmo_cc_set_rtp(osmo_cc_endpoint_t *ep, const char *text)
PDEBUG(DCC, DEBUG_ERROR, "Given 'to' port in '%s' is invalid.\n", text);
return -EINVAL;
}
from = from * 10 + *text - '0';
to = to * 10 + *text - '0';
text++;
}
osmo_cc_set_rtp_ports(&ep->session_config, from, to);
@ -1247,9 +1289,10 @@ static int osmo_cc_set_rtp(osmo_cc_endpoint_t *ep, const char *text)
void osmo_cc_help(void)
{
osmo_cc_help_screen();
osmo_cc_help_name();
osmo_cc_help_address();
osmo_cc_help_rtp();
osmo_cc_help_screen();
}
/* create a new endpoint instance */
@ -1286,6 +1329,12 @@ int osmo_cc_new(osmo_cc_endpoint_t *ep, const char *version, const char *name, u
/* apply args */
for (i = 0; i < argc; i++) {
if (!strncasecmp(argv[i], "name", 4)) {
rc = osmo_cc_set_name(ep, argv[i]);
if (rc < 0) {
return rc;
}
} else
if (!strncasecmp(argv[i], "local", 5)) {
rc = osmo_cc_set_address(ep, argv[i]);
if (rc < 0) {

View File

@ -29,7 +29,7 @@
#include "endpoint.h"
#include "helper.h"
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug)
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug)
{
osmo_cc_session_t *session;
osmo_cc_session_media_t *media;
@ -52,7 +52,7 @@ osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, vo
return session;
}
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec)
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec)
{
char offer_sdp[65536];
const char *accept_sdp;

View File

@ -7,7 +7,7 @@ struct osmo_cc_helper_audio_codecs {
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
};
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug);
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec);
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug);
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec);
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p);

View File

@ -33,7 +33,8 @@
}
#define _OSMO_CC_NAME2VALUE(array) { \
for (int value = 0; (size_t)value < (sizeof(array) / sizeof(array[0])); value++) { \
int value; \
for (value = 0; (size_t)value < (sizeof(array) / sizeof(array[0])); value++) { \
if (!strcasecmp(array[value], name)) \
return value; \
} \

View File

@ -52,7 +52,7 @@ struct rtp_x_hdr {
uint16_t length;
} __attribute__((packed));
static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_t *marker_p, uint8_t *pt_p, uint16_t *sequence_p, uint32_t *timestamp_p)
static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_t *marker_p, uint8_t *pt_p, uint16_t *sequence_p, uint32_t *timestamp_p, uint32_t *ssrc_p)
{
static uint8_t data[2048];
int len;
@ -83,6 +83,7 @@ static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_
payload_type = rtph->byte1 & 0x7f;
*sequence_p = ntohs(rtph->sequence);
*timestamp_p = ntohl(rtph->timestamp);
*ssrc_p = ntohl(rtph->ssrc);
if (version != RTP_VERSION) {
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP version %d not supported.\n", version);
@ -172,7 +173,7 @@ int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
int flags;
int rc;
media->rtp_ssrc = rand();
media->tx_ssrc = rand();
osmo_cc_rtp_close(media);
@ -182,7 +183,12 @@ int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
memset(&sa, 0, sizeof(sa));
sa4 = (struct sockaddr_in *)&sa;
sa4->sin_family = domain;
sa4->sin_addr.s_addr = INADDR_ANY;
rc = inet_pton(AF_INET, media->connection_data_local.address, &sa4->sin_addr);
if (rc < 1) {
pton_error:
PDEBUG(DCC, DEBUG_NOTICE, "Cannot bind to address '%s'.\n", media->connection_data_local.address);
return -EINVAL;
}
sport = &sa4->sin_port;
slen = sizeof(*sa4);
break;
@ -191,7 +197,9 @@ int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
memset(&sa, 0, sizeof(sa));
sa6 = (struct sockaddr_in6 *)&sa;
sa6->sin6_family = domain;
sa6->sin6_addr = in6addr_any;
rc = inet_pton(AF_INET6, media->connection_data_local.address, &sa6->sin6_addr);
if (rc < 1)
goto pton_error;
sport = &sa6->sin6_port;
slen = sizeof(*sa6);
break;
@ -328,7 +336,7 @@ void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, in
payload_len = len;
}
rtp_send(codec->media->rtp_socket, payload, payload_len, codec->payload_type_remote, codec->media->tx_sequence, codec->media->tx_timestamp, codec->media->rtp_ssrc);
rtp_send(codec->media->rtp_socket, payload, payload_len, codec->payload_type_remote, codec->media->tx_sequence, codec->media->tx_timestamp, codec->media->tx_ssrc);
codec->media->tx_sequence += inc_sequence;
codec->media->tx_timestamp += inc_timestamp;
@ -351,7 +359,7 @@ int osmo_cc_rtp_receive(osmo_cc_session_media_t *media)
if (!media || media->rtp_socket <= 0)
return -EIO;
rc = rtp_receive(media->rtp_socket, &payload, &payload_len, &marker, &payload_type, &media->rx_sequence, &media->rx_timestamp);
rc = rtp_receive(media->rtp_socket, &payload, &payload_len, &marker, &payload_type, &media->rx_sequence, &media->rx_timestamp, &media->rx_ssrc);
if (rc < 0)
return rc;
@ -374,7 +382,7 @@ int osmo_cc_rtp_receive(osmo_cc_session_media_t *media)
}
if (codec->media->receive)
codec->media->receiver(codec, media->rx_sequence, media->rx_timestamp, data, len);
codec->media->receiver(codec, media->rx_sequence, media->rx_timestamp, media->rx_ssrc, data, len);
if (codec->decoder)
free(data);

View File

@ -58,6 +58,10 @@ void osmo_cc_help_screen(void)
printf("to allow any suffix to match from now on. The new caller ID or dialed number\n");
printf("may contain a '*', to append the suffix from the current caller ID or dialed\n");
printf("number.\n\n");
printf("When screening an incoming caller ID or dialed number, the '@' can be appended\n");
printf("to the 'new caller ID', followed by a 'host:port', to route call to a special\n");
printf("Osmo-CC endpoint. This way it is possible to do simple routing.\n\n");
}
char *osmo_cc_strtok_quotes(const char **text_p)
@ -139,6 +143,7 @@ int osmo_cc_add_screen(osmo_cc_endpoint_t *ep, const char *text)
} else if (!strncasecmp(text, "screen-called-in", 16)) {
text += 16;
list_p = &ep->screen_called_in;
calling_in = 1;
} else if (!strncasecmp(text, "screen-calling-out", 18)) {
text += 18;
list_p = &ep->screen_calling_out;
@ -218,6 +223,7 @@ no_present_error:
list->from_present = OSMO_CC_PRESENT_RESTRICTED;
goto next_from;
} else {
star_used = 0;
for (i = j = 0; token[i] && j < (int)sizeof(list->from) - 1; i++, j++) {
if (token[i] == '?')
list->from[j] = SCREEN_QUESTIONMARK;
@ -240,7 +246,6 @@ no_present_error:
list->from[j] = '\0';
}
star_used = 0;
next_to:
token = osmo_cc_strtok_quotes(&text);
if (!token) {
@ -293,6 +298,7 @@ next_to:
list->to_present = OSMO_CC_PRESENT_RESTRICTED;
goto next_to;
} else {
at_used = star_used = 0;
for (i = j = 0; token[i] && j < (int)sizeof(list->to) - 1; i++, j++) {
if (token[i] == '*') {
if (star_used) {
@ -506,7 +512,8 @@ static int osmo_cc_screen(const char *what, osmo_cc_screen_list_t *list, uint8_t
continue;
/* '@' means to stop and return routing also */
} else if (list->to[i] == SCREEN_AT) {
*routing_p = &list->to[i];
if (routing_p)
*routing_p = &list->to[i + 1];
break;
}
/* copy output digit */
@ -543,6 +550,8 @@ static int osmo_cc_screen(const char *what, osmo_cc_screen_list_t *list, uint8_t
PDEBUG(DCC, DEBUG_INFO, " -> present = restricted\n");
break;
}
if (routing_p && *routing_p)
PDEBUG(DCC, DEBUG_INFO, " -> remote = %s\n", *routing_p);
return 0;
}
@ -586,13 +595,13 @@ osmo_cc_msg_t *osmo_cc_screen_msg(osmo_cc_endpoint_t *ep, osmo_cc_msg_t *old_msg
if (in && ep->screen_called_in) {
rc = osmo_cc_get_ie_called(old_msg, 0, &called_type, &called_plan, id, sizeof(id));
if (rc >= 0) {
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), id, NULL);
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), id, routing_p);
if (rc >= 0)
called_status = 1;
} else {
called_type = OSMO_CC_TYPE_UNKNOWN;
called_plan = OSMO_CC_PLAN_TELEPHONY;
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), "", NULL);
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), "", routing_p);
if (rc >= 0)
called_status = 1;
}

View File

@ -260,7 +260,7 @@ struct osmo_cc_session *osmo_cc_session_parsesdp(osmo_cc_session_config_t *conf,
memset(&ccd, 0, sizeof(ccd));
/* create SDP session description */
session = osmo_cc_new_session(conf, priv, NULL, NULL, NULL, osmo_cc_session_nettype_inet, osmo_cc_session_addrtype_ipv4, "127.0.0.1", NULL, 0); // values will be replaced by local definitions during negotiation
session = osmo_cc_new_session(conf, priv, NULL, NULL, NULL, 0, 0, NULL, NULL, 0);
/* check every line of SDP and parse its data */
while(*sdp) {

View File

@ -29,7 +29,7 @@
#include "../liboptions/options.h"
#include "endpoint.h"
#define NTP_OFFSET 2208988800
#define NTP_OFFSET 2208988800U
void osmo_cc_set_local_peer(osmo_cc_session_config_t *conf, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address)
{
@ -127,7 +127,7 @@ void osmo_cc_free_session(osmo_cc_session_t *session)
free(session);
}
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), int debug)
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), int debug)
{
osmo_cc_session_config_t *conf = session->config;
osmo_cc_session_media_t *media, **mediap;
@ -374,7 +374,7 @@ osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf,
return session;
}
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len))
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len))
{
osmo_cc_session_config_t *conf = media->session->config;

View File

@ -79,10 +79,10 @@ typedef struct osmo_cc_session_media {
osmo_cc_session_connection_data_t connection_data_local, connection_data_remote;
struct osmo_cc_session_codec *codec_list;
int send, receive;
void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len);
void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
int rtp_socket;
int rtcp_socket;
uint32_t rtp_ssrc;
uint32_t tx_ssrc, rx_ssrc;
uint16_t tx_sequence, rx_sequence;
uint32_t tx_timestamp, rx_timestamp;
int accepted;
@ -110,14 +110,14 @@ typedef struct osmo_cc_session_codec {
void osmo_cc_set_local_peer(osmo_cc_session_config_t *conf, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address);
osmo_cc_session_t *osmo_cc_new_session(osmo_cc_session_config_t *conf, void *priv, const char *username, const char *sess_id, const char *sess_version, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *unicast_address, const char *session_name, int debug);
void osmo_cc_free_session(osmo_cc_session_t *session);
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), int debug);
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), int debug);
void osmo_cc_free_media(osmo_cc_session_media_t *media);
osmo_cc_session_codec_t *osmo_cc_add_codec(osmo_cc_session_media_t *media, const char *playload_name, uint32_t playload_rate, int playload_channels, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), int debug);
void osmo_cc_free_codec(osmo_cc_session_codec_t *codec);
int osmo_cc_session_check(struct osmo_cc_session *session, int remote);
const char *osmo_cc_session_send_offer(osmo_cc_session_t *session);
osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf, void *priv, const char *sdp);
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len));
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len));
void osmo_cc_session_accept_codec(osmo_cc_session_codec_t *codec, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len));
const char *osmo_cc_session_send_answer(osmo_cc_session_t *session);
int osmo_cc_session_receive_answer(osmo_cc_session_t *session, const char *sdp);