Move from local to external osmo* libraries
src/libdebug -> libosmocore src/libselect -> libosmocore src/libtimer -> libosmocore src/libosmocc -> libosmo-cc src/libg711 -> libosmo-cc
This commit is contained in:
parent
212aac461d
commit
02510a9973
|
@ -33,14 +33,10 @@ Doxyfile
|
|||
|
||||
.*.sw?
|
||||
|
||||
src/libdebug/libdebug.a
|
||||
src/libg711/libg711.a
|
||||
src/liblogging/liblogging.a
|
||||
src/libjitter/libjitter.a
|
||||
src/liboptions/liboptions.a
|
||||
src/libosmocc/libosmocc.a
|
||||
src/libsample/libsample.a
|
||||
src/libtimer/libtimer.a
|
||||
src/libselect/libselect.a
|
||||
src/libwave/libwave.a
|
||||
src/libdtmf/libdtmf.a
|
||||
src/libfm/libfm.a
|
||||
|
|
|
@ -77,6 +77,9 @@ AM_CONFIG_HEADER(config.h)
|
|||
AC_CHECK_LIB([m], [main], [], [echo "Failed to find lib!" ; exit -1])
|
||||
AC_CHECK_LIB([pthread], [main], [], [echo "Failed to find lib!" ; exit -1])
|
||||
|
||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
|
||||
PKG_CHECK_MODULES(LIBOSMOCC, libosmocc >= 1.0.0)
|
||||
|
||||
AC_ARG_WITH([gsm], [AS_HELP_STRING([--with-gsm], [compile with GSM codec support @<:@default=check@:>@]) ], [], [with_gsm="check"])
|
||||
AS_IF([test "x$with_gsm" != xno], [AC_CHECK_LIB([gsm], [main], [with_gsm="yes"], [with_gsm="no"])])
|
||||
AM_CONDITIONAL(HAVE_GSM, test "x$with_gsm" == "xyes" )
|
||||
|
@ -84,13 +87,9 @@ AS_IF([test "x$with_gsm" == "xyes"],[AC_MSG_NOTICE( Compiling with GSM codec sup
|
|||
|
||||
AC_OUTPUT(
|
||||
src/liboptions/Makefile
|
||||
src/libdebug/Makefile
|
||||
src/liblogging/Makefile
|
||||
src/libsample/Makefile
|
||||
src/libtimer/Makefile
|
||||
src/libselect/Makefile
|
||||
src/libjitter/Makefile
|
||||
src/libosmocc/Makefile
|
||||
src/libg711/Makefile
|
||||
src/libwave/Makefile
|
||||
src/libdtmf/Makefile
|
||||
src/libfm/Makefile
|
||||
|
|
|
@ -2,13 +2,9 @@ AUTOMAKE_OPTIONS = foreign
|
|||
|
||||
SUBDIRS = \
|
||||
liboptions \
|
||||
libdebug \
|
||||
liblogging \
|
||||
libsample \
|
||||
libtimer \
|
||||
libselect \
|
||||
libjitter \
|
||||
libosmocc \
|
||||
libg711 \
|
||||
libwave \
|
||||
libdtmf \
|
||||
libfm \
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||
|
||||
noinst_LIBRARIES = libdebug.a
|
||||
|
||||
libdebug_a_SOURCES = \
|
||||
debug.c
|
||||
|
|
@ -1,331 +0,0 @@
|
|||
/* Simple debug functions for level and category filtering
|
||||
*
|
||||
* (C) 2016 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 <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include <time.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/time.h>
|
||||
#include "debug.h"
|
||||
|
||||
const char *debug_level[] = {
|
||||
"debug ",
|
||||
"info ",
|
||||
"notice ",
|
||||
"error ",
|
||||
NULL,
|
||||
};
|
||||
|
||||
struct debug_cat {
|
||||
const char *name;
|
||||
const char *color;
|
||||
} debug_cat[] = {
|
||||
{ "options", "\033[0;33m" },
|
||||
{ "sender", "\033[1;33m" },
|
||||
{ "sound", "\033[0;35m" },
|
||||
{ "dsp", "\033[0;31m" },
|
||||
{ "anetz", "\033[1;34m" },
|
||||
{ "bnetz", "\033[1;34m" },
|
||||
{ "cnetz", "\033[1;34m" },
|
||||
{ "nmt", "\033[1;34m" },
|
||||
{ "amps", "\033[1;34m" },
|
||||
{ "r2000", "\033[1;34m" },
|
||||
{ "imts", "\033[1;34m" },
|
||||
{ "mpt1327", "\033[1;34m" },
|
||||
{ "jollycom", "\033[1;34m" },
|
||||
{ "eurosignal", "\033[1;34m" },
|
||||
{ "pocsag", "\033[1;34m" },
|
||||
{ "golay", "\033[1;34m" },
|
||||
{ "5-ton-folge", "\033[1;34m" },
|
||||
{ "frame", "\033[0;36m" },
|
||||
{ "call", "\033[0;37m" },
|
||||
{ "cc", "\033[1;32m" },
|
||||
{ "database", "\033[0;33m" },
|
||||
{ "transaction", "\033[0;32m" },
|
||||
{ "dms", "\033[0;33m" },
|
||||
{ "sms", "\033[1;37m" },
|
||||
{ "sdr", "\033[1;31m" },
|
||||
{ "uhd", "\033[1;35m" },
|
||||
{ "soapy", "\033[1;35m" },
|
||||
{ "wave", "\033[1;33m" },
|
||||
{ "radio", "\033[1;34m" },
|
||||
{ "am791x", "\033[0;31m" },
|
||||
{ "uart", "\033[0;32m" },
|
||||
{ "device", "\033[0;33m" },
|
||||
{ "datenklo", "\033[1;34m" },
|
||||
{ "zeit", "\033[1;34m" },
|
||||
{ "sim layer 1", "\033[0;31m" },
|
||||
{ "sim layer 2", "\033[0;33m" },
|
||||
{ "sim ICL layer", "\033[0;36m" },
|
||||
{ "sim layer 7", "\033[0;37m" },
|
||||
{ "mtp layer 2", "\033[1;33m" },
|
||||
{ "mtp layer 3", "\033[1;36m" },
|
||||
{ "MuP", "\033[1;37m" },
|
||||
{ "router", "\033[1;35m" },
|
||||
{ "stderr", "\033[1;37m" },
|
||||
{ "ss5", "\033[1;34m" },
|
||||
{ "r1", "\033[1;34m" },
|
||||
{ "isdn", "\033[1;35m" },
|
||||
{ "misdn", "\033[0;34m" },
|
||||
{ "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[2] = { ~0, ~0 };
|
||||
extern int num_kanal;
|
||||
|
||||
void (*clear_console_text)(void) = NULL;
|
||||
void (*print_console_text)(void) = NULL;
|
||||
|
||||
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;
|
||||
int rc;
|
||||
|
||||
rc = ioctl(0, TIOCGWINSZ, &win);
|
||||
if (rc) {
|
||||
*w = 80;
|
||||
*h = 25;
|
||||
return;
|
||||
}
|
||||
|
||||
*h = win.ws_row;
|
||||
*w = win.ws_col;
|
||||
}
|
||||
|
||||
void _printdebug(const char *file, const char __attribute__((unused)) *function, int line, int cat, int level, const char *kanal, const char *fmt, ...)
|
||||
{
|
||||
char buffer[4096], *b = buffer;
|
||||
int s = sizeof(buffer) - 1;
|
||||
const char *p;
|
||||
va_list args;
|
||||
int w, h = 0; // make GCC happy
|
||||
|
||||
if (debuglevel > level)
|
||||
return;
|
||||
|
||||
if (!(debug_mask[cat >> 6] & ((uint64_t)1 << (cat & 63))))
|
||||
return;
|
||||
|
||||
lock_debug();
|
||||
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
|
||||
/* if kanal is used, prefix the channel number */
|
||||
if (num_kanal > 1 && kanal) {
|
||||
sprintf(buffer, "(chan %s) ", kanal);
|
||||
b = strchr(buffer, '\0');
|
||||
s -= strlen(buffer);
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
vsnprintf(b, s, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
while ((p = strchr(file, '/')))
|
||||
file = p + 1;
|
||||
if (clear_console_text)
|
||||
clear_console_text();
|
||||
if (debug_limit_scroll) {
|
||||
get_win_size(&w, &h);
|
||||
printf("\0337\033[%d;%dr\0338", debug_limit_scroll + 1, h);
|
||||
}
|
||||
if (debug_date) {
|
||||
struct timeval tv;
|
||||
struct tm *tm;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
tm = localtime(&tv.tv_sec);
|
||||
|
||||
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: %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);
|
||||
|
||||
unlock_debug();
|
||||
}
|
||||
|
||||
const char *debug_amplitude(double level)
|
||||
{
|
||||
static char text[42];
|
||||
|
||||
strcpy(text, " : ");
|
||||
if (level > 1.0)
|
||||
level = 1.0;
|
||||
if (level < -1.0)
|
||||
level = -1.0;
|
||||
text[20 + (int)(level * 20)] = '*';
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
#define level2db(level) (20 * log10(level))
|
||||
|
||||
const char *debug_db(double level_db)
|
||||
{
|
||||
static char text[128];
|
||||
int l;
|
||||
|
||||
strcpy(text, ": . : . : . : . : . : . : . : . | . : . : . : . : . : . : . : . :");
|
||||
if (level_db <= 0.0)
|
||||
return text;
|
||||
l = (int)round(level2db(level_db));
|
||||
if (l > 48)
|
||||
return text;
|
||||
if (l < -48)
|
||||
return text;
|
||||
text[l + 48] = '*';
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
void debug_print_help(void)
|
||||
{
|
||||
printf(" -v --verbose <level> | <level>,<category>[,<category>[,...]] | list\n");
|
||||
printf(" Use 'list' to get a list of all levels and categories\n");
|
||||
printf(" Verbose level: digit of debug level (default = '%d')\n", debuglevel);
|
||||
printf(" Verbose level+category: level digit followed by one or more categories\n");
|
||||
printf(" -> If no category is specified, all categories are selected\n");
|
||||
printf(" -v --verbose date\n");
|
||||
printf(" Show date with debug output\n");
|
||||
}
|
||||
|
||||
void debug_list_cat(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Give number of debug level:\n");
|
||||
for (i = 0; debug_level[i]; i++)
|
||||
printf(" %d = %s\n", i, debug_level[i]);
|
||||
printf("\n");
|
||||
|
||||
printf("Give name(s) of debug category:\n");
|
||||
for (i = 0; debug_cat[i].name; i++)
|
||||
printf(" %s%s\033[0;39m\n", debug_cat[i].color, debug_cat[i].name);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int parse_debug_opt(const char *optarg)
|
||||
{
|
||||
int i, max_level = 0;
|
||||
char *dup, *dstring, *p;
|
||||
|
||||
if (!strcasecmp(optarg, "date")) {
|
||||
debug_date = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (i = 0; debug_level[i]; i++)
|
||||
max_level = i;
|
||||
|
||||
dup = dstring = strdup(optarg);
|
||||
p = strsep(&dstring, ",");
|
||||
for (i = 0; i < p[i]; i++) {
|
||||
if (p[i] < '0' || p[i] > '9') {
|
||||
fprintf(stderr, "Only digits are allowed for debug level!\n");
|
||||
free(dup);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
debuglevel = atoi(p);
|
||||
if (debuglevel > max_level) {
|
||||
fprintf(stderr, "Debug level too high, use 'list' to show available levels!\n");
|
||||
free(dup);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (dstring)
|
||||
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))
|
||||
break;
|
||||
}
|
||||
if (!debug_cat[i].name) {
|
||||
fprintf(stderr, "Given debug category '%s' unknown, use 'list' to show available categories!\n", p);
|
||||
free(dup);
|
||||
return -EINVAL;
|
||||
}
|
||||
debug_mask[i >> 6] |= ((uint64_t)1 << (i & 63));
|
||||
}
|
||||
|
||||
free(dup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *debug_hex(const uint8_t *data, int len)
|
||||
{
|
||||
static char *text = NULL;
|
||||
char *p;
|
||||
int i;
|
||||
|
||||
if (text)
|
||||
free(text);
|
||||
p = text = calloc(1, len * 3 + 1);
|
||||
for (i = 0; i < len; i++) {
|
||||
sprintf(p, "%02x ", *data++);
|
||||
p += 3;
|
||||
}
|
||||
if (text[0])
|
||||
p[-1] = '\0';
|
||||
|
||||
return text;
|
||||
}
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
|
||||
#define DEBUG_DEBUG 0 /* debug info, not for normal use */
|
||||
#define DEBUG_INFO 1 /* all info about process */
|
||||
#define DEBUG_NOTICE 2 /* something unexpected happens */
|
||||
#define DEBUG_ERROR 3 /* there is an error with this software */
|
||||
|
||||
#define DOPTIONS 0
|
||||
#define DSENDER 1
|
||||
#define DSOUND 2
|
||||
#define DDSP 3
|
||||
#define DANETZ 4
|
||||
#define DBNETZ 5
|
||||
#define DCNETZ 6
|
||||
#define DNMT 7
|
||||
#define DAMPS 8
|
||||
#define DR2000 9
|
||||
#define DIMTS 10
|
||||
#define DMPT1327 11
|
||||
#define DJOLLY 12
|
||||
#define DEURO 13
|
||||
#define DPOCSAG 14
|
||||
#define DGOLAY 15
|
||||
#define DFUENF 16
|
||||
#define DFRAME 17
|
||||
#define DCALL 18
|
||||
#define DCC 19
|
||||
#define DDB 20
|
||||
#define DTRANS 21
|
||||
#define DDMS 22
|
||||
#define DSMS 23
|
||||
#define DSDR 24
|
||||
#define DUHD 25
|
||||
#define DSOAPY 26
|
||||
#define DWAVE 27
|
||||
#define DRADIO 28
|
||||
#define DAM791X 29
|
||||
#define DUART 30
|
||||
#define DDEVICE 31
|
||||
#define DDATENKLO 32
|
||||
#define DZEIT 33
|
||||
#define DSIM1 34
|
||||
#define DSIM2 35
|
||||
#define DSIMI 36
|
||||
#define DSIM7 37
|
||||
#define DMTP2 38
|
||||
#define DMTP3 39
|
||||
#define DMUP 40
|
||||
#define DROUTER 41
|
||||
#define DSTDERR 42
|
||||
#define DSS5 43
|
||||
#define DR1 44
|
||||
#define DISDN 45
|
||||
#define DMISDN 46
|
||||
#define DDSS1 47
|
||||
#define DSIP 48
|
||||
#define DTEL 49
|
||||
#define DUK0 50
|
||||
#define DPH 51
|
||||
#define DDCF77 52
|
||||
#define DJITTER 53
|
||||
//NOTE: increment mask array, if 127 is exceeded
|
||||
|
||||
void lock_debug(void);
|
||||
void unlock_debug(void);
|
||||
|
||||
void get_win_size(int *w, int *h);
|
||||
|
||||
#define PDEBUG(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, NULL, fmt, ## arg)
|
||||
#define PDEBUG_CHAN(cat, level, fmt, arg...) _printdebug(__FILE__, __FUNCTION__, __LINE__, cat, level, CHAN, fmt, ## arg)
|
||||
void _printdebug(const char *file, const char *function, int line, int cat, int level, const char *chan_str, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 7, 8)));
|
||||
|
||||
const char *debug_amplitude(double level);
|
||||
const char *debug_db(double level_db);
|
||||
|
||||
void debug_print_help(void);
|
||||
void debug_list_cat(void);
|
||||
int parse_debug_opt(const char *opt);
|
||||
|
||||
extern int debuglevel;
|
||||
|
||||
extern void (*clear_console_text)(void);
|
||||
extern void (*print_console_text)(void);
|
||||
|
||||
extern int debug_limit_scroll;
|
||||
|
||||
const char *debug_hex(const uint8_t *data, int len);
|
||||
|
||||
#define LOGP PDEBUG
|
||||
#define LOGL_DEBUG DEBUG_DEBUG
|
||||
#define LOGL_INFO DEBUG_INFO
|
||||
#define LOGL_NOTICE DEBUG_NOTICE
|
||||
#define LOGL_ERROR DEBUG_ERROR
|
||||
#define osmo_hexdump debug_hex
|
||||
|
|
@ -22,10 +22,10 @@
|
|||
#include <string.h>
|
||||
#include <math.h>
|
||||
#include "../libsample/sample.h"
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../liblogging/logging.h"
|
||||
#include "dtmf_decode.h"
|
||||
|
||||
//#define DEBUG
|
||||
//#define DEBUG_DTMF
|
||||
|
||||
#define level2db(level) (20 * log10(level))
|
||||
#define db2level(db) pow(10, (double)db / 20.0)
|
||||
|
@ -133,7 +133,7 @@ void dtmf_decode(dtmf_dec_t *dtmf, sample_t *samples, int length)
|
|||
dtmf_decode_filter(dtmf, samples, length, frequency_low, frequency_high, amplitude_low, amplitude_high);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_DTMF
|
||||
// printf("%s %.5f\n", debug_amplitude(samples[i]/2.0), samples[i]/2.0);
|
||||
#endif
|
||||
/* get frequency of low frequencies, correct amplitude drop at cutoff point */
|
||||
|
@ -194,7 +194,7 @@ void dtmf_decode(dtmf_dec_t *dtmf, sample_t *samples, int length)
|
|||
/* check for limits */
|
||||
if (amplitude_low[i] <= max_amplitude && amplitude_low[i] >= min_amplitude && amplitude_high[i] <= max_amplitude && amplitude_high[i] >= min_amplitude) {
|
||||
amplitude_ok = 1;
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_DTMF
|
||||
printf("%.1f %.1f (limits %.1f .. %.1f) %.1f\n", level2db(amplitude_low[i]), level2db(amplitude_high[i]), level2db(min_amplitude), level2db(max_amplitude), level2db(amplitude_high[i] / amplitude_low[i]));
|
||||
#endif
|
||||
if (amplitude_high[i] / amplitude_low[i] <= forward_twist && amplitude_low[i] / amplitude_high[i] <= reverse_twist)
|
||||
|
@ -231,14 +231,14 @@ void dtmf_decode(dtmf_dec_t *dtmf, sample_t *samples, int length)
|
|||
count++;
|
||||
if (count >= time_pause) {
|
||||
detected = 0;
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_DTMF
|
||||
printf("lost!\n");
|
||||
#endif
|
||||
}
|
||||
} else
|
||||
count = 0;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
#ifdef DEBUG_DTMF
|
||||
if (digit)
|
||||
printf("DTMF tone='%c' diff frequency=%.1f %.1f amplitude=%.1f %.1f dB (%s) twist=%.1f dB (%s)\n", digit, f1, f2, level2db(amplitude_low[i]), level2db(amplitude_high[i]), (amplitude_ok) ? "OK" : "nok", level2db(amplitude_high[i] / amplitude_low[i]), (twist_ok) ? "OK" : "nok");
|
||||
#endif
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||
|
||||
noinst_LIBRARIES = libg711.a
|
||||
|
||||
libg711_a_SOURCES = \
|
||||
g711.c
|
||||
|
|
@ -1,537 +0,0 @@
|
|||
/*****************************************************************************\
|
||||
** **
|
||||
** PBX4Linux **
|
||||
** **
|
||||
**---------------------------------------------------------------------------**
|
||||
** Copyright: Andreas Eversberg (GPL) **
|
||||
** **
|
||||
** audio conversions for alaw and ulaw **
|
||||
** **
|
||||
\*****************************************************************************/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* ulaw -> signed 16-bit */
|
||||
static int16_t g711_ulaw_to_linear[256] =
|
||||
{
|
||||
0x8284, 0x8684, 0x8a84, 0x8e84, 0x9284, 0x9684, 0x9a84, 0x9e84,
|
||||
0xa284, 0xa684, 0xaa84, 0xae84, 0xb284, 0xb684, 0xba84, 0xbe84,
|
||||
0xc184, 0xc384, 0xc584, 0xc784, 0xc984, 0xcb84, 0xcd84, 0xcf84,
|
||||
0xd184, 0xd384, 0xd584, 0xd784, 0xd984, 0xdb84, 0xdd84, 0xdf84,
|
||||
0xe104, 0xe204, 0xe304, 0xe404, 0xe504, 0xe604, 0xe704, 0xe804,
|
||||
0xe904, 0xea04, 0xeb04, 0xec04, 0xed04, 0xee04, 0xef04, 0xf004,
|
||||
0xf0c4, 0xf144, 0xf1c4, 0xf244, 0xf2c4, 0xf344, 0xf3c4, 0xf444,
|
||||
0xf4c4, 0xf544, 0xf5c4, 0xf644, 0xf6c4, 0xf744, 0xf7c4, 0xf844,
|
||||
0xf8a4, 0xf8e4, 0xf924, 0xf964, 0xf9a4, 0xf9e4, 0xfa24, 0xfa64,
|
||||
0xfaa4, 0xfae4, 0xfb24, 0xfb64, 0xfba4, 0xfbe4, 0xfc24, 0xfc64,
|
||||
0xfc94, 0xfcb4, 0xfcd4, 0xfcf4, 0xfd14, 0xfd34, 0xfd54, 0xfd74,
|
||||
0xfd94, 0xfdb4, 0xfdd4, 0xfdf4, 0xfe14, 0xfe34, 0xfe54, 0xfe74,
|
||||
0xfe8c, 0xfe9c, 0xfeac, 0xfebc, 0xfecc, 0xfedc, 0xfeec, 0xfefc,
|
||||
0xff0c, 0xff1c, 0xff2c, 0xff3c, 0xff4c, 0xff5c, 0xff6c, 0xff7c,
|
||||
0xff88, 0xff90, 0xff98, 0xffa0, 0xffa8, 0xffb0, 0xffb8, 0xffc0,
|
||||
0xffc8, 0xffd0, 0xffd8, 0xffe0, 0xffe8, 0xfff0, 0xfff8, 0xffff,
|
||||
0x7d7c, 0x797c, 0x757c, 0x717c, 0x6d7c, 0x697c, 0x657c, 0x617c,
|
||||
0x5d7c, 0x597c, 0x557c, 0x517c, 0x4d7c, 0x497c, 0x457c, 0x417c,
|
||||
0x3e7c, 0x3c7c, 0x3a7c, 0x387c, 0x367c, 0x347c, 0x327c, 0x307c,
|
||||
0x2e7c, 0x2c7c, 0x2a7c, 0x287c, 0x267c, 0x247c, 0x227c, 0x207c,
|
||||
0x1efc, 0x1dfc, 0x1cfc, 0x1bfc, 0x1afc, 0x19fc, 0x18fc, 0x17fc,
|
||||
0x16fc, 0x15fc, 0x14fc, 0x13fc, 0x12fc, 0x11fc, 0x10fc, 0x0ffc,
|
||||
0x0f3c, 0x0ebc, 0x0e3c, 0x0dbc, 0x0d3c, 0x0cbc, 0x0c3c, 0x0bbc,
|
||||
0x0b3c, 0x0abc, 0x0a3c, 0x09bc, 0x093c, 0x08bc, 0x083c, 0x07bc,
|
||||
0x075c, 0x071c, 0x06dc, 0x069c, 0x065c, 0x061c, 0x05dc, 0x059c,
|
||||
0x055c, 0x051c, 0x04dc, 0x049c, 0x045c, 0x041c, 0x03dc, 0x039c,
|
||||
0x036c, 0x034c, 0x032c, 0x030c, 0x02ec, 0x02cc, 0x02ac, 0x028c,
|
||||
0x026c, 0x024c, 0x022c, 0x020c, 0x01ec, 0x01cc, 0x01ac, 0x018c,
|
||||
0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104,
|
||||
0x00f4, 0x00e4, 0x00d4, 0x00c4, 0x00b4, 0x00a4, 0x0094, 0x0084,
|
||||
0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040,
|
||||
0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000
|
||||
};
|
||||
|
||||
/* alaw -> signed 16-bit */
|
||||
static int16_t g711_alaw_flipped_to_linear[256] =
|
||||
{
|
||||
0x13fc, 0xec04, 0x0144, 0xfebc, 0x517c, 0xae84, 0x051c, 0xfae4,
|
||||
0x0a3c, 0xf5c4, 0x0048, 0xffb8, 0x287c, 0xd784, 0x028c, 0xfd74,
|
||||
0x1bfc, 0xe404, 0x01cc, 0xfe34, 0x717c, 0x8e84, 0x071c, 0xf8e4,
|
||||
0x0e3c, 0xf1c4, 0x00c4, 0xff3c, 0x387c, 0xc784, 0x039c, 0xfc64,
|
||||
0x0ffc, 0xf004, 0x0104, 0xfefc, 0x417c, 0xbe84, 0x041c, 0xfbe4,
|
||||
0x083c, 0xf7c4, 0x0008, 0xfff8, 0x207c, 0xdf84, 0x020c, 0xfdf4,
|
||||
0x17fc, 0xe804, 0x018c, 0xfe74, 0x617c, 0x9e84, 0x061c, 0xf9e4,
|
||||
0x0c3c, 0xf3c4, 0x0084, 0xff7c, 0x307c, 0xcf84, 0x030c, 0xfcf4,
|
||||
0x15fc, 0xea04, 0x0164, 0xfe9c, 0x597c, 0xa684, 0x059c, 0xfa64,
|
||||
0x0b3c, 0xf4c4, 0x0068, 0xff98, 0x2c7c, 0xd384, 0x02cc, 0xfd34,
|
||||
0x1dfc, 0xe204, 0x01ec, 0xfe14, 0x797c, 0x8684, 0x07bc, 0xf844,
|
||||
0x0f3c, 0xf0c4, 0x00e4, 0xff1c, 0x3c7c, 0xc384, 0x03dc, 0xfc24,
|
||||
0x11fc, 0xee04, 0x0124, 0xfedc, 0x497c, 0xb684, 0x049c, 0xfb64,
|
||||
0x093c, 0xf6c4, 0x0028, 0xffd8, 0x247c, 0xdb84, 0x024c, 0xfdb4,
|
||||
0x19fc, 0xe604, 0x01ac, 0xfe54, 0x697c, 0x9684, 0x069c, 0xf964,
|
||||
0x0d3c, 0xf2c4, 0x00a4, 0xff5c, 0x347c, 0xcb84, 0x034c, 0xfcb4,
|
||||
0x12fc, 0xed04, 0x0134, 0xfecc, 0x4d7c, 0xb284, 0x04dc, 0xfb24,
|
||||
0x09bc, 0xf644, 0x0038, 0xffc8, 0x267c, 0xd984, 0x026c, 0xfd94,
|
||||
0x1afc, 0xe504, 0x01ac, 0xfe54, 0x6d7c, 0x9284, 0x06dc, 0xf924,
|
||||
0x0dbc, 0xf244, 0x00b4, 0xff4c, 0x367c, 0xc984, 0x036c, 0xfc94,
|
||||
0x0f3c, 0xf0c4, 0x00f4, 0xff0c, 0x3e7c, 0xc184, 0x03dc, 0xfc24,
|
||||
0x07bc, 0xf844, 0x0008, 0xfff8, 0x1efc, 0xe104, 0x01ec, 0xfe14,
|
||||
0x16fc, 0xe904, 0x0174, 0xfe8c, 0x5d7c, 0xa284, 0x05dc, 0xfa24,
|
||||
0x0bbc, 0xf444, 0x0078, 0xff88, 0x2e7c, 0xd184, 0x02ec, 0xfd14,
|
||||
0x14fc, 0xeb04, 0x0154, 0xfeac, 0x557c, 0xaa84, 0x055c, 0xfaa4,
|
||||
0x0abc, 0xf544, 0x0058, 0xffa8, 0x2a7c, 0xd584, 0x02ac, 0xfd54,
|
||||
0x1cfc, 0xe304, 0x01cc, 0xfe34, 0x757c, 0x8a84, 0x075c, 0xf8a4,
|
||||
0x0ebc, 0xf144, 0x00d4, 0xff2c, 0x3a7c, 0xc584, 0x039c, 0xfc64,
|
||||
0x10fc, 0xef04, 0x0114, 0xfeec, 0x457c, 0xba84, 0x045c, 0xfba4,
|
||||
0x08bc, 0xf744, 0x0018, 0xffe8, 0x227c, 0xdd84, 0x022c, 0xfdd4,
|
||||
0x18fc, 0xe704, 0x018c, 0xfe74, 0x657c, 0x9a84, 0x065c, 0xf9a4,
|
||||
0x0cbc, 0xf344, 0x0094, 0xff6c, 0x327c, 0xcd84, 0x032c, 0xfcd4
|
||||
};
|
||||
|
||||
/* Xlaw -> signed 16-bit */
|
||||
static int16_t g711_alaw_to_linear[256];
|
||||
static int16_t g711_ulaw_flipped_to_linear[256];
|
||||
|
||||
/* signed 16-bit -> Xlaw */
|
||||
static uint8_t g711_linear_to_alaw_flipped[65536];
|
||||
static uint8_t g711_linear_to_ulaw_flipped[65536];
|
||||
static uint8_t g711_linear_to_alaw[65536];
|
||||
static uint8_t g711_linear_to_ulaw[65536];
|
||||
|
||||
/* transcode */
|
||||
static uint8_t g711_alaw_to_ulaw[256];
|
||||
static uint8_t g711_ulaw_to_alaw[256];
|
||||
static uint8_t g711_alaw_flipped_to_ulaw[256];
|
||||
static uint8_t g711_ulaw_flipped_to_alaw[256];
|
||||
static uint8_t g711_alaw_to_ulaw_flipped[256];
|
||||
static uint8_t g711_ulaw_to_alaw_flipped[256];
|
||||
|
||||
/* table is used to generate linear_to_alaw */
|
||||
static int16_t g711_alaw_relations[] =
|
||||
{
|
||||
0x8684, 0x55, 0x8a84, 0xd5, 0x8e84, 0x15, 0x9284, 0x95,
|
||||
0x9684, 0x75, 0x9a84, 0xf5, 0x9e84, 0x35, 0xa284, 0xb5,
|
||||
0xa684, 0x45, 0xaa84, 0xc5, 0xae84, 0x05, 0xb284, 0x85,
|
||||
0xb684, 0x65, 0xba84, 0xe5, 0xbe84, 0x25, 0xc184, 0xa5,
|
||||
0xc384, 0x5d, 0xc584, 0xdd, 0xc784, 0x1d, 0xc984, 0x9d,
|
||||
0xcb84, 0x7d, 0xcd84, 0xfd, 0xcf84, 0x3d, 0xd184, 0xbd,
|
||||
0xd384, 0x4d, 0xd584, 0xcd, 0xd784, 0x0d, 0xd984, 0x8d,
|
||||
0xdb84, 0x6d, 0xdd84, 0xed, 0xdf84, 0x2d, 0xe104, 0xad,
|
||||
0xe204, 0x51, 0xe304, 0xd1, 0xe404, 0x11, 0xe504, 0x91,
|
||||
0xe604, 0x71, 0xe704, 0xf1, 0xe804, 0x31, 0xe904, 0xb1,
|
||||
0xea04, 0x41, 0xeb04, 0xc1, 0xec04, 0x01, 0xed04, 0x81,
|
||||
0xee04, 0x61, 0xef04, 0xe1, 0xf004, 0x21, 0xf0c4, 0x59,
|
||||
0xf0c4, 0xa1, 0xf144, 0xd9, 0xf1c4, 0x19, 0xf244, 0x99,
|
||||
0xf2c4, 0x79, 0xf344, 0xf9, 0xf3c4, 0x39, 0xf444, 0xb9,
|
||||
0xf4c4, 0x49, 0xf544, 0xc9, 0xf5c4, 0x09, 0xf644, 0x89,
|
||||
0xf6c4, 0x69, 0xf744, 0xe9, 0xf7c4, 0x29, 0xf844, 0x57,
|
||||
0xf844, 0xa9, 0xf8a4, 0xd7, 0xf8e4, 0x17, 0xf924, 0x97,
|
||||
0xf964, 0x77, 0xf9a4, 0xf7, 0xf9e4, 0x37, 0xfa24, 0xb7,
|
||||
0xfa64, 0x47, 0xfaa4, 0xc7, 0xfae4, 0x07, 0xfb24, 0x87,
|
||||
0xfb64, 0x67, 0xfba4, 0xe7, 0xfbe4, 0x27, 0xfc24, 0x5f,
|
||||
0xfc24, 0xa7, 0xfc64, 0x1f, 0xfc64, 0xdf, 0xfc94, 0x9f,
|
||||
0xfcb4, 0x7f, 0xfcd4, 0xff, 0xfcf4, 0x3f, 0xfd14, 0xbf,
|
||||
0xfd34, 0x4f, 0xfd54, 0xcf, 0xfd74, 0x0f, 0xfd94, 0x8f,
|
||||
0xfdb4, 0x6f, 0xfdd4, 0xef, 0xfdf4, 0x2f, 0xfe14, 0x53,
|
||||
0xfe14, 0xaf, 0xfe34, 0x13, 0xfe34, 0xd3, 0xfe54, 0x73,
|
||||
0xfe54, 0x93, 0xfe74, 0x33, 0xfe74, 0xf3, 0xfe8c, 0xb3,
|
||||
0xfe9c, 0x43, 0xfeac, 0xc3, 0xfebc, 0x03, 0xfecc, 0x83,
|
||||
0xfedc, 0x63, 0xfeec, 0xe3, 0xfefc, 0x23, 0xff0c, 0xa3,
|
||||
0xff1c, 0x5b, 0xff2c, 0xdb, 0xff3c, 0x1b, 0xff4c, 0x9b,
|
||||
0xff5c, 0x7b, 0xff6c, 0xfb, 0xff7c, 0x3b, 0xff88, 0xbb,
|
||||
0xff98, 0x4b, 0xffa8, 0xcb, 0xffb8, 0x0b, 0xffc8, 0x8b,
|
||||
0xffd8, 0x6b, 0xffe8, 0xeb, 0xfff8, 0x2b, 0xfff8, 0xab,
|
||||
0x0008, 0x2a, 0x0008, 0xaa, 0x0018, 0xea, 0x0028, 0x6a,
|
||||
0x0038, 0x8a, 0x0048, 0x0a, 0x0058, 0xca, 0x0068, 0x4a,
|
||||
0x0078, 0xba, 0x0084, 0x3a, 0x0094, 0xfa, 0x00a4, 0x7a,
|
||||
0x00b4, 0x9a, 0x00c4, 0x1a, 0x00d4, 0xda, 0x00e4, 0x5a,
|
||||
0x00f4, 0xa2, 0x0104, 0x22, 0x0114, 0xe2, 0x0124, 0x62,
|
||||
0x0134, 0x82, 0x0144, 0x02, 0x0154, 0xc2, 0x0164, 0x42,
|
||||
0x0174, 0xb2, 0x018c, 0x32, 0x018c, 0xf2, 0x01ac, 0x72,
|
||||
0x01ac, 0x92, 0x01cc, 0x12, 0x01cc, 0xd2, 0x01ec, 0x52,
|
||||
0x01ec, 0xae, 0x020c, 0x2e, 0x022c, 0xee, 0x024c, 0x6e,
|
||||
0x026c, 0x8e, 0x028c, 0x0e, 0x02ac, 0xce, 0x02cc, 0x4e,
|
||||
0x02ec, 0xbe, 0x030c, 0x3e, 0x032c, 0xfe, 0x034c, 0x7e,
|
||||
0x036c, 0x9e, 0x039c, 0x1e, 0x039c, 0xde, 0x03dc, 0x5e,
|
||||
0x03dc, 0xa6, 0x041c, 0x26, 0x045c, 0xe6, 0x049c, 0x66,
|
||||
0x04dc, 0x86, 0x051c, 0x06, 0x055c, 0xc6, 0x059c, 0x46,
|
||||
0x05dc, 0xb6, 0x061c, 0x36, 0x065c, 0xf6, 0x069c, 0x76,
|
||||
0x06dc, 0x96, 0x071c, 0x16, 0x075c, 0xd6, 0x07bc, 0x56,
|
||||
0x07bc, 0xa8, 0x083c, 0x28, 0x08bc, 0xe8, 0x093c, 0x68,
|
||||
0x09bc, 0x88, 0x0a3c, 0x08, 0x0abc, 0xc8, 0x0b3c, 0x48,
|
||||
0x0bbc, 0xb8, 0x0c3c, 0x38, 0x0cbc, 0xf8, 0x0d3c, 0x78,
|
||||
0x0dbc, 0x98, 0x0e3c, 0x18, 0x0ebc, 0xd8, 0x0f3c, 0x58,
|
||||
0x0f3c, 0xa0, 0x0ffc, 0x20, 0x10fc, 0xe0, 0x11fc, 0x60,
|
||||
0x12fc, 0x80, 0x13fc, 0x00, 0x14fc, 0xc0, 0x15fc, 0x40,
|
||||
0x16fc, 0xb0, 0x17fc, 0x30, 0x18fc, 0xf0, 0x19fc, 0x70,
|
||||
0x1afc, 0x90, 0x1bfc, 0x10, 0x1cfc, 0xd0, 0x1dfc, 0x50,
|
||||
0x1efc, 0xac, 0x207c, 0x2c, 0x227c, 0xec, 0x247c, 0x6c,
|
||||
0x267c, 0x8c, 0x287c, 0x0c, 0x2a7c, 0xcc, 0x2c7c, 0x4c,
|
||||
0x2e7c, 0xbc, 0x307c, 0x3c, 0x327c, 0xfc, 0x347c, 0x7c,
|
||||
0x367c, 0x9c, 0x387c, 0x1c, 0x3a7c, 0xdc, 0x3c7c, 0x5c,
|
||||
0x3e7c, 0xa4, 0x417c, 0x24, 0x457c, 0xe4, 0x497c, 0x64,
|
||||
0x4d7c, 0x84, 0x517c, 0x04, 0x557c, 0xc4, 0x597c, 0x44,
|
||||
0x5d7c, 0xb4, 0x617c, 0x34, 0x657c, 0xf4, 0x697c, 0x74,
|
||||
0x6d7c, 0x94, 0x717c, 0x14, 0x757c, 0xd4, 0x797c, 0x54
|
||||
};
|
||||
|
||||
uint8_t g711_flip[256];
|
||||
|
||||
static int g711_initialized = 0;
|
||||
|
||||
/* generate tables
|
||||
*/
|
||||
void g711_init(void)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
/* flip tables */
|
||||
for (i = 0; i < 256; i++) {
|
||||
g711_flip[i]
|
||||
= ((i & 1) << 7)
|
||||
+ ((i & 2) << 5)
|
||||
+ ((i & 4) << 3)
|
||||
+ ((i & 8) << 1)
|
||||
+ ((i & 16) >> 1)
|
||||
+ ((i & 32) >> 3)
|
||||
+ ((i & 64) >> 5)
|
||||
+ ((i & 128) >> 7);
|
||||
g711_alaw_to_linear[i] = g711_alaw_flipped_to_linear[g711_flip[i]];
|
||||
g711_ulaw_flipped_to_linear[i] = g711_ulaw_to_linear[g711_flip[i]];
|
||||
}
|
||||
|
||||
/* linear to alaw tables */
|
||||
i = j = 0;
|
||||
while(i < 65536) {
|
||||
if (i - 32768 > g711_alaw_relations[j << 1])
|
||||
j++;
|
||||
if (j > 255)
|
||||
j = 255;
|
||||
g711_linear_to_alaw_flipped[(i - 32768) & 0xffff] = g711_alaw_relations[(j << 1) | 1];
|
||||
g711_linear_to_alaw[(i - 32768) & 0xffff] = g711_flip[g711_alaw_relations[(j << 1) | 1]];
|
||||
i++;
|
||||
}
|
||||
|
||||
/* linear to ulaw tables */
|
||||
i = j = 0;
|
||||
while(i < 32768) {
|
||||
if (i - 32768 > g711_ulaw_to_linear[j])
|
||||
j++;
|
||||
g711_linear_to_ulaw[(i - 32768) & 0xffff] = j;
|
||||
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = g711_flip[j];
|
||||
i++;
|
||||
}
|
||||
j = 255;
|
||||
while(i < 65536) {
|
||||
if (i - 32768 > g711_ulaw_to_linear[j])
|
||||
j--;
|
||||
g711_linear_to_ulaw[(i - 32768) & 0xffff] = j;
|
||||
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = g711_flip[j];
|
||||
i++;
|
||||
}
|
||||
|
||||
/* transcode */
|
||||
for (i = 0; i < 256; i++) {
|
||||
g711_alaw_to_ulaw[i] = g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[i]];
|
||||
g711_ulaw_to_alaw[i] = g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[i]];
|
||||
g711_alaw_flipped_to_ulaw[i] = g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[g711_flip[i]]];
|
||||
g711_ulaw_flipped_to_alaw[i] = g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[g711_flip[i]]];
|
||||
g711_alaw_to_ulaw_flipped[i] = g711_flip[g711_linear_to_ulaw[(uint16_t)g711_alaw_to_linear[i]]];
|
||||
g711_ulaw_to_alaw_flipped[i] = g711_flip[g711_linear_to_alaw[(uint16_t)g711_ulaw_to_linear[i]]];
|
||||
}
|
||||
|
||||
g711_initialized = 1;
|
||||
}
|
||||
|
||||
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
int16_t *src = (int16_t *)src_data;
|
||||
uint8_t *dst;
|
||||
int len = src_len / 2, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_linear_to_alaw_flipped[(uint16_t)src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
int16_t *src = (int16_t *)src_data;
|
||||
uint8_t *dst;
|
||||
int len = src_len / 2, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_linear_to_ulaw_flipped[(uint16_t)src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data;
|
||||
int16_t *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len * 2);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_alaw_flipped_to_linear[src[i]];
|
||||
*dst_data = (uint8_t *)dst;
|
||||
*dst_len = len * 2;
|
||||
}
|
||||
|
||||
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data;
|
||||
int16_t *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len * 2);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_ulaw_flipped_to_linear[src[i]];
|
||||
*dst_data = (uint8_t *)dst;
|
||||
*dst_len = len * 2;
|
||||
}
|
||||
|
||||
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
int16_t *src = (int16_t *)src_data;
|
||||
uint8_t *dst;
|
||||
int len = src_len / 2, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_linear_to_alaw[(uint16_t)src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
int16_t *src = (int16_t *)src_data;
|
||||
uint8_t *dst;
|
||||
int len = src_len / 2, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_linear_to_ulaw[(uint16_t)src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data;
|
||||
int16_t *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len * 2);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_alaw_to_linear[src[i]];
|
||||
*dst_data = (uint8_t *)dst;
|
||||
*dst_len = len * 2;
|
||||
}
|
||||
|
||||
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data;
|
||||
int16_t *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len * 2);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_ulaw_to_linear[src[i]];
|
||||
*dst_data = (uint8_t *)dst;
|
||||
*dst_len = len * 2;
|
||||
}
|
||||
|
||||
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_alaw_to_ulaw[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_alaw_flipped_to_ulaw[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_alaw_to_ulaw_flipped[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_ulaw_to_alaw[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_ulaw_flipped_to_alaw[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_ulaw_to_alaw_flipped[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
||||
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void __attribute__((unused)) *priv)
|
||||
{
|
||||
uint8_t *src = src_data, *dst;
|
||||
int len = src_len, i;
|
||||
|
||||
if (!g711_initialized) {
|
||||
fprintf(stderr, "G711 codec not initialized! Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
dst = malloc(len);
|
||||
if (!dst)
|
||||
return;
|
||||
for (i = 0; i < len; i++)
|
||||
dst[i] = g711_flip[src[i]];
|
||||
*dst_data = dst;
|
||||
*dst_len = len;
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
void g711_init(void);
|
||||
void g711_encode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_encode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_decode_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_decode_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_encode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_encode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_decode_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_decode_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_alaw_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_alaw_flipped_to_ulaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_alaw_to_ulaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_ulaw_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_ulaw_flipped_to_alaw(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_ulaw_to_alaw_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void g711_transcode_flipped(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
|
|
@ -77,7 +77,7 @@
|
|||
#include <errno.h>
|
||||
#include <math.h>
|
||||
#include "../libsample/sample.h"
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../liblogging/logging.h"
|
||||
#include "jitter.h"
|
||||
|
||||
#define INITIAL_DELAY_INTERVAL 0.5
|
||||
|
@ -104,7 +104,7 @@ int jitter_create(jitter_t *jb, const char *name, double samplerate, int sample_
|
|||
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");
|
||||
LOGP(DJITTER, LOGL_ERROR, "No memory for frame.\n");
|
||||
rc = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
@ -118,7 +118,7 @@ int jitter_create(jitter_t *jb, const char *name, double samplerate, int sample_
|
|||
|
||||
jitter_reset(jb);
|
||||
|
||||
PDEBUG(DJITTER, DEBUG_INFO, "%sCreated jitter buffer. (samplerate=%.0f, target_window=%.0fms, max_window=%.0fms, flag:latency=%s flag:repeat=%s)\n", jb->name, samplerate, target_window_duration * 1000.0, max_window_duration * 1000.0, (window_flags & JITTER_FLAG_LATENCY) ? "true" : "false", (window_flags & JITTER_FLAG_REPEAT) ? "true" : "false");
|
||||
LOGP(DJITTER, LOGL_INFO, "%sCreated jitter buffer. (samplerate=%.0f, target_window=%.0fms, max_window=%.0fms, flag:latency=%s flag:repeat=%s)\n", jb->name, samplerate, target_window_duration * 1000.0, max_window_duration * 1000.0, (window_flags & JITTER_FLAG_LATENCY) ? "true" : "false", (window_flags & JITTER_FLAG_REPEAT) ? "true" : "false");
|
||||
|
||||
error:
|
||||
if (rc)
|
||||
|
@ -139,7 +139,7 @@ void jitter_reset(jitter_t *jb)
|
|||
{
|
||||
jitter_frame_t *jf, *temp;
|
||||
|
||||
PDEBUG(DJITTER, DEBUG_INFO, "%sReset jitter buffer.\n", jb->name);
|
||||
LOGP(DJITTER, LOGL_INFO, "%sReset jitter buffer.\n", jb->name);
|
||||
|
||||
/* jitter buffer locked */
|
||||
jb->unlocked = 0;
|
||||
|
@ -172,7 +172,7 @@ void jitter_destroy(jitter_t *jb)
|
|||
{
|
||||
jitter_reset(jb);
|
||||
|
||||
PDEBUG(DJITTER, DEBUG_INFO, "%sDestroying jitter buffer.\n", jb->name);
|
||||
LOGP(DJITTER, LOGL_INFO, "%sDestroying jitter buffer.\n", jb->name);
|
||||
|
||||
if (jb->extra_samples) {
|
||||
free(jb->extra_samples);
|
||||
|
@ -201,7 +201,7 @@ void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint
|
|||
/* 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);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%sSave frame of %d samples (no seqence).\n", jb->name, length);
|
||||
#endif
|
||||
sequence = jb->next_sequence;
|
||||
jb->next_sequence++;
|
||||
|
@ -210,7 +210,7 @@ void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint
|
|||
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);
|
||||
LOGP(DJITTER, LOGL_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;
|
||||
|
@ -219,9 +219,9 @@ void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint
|
|||
/* 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);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%s Initial frame after init or reset.\n", jb->name);
|
||||
else
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s SSRC changed.\n", jb->name);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%s SSRC changed.\n", jb->name);
|
||||
// NOTE: Reset must be called before finding the frame location below, because there will be no frame in list anymore!
|
||||
jitter_reset(jb);
|
||||
jb->unlocked = 1;
|
||||
|
@ -241,7 +241,7 @@ void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint
|
|||
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);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%s Dropping double packet (sequence = %d)\n", jb->name, sequence);
|
||||
return;
|
||||
}
|
||||
/* offset is negative, so we found the position to insert frame */
|
||||
|
@ -252,7 +252,7 @@ void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint
|
|||
|
||||
offset_timestamp = timestamp - jb->window_timestamp;
|
||||
#ifdef HEAVY_DEBUG
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%sFrame has offset of %.0fms in jitter buffer.\n", jb->name, (double)offset_timestamp * jb->sample_duration * 1000.0);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%sFrame has offset of %.0fms in jitter buffer.\n", jb->name, (double)offset_timestamp * jb->sample_duration * 1000.0);
|
||||
#endif
|
||||
|
||||
/* measure delay */
|
||||
|
@ -262,11 +262,11 @@ void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint
|
|||
/* 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, to make the frame fit to the end. (offset_timestamp(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%s Frame too early: Shift jitter buffer to the future, to make the frame fit to the end. (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;
|
||||
} else {
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too early: Shift jitter buffer to the future, to make the frame fit to the target delay. (offset_timestamp(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%s Frame too early: Shift jitter buffer to the future, to make the frame fit to the target delay. (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);
|
||||
}
|
||||
|
@ -275,11 +275,11 @@ void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint
|
|||
/* 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);
|
||||
LOGP(DJITTER, LOGL_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 + half of target delay */
|
||||
jb->window_timestamp = timestamp - (uint32_t)(jb->target_window_size) / 2;
|
||||
} else {
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too late: Shift jitter buffer to the past, and add half target window size. (offset_timestamp(%d) < 0)\n", jb->name, offset_timestamp);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%s Frame too late: Shift jitter buffer to the past, and add half 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);
|
||||
}
|
||||
|
@ -287,11 +287,11 @@ void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint
|
|||
|
||||
/* insert or append frame */
|
||||
#ifdef HEAVY_DEBUG
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Store frame\n", jb->name);
|
||||
LOGP(DJITTER, LOGL_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");
|
||||
LOGP(DJITTER, LOGL_ERROR, "No memory for frame.\n");
|
||||
return;
|
||||
}
|
||||
memset(jf, 0, sizeof(*jf)); // note: clear header only
|
||||
|
@ -311,7 +311,7 @@ void jitter_load(jitter_t *jb, void *samples, int length)
|
|||
int32_t count, count2, index;
|
||||
|
||||
#ifdef HEAVY_DEBUG
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%sLoad chunk of %d samples.\n", jb->name, length);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%sLoad chunk of %d samples.\n", jb->name, length);
|
||||
#endif
|
||||
|
||||
/* now unlock jitter buffer */
|
||||
|
@ -321,10 +321,10 @@ void jitter_load(jitter_t *jb, void *samples, int length)
|
|||
jb->delay_counter += jb->sample_duration * (double)length;
|
||||
if (jb->delay_counter >= jb->delay_interval) {
|
||||
if (jb->min_delay_value >= 0)
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Statistics: target_window_delay=%.0fms max_window_delay=%.0fms current min_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->min_delay_value * jb->sample_duration * 1000.0);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%s Statistics: target_window_delay=%.0fms max_window_delay=%.0fms current min_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->min_delay_value * jb->sample_duration * 1000.0);
|
||||
/* delay reduction, if maximum delay is greater than target jitter window size */
|
||||
if ((jb->window_flags & JITTER_FLAG_LATENCY) && jb->min_delay_value > jb->target_window_size) {
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Reducing current minimum delay of %.0fms, because maximum delay is greater than target window size of %.0fms.\n", jb->name, (double)jb->min_delay_value * jb->sample_duration * 1000.0, (double)jb->target_window_size * jb->sample_duration * 1000.0);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%s Reducing current minimum delay of %.0fms, because maximum delay is greater than target window size of %.0fms.\n", jb->name, (double)jb->min_delay_value * jb->sample_duration * 1000.0, (double)jb->target_window_size * jb->sample_duration * 1000.0);
|
||||
/* only reduce delay to half of the target window size */
|
||||
jb->window_timestamp += jb->min_delay_value - jb->target_window_size / 2;
|
||||
|
||||
|
@ -349,9 +349,9 @@ void jitter_load(jitter_t *jb, void *samples, int length)
|
|||
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);
|
||||
LOGP(DJITTER, LOGL_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);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%s There is no frame ahead in buffer. Interpolating gap.\n", jb->name);
|
||||
#endif
|
||||
/* extrapolate by playing the extrapolation buffer */
|
||||
while (count) {
|
||||
|
@ -366,7 +366,7 @@ void jitter_load(jitter_t *jb, void *samples, int length)
|
|||
jb->extra_timeout_count++;
|
||||
if (jb->extra_timeout_count == jb->extra_timeout_max) {
|
||||
#ifdef HEAVY_DEBUG
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Repeated jitter buffer enough, clearing to silence.\n", jb->name);
|
||||
LOGP(DJITTER, LOGL_DEBUG, "%s Repeated jitter buffer enough, clearing to silence.\n", jb->name);
|
||||
#endif
|
||||
clear_extra_buffer(jb);
|
||||
}
|
||||
|
@ -393,7 +393,7 @@ void jitter_load(jitter_t *jb, void *samples, int length)
|
|||
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);
|
||||
LOGP(DJITTER, LOGL_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;
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||
|
||||
noinst_LIBRARIES = liblogging.a
|
||||
|
||||
liblogging_a_SOURCES = \
|
||||
logging.c \
|
||||
categories.c
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/logging.h>
|
||||
#include "categories.h"
|
||||
|
||||
/* All logging categories used by this project. */
|
||||
|
||||
struct log_info_cat log_categories[] = {
|
||||
[DLCC] = {
|
||||
.name = "DLCC",
|
||||
.description = "libosmo-cc CC Layer",
|
||||
.color = "\033[0;37m",
|
||||
},
|
||||
[DOPTIONS] = {
|
||||
.name = "DOPTIONS",
|
||||
.description = "config options",
|
||||
.color = "\033[0;33m",
|
||||
},
|
||||
[DJITTER] = {
|
||||
.name = "DJITTER",
|
||||
.description = "jitter buffer handling",
|
||||
.color = "\033[0;36m",
|
||||
},
|
||||
[DPH] = {
|
||||
.name = "DPH",
|
||||
.description = "PH SAP socket interface",
|
||||
.color = "\033[0;33m",
|
||||
},
|
||||
[DWAVE] = {
|
||||
.name = "DWAVE",
|
||||
.description = "WAVE file handling",
|
||||
.color = "\033[1;33m",
|
||||
},
|
||||
[DROUTER] = {
|
||||
.name = "DROUTER",
|
||||
.description = "Call Router",
|
||||
.color = "\033[1;35m",
|
||||
},
|
||||
[DSTDERR] = {
|
||||
.name = "DSTDERR",
|
||||
.description = "stderr output",
|
||||
.color = "\033[1;37m",
|
||||
},
|
||||
};
|
||||
|
||||
size_t log_categories_size = ARRAY_SIZE(log_categories);
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
enum {
|
||||
DLCC,
|
||||
DOPTIONS,
|
||||
DJITTER,
|
||||
DPH,
|
||||
DWAVE,
|
||||
DROUTER,
|
||||
DSTDERR,
|
||||
};
|
||||
|
||||
extern struct log_info_cat log_categories[];
|
||||
extern size_t log_categories_size;
|
||||
|
|
@ -0,0 +1,264 @@
|
|||
/* Logging (on segmented part of the window)
|
||||
*
|
||||
* (C) 2016 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 <sys/ioctl.h>
|
||||
#include <math.h>
|
||||
#include <errno.h>
|
||||
#include <osmocom/core/utils.h>
|
||||
#include <osmocom/core/application.h>
|
||||
#include <osmocom/cc/misc.h>
|
||||
#include "logging.h"
|
||||
|
||||
int loglevel = LOGL_INFO;
|
||||
|
||||
static int scroll_window_start = 0;
|
||||
static int scroll_window_end = 0;
|
||||
static int scroll_window_height = 0;
|
||||
|
||||
void lock_logging(void)
|
||||
{
|
||||
log_tgt_mutex_lock();
|
||||
}
|
||||
|
||||
void unlock_logging(void)
|
||||
{
|
||||
log_tgt_mutex_unlock();
|
||||
}
|
||||
|
||||
void get_win_size(int *w, int *h)
|
||||
{
|
||||
struct winsize win;
|
||||
int rc;
|
||||
|
||||
rc = ioctl(0, TIOCGWINSZ, &win);
|
||||
if (rc) {
|
||||
*w = 80;
|
||||
*h = 25;
|
||||
return;
|
||||
}
|
||||
|
||||
if (h)
|
||||
*h = win.ws_row;
|
||||
if (w)
|
||||
*w = win.ws_col;
|
||||
}
|
||||
|
||||
void enable_limit_scroll(bool enable)
|
||||
{
|
||||
/* Before the window is set, keep scrolling everything. */
|
||||
if (scroll_window_height == 0)
|
||||
return;
|
||||
|
||||
/* If window is too small. */
|
||||
if (scroll_window_end - scroll_window_start <= 0)
|
||||
return;
|
||||
|
||||
if (enable) {
|
||||
printf("\0337\033[%d;%dr\0338", scroll_window_start, scroll_window_end);
|
||||
} else
|
||||
printf("\0337\033[%d;%dr\0338", 1, scroll_window_height);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void logging_limit_scroll_top(int lines)
|
||||
{
|
||||
lock_logging();
|
||||
|
||||
get_win_size(NULL, &scroll_window_height);
|
||||
scroll_window_start = lines + 1;
|
||||
if (scroll_window_end == 0)
|
||||
scroll_window_end = scroll_window_height;
|
||||
|
||||
enable_limit_scroll(true);
|
||||
|
||||
unlock_logging();
|
||||
}
|
||||
|
||||
void logging_limit_scroll_bottom(int lines)
|
||||
{
|
||||
int i;
|
||||
|
||||
lock_logging();
|
||||
|
||||
get_win_size(NULL, &scroll_window_height);
|
||||
scroll_window_end = scroll_window_height - lines;
|
||||
if (scroll_window_start == 0)
|
||||
scroll_window_start = 1;
|
||||
|
||||
/* Make space by adding empty lines. */
|
||||
for (i = scroll_window_end; i < scroll_window_height; i++)
|
||||
printf("\n");
|
||||
/* Go up by number of lines to be in window. */
|
||||
printf("\033[%dA", scroll_window_height - scroll_window_end);
|
||||
/* Enable window. */
|
||||
enable_limit_scroll(true);
|
||||
|
||||
unlock_logging();
|
||||
}
|
||||
|
||||
const char *debug_amplitude(double level)
|
||||
{
|
||||
static char text[42];
|
||||
|
||||
strcpy(text, " : ");
|
||||
if (level > 1.0)
|
||||
level = 1.0;
|
||||
if (level < -1.0)
|
||||
level = -1.0;
|
||||
text[20 + (int)(level * 20)] = '*';
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
#define level2db(level) (20 * log10(level))
|
||||
|
||||
const char *debug_db(double level_db)
|
||||
{
|
||||
static char text[128];
|
||||
int l;
|
||||
|
||||
strcpy(text, ": . : . : . : . : . : . : . : . | . : . : . : . : . : . : . : . :");
|
||||
if (level_db <= 0.0)
|
||||
return text;
|
||||
l = (int)round(level2db(level_db));
|
||||
if (l > 48)
|
||||
return text;
|
||||
if (l < -48)
|
||||
return text;
|
||||
text[l + 48] = '*';
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
void logging_print_help(void)
|
||||
{
|
||||
printf(" -v --verbose <level> | <level>,<category>[,<category>[,...]] | list\n");
|
||||
printf(" Use 'list' to get a list of all levels and categories.\n");
|
||||
printf(" Verbose level: digit of debug level (default = '%d')\n", loglevel);
|
||||
printf(" Verbose level+category: level digit followed by one or more categories\n");
|
||||
printf(" -> If no category is specified, all categories are selected\n");
|
||||
printf(" -v --verbose date\n");
|
||||
printf(" Show date with debug output\n");
|
||||
}
|
||||
|
||||
static unsigned char log_levels[] = { LOGL_DEBUG, LOGL_INFO, LOGL_NOTICE, LOGL_ERROR };
|
||||
static char *log_level_names[] = { "debug", "info", "notice", "error" };
|
||||
|
||||
static void list_cat(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("Give number of debug level:\n");
|
||||
for (i = 0; i < (int)sizeof(log_levels); i++)
|
||||
printf(" %d = %s\n", log_levels[i], log_level_names[i]);
|
||||
printf("\n");
|
||||
|
||||
printf("Give name(s) of debug category:\n");
|
||||
for (i = 0; i < (int)log_categories_size; i++) {
|
||||
if (!log_categories[i].name)
|
||||
continue;
|
||||
printf(" ");
|
||||
if (log_categories[i].color)
|
||||
printf("%s", log_categories[i].color);
|
||||
if (log_categories[i].name)
|
||||
printf("%s\033[0;39m = %s\n", log_categories[i].name, log_categories[i].description);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
int parse_logging_opt(const char *optarg)
|
||||
{
|
||||
int i;
|
||||
char *dup, *dstring, *p;
|
||||
|
||||
if (!strcasecmp(optarg, "list")) {
|
||||
list_cat();
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!strcasecmp(optarg, "date")) {
|
||||
log_set_print_timestamp(osmo_stderr_target, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dup = dstring = strdup(optarg);
|
||||
p = strsep(&dstring, ",");
|
||||
for (i = 0; i < p[i]; i++) {
|
||||
if (p[i] < '0' || p[i] > '9') {
|
||||
fprintf(stderr, "Only digits are allowed for debug level!\n");
|
||||
free(dup);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
loglevel = atoi(p);
|
||||
for (i = 0; i < (int)sizeof(log_levels); i++) {
|
||||
if (log_levels[i] == loglevel)
|
||||
break;
|
||||
}
|
||||
if (i == (int)sizeof(log_levels)) {
|
||||
fprintf(stderr, "Logging level does not exist, use '-v list' to show available levels!\n");
|
||||
free(dup);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Set loglevel and enable all categories, if dstring is not set. Else set loglevel and disable all categories. */
|
||||
for (i = 0; i < (int)log_categories_size; i++)
|
||||
log_set_category_filter(osmo_stderr_target, i, (!dstring), loglevel);
|
||||
/* Enable each given category. */
|
||||
while((p = strsep(&dstring, ","))) {
|
||||
for (i = 0; i < (int)log_categories_size; i++) {
|
||||
if (!log_category_name(i))
|
||||
continue;
|
||||
if (!strcasecmp(p, log_category_name(i)))
|
||||
break;
|
||||
}
|
||||
if (i == (int)log_categories_size) {
|
||||
fprintf(stderr, "Given logging category '%s' unknown, use '-v list' to show available categories!\n", p);
|
||||
free(dup);
|
||||
return -EINVAL;
|
||||
}
|
||||
log_set_category_filter(osmo_stderr_target, i, 1, loglevel);
|
||||
}
|
||||
|
||||
free(dup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Call after configuation above. */
|
||||
void logging_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
struct log_info log_info = {
|
||||
.cat = log_categories,
|
||||
.num_cat = log_categories_size,
|
||||
};
|
||||
|
||||
osmo_cc_set_log_cat(DLCC);
|
||||
|
||||
osmo_init_logging2(NULL, &log_info);
|
||||
log_set_print_timestamp(osmo_stderr_target, 0);
|
||||
log_set_print_level(osmo_stderr_target, 1);
|
||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
||||
log_set_print_category(osmo_stderr_target, 1);
|
||||
|
||||
/* Set loglevel and enable all categories. */
|
||||
for (i = 0; i < (int)log_categories_size; i++)
|
||||
log_set_category_filter(osmo_stderr_target, i, 1, loglevel);
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
#pragma once
|
||||
|
||||
#include <osmocom/core/logging.h>
|
||||
#include "categories.h"
|
||||
|
||||
extern int loglevel;
|
||||
|
||||
#define LOGP_CHAN(cat, level, fmt, arg...) LOGP(cat, level, "(chan %s) " fmt, CHAN, ## arg)
|
||||
|
||||
void get_win_size(int *w, int *h);
|
||||
void lock_logging(void);
|
||||
void unlock_logging(void);
|
||||
void enable_limit_scroll(bool enable);
|
||||
void logging_limit_scroll_top(int lines);
|
||||
void logging_limit_scroll_bottom(int lines);
|
||||
const char *debug_amplitude(double level);
|
||||
const char *debug_db(double level_db);
|
||||
void logging_print_help(void);
|
||||
int parse_logging_opt(const char *optarg);
|
||||
void logging_init(void);
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include "options.h"
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../liblogging/logging.h"
|
||||
|
||||
typedef struct option {
|
||||
struct option *next;
|
||||
|
@ -47,7 +47,7 @@ char *options_strdup(const char *s)
|
|||
|
||||
o = malloc(sizeof(struct options_strdup_entry) + strlen(s));
|
||||
if (!o) {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "No mem!\n");
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "No mem!\n");
|
||||
abort();
|
||||
}
|
||||
o->next = options_strdup_list;
|
||||
|
@ -64,19 +64,19 @@ void option_add(int short_option, const char *long_option, int parameter_count)
|
|||
/* check if option already exists or is not allowed */
|
||||
for (option = option_head; option; option = option->next) {
|
||||
if (!strcmp(option->long_option, "config")) {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "Option '%s' is not allowed to add, please fix!\n", option->long_option);
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "Option '%s' is not allowed to add, please fix!\n", option->long_option);
|
||||
abort();
|
||||
}
|
||||
if (option->short_option == short_option
|
||||
|| !strcmp(option->long_option, long_option)) {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "Option '%s' added twice, please fix!\n", option->long_option);
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "Option '%s' added twice, please fix!\n", option->long_option);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
option = calloc(1, sizeof(*option));
|
||||
if (!option) {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "No mem!\n");
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "No mem!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ int options_config_file(int argc, char *argv[], const char *config_file, int (*h
|
|||
/* open config file */
|
||||
fp = fopen(config, "r");
|
||||
if (!fp) {
|
||||
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file '%s' seems not to exist, using command line options only.\n", config);
|
||||
LOGP(DOPTIONS, LOGL_INFO, "Config file '%s' seems not to exist, using command line options only.\n", config);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -201,21 +201,21 @@ int options_config_file(int argc, char *argv[], const char *config_file, int (*h
|
|||
/* search option */
|
||||
for (option = option_head; option; option = option->next) {
|
||||
if (opt[0] == option->short_option && opt[1] == '\0') {
|
||||
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file option '%s' ('%s'), parameter%s\n", opt, option->long_option, params);
|
||||
LOGP(DOPTIONS, LOGL_INFO, "Config file option '%s' ('%s'), parameter%s\n", opt, option->long_option, params);
|
||||
break;
|
||||
}
|
||||
if (!strcmp(opt, option->long_option)) {
|
||||
PDEBUG(DOPTIONS, DEBUG_INFO, "Config file option '%s', parameter%s\n", opt, params);
|
||||
LOGP(DOPTIONS, LOGL_INFO, "Config file option '%s', parameter%s\n", opt, params);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!option) {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given option '%s' in config file '%s' at line %d is not a valid option, use '-h' for help!\n", opt, config_file, line);
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "Given option '%s' in config file '%s' at line %d is not a valid option, use '-h' for help!\n", opt, config_file, line);
|
||||
rc = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
if (option->parameter_count != i) {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given option '%s' in config file '%s' at line %d requires %d parameter(s), use '-h' for help!\n", opt, config_file, line, option->parameter_count);
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "Given option '%s' in config file '%s' at line %d requires %d parameter(s), use '-h' for help!\n", opt, config_file, line, option->parameter_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = handle_options(option->short_option, 0, args);
|
||||
|
@ -242,11 +242,11 @@ int options_command_line(int argc, char *argv[], int (*handle_options)(int short
|
|||
/* --config */
|
||||
if (!strcmp(argv[argi], "--config")) {
|
||||
if (argi > 1) {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' must be the first option specified, use '-h' for help!\n", argv[argi]);
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' must be the first option specified, use '-h' for help!\n", argv[argi]);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (argc <= 2) {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' requires 1 parameter, use '-h' for help!\n", argv[argi]);
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' requires 1 parameter, use '-h' for help!\n", argv[argi]);
|
||||
return -EINVAL;
|
||||
}
|
||||
argi += 1;
|
||||
|
@ -255,7 +255,7 @@ int options_command_line(int argc, char *argv[], int (*handle_options)(int short
|
|||
if (argv[argi][0] == '-') {
|
||||
if (argv[argi][1] != '-') {
|
||||
if (strlen(argv[argi]) != 2) {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' exceeds one character, use '-h' for help!\n", argv[argi]);
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' exceeds one character, use '-h' for help!\n", argv[argi]);
|
||||
return -EINVAL;
|
||||
}
|
||||
/* -x */
|
||||
|
@ -265,9 +265,9 @@ int options_command_line(int argc, char *argv[], int (*handle_options)(int short
|
|||
params[0] = '\0';
|
||||
for (i = 0; i < option->parameter_count; i++)
|
||||
sprintf(strchr(params, '\0'), " '%s'", argv[argi + 1 + i]);
|
||||
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s' ('--%s'), parameter%s\n", argv[argi], option->long_option, params);
|
||||
LOGP(DOPTIONS, LOGL_INFO, "Command line option '%s' ('--%s'), parameter%s\n", argv[argi], option->long_option, params);
|
||||
} else
|
||||
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s' ('--%s')\n", argv[argi], option->long_option);
|
||||
LOGP(DOPTIONS, LOGL_INFO, "Command line option '%s' ('--%s')\n", argv[argi], option->long_option);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -279,19 +279,19 @@ int options_command_line(int argc, char *argv[], int (*handle_options)(int short
|
|||
params[0] = '\0';
|
||||
for (i = 0; i < option->parameter_count; i++)
|
||||
sprintf(strchr(params, '\0'), " '%s'", argv[argi + 1 + i]);
|
||||
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s', parameter%s\n", argv[argi], params);
|
||||
LOGP(DOPTIONS, LOGL_INFO, "Command line option '%s', parameter%s\n", argv[argi], params);
|
||||
} else
|
||||
PDEBUG(DOPTIONS, DEBUG_INFO, "Command line option '%s'\n", argv[argi]);
|
||||
LOGP(DOPTIONS, LOGL_INFO, "Command line option '%s'\n", argv[argi]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!option) {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' is not a valid option, use '-h' for help!\n", argv[argi]);
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' is not a valid option, use '-h' for help!\n", argv[argi]);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (argi + option->parameter_count >= argc) {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' requires %d parameter(s), use '-h' for help!\n", argv[argi], option->parameter_count);
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' requires %d parameter(s), use '-h' for help!\n", argv[argi], option->parameter_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
rc = handle_options(option->short_option, argi + 1, argv);
|
||||
|
@ -306,7 +306,7 @@ int options_command_line(int argc, char *argv[], int (*handle_options)(int short
|
|||
/* no more options, so we check if there is an option after a non-option parameter */
|
||||
for (i = argi; i < argc; i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
PDEBUG(DOPTIONS, DEBUG_ERROR, "Given command line option '%s' behind command line parameter '%s' not allowed! Please put all command line options before command line parameter(s).\n", argv[i], argv[argi]);
|
||||
LOGP(DOPTIONS, LOGL_ERROR, "Given command line option '%s' behind command line parameter '%s' not allowed! Please put all command line options before command line parameter(s).\n", argv[i], argv[argi]);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||
|
||||
noinst_LIBRARIES = libosmocc.a
|
||||
|
||||
libosmocc_a_SOURCES = \
|
||||
message.c \
|
||||
socket.c \
|
||||
cause.c \
|
||||
screen.c \
|
||||
endpoint.c \
|
||||
session.c \
|
||||
sdp.c \
|
||||
rtp.c \
|
||||
helper.c
|
||||
|
|
@ -1,251 +0,0 @@
|
|||
/* OSMO-CC Processing: convert causes
|
||||
*
|
||||
* (C) 2019 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 <stdint.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "message.h"
|
||||
#include "cause.h"
|
||||
|
||||
/* 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)
|
||||
{
|
||||
switch (status) {
|
||||
case 200:
|
||||
return 16; //SWITCH_CAUSE_NORMAL_CLEARING;
|
||||
case 401:
|
||||
case 402:
|
||||
case 403:
|
||||
case 407:
|
||||
case 603:
|
||||
return 21; //SWITCH_CAUSE_CALL_REJECTED;
|
||||
case 404:
|
||||
case 485:
|
||||
case 604:
|
||||
return 1; //SWITCH_CAUSE_UNALLOCATED_NUMBER;
|
||||
case 408:
|
||||
case 504:
|
||||
return 102; //SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
|
||||
case 410:
|
||||
return 22; //SWITCH_CAUSE_NUMBER_CHANGED;
|
||||
case 413:
|
||||
case 414:
|
||||
case 416:
|
||||
case 420:
|
||||
case 421:
|
||||
case 423:
|
||||
case 505:
|
||||
case 513:
|
||||
return 127; //SWITCH_CAUSE_INTERWORKING;
|
||||
case 480:
|
||||
return 18; //SWITCH_CAUSE_NO_USER_RESPONSE;
|
||||
case 400:
|
||||
case 481:
|
||||
case 500:
|
||||
case 503:
|
||||
return 41; //SWITCH_CAUSE_NORMAL_TEMPORARY_FAILURE;
|
||||
case 486:
|
||||
case 600:
|
||||
return 17; //SWITCH_CAUSE_USER_BUSY;
|
||||
case 484:
|
||||
return 28; //SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
|
||||
case 488:
|
||||
case 606:
|
||||
return 65; //SWITCH_CAUSE_BERER_CAPABILITY_NOT_IMPLEMENTED;
|
||||
case 502:
|
||||
return 38; //SWITCH_CAUSE_NETWORK_OUT_OF_ORDER;
|
||||
case 405:
|
||||
return 63; //SWITCH_CAUSE_SERVICE_UNAVAILABLE;
|
||||
case 406:
|
||||
case 415:
|
||||
case 501:
|
||||
return 79; //SWITCH_CAUSE_SERVICE_NOT_IMPLEMENTED;
|
||||
case 482:
|
||||
case 483:
|
||||
return 25; //SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR;
|
||||
case 487:
|
||||
return 31; //??? SWITCH_CAUSE_ORIGINATOR_CANCEL; (not specified)
|
||||
default:
|
||||
return 31; //SWITCH_CAUSE_NORMAL_UNSPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
static uint16_t isdn2status_cause(uint8_t cause, uint8_t location)
|
||||
{
|
||||
switch (cause) {
|
||||
case 1:
|
||||
return 404;
|
||||
case 2:
|
||||
return 404;
|
||||
case 3:
|
||||
return 404;
|
||||
case 17:
|
||||
return 486;
|
||||
case 18:
|
||||
return 408;
|
||||
case 19:
|
||||
return 480;
|
||||
case 20:
|
||||
return 480;
|
||||
case 21:
|
||||
if (location == OSMO_CC_LOCATION_USER)
|
||||
return 603;
|
||||
return 403;
|
||||
case 22:
|
||||
//return 301;
|
||||
return 410;
|
||||
case 23:
|
||||
return 410;
|
||||
case 26:
|
||||
return 404;
|
||||
case 27:
|
||||
return 502;
|
||||
case 28:
|
||||
return 484;
|
||||
case 29:
|
||||
return 501;
|
||||
case 31:
|
||||
return 480;
|
||||
case 34:
|
||||
return 503;
|
||||
case 38:
|
||||
return 503;
|
||||
case 41:
|
||||
return 503;
|
||||
case 42:
|
||||
return 503;
|
||||
case 47:
|
||||
return 503;
|
||||
case 55:
|
||||
return 403;
|
||||
case 57:
|
||||
return 403;
|
||||
case 58:
|
||||
return 503;
|
||||
case 65:
|
||||
return 488;
|
||||
case 69:
|
||||
return 501;
|
||||
case 70:
|
||||
return 488;
|
||||
case 79:
|
||||
return 501;
|
||||
case 87:
|
||||
return 403;
|
||||
case 88:
|
||||
return 503;
|
||||
case 102:
|
||||
return 504;
|
||||
case 111:
|
||||
return 500;
|
||||
case 127:
|
||||
return 500;
|
||||
default:
|
||||
return 468;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t socket2isdn_cause(uint8_t sock)
|
||||
{
|
||||
switch (sock) {
|
||||
case OSMO_CC_SOCKET_CAUSE_FAILED:
|
||||
return 47;
|
||||
case OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE:
|
||||
return 41;
|
||||
case OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH:
|
||||
return 38;
|
||||
case OSMO_CC_SOCKET_CAUSE_TIMEOUT:
|
||||
return 41;
|
||||
default:
|
||||
return 31;
|
||||
}
|
||||
}
|
||||
|
||||
void osmo_cc_convert_cause(struct osmo_cc_ie_cause *cause)
|
||||
{
|
||||
/* complete cause, from socket cause */
|
||||
if (cause->socket_cause && cause->isdn_cause == 0 && ntohs(cause->sip_cause_networkorder) == 0)
|
||||
cause->isdn_cause = socket2isdn_cause(cause->socket_cause);
|
||||
|
||||
/* convert ISDN cause to SIP cause */
|
||||
if (cause->isdn_cause && ntohs(cause->sip_cause_networkorder) == 0) {
|
||||
cause->sip_cause_networkorder = htons(isdn2status_cause(cause->isdn_cause, cause->location));
|
||||
}
|
||||
|
||||
/* convert SIP cause to ISDN cause */
|
||||
if (ntohs(cause->sip_cause_networkorder) && cause->isdn_cause == 0) {
|
||||
cause->isdn_cause = status2isdn_cause(ntohs(cause->sip_cause_networkorder));
|
||||
}
|
||||
|
||||
/* no cause at all: use Normal Call Clearing */
|
||||
if (cause->isdn_cause == 0 && ntohs(cause->sip_cause_networkorder) == 0) {
|
||||
cause->isdn_cause = OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR;
|
||||
cause->sip_cause_networkorder = htons(486);
|
||||
}
|
||||
}
|
||||
|
||||
void osmo_cc_convert_cause_msg(osmo_cc_msg_t *msg)
|
||||
{
|
||||
void *ie;
|
||||
uint8_t type;
|
||||
uint16_t length;
|
||||
void *value;
|
||||
|
||||
/* search for (all) cause IE and convert the values, if needed */
|
||||
ie = msg->data;
|
||||
while ((value = osmo_cc_msg_sep_ie(msg, &ie, &type, &length))) {
|
||||
if (type == OSMO_CC_IE_CAUSE && length >= sizeof(struct osmo_cc_ie_cause)) {
|
||||
osmo_cc_convert_cause(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t osmo_cc_collect_cause(uint8_t old_cause, uint8_t new_cause)
|
||||
{
|
||||
/* first cause */
|
||||
if (old_cause == 0)
|
||||
return new_cause;
|
||||
|
||||
/* first prio: return 17 */
|
||||
if (old_cause == OSMO_CC_ISDN_CAUSE_USER_BUSY
|
||||
|| new_cause == OSMO_CC_ISDN_CAUSE_USER_BUSY)
|
||||
return OSMO_CC_ISDN_CAUSE_USER_BUSY;
|
||||
|
||||
/* second prio: return 21 */
|
||||
if (old_cause == OSMO_CC_ISDN_CAUSE_CALL_REJECTED
|
||||
|| new_cause == OSMO_CC_ISDN_CAUSE_CALL_REJECTED)
|
||||
return OSMO_CC_ISDN_CAUSE_CALL_REJECTED;
|
||||
|
||||
/* third prio: return other than 88 and 18 (what ever was first) */
|
||||
if (old_cause != OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
|
||||
&& old_cause != OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND)
|
||||
return old_cause;
|
||||
if (new_cause != OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
|
||||
&& new_cause != OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND)
|
||||
return new_cause;
|
||||
|
||||
/* fourth prio: return 88 */
|
||||
if (old_cause == OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST
|
||||
|| new_cause == OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST)
|
||||
return OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST;
|
||||
|
||||
/* fith prio: return 18 */
|
||||
return OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND;
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
|
||||
void osmo_cc_convert_cause(struct osmo_cc_ie_cause *cause);
|
||||
void osmo_cc_convert_cause_msg(osmo_cc_msg_t *msg);
|
||||
uint8_t osmo_cc_collect_cause(uint8_t old_cause, uint8_t new_cause);
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,131 +0,0 @@
|
|||
#ifndef OSMO_CC_ENDPOINT_H
|
||||
#define OSMO_CC_ENDPOINT_H
|
||||
|
||||
#include "message.h"
|
||||
#include "socket.h"
|
||||
#include "cause.h"
|
||||
|
||||
/* special osmo-cc error codes */
|
||||
#define OSMO_CC_RC_SEE_ERRNO -1
|
||||
#define OSMO_CC_RC_VERSION_MISMATCH 1
|
||||
|
||||
#define OSMO_CC_ATTACH_TIMER 2
|
||||
|
||||
/* call control state */
|
||||
enum osmo_cc_state {
|
||||
OSMO_CC_STATE_IDLE = 0,
|
||||
/* call states */
|
||||
OSMO_CC_STATE_INIT_OUT, /* outgoing CC-SETUP-REQ sent */
|
||||
OSMO_CC_STATE_INIT_IN, /* incoming CC-SETUP-IND received */
|
||||
OSMO_CC_STATE_OVERLAP_OUT, /* received CC-SETUP-ACK-IND on outgoing call */
|
||||
OSMO_CC_STATE_OVERLAP_IN, /* sent CC-SETUP-ACK-REQ on incoming call */
|
||||
OSMO_CC_STATE_PROCEEDING_OUT, /* received CC-PROC-IND on outgoing call */
|
||||
OSMO_CC_STATE_PROCEEDING_IN, /* sent CC-PROC-REQ on incoming call */
|
||||
OSMO_CC_STATE_ALERTING_OUT, /* received CC-ALERT-IND on outgoing call */
|
||||
OSMO_CC_STATE_ALERTING_IN, /* sent CC-ALERT-REQ on incoming call */
|
||||
OSMO_CC_STATE_CONNECTING_OUT, /* received CC-SETUP-CNF on outgoing call */
|
||||
OSMO_CC_STATE_CONNECTING_IN, /* sent CC-SETUP-RSP on incoming call */
|
||||
OSMO_CC_STATE_ACTIVE, /* received or sent CC-SETUP-COMPL-* */
|
||||
OSMO_CC_STATE_DISCONNECTING_OUT, /* sent CC-DISC-REQ */
|
||||
OSMO_CC_STATE_DISCONNECTING_IN, /* received CC-DISC-IND */
|
||||
OSMO_CC_STATE_DISC_COLLISION, /* received CC-DISC-IND after sending CC-DISC_REQ */
|
||||
OSMO_CC_STATE_RELEASING_OUT, /* sent CC-REL-REQ */
|
||||
/* attachment states */
|
||||
OSMO_CC_STATE_ATTACH_SENT, /* outgoing CC-ATT-REQ sent to socket */
|
||||
OSMO_CC_STATE_ATTACH_OUT, /* received CC-ATT-RSP on outgoing socket */
|
||||
OSMO_CC_STATE_ATTACH_WAIT, /* wait for outgoing attachment after failure */
|
||||
OSMO_CC_STATE_ATTACH_IN, /* incoming CC-ATT-REQ received from socket*/
|
||||
};
|
||||
|
||||
/* sample type */
|
||||
typedef int16_t osmo_cc_sample_t;
|
||||
|
||||
#define OSMO_CC_SAMPLE_MILLIWATT 23170 /* peak sine at -3 dB of full sample range */
|
||||
#define OSMO_CC_SAMPLE_SPEECH 3672 /* peak speech at -16 dB of milliwatt */
|
||||
#define OSMO_CC_SAMPLE_MIN -32768 /* lowest level */
|
||||
#define OSMO_CC_SAMPLE_MAX 32767 /* highest level */
|
||||
|
||||
#include "session.h"
|
||||
|
||||
struct osmo_cc_call;
|
||||
|
||||
typedef struct osmo_cc_screen_list {
|
||||
struct osmo_cc_screen_list *next;
|
||||
int has_from_type;
|
||||
uint8_t from_type;
|
||||
int has_from_present;
|
||||
uint8_t from_present;
|
||||
char from[128];
|
||||
int has_to_type;
|
||||
uint8_t to_type;
|
||||
int has_to_present;
|
||||
uint8_t to_present;
|
||||
char to[128];
|
||||
} osmo_cc_screen_list_t;
|
||||
|
||||
/* endpoint instance */
|
||||
typedef struct osmo_cc_endpoint {
|
||||
struct osmo_cc_endpoint *next;
|
||||
void *priv;
|
||||
void (*ll_msg_cb)(struct osmo_cc_endpoint *ep, uint32_t callref, osmo_cc_msg_t *msg);
|
||||
void (*ul_msg_cb)(struct osmo_cc_call *call, osmo_cc_msg_t *msg);
|
||||
osmo_cc_msg_list_t *ll_queue; /* messages towards lower layer */
|
||||
struct osmo_cc_call *call_list;
|
||||
const char *local_name; /* name of interface */
|
||||
const char *local_address; /* host+port */
|
||||
const char *local_host;
|
||||
uint16_t local_port;
|
||||
const char *remote_address; /* host+port */
|
||||
const char *remote_host;
|
||||
uint16_t remote_port;
|
||||
uint8_t serving_location;
|
||||
osmo_cc_socket_t os;
|
||||
osmo_cc_screen_list_t *screen_calling_in;
|
||||
osmo_cc_screen_list_t *screen_called_in;
|
||||
osmo_cc_screen_list_t *screen_calling_out;
|
||||
osmo_cc_screen_list_t *screen_called_out;
|
||||
int remote_auto; /* automatic remote address */
|
||||
struct timer attach_timer; /* timer to retry attachment */
|
||||
osmo_cc_session_config_t session_config; /* SDP/RTP default configuration */
|
||||
} osmo_cc_endpoint_t;
|
||||
|
||||
extern osmo_cc_endpoint_t *osmo_cc_endpoint_list;
|
||||
|
||||
/* call process */
|
||||
typedef struct osmo_cc_call {
|
||||
struct osmo_cc_call *next;
|
||||
osmo_cc_endpoint_t *ep;
|
||||
enum osmo_cc_state state;
|
||||
int lower_layer_released; /* when lower layer sent release, while upper layer gets a disconnect */
|
||||
int upper_layer_released; /* when upper layer sent release, while lower layer gets a disconnect */
|
||||
uint32_t callref;
|
||||
osmo_cc_msg_list_t *sock_queue; /* messages from socket */
|
||||
const char *attached_host; /* host and port from remote peer that attached to us */
|
||||
uint16_t attached_port;
|
||||
const char *attached_name; /* interface name from remote peer that attached to us */
|
||||
} osmo_cc_call_t;
|
||||
|
||||
/* returns 0 if ok
|
||||
* returns <0 for error as indicated
|
||||
* returns >=1 to indicate osmo-cc error code
|
||||
*/
|
||||
|
||||
void osmo_cc_help(void);
|
||||
int osmo_cc_new(osmo_cc_endpoint_t *ep, const char *version, const char *name, uint8_t serving_location, void (*ll_msg_cb)(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg), void (*ul_msg_cb)(osmo_cc_call_t *call, osmo_cc_msg_t *msg), void *priv, int argc, const char *argv[]);
|
||||
void osmo_cc_delete(struct osmo_cc_endpoint *ep);
|
||||
int osmo_cc_handle(void);
|
||||
osmo_cc_call_t *osmo_cc_call_by_callref(osmo_cc_endpoint_t *ep, uint32_t callref);
|
||||
osmo_cc_call_t *osmo_cc_get_attached_interface(osmo_cc_endpoint_t *ep, const char *interface);
|
||||
void osmo_cc_ll_msg(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg);
|
||||
void osmo_cc_ul_msg(void *priv, uint32_t callref, osmo_cc_msg_t *msg);
|
||||
osmo_cc_call_t *osmo_cc_call_new(osmo_cc_endpoint_t *ep);
|
||||
void osmo_cc_call_delete(struct osmo_cc_call *call);
|
||||
enum osmo_cc_session_addrtype osmo_cc_address_type(const char *address);
|
||||
const char *osmo_cc_host_of_address(const char *address);
|
||||
const char *osmo_cc_port_of_address(const char *address);
|
||||
|
||||
#include "rtp.h"
|
||||
#include "sdp.h"
|
||||
#include "screen.h"
|
||||
|
||||
#endif /* OSMO_CC_ENDPOINT_H */
|
|
@ -1,220 +0,0 @@
|
|||
/* Osmo-CC: helpers to simplify Osmo-CC usage
|
||||
*
|
||||
* (C) 2016 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <inttypes.h>
|
||||
#include "../libtimer/timer.h"
|
||||
#include "../libselect/select.h"
|
||||
#include "../libdebug/debug.h"
|
||||
#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, uint8_t marker, 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;
|
||||
const char *sdp;
|
||||
int i;
|
||||
|
||||
session = osmo_cc_new_session(conf, priv, NULL, NULL, NULL, 0, 0, NULL, NULL, debug);
|
||||
if (!session)
|
||||
return NULL;
|
||||
|
||||
media = osmo_cc_add_media(session, 0, 0, NULL, osmo_cc_session_media_type_audio, 0, osmo_cc_session_media_proto_rtp, 1, 1, receiver, debug);
|
||||
osmo_cc_rtp_open(media);
|
||||
|
||||
for (i = 0; codecs[i].payload_name; i++)
|
||||
osmo_cc_add_codec(media, codecs[i].payload_name, codecs[i].payload_rate, codecs[i].payload_channels, codecs[i].encoder, codecs[i].decoder, debug);
|
||||
|
||||
sdp = osmo_cc_session_send_offer(session);
|
||||
osmo_cc_add_ie_sdp(msg, sdp);
|
||||
|
||||
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, uint8_t marker, 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)
|
||||
{
|
||||
return osmo_cc_helper_audio_accept_te(conf, priv, codecs, receiver, msg, session_p, codec_p, NULL, force_our_codec);
|
||||
}
|
||||
|
||||
const char *osmo_cc_helper_audio_accept_te(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, 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, osmo_cc_session_codec_t **telephone_event_p, int force_our_codec)
|
||||
{
|
||||
char offer_sdp[65536];
|
||||
const char *accept_sdp;
|
||||
osmo_cc_session_media_t *media, *selected_media;
|
||||
osmo_cc_session_codec_t *codec, *selected_codec, *telephone_event;
|
||||
int rc;
|
||||
int i, selected_codec_i, telephone_event_i;
|
||||
|
||||
if (*session_p) {
|
||||
LOGP(DCC, LOGL_ERROR, "Session already set, please fix!\n");
|
||||
abort();
|
||||
}
|
||||
if (*codec_p) {
|
||||
LOGP(DCC, LOGL_ERROR, "Codec already set, please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* SDP IE */
|
||||
rc = osmo_cc_get_ie_sdp(msg, 0, offer_sdp, sizeof(offer_sdp));
|
||||
if (rc < 0) {
|
||||
LOGP(DCC, LOGL_ERROR, "There is no SDP included in setup request.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*session_p = osmo_cc_session_receive_offer(conf, priv, offer_sdp);
|
||||
if (!*session_p) {
|
||||
LOGP(DCC, LOGL_ERROR, "Failed to parse SDP.\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
selected_media = NULL;
|
||||
osmo_cc_session_for_each_media((*session_p)->media_list, media) {
|
||||
/* only audio */
|
||||
if (media->description.type != osmo_cc_session_media_type_audio)
|
||||
continue;
|
||||
selected_codec_i = -1;
|
||||
selected_codec = NULL;
|
||||
telephone_event_i = -1;
|
||||
telephone_event = NULL;
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
if (!!strcasecmp(codec->payload_name, "telephone-event")) {
|
||||
for (i = 0; codecs[i].payload_name; i++) {
|
||||
if (osmo_cc_session_if_codec(codec, codecs[i].payload_name, codecs[i].payload_rate, codecs[i].payload_channels)) {
|
||||
/* select the first matchting codec or the one we prefer */
|
||||
if (selected_codec_i < 0 || i < selected_codec_i) {
|
||||
selected_codec = codec;
|
||||
selected_codec_i = i;
|
||||
selected_media = media;
|
||||
}
|
||||
/* if we don't force our preferred codec, use the preferred one from the remote */
|
||||
if (!force_our_codec)
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* special case: add telephone-event, if supported */
|
||||
for (i = 0; codecs[i].payload_name; i++) {
|
||||
if (!!strcasecmp(codecs[i].payload_name, "telephone-event"))
|
||||
continue;
|
||||
telephone_event = codec;
|
||||
telephone_event_i = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* codec is selected within this media, we are done */
|
||||
if (selected_codec)
|
||||
break;
|
||||
}
|
||||
if (!selected_codec) {
|
||||
LOGP(DCC, LOGL_ERROR, "No codec found in setup message that we support.\n");
|
||||
osmo_cc_free_session(*session_p);
|
||||
*session_p = NULL;
|
||||
return NULL;
|
||||
}
|
||||
osmo_cc_session_accept_codec(selected_codec, codecs[selected_codec_i].encoder, codecs[selected_codec_i].decoder);
|
||||
if (telephone_event)
|
||||
osmo_cc_session_accept_codec(telephone_event, codecs[telephone_event_i].encoder, codecs[telephone_event_i].decoder);
|
||||
osmo_cc_session_accept_media(selected_media, 0, 0, NULL, 1, 1, receiver);
|
||||
osmo_cc_rtp_open(selected_media);
|
||||
osmo_cc_rtp_connect(selected_media);
|
||||
*codec_p = selected_codec;
|
||||
if (telephone_event_p)
|
||||
*telephone_event_p = telephone_event;
|
||||
|
||||
accept_sdp = osmo_cc_session_send_answer(*session_p);
|
||||
if (!accept_sdp) {
|
||||
osmo_cc_free_session(*session_p);
|
||||
*session_p = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return accept_sdp;
|
||||
}
|
||||
|
||||
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p)
|
||||
{
|
||||
return osmo_cc_helper_audio_negotiate_te(msg, session_p, codec_p, NULL);
|
||||
}
|
||||
|
||||
int osmo_cc_helper_audio_negotiate_te(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, osmo_cc_session_codec_t **telephone_event_p)
|
||||
{
|
||||
char sdp[65536];
|
||||
osmo_cc_session_media_t *media;
|
||||
osmo_cc_session_codec_t *codec;
|
||||
int rc;
|
||||
|
||||
if (!(*session_p)) {
|
||||
LOGP(DCC, LOGL_ERROR, "Session not set, please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* SDP IE */
|
||||
rc = osmo_cc_get_ie_sdp(msg, 0, sdp, sizeof(sdp));
|
||||
if (rc < 0)
|
||||
return 0; // no reply in this message
|
||||
|
||||
rc = osmo_cc_session_receive_answer(*session_p, sdp);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
osmo_cc_session_for_each_media((*session_p)->media_list, media) {
|
||||
/* skip not accepted medias */
|
||||
if (!media->accepted)
|
||||
continue;
|
||||
/* only audio */
|
||||
if (media->description.type != osmo_cc_session_media_type_audio)
|
||||
continue;
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
/* skip not accepted codecs */
|
||||
if (!codec->accepted)
|
||||
continue;
|
||||
if (!!strcasecmp(codec->payload_name, "telephone-event")) {
|
||||
/* select first codec, if one was accpeted */
|
||||
if (!(*codec_p)) {
|
||||
LOGP(DCC, LOGL_DEBUG, "Select codec '%s'.\n", codec->payload_name);
|
||||
*codec_p = codec;
|
||||
}
|
||||
} else {
|
||||
if (telephone_event_p && !(*telephone_event_p)) {
|
||||
LOGP(DCC, LOGL_DEBUG, "select telephone event codec.\n");
|
||||
*telephone_event_p = codec;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (*codec_p) {
|
||||
osmo_cc_rtp_connect(media);
|
||||
/* no more media streams */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!(*codec_p)) {
|
||||
LOGP(DCC, LOGL_ERROR, "No codec found in setup reply message that we support.\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
|
||||
struct osmo_cc_helper_audio_codecs {
|
||||
const char *payload_name;
|
||||
uint32_t payload_rate;
|
||||
int payload_channels;
|
||||
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
};
|
||||
|
||||
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, uint8_t marker, 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, uint8_t marker, 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);
|
||||
const char *osmo_cc_helper_audio_accept_te(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, 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, osmo_cc_session_codec_t **telephone_event_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);
|
||||
int osmo_cc_helper_audio_negotiate_te(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, osmo_cc_session_codec_t **telephone_event_p);
|
||||
|
File diff suppressed because it is too large
Load Diff
|
@ -1,513 +0,0 @@
|
|||
#ifndef OSMO_CC_MSG_H
|
||||
#define OSMO_CC_MSG_H
|
||||
|
||||
#define OSMO_CC_VERSION "OSMOCCv1"
|
||||
|
||||
/* call control messages types */
|
||||
enum osmo_cc_msg_type {
|
||||
OSMO_CC_MSG_SETUP_REQ = 0x00,
|
||||
OSMO_CC_MSG_SETUP_IND = 0x01,
|
||||
OSMO_CC_MSG_REJ_REQ = 0x10,
|
||||
OSMO_CC_MSG_REJ_IND = 0x11,
|
||||
OSMO_CC_MSG_SETUP_ACK_REQ = 0x20,
|
||||
OSMO_CC_MSG_SETUP_ACK_IND = 0x21,
|
||||
OSMO_CC_MSG_PROC_REQ = 0x30,
|
||||
OSMO_CC_MSG_PROC_IND = 0x31,
|
||||
OSMO_CC_MSG_ALERT_REQ = 0x40,
|
||||
OSMO_CC_MSG_ALERT_IND = 0x41,
|
||||
OSMO_CC_MSG_SETUP_RSP = 0x02,
|
||||
OSMO_CC_MSG_SETUP_CNF = 0x03,
|
||||
OSMO_CC_MSG_SETUP_COMP_REQ = 0x50,
|
||||
OSMO_CC_MSG_SETUP_COMP_IND = 0x51,
|
||||
OSMO_CC_MSG_DISC_REQ = 0x60,
|
||||
OSMO_CC_MSG_DISC_IND = 0x61,
|
||||
OSMO_CC_MSG_REL_REQ = 0x70,
|
||||
OSMO_CC_MSG_REL_CNF = 0x73,
|
||||
OSMO_CC_MSG_REL_IND = 0x71,
|
||||
OSMO_CC_MSG_PROGRESS_REQ = 0x80,
|
||||
OSMO_CC_MSG_PROGRESS_IND = 0x81,
|
||||
OSMO_CC_MSG_NOTIFY_REQ = 0x84,
|
||||
OSMO_CC_MSG_NOTIFY_IND = 0x85,
|
||||
OSMO_CC_MSG_INFO_REQ = 0x88,
|
||||
OSMO_CC_MSG_INFO_IND = 0x89,
|
||||
OSMO_CC_MSG_MODIFY_REQ = 0x90,
|
||||
OSMO_CC_MSG_MODIFY_IND = 0x91,
|
||||
OSMO_CC_MSG_MODIFY_RSP = 0x92,
|
||||
OSMO_CC_MSG_MODIFY_CNF = 0x93,
|
||||
OSMO_CC_MSG_ATTACH_REQ = 0xf8,
|
||||
OSMO_CC_MSG_ATTACH_IND = 0xf9,
|
||||
OSMO_CC_MSG_ATTACH_RSP = 0xfa,
|
||||
OSMO_CC_MSG_ATTACH_CNF = 0xfb,
|
||||
OSMO_CC_MSG_DUMMY_REQ = 0xfc,
|
||||
};
|
||||
#define OSMO_CC_MSG_NUM 0x100
|
||||
|
||||
#define OSMO_CC_MSG_MASK 0x03,
|
||||
#define OSMO_CC_MSG_REQ 0x00,
|
||||
#define OSMO_CC_MSG_IND 0x01,
|
||||
#define OSMO_CC_MSG_RSP 0x02,
|
||||
#define OSMO_CC_MSG_CNF 0x03,
|
||||
|
||||
const char *osmo_cc_msg_value2name(int value);
|
||||
int osmo_cc_msg_name2value(const char *name);
|
||||
|
||||
/* information elements */
|
||||
enum osmo_cc_ie_type {
|
||||
OSMO_CC_IE_CALLED = 0x11,
|
||||
OSMO_CC_IE_CALLED_SUB = 0x12,
|
||||
OSMO_CC_IE_CALLED_NAME = 0x13,
|
||||
OSMO_CC_IE_CALLED_INTERFACE = 0x14,
|
||||
OSMO_CC_IE_DTMF = 0x1d,
|
||||
OSMO_CC_IE_KEYPAD = 0x1e,
|
||||
OSMO_CC_IE_COMPLETE = 0x1f,
|
||||
OSMO_CC_IE_CALLING = 0x21,
|
||||
OSMO_CC_IE_CALLING_SUB = 0x22,
|
||||
OSMO_CC_IE_CALLING_NAME = 0x23,
|
||||
OSMO_CC_IE_CALLING_INTERFACE = 0x24,
|
||||
OSMO_CC_IE_CALLING_NETWORK = 0x2f,
|
||||
OSMO_CC_IE_REDIR = 0x31,
|
||||
OSMO_CC_IE_PROGRESS = 0x32,
|
||||
OSMO_CC_IE_NOTIFY = 0x33,
|
||||
OSMO_CC_IE_DISPLAY = 0x34,
|
||||
OSMO_CC_IE_CAUSE = 0x41,
|
||||
OSMO_CC_IE_BEARER = 0x51,
|
||||
OSMO_CC_IE_SDP = 0x52,
|
||||
OSMO_CC_IE_SOCKET_ADDRESS = 0x5e,
|
||||
OSMO_CC_IE_PRIVATE = 0x5f,
|
||||
};
|
||||
#define OSMO_CC_IE_NUM 0x100
|
||||
|
||||
const char *osmo_cc_ie_value2name(int value);
|
||||
int osmo_cc_ie_name2value(const char *name);
|
||||
|
||||
/* type of number, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_TYPE_UNKNOWN 0
|
||||
#define OSMO_CC_TYPE_INTERNATIONAL 1
|
||||
#define OSMO_CC_TYPE_NATIONAL 2
|
||||
#define OSMO_CC_TYPE_NETWORK 3
|
||||
#define OSMO_CC_TYPE_SUBSCRIBER 4
|
||||
#define OSMO_CC_TYPE_ABBREVIATED 5
|
||||
#define OSMO_CC_TYPE_RESERVED 7
|
||||
#define OSMO_CC_TYPE_NUM 8
|
||||
|
||||
const char *osmo_cc_type_value2name(int value);
|
||||
int osmo_cc_type_name2value(const char *name);
|
||||
|
||||
/* numbering plan, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_PLAN_UNKNOWN 0
|
||||
#define OSMO_CC_PLAN_TELEPHONY 1
|
||||
#define OSMO_CC_PLAN_DATA 3
|
||||
#define OSMO_CC_PLAN_TTY 4
|
||||
#define OSMO_CC_PLAN_NATIONAL_STANDARD 8
|
||||
#define OSMO_CC_PLAN_PRIVATE 9
|
||||
#define OSMO_CC_PLAN_RESERVED 15
|
||||
#define OSMO_CC_PLAN_NUM 16
|
||||
|
||||
const char *osmo_cc_plan_value2name(int value);
|
||||
int osmo_cc_plan_name2value(const char *name);
|
||||
|
||||
/* presentation indicator, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_PRESENT_ALLOWED 0
|
||||
#define OSMO_CC_PRESENT_RESTRICTED 1
|
||||
#define OSMO_CC_PRESENT_NOT_AVAIL 2
|
||||
#define OSMO_CC_PRESENT_RESERVED 3
|
||||
#define OSMO_CC_PRESENT_NUM 4
|
||||
|
||||
const char *osmo_cc_present_value2name(int value);
|
||||
int osmo_cc_present_name2value(const char *name);
|
||||
|
||||
/* screening indicator, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_SCREEN_USER_UNSCREENED 0
|
||||
#define OSMO_CC_SCREEN_USER_VERIFIED_PASSED 1
|
||||
#define OSMO_CC_SCREEN_USER_VERIFIED_FAILED 2
|
||||
#define OSMO_CC_SCREEN_NETWORK 3
|
||||
#define OSMO_CC_SCREEN_NUM 4
|
||||
|
||||
const char *osmo_cc_screen_value2name(int value);
|
||||
int osmo_cc_screen_name2value(const char *name);
|
||||
|
||||
/* screening indicator, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_REDIR_REASON_UNKNOWN 0
|
||||
#define OSMO_CC_REDIR_REASON_CFB 1
|
||||
#define OSMO_CC_REDIR_REASON_CFNR 2
|
||||
#define OSMO_CC_REDIR_REASON_CD 4
|
||||
#define OSMO_CC_REDIR_REASON_CF_OUTOFORDER 9
|
||||
#define OSMO_CC_REDIR_REASON_CF_BY_DTE 10
|
||||
#define OSMO_CC_REDIR_REASON_CFU 15
|
||||
#define OSMO_CC_REDIR_REASON_NUM 16
|
||||
|
||||
const char *osmo_cc_redir_reason_value2name(int value);
|
||||
int osmo_cc_redir_reason_name2value(const char *name);
|
||||
|
||||
/* notification indicator, see ITU-T Rec. Q.931 ff. */
|
||||
#define OSMO_CC_NOTIFY_USER_SUSPENDED 0x00
|
||||
#define OSMO_CC_NOTIFY_USER_RESUMED 0x01
|
||||
#define OSMO_CC_NOTIFY_BEARER_SERVICE_CHANGE 0x02
|
||||
#define OSMO_CC_NOTIFY_CALL_COMPLETION_DELAY 0x03
|
||||
#define OSMO_CC_NOTIFY_CONFERENCE_ESTABLISHED 0x42
|
||||
#define OSMO_CC_NOTIFY_CONFERENCE_DISCONNECTED 0x43
|
||||
#define OSMO_CC_NOTIFY_OTHER_PARTY_ADDED 0x44
|
||||
#define OSMO_CC_NOTIFY_ISOLATED 0x45
|
||||
#define OSMO_CC_NOTIFY_REATTACHED 0x46
|
||||
#define OSMO_CC_NOTIFY_OTHER_PARTY_ISOLATED 0x47
|
||||
#define OSMO_CC_NOTIFY_OTHER_PARTY_REATTACHED 0x48
|
||||
#define OSMO_CC_NOTIFY_OTHER_PARTY_SPLIT 0x49
|
||||
#define OSMO_CC_NOTIFY_OTHER_PARTY_DISCONNECTED 0x4a
|
||||
#define OSMO_CC_NOTIFY_CONFERENCE_FLOATING 0x4b
|
||||
#define OSMO_CC_NOTIFY_CONFERENCE_DISC_PREEMPT 0x4c /* disconnect preemted */
|
||||
#define OSMO_CC_NOTIFY_CONFERENCE_FLOATING_SUP 0x4f /* served user preemted */
|
||||
#define OSMO_CC_NOTIFY_CALL_IS_A_WAITING_CALL 0x60
|
||||
#define OSMO_CC_NOTIFY_DIVERSION_ACTIVATED 0x68
|
||||
#define OSMO_CC_NOTIFY_RESERVED_CT_1 0x69
|
||||
#define OSMO_CC_NOTIFY_RESERVED_CT_2 0x6a
|
||||
#define OSMO_CC_NOTIFY_REVERSE_CHARGING 0x6e
|
||||
#define OSMO_CC_NOTIFY_REMOTE_HOLD 0x79
|
||||
#define OSMO_CC_NOTIFY_REMOTE_RETRIEVAL 0x7a
|
||||
#define OSMO_CC_NOTIFY_CALL_IS_DIVERTING 0x7b
|
||||
#define OSMO_CC_NOTIFY_NUM 0x100
|
||||
|
||||
const char *osmo_cc_notify_value2name(int value);
|
||||
int osmo_cc_notify_name2value(const char *name);
|
||||
|
||||
/* coding standard, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_CODING_ITU_T 0
|
||||
#define OSMO_CC_CODING_ISO_IEC 1
|
||||
#define OSMO_CC_CODING_NATIONAL 2
|
||||
#define OSMO_CC_CODING_STANDARD_SPECIFIC 3
|
||||
#define OSMO_CC_CODING_NUM 4
|
||||
|
||||
const char *osmo_cc_coding_value2name(int value);
|
||||
int osmo_cc_coding_name2value(const char *name);
|
||||
|
||||
/* cause, see ITU-T Rec. Q.850 */
|
||||
#define OSMO_CC_ISDN_CAUSE_UNASSIGNED_NR 1
|
||||
#define OSMO_CC_ISDN_CAUSE_NO_ROUTE_TRANSIT 2
|
||||
#define OSMO_CC_ISDN_CAUSE_NO_ROUTE 3
|
||||
#define OSMO_CC_ISDN_CAUSE_CHAN_UNACCEPT 6
|
||||
#define OSMO_CC_ISDN_CAUSE_OP_DET_BARRING 8
|
||||
#define OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR 16
|
||||
#define OSMO_CC_ISDN_CAUSE_USER_BUSY 17
|
||||
#define OSMO_CC_ISDN_CAUSE_USER_NOTRESPOND 18
|
||||
#define OSMO_CC_ISDN_CAUSE_USER_ALERTING_NA 19
|
||||
#define OSMO_CC_ISDN_CAUSE_CALL_REJECTED 21
|
||||
#define OSMO_CC_ISDN_CAUSE_NUMBER_CHANGED 22
|
||||
#define OSMO_CC_ISDN_CAUSE_PRE_EMPTION 25
|
||||
#define OSMO_CC_ISDN_CAUSE_NONSE_USER_CLR 26
|
||||
#define OSMO_CC_ISDN_CAUSE_DEST_OOO 27
|
||||
#define OSMO_CC_ISDN_CAUSE_INV_NR_FORMAT 28
|
||||
#define OSMO_CC_ISDN_CAUSE_FACILITY_REJ 29
|
||||
#define OSMO_CC_ISDN_CAUSE_RESP_STATUS_INQ 30
|
||||
#define OSMO_CC_ISDN_CAUSE_NORMAL_UNSPEC 31
|
||||
#define OSMO_CC_ISDN_CAUSE_NO_CIRCUIT_CHAN 34
|
||||
#define OSMO_CC_ISDN_CAUSE_NETWORK_OOO 38
|
||||
#define OSMO_CC_ISDN_CAUSE_TEMP_FAILURE 41
|
||||
#define OSMO_CC_ISDN_CAUSE_SWITCH_CONG 42
|
||||
#define OSMO_CC_ISDN_CAUSE_ACC_INF_DISCARD 43
|
||||
#define OSMO_CC_ISDN_CAUSE_REQ_CHAN_UNAVAIL 44
|
||||
#define OSMO_CC_ISDN_CAUSE_RESOURCE_UNAVAIL 47
|
||||
#define OSMO_CC_ISDN_CAUSE_QOS_UNAVAIL 49
|
||||
#define OSMO_CC_ISDN_CAUSE_REQ_FAC_NOT_SUBSC 50
|
||||
#define OSMO_CC_ISDN_CAUSE_INC_BARRED_CUG 55
|
||||
#define OSMO_CC_ISDN_CAUSE_BEARER_CAP_UNAUTH 57
|
||||
#define OSMO_CC_ISDN_CAUSE_BEARER_CA_UNAVAIL 58
|
||||
#define OSMO_CC_ISDN_CAUSE_SERV_OPT_UNAVAIL 63
|
||||
#define OSMO_CC_ISDN_CAUSE_BEARERSERV_UNIMPL 65
|
||||
#define OSMO_CC_ISDN_CAUSE_ACM_GE_ACM_MAX 68
|
||||
#define OSMO_CC_ISDN_CAUSE_REQ_FAC_NOTIMPL 69
|
||||
#define OSMO_CC_ISDN_CAUSE_RESTR_BCAP_AVAIL 70
|
||||
#define OSMO_CC_ISDN_CAUSE_SERV_OPT_UNIMPL 79
|
||||
#define OSMO_CC_ISDN_CAUSE_INVAL_CALLREF 81
|
||||
#define OSMO_CC_ISDN_CAUSE_USER_NOT_IN_CUG 87
|
||||
#define OSMO_CC_ISDN_CAUSE_INCOMPAT_DEST 88
|
||||
#define OSMO_CC_ISDN_CAUSE_INVAL_TRANS_NET 91
|
||||
#define OSMO_CC_ISDN_CAUSE_SEMANTIC_INCORR 95
|
||||
#define OSMO_CC_ISDN_CAUSE_INVAL_MAND_INF 96
|
||||
#define OSMO_CC_ISDN_CAUSE_MSGTYPE_NOTEXIST 97
|
||||
#define OSMO_CC_ISDN_CAUSE_MSGTYPE_INCOMPAT 98
|
||||
#define OSMO_CC_ISDN_CAUSE_IE_NOTEXIST 99
|
||||
#define OSMO_CC_ISDN_CAUSE_COND_IE_ERR 100
|
||||
#define OSMO_CC_ISDN_CAUSE_MSG_INCOMP_STATE 101
|
||||
#define OSMO_CC_ISDN_CAUSE_RECOVERY_TIMER 102
|
||||
#define OSMO_CC_ISDN_CAUSE_PROTO_ERR 111
|
||||
#define OSMO_CC_ISDN_CAUSE_INTERWORKING 127
|
||||
#define OSMO_CC_ISDN_CAUSE_NUM 128
|
||||
|
||||
const char *osmo_cc_isdn_cause_value2name(int value);
|
||||
int osmo_cc_isdn_cause_name2value(const char *name);
|
||||
|
||||
/* location, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_LOCATION_USER 0
|
||||
#define OSMO_CC_LOCATION_PRIV_SERV_LOC_USER 1
|
||||
#define OSMO_CC_LOCATION_PUB_SERV_LOC_USER 2
|
||||
#define OSMO_CC_LOCATION_TRANSIT 3
|
||||
#define OSMO_CC_LOCATION_PUB_SERV_REM_USER 4
|
||||
#define OSMO_CC_LOCATION_PRIV_SERV_REM_USER 5
|
||||
#define OSMO_CC_LOCATION_BEYOND_INTERWORKING 10
|
||||
#define OSMO_CC_LOCATION_NUM 16
|
||||
|
||||
const char *osmo_cc_location_value2name(int value);
|
||||
int osmo_cc_location_name2value(const char *name);
|
||||
|
||||
/* progress description, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_PROGRESS_NOT_END_TO_END_ISDN 1
|
||||
#define OSMO_CC_PROGRESS_DEST_NOT_ISDN 2
|
||||
#define OSMO_CC_PROGRESS_ORIG_NOT_ISDN 3
|
||||
#define OSMO_CC_PROGRESS_RETURN_TO_ISDN 4
|
||||
#define OSMO_CC_PROGRESS_INTERWORKING 5
|
||||
#define OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE 8
|
||||
#define OSMO_CC_PROGRESS_NUM 16
|
||||
|
||||
const char *osmo_cc_progress_value2name(int value);
|
||||
int osmo_cc_progress_name2value(const char *name);
|
||||
|
||||
/* information transfer capability, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_CAPABILITY_SPEECH 0
|
||||
#define OSMO_CC_CAPABILITY_DATA 8
|
||||
#define OSMO_CC_CAPABILITY_DATA_RESTRICTED 9
|
||||
#define OSMO_CC_CAPABILITY_AUDIO 16
|
||||
#define OSMO_CC_CAPABILITY_DATA_WITH_TONES 17
|
||||
#define OSMO_CC_CAPABILITY_VIDEO 24
|
||||
#define OSMO_CC_CAPABILITY_NUM 32
|
||||
|
||||
const char *osmo_cc_capability_value2name(int value);
|
||||
int osmo_cc_capability_name2value(const char *name);
|
||||
|
||||
/* transfer mode, see ITU-T Rec. Q.931 */
|
||||
#define OSMO_CC_MODE_CIRCUIT 0
|
||||
#define OSMO_CC_MODE_PACKET 2
|
||||
#define OSMO_CC_MODE_NUM 4
|
||||
|
||||
const char *osmo_cc_mode_value2name(int value);
|
||||
int osmo_cc_mode_name2value(const char *name);
|
||||
|
||||
#define OSMO_CC_DTMF_MODE_OFF 0 /* stop tone */
|
||||
#define OSMO_CC_DTMF_MODE_ON 1 /* start tone */
|
||||
#define OSMO_CC_DTMF_MODE_DIGITS 2 /* play tone(s) with duration and pauses */
|
||||
#define OSMO_CC_DTMF_MODE_NUM 3
|
||||
|
||||
const char *osmo_cc_dtmf_mode_value2name(int value);
|
||||
int osmo_cc_dtmf_mode_name2value(const char *name);
|
||||
|
||||
#define OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH 1 /* version mismatch */
|
||||
#define OSMO_CC_SOCKET_CAUSE_FAILED 2 /* connection failed */
|
||||
#define OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE 3 /* connected socket failed */
|
||||
#define OSMO_CC_SOCKET_CAUSE_TIMEOUT 4 /* keepalive packets timeout */
|
||||
// if you add causes here, add them in process_cause.c also!
|
||||
#define OSMO_CC_SOCKET_CAUSE_NUM 5
|
||||
|
||||
const char *osmo_cc_socket_cause_value2name(int value);
|
||||
int osmo_cc_socket_cause_name2value(const char *name);
|
||||
|
||||
/* network type (network IE) and meaning of 'id' */
|
||||
#define OSMO_CC_NETWORK_UNDEFINED 0x00
|
||||
#define OSMO_CC_NETWORK_ALSA_NONE 0x01
|
||||
#define OSMO_CC_NETWORK_POTS_NONE 0x02
|
||||
#define OSMO_CC_NETWORK_ISDN_NONE 0x03
|
||||
#define OSMO_CC_NETWORK_SIP_NONE 0x04
|
||||
#define OSMO_CC_NETWORK_GSM_IMSI 0x05 /* id has decimal IMSI */
|
||||
#define OSMO_CC_NETWORK_GSM_IMEI 0x06 /* id has decimal IMEI */
|
||||
#define OSMO_CC_NETWORK_WEB_NONE 0x07
|
||||
#define OSMO_CC_NETWORK_DECT_NONE 0x08
|
||||
#define OSMO_CC_NETWORK_BLUETOOTH_NONE 0x09
|
||||
#define OSMO_CC_NETWORK_SS5_NONE 0x0a
|
||||
#define OSMO_CC_NETWORK_R1_NONE 0x0b
|
||||
#define OSMO_CC_NETWORK_ANETZ_NONE 0x80
|
||||
#define OSMO_CC_NETWORK_BNETZ_MUENZ 0x81 /* id starts with 'M' */
|
||||
#define OSMO_CC_NETWORK_CNETZ_NONE 0x82
|
||||
#define OSMO_CC_NETWORK_NMT_NONE 0x83 /* id has decimal password */
|
||||
#define OSMO_CC_NETWORK_R2000_NONE 0x84
|
||||
#define OSMO_CC_NETWORK_AMPS_ESN 0x85 /* if has decimal ESN (TACS also) */
|
||||
#define OSMO_CC_NETWORK_MTS_NONE 0x86
|
||||
#define OSMO_CC_NETWORK_IMTS_NONE 0x87
|
||||
#define OSMO_CC_NETWORK_EUROSIGNAL_NONE 0x88
|
||||
#define OSMO_CC_NETWORK_JOLLYCOM_NONE 0x89 /* call from JollyCom... */
|
||||
#define OSMO_CC_NETWORK_MPT1327_PSTN 0x8a /* call from MPT1327 */
|
||||
#define OSMO_CC_NETWORK_MPT1327_PBX 0x8b /* id is selected PBX number */
|
||||
#define OSMO_CC_NETWORK_NUM 0x100
|
||||
|
||||
const char *osmo_cc_network_value2name(int value);
|
||||
int osmo_cc_network_name2value(const char *name);
|
||||
|
||||
typedef struct osmo_cc_msg {
|
||||
uint8_t type;
|
||||
uint16_t length_networkorder;
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed)) osmo_cc_msg_t;
|
||||
|
||||
typedef struct osmo_cc_msg_list {
|
||||
struct osmo_cc_msg_list *next;
|
||||
struct osmo_cc_msg *msg;
|
||||
uint32_t callref;
|
||||
char host[128];
|
||||
uint16_t port;
|
||||
} osmo_cc_msg_list_t;
|
||||
|
||||
typedef struct osmo_cc_ie {
|
||||
uint8_t type;
|
||||
uint16_t length_networkorder;
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed)) osmo_cc_ie_t;
|
||||
|
||||
struct osmo_cc_ie_called {
|
||||
uint8_t type;
|
||||
uint8_t plan;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_called_sub {
|
||||
uint8_t type;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_called_name {
|
||||
char name[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_called_interface {
|
||||
char name[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_calling {
|
||||
uint8_t type;
|
||||
uint8_t plan;
|
||||
uint8_t present;
|
||||
uint8_t screen;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_calling_sub {
|
||||
uint8_t type;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_calling_name {
|
||||
char name[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_calling_interface {
|
||||
char name[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_network {
|
||||
uint8_t type;
|
||||
char id[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_bearer {
|
||||
uint8_t coding;
|
||||
uint8_t capability;
|
||||
uint8_t mode;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_redir {
|
||||
uint8_t type;
|
||||
uint8_t plan;
|
||||
uint8_t present;
|
||||
uint8_t screen;
|
||||
uint8_t redir_reason;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_dtmf {
|
||||
uint8_t duration_ms;
|
||||
uint8_t pause_ms;
|
||||
uint8_t dtmf_mode;
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_keypad {
|
||||
char digits[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_progress {
|
||||
uint8_t coding;
|
||||
uint8_t location;
|
||||
uint8_t progress;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_notify {
|
||||
uint8_t notify;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_cause {
|
||||
uint8_t location;
|
||||
uint8_t isdn_cause;
|
||||
uint16_t sip_cause_networkorder;
|
||||
uint8_t socket_cause;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_display {
|
||||
char text[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_sdp {
|
||||
char sdp[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_socket_address {
|
||||
char address[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct osmo_cc_ie_private {
|
||||
uint32_t unique_networkorder;
|
||||
uint8_t data[0];
|
||||
} __attribute__((packed));
|
||||
|
||||
uint32_t osmo_cc_new_callref(void);
|
||||
osmo_cc_msg_t *osmo_cc_new_msg(uint8_t msg_type);
|
||||
osmo_cc_msg_t *osmo_cc_clone_msg(osmo_cc_msg_t *msg);
|
||||
osmo_cc_msg_t *osmo_cc_msg_list_dequeue(osmo_cc_msg_list_t **mlp, uint32_t *callref_p);
|
||||
osmo_cc_msg_list_t *osmo_cc_msg_list_enqueue(osmo_cc_msg_list_t **mlp, osmo_cc_msg_t *msg, uint32_t callref);
|
||||
void osmo_cc_free_msg(osmo_cc_msg_t *msg);
|
||||
void osmo_cc_debug_ie(osmo_cc_msg_t *msg, int level);
|
||||
int osmo_cc_get_ie_struct(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat, int ie_len, const osmo_cc_ie_t **ie_struct);
|
||||
int osmo_cc_get_ie_data(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat, int ie_len, const void **ie_data);
|
||||
int osmo_cc_has_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat);
|
||||
int osmo_cc_remove_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_repeat);
|
||||
void *osmo_cc_add_ie(osmo_cc_msg_t *msg, uint8_t ie_type, int ie_len);
|
||||
void *osmo_cc_msg_sep_ie(osmo_cc_msg_t *msg, void **iep, uint8_t *ie_type, uint16_t *ie_length);
|
||||
|
||||
void osmo_cc_add_ie_called(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, const char *dialing);
|
||||
int osmo_cc_get_ie_called(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, char *dialing, size_t dialing_size);
|
||||
void osmo_cc_add_ie_called_sub(osmo_cc_msg_t *msg, uint8_t type, const char *dialing);
|
||||
int osmo_cc_get_ie_called_sub(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *dialing, size_t dialing_size);
|
||||
void osmo_cc_add_ie_called_name(osmo_cc_msg_t *msg, const char *name);
|
||||
int osmo_cc_get_ie_called_name(osmo_cc_msg_t *msg, int ie_repeat, char *name, size_t name_size);
|
||||
void osmo_cc_add_ie_called_interface(osmo_cc_msg_t *msg, const char *interface);
|
||||
int osmo_cc_get_ie_called_interface(osmo_cc_msg_t *msg, int ie_repeat, char *interface, size_t interface_size);
|
||||
void osmo_cc_add_ie_complete(osmo_cc_msg_t *msg);
|
||||
int osmo_cc_get_ie_complete(osmo_cc_msg_t *msg, int ie_repeat);
|
||||
void osmo_cc_add_ie_calling(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, uint8_t present, uint8_t screen, const char *callerid);
|
||||
int osmo_cc_get_ie_calling(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, uint8_t *present, uint8_t *screen, char *callerid, size_t callerid_size);
|
||||
void osmo_cc_add_ie_calling_sub(osmo_cc_msg_t *msg, uint8_t type, const char *callerid);
|
||||
int osmo_cc_get_ie_calling_sub(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *callerid, size_t callerid_size);
|
||||
void osmo_cc_add_ie_calling_name(osmo_cc_msg_t *msg, const char *name);
|
||||
int osmo_cc_get_ie_calling_name(osmo_cc_msg_t *msg, int ie_repeat, char *name, size_t name_size);
|
||||
void osmo_cc_add_ie_calling_interface(osmo_cc_msg_t *msg, const char *interface);
|
||||
int osmo_cc_get_ie_calling_interface(osmo_cc_msg_t *msg, int ie_repeat, char *interface, size_t interface_size);
|
||||
void osmo_cc_add_ie_calling_network(osmo_cc_msg_t *msg, uint8_t type, const char *networkid);
|
||||
int osmo_cc_get_ie_calling_network(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, char *networkid, size_t networkid_size);
|
||||
void osmo_cc_add_ie_bearer(osmo_cc_msg_t *msg, uint8_t coding, uint8_t capability, uint8_t mode);
|
||||
int osmo_cc_get_ie_bearer(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *coding, uint8_t *capability, uint8_t *mode);
|
||||
void osmo_cc_add_ie_redir(osmo_cc_msg_t *msg, uint8_t type, uint8_t plan, uint8_t present, uint8_t screen, uint8_t redir_reason, const char *callerid);
|
||||
int osmo_cc_get_ie_redir(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *type, uint8_t *plan, uint8_t *present, uint8_t *screen, uint8_t *reason, char *callerid, size_t callerid_size);
|
||||
void osmo_cc_add_ie_dtmf(osmo_cc_msg_t *msg, uint8_t duration_ms, uint8_t pause_ms, uint8_t dtmf_mode, const char *digits);
|
||||
int osmo_cc_get_ie_dtmf(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *duration_ms, uint8_t *pause_ms, uint8_t *dtmf_mode, char *digits, size_t digits_size);
|
||||
void osmo_cc_add_ie_keypad(osmo_cc_msg_t *msg, const char *digits);
|
||||
int osmo_cc_get_ie_keypad(osmo_cc_msg_t *msg, int ie_repeat, char *digits, size_t digits_size);
|
||||
void osmo_cc_add_ie_progress(osmo_cc_msg_t *msg, uint8_t coding, uint8_t location, uint8_t progress);
|
||||
int osmo_cc_get_ie_progress(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *coding, uint8_t *location, uint8_t *progress);
|
||||
void osmo_cc_add_ie_notify(osmo_cc_msg_t *msg, uint8_t notify);
|
||||
int osmo_cc_get_ie_notify(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *notify);
|
||||
void osmo_cc_add_ie_cause(osmo_cc_msg_t *msg, uint8_t location, uint8_t isdn_cause, uint16_t sip_cause, uint8_t socket_cause);
|
||||
int osmo_cc_get_ie_cause(osmo_cc_msg_t *msg, int ie_repeat, uint8_t *location, uint8_t *isdn_cause, uint16_t *sip_cause, uint8_t *socket_cause);
|
||||
void osmo_cc_add_ie_display(osmo_cc_msg_t *msg, const char *text);
|
||||
int osmo_cc_get_ie_display(osmo_cc_msg_t *msg, int ie_repeat, char *text, size_t text_size);
|
||||
void osmo_cc_add_ie_sdp(osmo_cc_msg_t *msg, const char *sdp);
|
||||
int osmo_cc_get_ie_sdp(osmo_cc_msg_t *msg, int ie_repeat, char *sdp, size_t sdp_size);
|
||||
void osmo_cc_add_ie_socket_address(osmo_cc_msg_t *msg, const char *address);
|
||||
int osmo_cc_get_ie_socket_address(osmo_cc_msg_t *msg, int ie_repeat, char *address, size_t address_size);
|
||||
void osmo_cc_add_ie_private(osmo_cc_msg_t *msg, uint32_t unique, const uint8_t *data, size_t data_size);
|
||||
int osmo_cc_get_ie_private(osmo_cc_msg_t *msg, int ie_repeat, uint32_t *unique, uint8_t *data, size_t data_size);
|
||||
|
||||
#endif /* OSMO_CC_MSG_H */
|
|
@ -1,508 +0,0 @@
|
|||
/* Osmo-CC: RTP handling
|
||||
*
|
||||
* (C) 2016 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../libtimer/timer.h"
|
||||
#include "../libselect/select.h"
|
||||
#include "endpoint.h"
|
||||
|
||||
#define RTP_VERSION 2
|
||||
|
||||
void osmo_cc_set_rtp_ports(osmo_cc_session_config_t *conf, uint16_t from, uint16_t to)
|
||||
{
|
||||
conf->rtp_port_next = from;
|
||||
conf->rtp_port_from = from;
|
||||
conf->rtp_port_to = to;
|
||||
}
|
||||
|
||||
struct rtp_hdr {
|
||||
uint8_t byte0;
|
||||
uint8_t byte1;
|
||||
uint16_t sequence;
|
||||
uint32_t timestamp;
|
||||
uint32_t ssrc;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct rtp_x_hdr {
|
||||
uint16_t by_profile;
|
||||
uint16_t length;
|
||||
} __attribute__((packed));
|
||||
|
||||
static int rtp_receive(struct sockaddr_storage *sa, socklen_t *slen, 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;
|
||||
struct rtp_hdr *rtph = (struct rtp_hdr *)data;
|
||||
uint8_t version, padding, extension, csrc_count, marker, payload_type;
|
||||
struct rtp_x_hdr *rtpxh;
|
||||
uint8_t *payload;
|
||||
int payload_len;
|
||||
int x_len;
|
||||
|
||||
len = recvfrom(sock, data, sizeof(data), 0, (struct sockaddr *)sa, slen);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return -EAGAIN;
|
||||
LOGP(DCC, LOGL_DEBUG, "Read errno = %d (%s)\n", errno, strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
if (len < 12) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Received RTP frame too short (len = %d).\n", len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
version = rtph->byte0 >> 6;
|
||||
padding = (rtph->byte0 >> 5) & 1;
|
||||
extension = (rtph->byte0 >> 4) & 1;
|
||||
csrc_count = rtph->byte0 & 0x0f;
|
||||
marker = rtph->byte1 >> 7;
|
||||
payload_type = rtph->byte1 & 0x7f;
|
||||
*sequence_p = ntohs(rtph->sequence);
|
||||
*timestamp_p = ntohl(rtph->timestamp);
|
||||
*ssrc_p = ntohl(rtph->ssrc);
|
||||
|
||||
if (version != RTP_VERSION) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Received RTP version %d not supported.\n", version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
payload = data + sizeof(*rtph) + (csrc_count << 2);
|
||||
payload_len = len - sizeof(*rtph) - (csrc_count << 2);
|
||||
if (payload_len < 0) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Received RTP frame too short (len = %d, csrc count = %d).\n", len, csrc_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (extension) {
|
||||
if (payload_len < (int)sizeof(*rtpxh)) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Received RTP frame too short for extension header.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
rtpxh = (struct rtp_x_hdr *)payload;
|
||||
x_len = ntohs(rtpxh->length) * 4 + sizeof(*rtpxh);
|
||||
payload += x_len;
|
||||
payload_len -= x_len;
|
||||
if (payload_len < (int)sizeof(*rtpxh)) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Received RTP frame too short, extension header exceeds frame length.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (padding) {
|
||||
if (payload_len < 1) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Received RTP frame too short for padding length.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
payload_len -= payload[payload_len - 1];
|
||||
if (payload_len < 0) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Received RTP frame padding is greater than payload.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
*payload_p = payload;
|
||||
*payload_len_p = payload_len;
|
||||
*marker_p = marker;
|
||||
*pt_p = payload_type;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtcp_receive(struct sockaddr_storage *sa, socklen_t *slen, int sock)
|
||||
{
|
||||
static uint8_t data[2048];
|
||||
int len;
|
||||
|
||||
len = recvfrom(sock, data, sizeof(data), 0, (struct sockaddr *)sa, slen);
|
||||
if (len < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return -EAGAIN;
|
||||
LOGP(DCC, LOGL_DEBUG, "Read errno = %d (%s)\n", errno, strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rtp_send(struct sockaddr_storage *sa, socklen_t slen, int sock, uint8_t *payload, int payload_len, uint8_t marker, uint8_t pt, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
|
||||
{
|
||||
struct rtp_hdr *rtph;
|
||||
char data[sizeof(*rtph) + payload_len];
|
||||
int len, rc;
|
||||
|
||||
rtph = (struct rtp_hdr *)data;
|
||||
len = sizeof(*rtph);
|
||||
rtph->byte0 = RTP_VERSION << 6;
|
||||
rtph->byte1 = pt | (marker << 7);
|
||||
rtph->sequence = htons(sequence);
|
||||
rtph->timestamp = htonl(timestamp);
|
||||
rtph->ssrc = htonl(ssrc);
|
||||
len += payload_len;
|
||||
if (len > (int)sizeof(data)) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Buffer overflow, please fix!.\n");
|
||||
abort();
|
||||
}
|
||||
memcpy(data + sizeof(*rtph), payload, payload_len);
|
||||
|
||||
rc = sendto(sock, data, len, 0, (struct sockaddr *)sa, slen);
|
||||
if (rc < 0)
|
||||
LOGP(DCC, LOGL_DEBUG, "sendto errno = %d (%s)\n", errno, strerror(errno));
|
||||
}
|
||||
|
||||
static int rtp_listen_cb(struct osmo_fd *ofd, unsigned int when);
|
||||
static int rtcp_listen_cb(struct osmo_fd *ofd, unsigned int when);
|
||||
|
||||
/* open and bind RTP
|
||||
* set local port to what we bound
|
||||
*/
|
||||
int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
|
||||
{
|
||||
osmo_cc_session_config_t *conf = media->session->config;
|
||||
int domain = 0; // make GCC happy
|
||||
uint16_t start_port;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t slen = 0; // make GCC happy
|
||||
struct sockaddr_in6 *sa6;
|
||||
struct sockaddr_in *sa4;
|
||||
uint16_t *sport;
|
||||
int flags;
|
||||
int rc;
|
||||
|
||||
media->tx_ssrc = rand();
|
||||
|
||||
osmo_cc_rtp_close(media);
|
||||
|
||||
switch (media->connection_data_local.addrtype) {
|
||||
case osmo_cc_session_addrtype_ipv4:
|
||||
domain = AF_INET;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa4 = (struct sockaddr_in *)&sa;
|
||||
sa4->sin_family = domain;
|
||||
rc = inet_pton(AF_INET, media->connection_data_local.address, &sa4->sin_addr);
|
||||
if (rc < 1) {
|
||||
pton_error:
|
||||
LOGP(DCC, LOGL_NOTICE, "Cannot bind to address '%s'.\n", media->connection_data_local.address);
|
||||
return -EINVAL;
|
||||
}
|
||||
sport = &sa4->sin_port;
|
||||
slen = sizeof(*sa4);
|
||||
break;
|
||||
case osmo_cc_session_addrtype_ipv6:
|
||||
domain = AF_INET6;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa6 = (struct sockaddr_in6 *)&sa;
|
||||
sa6->sin6_family = domain;
|
||||
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;
|
||||
case osmo_cc_session_addrtype_unknown:
|
||||
LOGP(DCC, LOGL_NOTICE, "Unsupported address type '%s'.\n", media->connection_data_local.addrtype_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* rtp_port_from/rtp_port_to may be changed at run time, so rtp_port_next can become out of range. */
|
||||
if (conf->rtp_port_next < conf->rtp_port_from || conf->rtp_port_next > conf->rtp_port_to)
|
||||
conf->rtp_port_next = conf->rtp_port_from;
|
||||
start_port = conf->rtp_port_next;
|
||||
while (1) {
|
||||
/* open sockets */
|
||||
rc = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (rc < 0) {
|
||||
socket_error:
|
||||
LOGP(DCC, LOGL_ERROR, "Cannot create socket (domain=%d, errno=%d(%s))\n", domain, errno, strerror(errno));
|
||||
osmo_cc_rtp_close(media);
|
||||
return -EIO;
|
||||
}
|
||||
media->rtp_ofd.fd = rc;
|
||||
media->rtp_ofd.cb = rtp_listen_cb;
|
||||
media->rtp_ofd.data = media;
|
||||
media->rtp_ofd.when = OSMO_FD_READ;
|
||||
osmo_fd_register(&media->rtp_ofd);
|
||||
rc = socket(domain, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (rc < 0)
|
||||
goto socket_error;
|
||||
media->rtcp_ofd.fd = rc;
|
||||
media->rtcp_ofd.cb = rtcp_listen_cb;
|
||||
media->rtcp_ofd.data = media;
|
||||
media->rtcp_ofd.when = OSMO_FD_READ;
|
||||
osmo_fd_register(&media->rtcp_ofd);
|
||||
|
||||
/* bind sockets */
|
||||
*sport = htons(conf->rtp_port_next);
|
||||
rc = bind(media->rtp_ofd.fd, (struct sockaddr *)&sa, slen);
|
||||
if (rc < 0) {
|
||||
bind_error:
|
||||
osmo_cc_rtp_close(media);
|
||||
conf->rtp_port_next = (conf->rtp_port_next + 2 > conf->rtp_port_to) ? conf->rtp_port_from : conf->rtp_port_next + 2;
|
||||
if (conf->rtp_port_next == start_port) {
|
||||
LOGP(DCC, LOGL_ERROR, "Cannot bind socket (errno=%d(%s))\n", errno, strerror(errno));
|
||||
return -EIO;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
*sport = htons(conf->rtp_port_next + 1);
|
||||
rc = bind(media->rtcp_ofd.fd, (struct sockaddr *)&sa, slen);
|
||||
if (rc < 0)
|
||||
goto bind_error;
|
||||
media->description.port_local = conf->rtp_port_next;
|
||||
conf->rtp_port_next = (conf->rtp_port_next + 2 > conf->rtp_port_to) ? conf->rtp_port_from : conf->rtp_port_next + 2;
|
||||
/* set nonblocking io, to prevent write to block */
|
||||
flags = fcntl(media->rtp_ofd.fd, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(media->rtp_ofd.fd, F_SETFL, flags);
|
||||
flags = fcntl(media->rtcp_ofd.fd, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(media->rtcp_ofd.fd, F_SETFL, flags);
|
||||
break;
|
||||
}
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Opening media port %d\n", media->description.port_local);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* connect RTP
|
||||
* use remote port to connect to
|
||||
*/
|
||||
int osmo_cc_rtp_connect(osmo_cc_session_media_t *media)
|
||||
{
|
||||
struct sockaddr_in6 *sa6;
|
||||
struct sockaddr_in *sa4;
|
||||
int rc;
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Connecting media port %d->%d (remote %s)\n", media->description.port_local, media->description.port_remote, media->connection_data_remote.address);
|
||||
|
||||
switch (media->connection_data_remote.addrtype) {
|
||||
case osmo_cc_session_addrtype_ipv4:
|
||||
memset(&media->rtp_sa, 0, sizeof(media->rtp_sa));
|
||||
sa4 = (struct sockaddr_in *)&media->rtp_sa;
|
||||
sa4->sin_family = AF_INET;
|
||||
rc = inet_pton(AF_INET, media->connection_data_remote.address, &sa4->sin_addr);
|
||||
if (rc < 1) {
|
||||
pton_error:
|
||||
LOGP(DCC, LOGL_NOTICE, "Cannot connect to address '%s'.\n", media->connection_data_remote.address);
|
||||
return -EINVAL;
|
||||
}
|
||||
media->rtp_sport = &sa4->sin_port;
|
||||
media->rtp_slen = sizeof(*sa4);
|
||||
memcpy(&media->rtcp_sa, &media->rtp_sa, sizeof(*sa4));
|
||||
sa4 = (struct sockaddr_in *)&media->rtcp_sa;
|
||||
media->rtcp_sport = &sa4->sin_port;
|
||||
media->rtcp_slen = sizeof(*sa4);
|
||||
break;
|
||||
case osmo_cc_session_addrtype_ipv6:
|
||||
memset(&media->rtp_sa, 0, sizeof(media->rtp_sa));
|
||||
sa6 = (struct sockaddr_in6 *)&media->rtp_sa;
|
||||
sa6->sin6_family = AF_INET6;
|
||||
rc = inet_pton(AF_INET6, media->connection_data_remote.address, &sa6->sin6_addr);
|
||||
if (rc < 1)
|
||||
goto pton_error;
|
||||
media->rtp_sport = &sa6->sin6_port;
|
||||
media->rtp_slen = sizeof(*sa6);
|
||||
memcpy(&media->rtcp_sa, &media->rtp_sa, sizeof(*sa6));
|
||||
sa6 = (struct sockaddr_in6 *)&media->rtcp_sa;
|
||||
media->rtcp_sport = &sa6->sin6_port;
|
||||
media->rtcp_slen = sizeof(*sa6);
|
||||
break;
|
||||
case osmo_cc_session_addrtype_unknown:
|
||||
LOGP(DCC, LOGL_NOTICE, "Unsupported address type '%s'.\n", media->connection_data_local.addrtype_name);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*media->rtp_sport = htons(media->description.port_remote);
|
||||
*media->rtcp_sport = htons(media->description.port_remote + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* send rtp data with given codec */
|
||||
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, uint8_t marker, int inc_sequence, int inc_timestamp, void *priv)
|
||||
{
|
||||
uint8_t *payload = NULL;
|
||||
int payload_len = 0;
|
||||
|
||||
if (!codec || !codec->media->rtp_ofd.fd)
|
||||
return;
|
||||
|
||||
if (codec->encoder)
|
||||
codec->encoder(data, len, &payload, &payload_len, priv);
|
||||
else {
|
||||
payload = data;
|
||||
payload_len = len;
|
||||
}
|
||||
|
||||
rtp_send(&codec->media->rtp_sa, codec->media->rtp_slen, codec->media->rtp_ofd.fd, payload, payload_len, marker, 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;
|
||||
|
||||
if (codec->encoder)
|
||||
free(payload);
|
||||
}
|
||||
|
||||
/* dito, but with absolute sequence and timestamp */
|
||||
void osmo_cc_rtp_send_ts(osmo_cc_session_codec_t *codec, uint8_t *data, int len, uint8_t marker, uint16_t tx_sequence, uint32_t tx_timestamp, void *priv)
|
||||
{
|
||||
uint8_t *payload = NULL;
|
||||
int payload_len = 0;
|
||||
|
||||
if (!codec || !codec->media->rtp_ofd.fd)
|
||||
return;
|
||||
|
||||
if (codec->encoder)
|
||||
codec->encoder(data, len, &payload, &payload_len, priv);
|
||||
else {
|
||||
payload = data;
|
||||
payload_len = len;
|
||||
}
|
||||
|
||||
rtp_send(&codec->media->rtp_sa, codec->media->rtp_slen, codec->media->rtp_ofd.fd, payload, payload_len, marker, codec->payload_type_remote, tx_sequence, tx_timestamp, codec->media->tx_ssrc);
|
||||
codec->media->tx_sequence = tx_sequence;
|
||||
codec->media->tx_timestamp = tx_timestamp;
|
||||
|
||||
if (codec->encoder)
|
||||
free(payload);
|
||||
}
|
||||
|
||||
static void check_port_translation(struct sockaddr_storage *sa, struct sockaddr_storage *media_sa, const char *what)
|
||||
{
|
||||
struct sockaddr_in6 *sa6, *sa6_2;
|
||||
struct sockaddr_in *sa4, *sa4_2;
|
||||
int from = 0, to = 0;
|
||||
|
||||
if (sa->ss_family != media_sa->ss_family)
|
||||
return;
|
||||
|
||||
switch (sa->ss_family) {
|
||||
case AF_INET:
|
||||
sa4 = (struct sockaddr_in *)sa;
|
||||
sa4_2 = (struct sockaddr_in *)media_sa;
|
||||
if (sa4->sin_port != sa4_2->sin_port) {
|
||||
if (!!memcmp(&sa4->sin_addr, &sa4_2->sin_addr, sizeof(struct in_addr)))
|
||||
break;
|
||||
from = ntohs(sa4_2->sin_port);
|
||||
to = ntohs(sa4->sin_port);
|
||||
sa4_2->sin_port = sa4->sin_port;
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
sa6 = (struct sockaddr_in6 *)sa;
|
||||
sa6_2 = (struct sockaddr_in6 *)media_sa;
|
||||
if (sa6->sin6_port != sa6_2->sin6_port) {
|
||||
if (!!memcmp(&sa6->sin6_addr, &sa6_2->sin6_addr, sizeof(struct in6_addr)))
|
||||
break;
|
||||
from = ntohs(sa6_2->sin6_port);
|
||||
to = ntohs(sa6->sin6_port);
|
||||
sa6_2->sin6_port = sa6->sin6_port;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (from)
|
||||
LOGP(DCC, LOGL_NOTICE, "Remote sends with different %s port, changing from %d to %d!\n", what, from, to);
|
||||
}
|
||||
|
||||
static int rtp_listen_cb(struct osmo_fd *ofd, unsigned int when)
|
||||
{
|
||||
osmo_cc_session_media_t *media = ofd->data;
|
||||
int rc;
|
||||
uint8_t *payload = NULL;
|
||||
int payload_len = 0;
|
||||
uint8_t marker;
|
||||
uint8_t payload_type;
|
||||
osmo_cc_session_codec_t *codec;
|
||||
uint8_t *data;
|
||||
int len;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t slen = sizeof(sa); // must be initialized and will be overwritten
|
||||
|
||||
if (when & OSMO_FD_READ) {
|
||||
rc = rtp_receive(&sa, &slen, media->rtp_ofd.fd, &payload, &payload_len, &marker, &payload_type, &media->rx_sequence, &media->rx_timestamp, &media->rx_ssrc);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
check_port_translation(&sa, &media->rtp_sa, "RTP");
|
||||
|
||||
/* search for codec */
|
||||
for (codec = media->codec_list; codec; codec = codec->next) {
|
||||
if (codec->payload_type_local == payload_type)
|
||||
break;
|
||||
}
|
||||
if (!codec) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Received RTP frame for unknown codec (payload_type = %d).\n", payload_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (codec->decoder)
|
||||
codec->decoder(payload, payload_len, &data, &len, media->session->priv);
|
||||
else {
|
||||
data = payload;
|
||||
len = payload_len;
|
||||
}
|
||||
|
||||
if (codec->media->receive)
|
||||
codec->media->receiver(codec, marker, media->rx_sequence, media->rx_timestamp, media->rx_ssrc, data, len);
|
||||
|
||||
if (codec->decoder)
|
||||
free(data);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtcp_listen_cb(struct osmo_fd *ofd, unsigned int when)
|
||||
{
|
||||
osmo_cc_session_media_t *media = ofd->data;
|
||||
int rc;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t slen = sizeof(sa); // must be initialized and will be overwritten
|
||||
|
||||
if (when & OSMO_FD_READ) {
|
||||
rc = rtcp_receive(&sa, &slen, media->rtcp_ofd.fd);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
check_port_translation(&sa, &media->rtcp_sa, "RTCP");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osmo_cc_rtp_close(osmo_cc_session_media_t *media)
|
||||
{
|
||||
if (media->rtp_ofd.fd) {
|
||||
osmo_fd_unregister(&media->rtp_ofd);
|
||||
close(media->rtp_ofd.fd);
|
||||
media->rtp_ofd.fd = 0;
|
||||
}
|
||||
if (media->rtcp_ofd.fd) {
|
||||
osmo_fd_unregister(&media->rtcp_ofd);
|
||||
close(media->rtcp_ofd.fd);
|
||||
media->rtcp_ofd.fd = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
|
||||
void osmo_cc_set_rtp_ports(osmo_cc_session_config_t *conf, uint16_t from, uint16_t to);
|
||||
int osmo_cc_rtp_open(osmo_cc_session_media_t *media);
|
||||
int osmo_cc_rtp_connect(osmo_cc_session_media_t *media);
|
||||
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, uint8_t marker, int inc_sequence, int inc_timestamp, void *priv);
|
||||
void osmo_cc_rtp_send_ts(osmo_cc_session_codec_t *codec, uint8_t *data, int len, uint8_t marker, uint16_t tx_sequence, uint32_t tx_timestamp, void *priv);
|
||||
void osmo_cc_rtp_close(osmo_cc_session_media_t *media);
|
||||
|
|
@ -1,694 +0,0 @@
|
|||
/* Endpoint and call process handling
|
||||
*
|
||||
* (C) 2019 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 <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include "../libtimer/timer.h"
|
||||
#include "../libselect/select.h"
|
||||
#include "../libdebug/debug.h"
|
||||
#include "endpoint.h"
|
||||
#include "message.h"
|
||||
|
||||
#define SCREEN_QUESTIONMARK 1
|
||||
#define SCREEN_STAR 2
|
||||
#define SCREEN_AT 3
|
||||
|
||||
void osmo_cc_help_screen(void)
|
||||
{
|
||||
printf("Screening options:\n\n");
|
||||
|
||||
printf("screen-calling-in [attrs] <current caller ID> [attrs] <new caller ID>\n");
|
||||
printf("screen-called-in [attrs] <current dialed number> [attrs] <new dialed number>\n");
|
||||
printf("screen-calling-out [attrs] <current caller ID> [attrs] <new caller ID>\n");
|
||||
printf("screen-called-out [attrs] <current dialed number> [attrs] <new dialed number>\n\n");
|
||||
|
||||
printf("These options allow to screen an incoming or outgoing caller ID or dialed\n");
|
||||
printf("number. If 'the current caller ID' or 'current dialed number' matches, it will\n");
|
||||
printf("be replaced by 'new caller ID' or 'new dialed number'. 'incoming' means from\n");
|
||||
printf(" the interface and 'outgoing' means towards the interface.\n\n");
|
||||
|
||||
printf("Attributes prior 'current caller ID' or 'new dialed number' may be used to\n");
|
||||
printf("perform screening only if the attribute match. Attributes prior\n");
|
||||
printf("'new caller ID' or 'new dialed number' may be used to alter them. Attribute to\n");
|
||||
printf("define the type of number can be: 'unknown', 'international', 'national',\n");
|
||||
printf("'network', 'subscriber', 'abbreviated' Attribute to define the restriction of a\n");
|
||||
printf("caller ID: 'allowed', 'restricted'\n\n");
|
||||
|
||||
printf("The current caller ID or dialed number may contain one or more '?', to allow\n");
|
||||
printf("any digit to match. The current caller ID or dialed number may contain a '*',\n");
|
||||
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)
|
||||
{
|
||||
static char token[1024];
|
||||
const char *text = *text_p;
|
||||
int i, quote;
|
||||
|
||||
/* skip spaces */
|
||||
while (*text) {
|
||||
if (*text > 32)
|
||||
break;
|
||||
text++;
|
||||
}
|
||||
|
||||
/* if eol, return NULL */
|
||||
if (!(*text))
|
||||
return NULL;
|
||||
|
||||
i = 0;
|
||||
quote = 0;
|
||||
while (*text) {
|
||||
/* escape allows all following characters */
|
||||
if (*text == '\\') {
|
||||
text++;
|
||||
if (*text)
|
||||
token[i++] = *text++;
|
||||
continue;
|
||||
}
|
||||
/* no quote, check for them or break on white space */
|
||||
if (quote == 0) {
|
||||
if (*text == '\'') {
|
||||
quote = 1;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
if (*text == '\"') {
|
||||
quote = 2;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
if (*text <= ' ')
|
||||
break;
|
||||
}
|
||||
/* single quote, check for unquote */
|
||||
if (quote == 1 && *text == '\'') {
|
||||
quote = 0;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
/* double quote, check for unquote */
|
||||
if (quote == 2 && *text == '\"') {
|
||||
quote = 0;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
/* copy character */
|
||||
token[i++] = *text++;
|
||||
}
|
||||
token[i] = '\0';
|
||||
|
||||
*text_p = text;
|
||||
return token;
|
||||
}
|
||||
|
||||
int osmo_cc_add_screen(osmo_cc_endpoint_t *ep, const char *text)
|
||||
{
|
||||
osmo_cc_screen_list_t **list_p = NULL, *list;
|
||||
const char *token;
|
||||
int no_present = 0, calling_in = 0, star_used, at_used;
|
||||
int i, j;
|
||||
|
||||
star_used = 0;
|
||||
if (!strncasecmp(text, "screen-calling-in", 17)) {
|
||||
text += 17;
|
||||
list_p = &ep->screen_calling_in;
|
||||
no_present = 1;
|
||||
calling_in = 1;
|
||||
} 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;
|
||||
no_present = 1;
|
||||
} else if (!strncasecmp(text, "screen-called-out", 17)) {
|
||||
text += 17;
|
||||
list_p = &ep->screen_called_out;
|
||||
} else {
|
||||
LOGP(DCC, LOGL_ERROR, "Invalid screening definition \"%s\". It must start with 'screen-calling-in' or 'screen-called-in' or 'screen-calling-out' or 'screen-called-out'\n", text);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* skip space behind screen list string */
|
||||
while (*text) {
|
||||
if (*text > 32)
|
||||
break;
|
||||
text++;
|
||||
}
|
||||
|
||||
list = calloc(1, sizeof(*list));
|
||||
if (!list)
|
||||
return -ENOMEM;
|
||||
|
||||
next_from:
|
||||
token = osmo_cc_strtok_quotes(&text);
|
||||
if (!token) {
|
||||
free(list);
|
||||
LOGP(DCC, LOGL_ERROR, "Missing 'from' string in screening definition \"%s\". If the string shall be empty, use double quotes. (\'\' or \"\")\n", text);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!strcasecmp(token, "unknown")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "international")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "national")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_NATIONAL;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "network")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_NETWORK;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "subscriber")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_SUBSCRIBER;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "abbreviated")) {
|
||||
list->has_from_type = 1;
|
||||
list->from_type = OSMO_CC_TYPE_ABBREVIATED;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "allowed")) {
|
||||
if (no_present) {
|
||||
no_present_error:
|
||||
free(list);
|
||||
LOGP(DCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DCC, LOGL_ERROR, "Keyword '%s' not allowed in screen entry for called number\n", token);
|
||||
return -EINVAL;
|
||||
}
|
||||
list->has_from_present = 1;
|
||||
list->from_present = OSMO_CC_PRESENT_ALLOWED;
|
||||
goto next_from;
|
||||
} else
|
||||
if (!strcasecmp(token, "restricted")) {
|
||||
if (no_present)
|
||||
goto no_present_error;
|
||||
list->has_from_present = 1;
|
||||
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;
|
||||
else
|
||||
if (token[i] == '*') {
|
||||
if (star_used) {
|
||||
free(list);
|
||||
LOGP(DCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DCC, LOGL_ERROR, "The '*' may be used only once.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
list->from[j] = SCREEN_STAR;
|
||||
star_used = 1;
|
||||
} else
|
||||
if (token[i] == '\\' && token[i + 1] != '\0')
|
||||
list->from[j] = token[++i];
|
||||
else
|
||||
list->from[j] = token[i];
|
||||
}
|
||||
list->from[j] = '\0';
|
||||
}
|
||||
|
||||
next_to:
|
||||
token = osmo_cc_strtok_quotes(&text);
|
||||
if (!token) {
|
||||
free(list);
|
||||
LOGP(DCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DCC, LOGL_ERROR, "Missing screening result. If the string shall be empty, use double quotes. (\'\' or \"\")\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!strcasecmp(token, "unknown")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "international")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_INTERNATIONAL;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "national")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_NATIONAL;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "network")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_NETWORK;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "subscriber")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_SUBSCRIBER;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "abbreviated")) {
|
||||
list->has_to_type = 1;
|
||||
list->to_type = OSMO_CC_TYPE_ABBREVIATED;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "allowed")) {
|
||||
if (no_present)
|
||||
goto no_present_error;
|
||||
list->has_to_present = 1;
|
||||
list->to_present = OSMO_CC_PRESENT_ALLOWED;
|
||||
goto next_to;
|
||||
} else
|
||||
if (!strcasecmp(token, "restricted")) {
|
||||
if (no_present)
|
||||
goto no_present_error;
|
||||
list->has_to_present = 1;
|
||||
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) {
|
||||
free(list);
|
||||
LOGP(DCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DCC, LOGL_ERROR, "The '*' may be used only once.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
list->to[j] = SCREEN_STAR;
|
||||
star_used = 1;
|
||||
} else
|
||||
if (token[i] == '@') {
|
||||
if (!calling_in) {
|
||||
free(list);
|
||||
LOGP(DCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DCC, LOGL_ERROR, "The '@' may be used only for incoming calls from interface.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (at_used) {
|
||||
free(list);
|
||||
LOGP(DCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DCC, LOGL_ERROR, "The '@' may be used only once.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
list->to[j] = SCREEN_AT;
|
||||
at_used = 1;
|
||||
} else
|
||||
if (token[i] == '\\' && token[i + 1] != '\0')
|
||||
list->to[j] = token[++i];
|
||||
else
|
||||
list->to[j] = token[i];
|
||||
}
|
||||
list->to[j] = '\0';
|
||||
}
|
||||
|
||||
token = osmo_cc_strtok_quotes(&text);
|
||||
if (token) {
|
||||
free(list);
|
||||
LOGP(DCC, LOGL_ERROR, "Error in screening definition '%s'.\n", text);
|
||||
LOGP(DCC, LOGL_ERROR, "Got garbage behind screening result.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* attach screen entry to list */
|
||||
while (*list_p)
|
||||
list_p = &((*list_p)->next);
|
||||
*list_p = list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osmo_cc_flush_screen(osmo_cc_screen_list_t *list)
|
||||
{
|
||||
osmo_cc_screen_list_t *temp;
|
||||
|
||||
while (list) {
|
||||
temp = list;
|
||||
list = list->next;
|
||||
free(temp);
|
||||
}
|
||||
}
|
||||
|
||||
const char *print_rule_string(const char *input)
|
||||
{
|
||||
static char output[256];
|
||||
int i;
|
||||
|
||||
for (i = 0; *input && i < (int)sizeof(output) - 1; i++, input++) {
|
||||
switch (*input) {
|
||||
case SCREEN_QUESTIONMARK:
|
||||
output[i] = '?';
|
||||
break;
|
||||
case SCREEN_STAR:
|
||||
output[i] = '*';
|
||||
break;
|
||||
case SCREEN_AT:
|
||||
output[i] = '@';
|
||||
break;
|
||||
default:
|
||||
output[i] = *input;
|
||||
}
|
||||
}
|
||||
|
||||
output[i] = '\0';
|
||||
return output;
|
||||
}
|
||||
|
||||
static int osmo_cc_screen(const char *what, osmo_cc_screen_list_t *list, uint8_t *type, uint8_t *present, char *id_to, int id_to_size, const char *id_from, const char **routing_p)
|
||||
{
|
||||
const char *suffix;
|
||||
int i, j, rule;
|
||||
|
||||
LOGP(DCC, LOGL_INFO, "Screening %s '%s':\n", what, id_from);
|
||||
switch (*type) {
|
||||
case OSMO_CC_TYPE_UNKNOWN:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = unknown\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_INTERNATIONAL:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = international\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NATIONAL:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = national\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NETWORK:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = network\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_SUBSCRIBER:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = subscriber\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_ABBREVIATED:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = abbreviated\n");
|
||||
break;
|
||||
}
|
||||
if (present) switch (*present) {
|
||||
case OSMO_CC_PRESENT_ALLOWED:
|
||||
LOGP(DCC, LOGL_INFO, " -> present = allowed\n");
|
||||
break;
|
||||
case OSMO_CC_PRESENT_RESTRICTED:
|
||||
LOGP(DCC, LOGL_INFO, " -> present = restricted\n");
|
||||
break;
|
||||
}
|
||||
|
||||
rule = 0;
|
||||
while (list) {
|
||||
rule++;
|
||||
LOGP(DCC, LOGL_INFO, "Comparing with rule #%d: '%s':\n", rule, print_rule_string(list->from));
|
||||
if (list->has_from_type) switch (list->from_type) {
|
||||
case OSMO_CC_TYPE_UNKNOWN:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = unknown\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_INTERNATIONAL:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = international\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NATIONAL:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = national\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NETWORK:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = network\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_SUBSCRIBER:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = subscriber\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_ABBREVIATED:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = abbreviated\n");
|
||||
break;
|
||||
}
|
||||
if (list->has_from_present) switch (list->from_present) {
|
||||
case OSMO_CC_PRESENT_ALLOWED:
|
||||
LOGP(DCC, LOGL_INFO, " -> present = allowed\n");
|
||||
break;
|
||||
case OSMO_CC_PRESENT_RESTRICTED:
|
||||
LOGP(DCC, LOGL_INFO, " -> present = restricted\n");
|
||||
break;
|
||||
}
|
||||
suffix = NULL;
|
||||
/* attributes do not match */
|
||||
if (list->has_from_type && list->from_type != *type) {
|
||||
LOGP(DCC, LOGL_INFO, "Rule does not match, because 'type' is different.\n");
|
||||
continue;
|
||||
}
|
||||
if (present && list->has_from_present && list->from_present != *present) {
|
||||
LOGP(DCC, LOGL_INFO, "Rule does not match, because 'present' is different.\n");
|
||||
continue;
|
||||
}
|
||||
for (i = 0; list->from[i] && id_from[i]; i++) {
|
||||
/* '?' means: any digit, so it machtes */
|
||||
if (list->from[i] == SCREEN_QUESTIONMARK) {
|
||||
continue;
|
||||
}
|
||||
/* '*' means: anything may follow, so it machtes */
|
||||
if (list->from[i] == SCREEN_STAR) {
|
||||
suffix = id_from + i;
|
||||
break;
|
||||
}
|
||||
/* check if digit doesn't matches */
|
||||
if (list->from[i] != id_from[i])
|
||||
break;
|
||||
}
|
||||
/* if last checked digit is '*', we have a match */
|
||||
/* also if we hit EOL at id_from and next check digit is '*' */
|
||||
if (list->from[i] == SCREEN_STAR)
|
||||
break;
|
||||
/* if all digits have matched */
|
||||
if (list->from[i] == '\0' && id_from[i] == '\0')
|
||||
break;
|
||||
LOGP(DCC, LOGL_INFO, "Rule does not match, because %s is different.\n", what);
|
||||
list = list->next;
|
||||
}
|
||||
|
||||
/* if no list entry matches */
|
||||
if (!list)
|
||||
return -1;
|
||||
|
||||
/* replace ID */
|
||||
if (list->has_to_type) {
|
||||
*type = list->to_type;
|
||||
}
|
||||
if (present && list->has_to_present) {
|
||||
*present = list->to_present;
|
||||
}
|
||||
for (i = j = 0; list->to[i]; i++) {
|
||||
if (j == id_to_size - 1)
|
||||
break;
|
||||
/* '*' means to use suffix of input string */
|
||||
if (list->to[i] == SCREEN_STAR && suffix) {
|
||||
while (*suffix) {
|
||||
id_to[j++] = *suffix++;
|
||||
if (j == id_to_size - 1)
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
/* '@' means to stop and return routing also */
|
||||
} else if (list->to[i] == SCREEN_AT) {
|
||||
if (routing_p)
|
||||
*routing_p = &list->to[i + 1];
|
||||
break;
|
||||
}
|
||||
/* copy output digit */
|
||||
id_to[j++] = list->to[i];
|
||||
}
|
||||
id_to[j] = '\0';
|
||||
|
||||
LOGP(DCC, LOGL_INFO, "Rule matches, changing %s to '%s'.\n", what, print_rule_string(id_to));
|
||||
if (list->has_to_type) switch (list->to_type) {
|
||||
case OSMO_CC_TYPE_UNKNOWN:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = unknown\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_INTERNATIONAL:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = international\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NATIONAL:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = national\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_NETWORK:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = network\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_SUBSCRIBER:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = subscriber\n");
|
||||
break;
|
||||
case OSMO_CC_TYPE_ABBREVIATED:
|
||||
LOGP(DCC, LOGL_INFO, " -> type = abbreviated\n");
|
||||
break;
|
||||
}
|
||||
if (list->has_to_present) switch (list->to_present) {
|
||||
case OSMO_CC_PRESENT_ALLOWED:
|
||||
LOGP(DCC, LOGL_INFO, " -> present = allowed\n");
|
||||
break;
|
||||
case OSMO_CC_PRESENT_RESTRICTED:
|
||||
LOGP(DCC, LOGL_INFO, " -> present = restricted\n");
|
||||
break;
|
||||
}
|
||||
if (routing_p && *routing_p)
|
||||
LOGP(DCC, LOGL_INFO, " -> remote = %s\n", *routing_p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
osmo_cc_msg_t *osmo_cc_screen_msg(osmo_cc_endpoint_t *ep, osmo_cc_msg_t *old_msg, int in, const char **routing_p)
|
||||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
char id[256], calling[256], called[256], redir[256];
|
||||
uint8_t calling_type, calling_plan, calling_present, calling_screen;
|
||||
uint8_t called_type, called_plan;
|
||||
uint8_t redir_type, redir_plan, redir_present, redir_screen, redir_reason;
|
||||
int calling_status = 0, called_status = 0, redir_status = 0;
|
||||
int rc;
|
||||
void *ie, *to_ie;
|
||||
uint8_t ie_type;
|
||||
uint16_t ie_length;
|
||||
void *ie_value;
|
||||
|
||||
if (in && ep->screen_calling_in) {
|
||||
rc = osmo_cc_get_ie_calling(old_msg, 0, &calling_type, &calling_plan, &calling_present, &calling_screen, id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("incoming caller ID", ep->screen_calling_in, &calling_type, &calling_present, calling, sizeof(calling), id, routing_p);
|
||||
if (rc >= 0)
|
||||
calling_status = 1;
|
||||
} else {
|
||||
calling_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
calling_plan = OSMO_CC_PLAN_TELEPHONY;
|
||||
calling_present = OSMO_CC_PRESENT_ALLOWED;
|
||||
calling_screen = OSMO_CC_SCREEN_NETWORK;
|
||||
rc = osmo_cc_screen("incoming caller ID", ep->screen_calling_in, &calling_type, &calling_present, calling, sizeof(calling), "", routing_p);
|
||||
if (rc >= 0)
|
||||
calling_status = 1;
|
||||
}
|
||||
rc = osmo_cc_get_ie_redir(old_msg, 0, &redir_type, &redir_plan, &redir_present, &redir_screen, &redir_reason, id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("incoming redirecting number", ep->screen_calling_in, &redir_type, &redir_present, redir, sizeof(redir), id, NULL);
|
||||
if (rc >= 0)
|
||||
redir_status = 1;
|
||||
}
|
||||
}
|
||||
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, 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), "", routing_p);
|
||||
if (rc >= 0)
|
||||
called_status = 1;
|
||||
}
|
||||
}
|
||||
if (!in && ep->screen_calling_out) {
|
||||
rc = osmo_cc_get_ie_calling(old_msg, 0, &calling_type, &calling_plan, &calling_present, &calling_screen, id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("outgoing caller ID", ep->screen_calling_out, &calling_type, &calling_present, calling, sizeof(calling), id, NULL);
|
||||
if (rc >= 0)
|
||||
calling_status = 1;
|
||||
} else {
|
||||
calling_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
calling_plan = OSMO_CC_PLAN_TELEPHONY;
|
||||
calling_present = OSMO_CC_PRESENT_ALLOWED;
|
||||
calling_screen = OSMO_CC_SCREEN_NETWORK;
|
||||
rc = osmo_cc_screen("outgoing caller ID", ep->screen_calling_out, &calling_type, &calling_present, calling, sizeof(calling), "", NULL);
|
||||
if (rc >= 0)
|
||||
calling_status = 1;
|
||||
}
|
||||
rc = osmo_cc_get_ie_redir(old_msg, 0, &redir_type, &redir_plan, &redir_present, &redir_screen, &redir_reason, id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("outgoing redirecting number", ep->screen_calling_out, &redir_type, &redir_present, redir, sizeof(redir), id, NULL);
|
||||
if (rc >= 0)
|
||||
redir_status = 1;
|
||||
}
|
||||
}
|
||||
if (!in && ep->screen_called_out) {
|
||||
rc = osmo_cc_get_ie_called(old_msg, 0, &called_type, &called_plan, id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("outgoing dialed number", ep->screen_called_out, &called_type, NULL, called, sizeof(called), id, NULL);
|
||||
if (rc >= 0)
|
||||
called_status = 1;
|
||||
} else {
|
||||
called_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
called_plan = OSMO_CC_PLAN_TELEPHONY;
|
||||
rc = osmo_cc_screen("outgoing dialed number", ep->screen_called_out, &called_type, NULL, called, sizeof(called), "", NULL);
|
||||
if (rc >= 0)
|
||||
called_status = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* nothing screened */
|
||||
if (!calling_status && !called_status && !redir_status)
|
||||
return old_msg;
|
||||
|
||||
new_msg = osmo_cc_new_msg(old_msg->type);
|
||||
|
||||
/* copy and replace */
|
||||
ie = old_msg->data;
|
||||
while ((ie_value = osmo_cc_msg_sep_ie(old_msg, &ie, &ie_type, &ie_length))) {
|
||||
switch (ie_type) {
|
||||
case OSMO_CC_IE_CALLING:
|
||||
if (calling_status) {
|
||||
osmo_cc_add_ie_calling(new_msg, calling_type, calling_plan, calling_present, calling_screen, calling);
|
||||
calling_status = 0;
|
||||
break;
|
||||
}
|
||||
goto copy;
|
||||
case OSMO_CC_IE_CALLED:
|
||||
if (called_status) {
|
||||
osmo_cc_add_ie_called(new_msg, called_type, called_plan, called);
|
||||
called_status = 0;
|
||||
break;
|
||||
}
|
||||
goto copy;
|
||||
case OSMO_CC_IE_REDIR:
|
||||
if (redir_status) {
|
||||
osmo_cc_add_ie_redir(new_msg, redir_type, redir_plan, redir_present, redir_screen, redir_reason, redir);
|
||||
redir_status = 0;
|
||||
break;
|
||||
}
|
||||
goto copy;
|
||||
default:
|
||||
copy:
|
||||
to_ie = osmo_cc_add_ie(new_msg, ie_type, ie_length);
|
||||
memcpy(to_ie, ie_value, ie_length);
|
||||
}
|
||||
}
|
||||
|
||||
/* applend, if not yet in message (except redir, since it must exist) */
|
||||
if (calling_status)
|
||||
osmo_cc_add_ie_calling(new_msg, calling_type, calling_plan, calling_present, calling_screen, calling);
|
||||
if (called_status)
|
||||
osmo_cc_add_ie_called(new_msg, called_type, called_plan, called);
|
||||
|
||||
free(old_msg);
|
||||
return new_msg;
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
|
||||
void osmo_cc_help_screen(void);
|
||||
char *osmo_cc_strtok_quotes(const char **text_p);
|
||||
int osmo_cc_add_screen(osmo_cc_endpoint_t *ep, const char *text);
|
||||
void osmo_cc_flush_screen(osmo_cc_screen_list_t *list);
|
||||
osmo_cc_msg_t *osmo_cc_screen_msg(osmo_cc_endpoint_t *ep, osmo_cc_msg_t *old_msg, int in, const char **routing_p);
|
||||
|
|
@ -1,556 +0,0 @@
|
|||
/* Session Description Protocol parsing and generator
|
||||
* This shall be simple and is incomplete.
|
||||
*
|
||||
* (C) 2019 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 <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <errno.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../libtimer/timer.h"
|
||||
#include "../libselect/select.h"
|
||||
#include "endpoint.h"
|
||||
#include "sdp.h"
|
||||
|
||||
#define strncat_printf(sdp, fmt, arg...) \
|
||||
{ \
|
||||
snprintf(sdp + strlen(sdp), sizeof(sdp) - strlen(sdp), fmt, ## arg); \
|
||||
sdp[sizeof(sdp) - 1] = '\0'; \
|
||||
}
|
||||
|
||||
/* generate SDP from session structure */
|
||||
char *osmo_cc_session_gensdp(osmo_cc_session_t *session, int accepted_only)
|
||||
{
|
||||
/* calc max size of SDP: quick an dirty (close to max UDP payload size) */
|
||||
static char sdp[65000];
|
||||
const char *username, *sess_id, *sess_version, *nettype, *addrtype, *unicast_address;
|
||||
const char *session_name;
|
||||
int individual_connection_data = 1; /* in case there is no media, there is no connection data */
|
||||
int individual_send_receive = 1; /* in case there is no media, there is no send/receive attribute */
|
||||
struct osmo_cc_session_media *media;
|
||||
struct osmo_cc_session_codec *codec;
|
||||
|
||||
sdp[0] = 0;
|
||||
|
||||
/* Version */
|
||||
strncat_printf(sdp, "v=0\r\n");
|
||||
|
||||
/* Origin */
|
||||
username = session->origin_local.username;
|
||||
sess_id = session->origin_local.sess_id;
|
||||
sess_version = session->origin_local.sess_version;
|
||||
nettype = session->origin_local.nettype;
|
||||
addrtype = session->origin_local.addrtype;
|
||||
unicast_address = session->origin_local.unicast_address;
|
||||
strncat_printf(sdp, "o=%s %s %s %s %s %s\r\n", username, sess_id, sess_version, nettype, addrtype, unicast_address);
|
||||
|
||||
/* Session */
|
||||
session_name = session->name;
|
||||
strncat_printf(sdp, "s=%s\r\n", session_name);
|
||||
|
||||
/* Connection Data (if all media have the same data) */
|
||||
if (session->media_list) {
|
||||
osmo_cc_session_for_each_media(session->media_list->next, media) {
|
||||
if (accepted_only && !media->accepted)
|
||||
continue;
|
||||
if (session->media_list->connection_data_local.nettype != media->connection_data_local.nettype)
|
||||
break;
|
||||
if (session->media_list->connection_data_local.addrtype != media->connection_data_local.addrtype)
|
||||
break;
|
||||
if (!!strcmp(session->media_list->connection_data_local.address, media->connection_data_local.address))
|
||||
break;
|
||||
}
|
||||
if (!media)
|
||||
individual_connection_data = 0;
|
||||
}
|
||||
if (!individual_connection_data)
|
||||
strncat_printf(sdp, "c=%s %s %s\r\n", osmo_cc_session_nettype2string(session->media_list->connection_data_local.nettype), osmo_cc_session_addrtype2string(session->media_list->connection_data_local.addrtype), session->media_list->connection_data_local.address);
|
||||
|
||||
/* timestamp */
|
||||
strncat_printf(sdp, "t=0 0\r\n");
|
||||
|
||||
/* sendonly /recvonly (if all media have the same data) */
|
||||
if (session->media_list) {
|
||||
osmo_cc_session_for_each_media(session->media_list->next, media) {
|
||||
if (accepted_only && !media->accepted)
|
||||
continue;
|
||||
if (session->media_list->send != media->send)
|
||||
break;
|
||||
if (session->media_list->receive != media->receive)
|
||||
break;
|
||||
}
|
||||
if (!media)
|
||||
individual_send_receive = 0;
|
||||
}
|
||||
if (!individual_send_receive) {
|
||||
if (session->media_list->send && !session->media_list->receive)
|
||||
strncat_printf(sdp, "a=sendonly\r\n");
|
||||
if (!session->media_list->send && session->media_list->receive)
|
||||
strncat_printf(sdp, "a=recvonly\r\n");
|
||||
if (!session->media_list->send && !session->media_list->receive)
|
||||
strncat_printf(sdp, "a=inactive\r\n");
|
||||
}
|
||||
|
||||
/* media */
|
||||
osmo_cc_session_for_each_media(session->media_list, media) {
|
||||
if (accepted_only && !media->accepted)
|
||||
continue;
|
||||
strncat_printf(sdp, "m=%s %u %s",
|
||||
osmo_cc_session_media_type2string(media->description.type) ? : media->description.type_name,
|
||||
media->description.port_local,
|
||||
osmo_cc_session_media_proto2string(media->description.proto) ? : media->description.proto_name);
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
if (accepted_only && !codec->accepted)
|
||||
continue;
|
||||
strncat_printf(sdp, " %u", codec->payload_type_local);
|
||||
}
|
||||
strncat_printf(sdp, "\r\n");
|
||||
/* don't list rtpmap when session was canceled by setting port to 0 */
|
||||
if (media->description.port_local == 0)
|
||||
continue;
|
||||
if (individual_connection_data)
|
||||
strncat_printf(sdp, "c=%s %s %s\r\n", osmo_cc_session_nettype2string(media->connection_data_local.nettype), osmo_cc_session_addrtype2string(media->connection_data_local.addrtype), media->connection_data_local.address);
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
if (accepted_only && !codec->accepted)
|
||||
continue;
|
||||
strncat_printf(sdp, "a=rtpmap:%u %s/%d", codec->payload_type_local, codec->payload_name, codec->payload_rate);
|
||||
if (codec->payload_channels >= 2)
|
||||
strncat_printf(sdp, "/%d", codec->payload_channels);
|
||||
strncat_printf(sdp, "\r\n");
|
||||
}
|
||||
if (individual_send_receive) {
|
||||
if (media->send && !media->receive)
|
||||
strncat_printf(sdp, "a=sendonly\r\n");
|
||||
if (!media->send && media->receive)
|
||||
strncat_printf(sdp, "a=recvonly\r\n");
|
||||
if (!media->send && !media->receive)
|
||||
strncat_printf(sdp, "a=inactive\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* check for overflow and return */
|
||||
if (strlen(sdp) == sizeof(sdp) - 1) {
|
||||
LOGP(DCC, LOGL_ERROR, "Fatal error: Allocated SDP buffer with %d bytes is too small, please fix!\n", (int)sizeof(sdp));
|
||||
return NULL;
|
||||
}
|
||||
return sdp;
|
||||
}
|
||||
|
||||
/* separate a word from string that is delimited with one or more space characters */
|
||||
static char *wordsep(char **text_p)
|
||||
{
|
||||
char *text = *text_p;
|
||||
static char word[256];
|
||||
int i;
|
||||
|
||||
/* no text */
|
||||
if (text == NULL || *text == '\0')
|
||||
return NULL;
|
||||
/* skip spaces before text */
|
||||
while (*text && *text <= ' ')
|
||||
text++;
|
||||
/* copy content */
|
||||
i = 0;
|
||||
while (*text > ' ' && i < (int)sizeof(word))
|
||||
word[i++] = *text++;
|
||||
word[i] = '\0';
|
||||
/* set next */
|
||||
*text_p = text;
|
||||
return word;
|
||||
}
|
||||
|
||||
/*
|
||||
* codecs and their default values
|
||||
*
|
||||
* if format is -1, payload type is dynamic
|
||||
* if rate is 0, rate may be any rate
|
||||
*/
|
||||
struct codec_defaults {
|
||||
int fmt;
|
||||
char *name;
|
||||
uint32_t rate;
|
||||
int channels;
|
||||
} codec_defaults[] = {
|
||||
{ 0, "PCMU", 8000, 1 },
|
||||
{ 3, "GSM", 8000, 1 },
|
||||
{ 4, "G723", 8000, 1 },
|
||||
{ 5, "DVI4", 8000, 1 },
|
||||
{ 6, "DVI4", 16000, 1 },
|
||||
{ 7, "LPC", 8000, 1 },
|
||||
{ 8, "PCMA", 8000, 1 },
|
||||
{ 9, "G722", 8000, 1 },
|
||||
{ 10, "L16", 44100, 2 },
|
||||
{ 11, "L16", 44100, 1 },
|
||||
{ 12, "QCELP", 8000, 1 },
|
||||
{ 13, "CN", 8000, 1 },
|
||||
{ 14, "MPA", 90000, 1 },
|
||||
{ 15, "G728", 8000, 1 },
|
||||
{ 16, "DVI4", 11025, 1 },
|
||||
{ 17, "DVI4", 22050, 1 },
|
||||
{ 18, "G729", 8000, 1 },
|
||||
{ 25, "CELB", 90000, 0 },
|
||||
{ 26, "JPEG", 90000, 0 },
|
||||
{ 28, "nv", 90000, 0 },
|
||||
{ 31, "H261", 90000, 0 },
|
||||
{ 32, "MPV", 90000, 0 },
|
||||
{ 33, "MP2T", 90000, 0 },
|
||||
{ 34, "H263", 90000, 0 },
|
||||
{ -1, NULL, 0, 0 },
|
||||
};
|
||||
|
||||
static void complete_codec_by_fmt(uint8_t fmt, const char **name, uint32_t *rate, int *channels)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; codec_defaults[i].name; i++) {
|
||||
if (codec_defaults[i].fmt == fmt)
|
||||
break;
|
||||
}
|
||||
if (!codec_defaults[i].name)
|
||||
return;
|
||||
|
||||
free((char *)*name);
|
||||
*name = strdup(codec_defaults[i].name);
|
||||
*rate = codec_defaults[i].rate;
|
||||
*channels = codec_defaults[i].channels;
|
||||
}
|
||||
|
||||
int osmo_cc_payload_type_by_attrs(uint8_t *fmt, const char *name, uint32_t *rate, int *channels)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; codec_defaults[i].name; i++) {
|
||||
if (!strcmp(codec_defaults[i].name, name)
|
||||
&& (*rate == 0 || codec_defaults[i].rate == *rate)
|
||||
&& (*channels == 0 || codec_defaults[i].channels == *channels))
|
||||
break;
|
||||
}
|
||||
if (!codec_defaults[i].name)
|
||||
return -EINVAL;
|
||||
|
||||
*fmt = codec_defaults[i].fmt;
|
||||
*rate = codec_defaults[i].rate;
|
||||
*channels = codec_defaults[i].channels;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* parses data and codec list from SDP
|
||||
*
|
||||
* sdp = given SDP text
|
||||
* return: SDP session description structure */
|
||||
struct osmo_cc_session *osmo_cc_session_parsesdp(osmo_cc_session_config_t *conf, void *priv, const char *_sdp)
|
||||
{
|
||||
char buffer[strlen(_sdp) + 1], *sdp = buffer;
|
||||
char *line, *p, *word, *next_word;
|
||||
int line_no = 0;
|
||||
struct osmo_cc_session_connection_data ccd, *cd;
|
||||
int csend = 1, creceive = 1; /* common default */
|
||||
struct osmo_cc_session *session = NULL;
|
||||
struct osmo_cc_session_media *media = NULL;
|
||||
struct osmo_cc_session_codec *codec = NULL;
|
||||
|
||||
/* prepare data */
|
||||
strcpy(sdp, _sdp);
|
||||
memset(&ccd, 0, sizeof(ccd));
|
||||
|
||||
/* create SDP session description */
|
||||
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) {
|
||||
if ((p = strchr(sdp, '\r'))) {
|
||||
*p++ = '\0';
|
||||
if (*p == '\n')
|
||||
p++;
|
||||
line = sdp;
|
||||
sdp = p;
|
||||
} else if ((p = strchr(sdp, '\n'))) {
|
||||
*p++ = '\0';
|
||||
line = sdp;
|
||||
sdp = p;
|
||||
} else {
|
||||
line = sdp;
|
||||
sdp = strchr(sdp, '\0');
|
||||
}
|
||||
next_word = line + 2;
|
||||
line_no++;
|
||||
|
||||
if (line[0] == '\0')
|
||||
continue;
|
||||
|
||||
if (line[1] != '=') {
|
||||
LOGP(DCC, LOGL_NOTICE, "SDP line %d = '%s' is garbage, expecting '=' as second character.\n", line_no, line);
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(line[0]) {
|
||||
case 'v':
|
||||
LOGP(DCC, LOGL_DEBUG, " -> Version: %s\n", next_word);
|
||||
if (atoi(next_word) != 0) {
|
||||
LOGP(DCC, LOGL_NOTICE, "SDP line %d = '%s' describes unsupported version.\n", line_no, line);
|
||||
osmo_cc_free_session(session);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'o':
|
||||
LOGP(DCC, LOGL_DEBUG, " -> Originator: %s\n", next_word);
|
||||
/* Originator */
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.username); // if already set
|
||||
session->origin_remote.username = strdup(word);
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.sess_id); // if already set
|
||||
session->origin_remote.sess_id = strdup(word);
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.sess_version); // if already set
|
||||
session->origin_remote.sess_version = strdup(word);
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.nettype); // if already set
|
||||
session->origin_remote.nettype = strdup(word);
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.addrtype); // if already set
|
||||
session->origin_remote.addrtype = strdup(word);
|
||||
word = wordsep(&next_word);
|
||||
if (!word)
|
||||
break;
|
||||
free((char *)session->origin_remote.unicast_address); // if already set
|
||||
session->origin_remote.unicast_address = strdup(word);
|
||||
break;
|
||||
case 's':
|
||||
/* Session Name */
|
||||
LOGP(DCC, LOGL_DEBUG, " -> Session Name: %s\n", next_word);
|
||||
free((char *)session->name); // if already set
|
||||
session->name = strdup(next_word);
|
||||
break;
|
||||
case 'c': /* Connection Data */
|
||||
LOGP(DCC, LOGL_DEBUG, " -> Connection Data: %s\n", next_word);
|
||||
if (media)
|
||||
cd = &media->connection_data_remote;
|
||||
else
|
||||
cd = &ccd;
|
||||
/* network type */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
if (!strcmp(word, "IN"))
|
||||
cd->nettype = osmo_cc_session_nettype_inet;
|
||||
else {
|
||||
LOGP(DCC, LOGL_NOTICE, "Unsupported network type '%s' in SDP line %d = '%s'\n", word, line_no, line);
|
||||
break;
|
||||
}
|
||||
/* address type */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
if (!strcmp(word, "IP4")) {
|
||||
cd->addrtype = osmo_cc_session_addrtype_ipv4;
|
||||
LOGP(DCC, LOGL_DEBUG, " -> Address Type = IPv4\n");
|
||||
} else
|
||||
if (!strcmp(word, "IP6")) {
|
||||
cd->addrtype = osmo_cc_session_addrtype_ipv6;
|
||||
LOGP(DCC, LOGL_DEBUG, " -> Address Type = IPv6\n");
|
||||
} else {
|
||||
LOGP(DCC, LOGL_NOTICE, "Unsupported address type '%s' in SDP line %d = '%s'\n", word, line_no, line);
|
||||
break;
|
||||
}
|
||||
/* connection address */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
if ((p = strchr(word, '/')))
|
||||
*p++ = '\0';
|
||||
free((char *)cd->address); // in case of multiple lines of 'c'
|
||||
cd->address = strdup(word);
|
||||
LOGP(DCC, LOGL_DEBUG, " -> Address = %s\n", word);
|
||||
break;
|
||||
case 'm': /* Media Description */
|
||||
LOGP(DCC, LOGL_DEBUG, " -> Media Description: %s\n", next_word);
|
||||
/* add media description */
|
||||
media = osmo_cc_add_media(session, 0, 0, NULL, 0, 0, 0, csend, creceive, NULL, 0);
|
||||
/* copy common connection data from common connection, if exists */
|
||||
cd = &media->connection_data_remote;
|
||||
memcpy(cd, &ccd, sizeof(*cd));
|
||||
/* media type */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
if (!strcmp(word, "audio"))
|
||||
media->description.type = osmo_cc_session_media_type_audio;
|
||||
else
|
||||
if (!strcmp(word, "video"))
|
||||
media->description.type = osmo_cc_session_media_type_video;
|
||||
else {
|
||||
media->description.type = osmo_cc_session_media_type_unknown;
|
||||
media->description.type_name = strdup(word);
|
||||
LOGP(DCC, LOGL_DEBUG, "Unsupported media type in SDP line %d = '%s'\n", line_no, line);
|
||||
}
|
||||
/* port */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
media->description.port_remote = atoi(word);
|
||||
/* proto */
|
||||
if (!(word = wordsep(&next_word)))
|
||||
break;
|
||||
if (!strcmp(word, "RTP/AVP"))
|
||||
media->description.proto = osmo_cc_session_media_proto_rtp;
|
||||
else {
|
||||
media->description.proto = osmo_cc_session_media_proto_unknown;
|
||||
media->description.proto_name = strdup(word);
|
||||
LOGP(DCC, LOGL_NOTICE, "Unsupported protocol type in SDP line %d = '%s'\n", line_no, line);
|
||||
break;
|
||||
}
|
||||
/* create codec description for each codec and link */
|
||||
while ((word = wordsep(&next_word))) {
|
||||
/* create codec */
|
||||
codec = osmo_cc_add_codec(media, NULL, 0, 1, NULL, NULL, 0);
|
||||
/* fmt */
|
||||
codec->payload_type_remote = atoi(word);
|
||||
complete_codec_by_fmt(codec->payload_type_remote, &codec->payload_name, &codec->payload_rate, &codec->payload_channels);
|
||||
LOGP(DCC, LOGL_DEBUG, " -> payload type = %d\n", codec->payload_type_remote);
|
||||
if (codec->payload_name)
|
||||
LOGP(DCC, LOGL_DEBUG, " -> payload name = %s\n", codec->payload_name);
|
||||
if (codec->payload_rate)
|
||||
LOGP(DCC, LOGL_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
|
||||
if (codec->payload_channels)
|
||||
LOGP(DCC, LOGL_DEBUG, " -> payload channels = %d\n", codec->payload_channels);
|
||||
}
|
||||
break;
|
||||
case 'a':
|
||||
LOGP(DCC, LOGL_DEBUG, " -> Attribute: %s\n", next_word);
|
||||
word = wordsep(&next_word);
|
||||
if (!strcmp(word, "sendrecv")) {
|
||||
if (media) {
|
||||
media->receive = 1;
|
||||
media->send = 1;
|
||||
} else {
|
||||
creceive = 1;
|
||||
csend = 1;
|
||||
}
|
||||
break;
|
||||
} else
|
||||
if (!strcmp(word, "recvonly")) {
|
||||
if (media) {
|
||||
media->receive = 1;
|
||||
media->send = 0;
|
||||
} else {
|
||||
creceive = 1;
|
||||
csend = 0;
|
||||
}
|
||||
break;
|
||||
} else
|
||||
if (!strcmp(word, "sendonly")) {
|
||||
if (media) {
|
||||
media->receive = 0;
|
||||
media->send = 1;
|
||||
} else {
|
||||
creceive = 0;
|
||||
csend = 1;
|
||||
}
|
||||
break;
|
||||
} else
|
||||
if (!strcmp(word, "inactive")) {
|
||||
if (media) {
|
||||
media->receive = 0;
|
||||
media->send = 0;
|
||||
} else {
|
||||
creceive = 0;
|
||||
csend = 0;
|
||||
}
|
||||
break;
|
||||
} else
|
||||
if (!media) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Attribute without previously defined media in SDP line %d = '%s'\n", line_no, line);
|
||||
break;
|
||||
}
|
||||
if (!strncmp(word, "rtpmap:", 7)) {
|
||||
int fmt = atoi(word + 7);
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
if (codec->payload_type_remote == fmt)
|
||||
break;
|
||||
}
|
||||
if (!codec) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Attribute without previously defined codec in SDP line %d = '%s'\n", line_no, line);
|
||||
break;
|
||||
}
|
||||
LOGP(DCC, LOGL_DEBUG, " -> (rtpmap) payload type = %d\n", codec->payload_type_remote);
|
||||
if (!(word = wordsep(&next_word)))
|
||||
goto rtpmap_done;
|
||||
if ((p = strchr(word, '/')))
|
||||
*p++ = '\0';
|
||||
free((char *)codec->payload_name); // in case it is already set above
|
||||
codec->payload_name = strdup(word);
|
||||
LOGP(DCC, LOGL_DEBUG, " -> (rtpmap) payload name = %s\n", codec->payload_name);
|
||||
if (!(word = p))
|
||||
goto rtpmap_done;
|
||||
if ((p = strchr(word, '/')))
|
||||
*p++ = '\0';
|
||||
codec->payload_rate = atoi(word);
|
||||
LOGP(DCC, LOGL_DEBUG, " -> (rtpmap) payload rate = %d\n", codec->payload_rate);
|
||||
if (!(word = p)) {
|
||||
/* if no channel is given and no default was specified, we must set 1 channel */
|
||||
if (!codec->payload_channels)
|
||||
codec->payload_channels = 1;
|
||||
goto rtpmap_done;
|
||||
}
|
||||
codec->payload_channels = atoi(word);
|
||||
LOGP(DCC, LOGL_DEBUG, " -> (rtpmap) payload channels = %d\n", codec->payload_channels);
|
||||
rtpmap_done:
|
||||
if (!codec->payload_name || !codec->payload_rate || !codec->payload_channels) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Broken 'rtpmap' definition in SDP line %d = '%s' Skipping codec!\n", line_no, line);
|
||||
osmo_cc_free_codec(codec);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* if something is incomplete, abort here */
|
||||
if (osmo_cc_session_check(session, 1)) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Parsing SDP failed.\n");
|
||||
osmo_cc_free_session(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
void osmo_cc_debug_sdp(const char *_sdp)
|
||||
{
|
||||
const unsigned char *sdp = (const unsigned char *)_sdp;
|
||||
char text[256];
|
||||
int i;
|
||||
|
||||
while (*sdp) {
|
||||
for (i = 0; *sdp > 0 && *sdp >= 32 && i < (int)sizeof(text) - 1; i++)
|
||||
text[i] = *sdp++;
|
||||
text[i] = '\0';
|
||||
LOGP(DCC, LOGL_DEBUG, " | %s\n", text);
|
||||
while (*sdp > 0 && *sdp < 32)
|
||||
sdp++;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
|
||||
char *osmo_cc_session_gensdp(struct osmo_cc_session *session, int accepted_only);
|
||||
struct osmo_cc_session *osmo_cc_session_parsesdp(osmo_cc_session_config_t *conf, void *priv, const char *_sdp);
|
||||
int osmo_cc_payload_type_by_attrs(uint8_t *fmt, const char *name, uint32_t *rate, int *channels);
|
||||
void osmo_cc_debug_sdp(const char *sdp);
|
||||
|
|
@ -1,606 +0,0 @@
|
|||
/* Osmo-CC: Media Session handling
|
||||
*
|
||||
* (C) 2016 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#include <inttypes.h>
|
||||
#include "../libtimer/timer.h"
|
||||
#include "../libselect/select.h"
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../liboptions/options.h"
|
||||
#include "endpoint.h"
|
||||
|
||||
#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)
|
||||
{
|
||||
conf->default_nettype = nettype;
|
||||
conf->default_addrtype = addrtype;
|
||||
conf->default_unicast_address = options_strdup(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)
|
||||
{
|
||||
osmo_cc_session_t *session;
|
||||
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, "Creating session structure.\n");
|
||||
|
||||
session = calloc(1, sizeof(*session));
|
||||
if (!session) {
|
||||
LOGP(DCC, LOGL_ERROR, "No mem!\n");
|
||||
abort();
|
||||
}
|
||||
session->config = conf;
|
||||
session->priv = priv;
|
||||
if (username) {
|
||||
int i;
|
||||
for (i = 0; username[i]; i++) {
|
||||
if ((uint8_t)username[i] < 33) {
|
||||
LOGP(DCC, LOGL_ERROR, "Fatal error: SDP's originator (username) uses invalid characters, please fix!\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
session->origin_local.username = strdup(username);
|
||||
}
|
||||
if (!username)
|
||||
session->origin_local.username = strdup("-");
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> user name = %s\n", session->origin_local.username);
|
||||
if (sess_id)
|
||||
session->origin_local.sess_id = strdup(sess_id);
|
||||
if (sess_version)
|
||||
session->origin_local.sess_version = strdup(sess_version);
|
||||
if (!sess_id || !sess_version) {
|
||||
struct timeval tv;
|
||||
char ntp_timestamp[32];
|
||||
/* get time NTP format time stamp (time since 1900) */
|
||||
gettimeofday(&tv, NULL);
|
||||
sprintf(ntp_timestamp, "%" PRIu64, (uint64_t)tv.tv_sec + NTP_OFFSET);
|
||||
if (!sess_id)
|
||||
session->origin_local.sess_id = strdup(ntp_timestamp);
|
||||
if (!sess_version)
|
||||
session->origin_local.sess_version = strdup(ntp_timestamp);
|
||||
}
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> session ID = %s\n", session->origin_local.sess_id);
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> session version = %s\n", session->origin_local.sess_version);
|
||||
if (nettype)
|
||||
session->origin_local.nettype = strdup(osmo_cc_session_nettype2string(nettype));
|
||||
else
|
||||
session->origin_local.nettype = strdup(osmo_cc_session_nettype2string(conf->default_nettype));
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> network type = %s\n", session->origin_local.nettype);
|
||||
if (addrtype)
|
||||
session->origin_local.addrtype = strdup(osmo_cc_session_addrtype2string(addrtype));
|
||||
else
|
||||
session->origin_local.addrtype = strdup(osmo_cc_session_addrtype2string(conf->default_addrtype));
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> address type = %s\n", session->origin_local.addrtype);
|
||||
if (unicast_address)
|
||||
session->origin_local.unicast_address = strdup(unicast_address);
|
||||
else
|
||||
session->origin_local.unicast_address = strdup(conf->default_unicast_address);
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> unicast address = %s\n", session->origin_local.unicast_address);
|
||||
if (session_name)
|
||||
session->name = strdup(session_name);
|
||||
if (!session_name)
|
||||
session->name = strdup("-");
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> session name = %s\n", session->name);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
void osmo_cc_free_session(osmo_cc_session_t *session)
|
||||
{
|
||||
LOGP(DCC, LOGL_DEBUG, "Free session structure.\n");
|
||||
|
||||
free((char *)session->origin_local.username);
|
||||
free((char *)session->origin_local.sess_id);
|
||||
free((char *)session->origin_local.sess_version);
|
||||
free((char *)session->origin_local.nettype);
|
||||
free((char *)session->origin_local.addrtype);
|
||||
free((char *)session->origin_local.unicast_address);
|
||||
free((char *)session->origin_remote.username);
|
||||
free((char *)session->origin_remote.sess_id);
|
||||
free((char *)session->origin_remote.sess_version);
|
||||
free((char *)session->origin_remote.nettype);
|
||||
free((char *)session->origin_remote.addrtype);
|
||||
free((char *)session->origin_remote.unicast_address);
|
||||
free((char *)session->name);
|
||||
while (session->media_list)
|
||||
osmo_cc_free_media(session->media_list);
|
||||
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, uint8_t marker, 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;
|
||||
|
||||
media = calloc(1, sizeof(*media));
|
||||
if (!media) {
|
||||
LOGP(DCC, LOGL_ERROR, "No mem!\n");
|
||||
abort();
|
||||
}
|
||||
media->session = session;
|
||||
if (nettype)
|
||||
media->connection_data_local.nettype = nettype;
|
||||
else
|
||||
media->connection_data_local.nettype = conf->default_nettype;
|
||||
if (addrtype)
|
||||
media->connection_data_local.addrtype = addrtype;
|
||||
else
|
||||
media->connection_data_local.addrtype = conf->default_addrtype;
|
||||
if (address)
|
||||
media->connection_data_local.address = strdup(address);
|
||||
else
|
||||
media->connection_data_local.address = strdup(conf->default_unicast_address);
|
||||
media->description.type = type;
|
||||
media->description.port_local = port;
|
||||
media->description.proto = proto;
|
||||
media->send = send;
|
||||
media->receive = receive;
|
||||
media->receiver = receiver;
|
||||
media->tx_sequence = random();
|
||||
media->tx_timestamp = random();
|
||||
mediap = &media->session->media_list;
|
||||
while (*mediap)
|
||||
mediap = &((*mediap)->next);
|
||||
*mediap = media;
|
||||
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, "Adding session media.\n");
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> network type = %s\n", osmo_cc_session_nettype2string(media->connection_data_local.nettype));
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> address type = %s\n", osmo_cc_session_addrtype2string(media->connection_data_local.addrtype));
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> address = %s\n", media->connection_data_local.address);
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> media type = %s\n", osmo_cc_session_media_type2string(media->description.type));
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> media port = %d\n", media->description.port_local);
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> media proto = %s\n", osmo_cc_session_media_proto2string(media->description.proto));
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, "Opening and binding media port %d\n", media->description.port_local);
|
||||
|
||||
return media;
|
||||
}
|
||||
|
||||
void osmo_cc_free_media(osmo_cc_session_media_t *media)
|
||||
{
|
||||
osmo_cc_session_media_t **mediap;
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Free session media.\n");
|
||||
|
||||
osmo_cc_rtp_close(media);
|
||||
free((char *)media->connection_data_local.nettype_name);
|
||||
free((char *)media->connection_data_local.addrtype_name);
|
||||
free((char *)media->connection_data_local.address);
|
||||
free((char *)media->connection_data_remote.nettype_name);
|
||||
free((char *)media->connection_data_remote.addrtype_name);
|
||||
free((char *)media->connection_data_remote.address);
|
||||
while (media->codec_list)
|
||||
osmo_cc_free_codec(media->codec_list);
|
||||
mediap = &media->session->media_list;
|
||||
while (*mediap != media)
|
||||
mediap = &((*mediap)->next);
|
||||
*mediap = media->next;
|
||||
free(media);
|
||||
}
|
||||
|
||||
osmo_cc_session_codec_t *osmo_cc_add_codec(osmo_cc_session_media_t *media, const char *payload_name, uint32_t payload_rate, int payload_channels, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), int debug)
|
||||
{
|
||||
osmo_cc_session_codec_t *codec, **codecp;
|
||||
int rc;
|
||||
|
||||
codec = calloc(1, sizeof(*codec));
|
||||
if (!codec) {
|
||||
LOGP(DCC, LOGL_ERROR, "No mem!\n");
|
||||
abort();
|
||||
}
|
||||
codec->media = media;
|
||||
if (payload_name) {
|
||||
codec->payload_name = strdup(payload_name);
|
||||
codec->payload_rate = payload_rate;
|
||||
codec->payload_channels = payload_channels;
|
||||
rc = osmo_cc_payload_type_by_attrs(&codec->payload_type_local, payload_name, &payload_rate, &payload_channels);
|
||||
if (rc < 0) {
|
||||
/* hunt for next free dynamic payload type */
|
||||
uint8_t fmt = 96;
|
||||
osmo_cc_session_codec_t *c;
|
||||
osmo_cc_session_for_each_codec(media->codec_list, c) {
|
||||
if (c->payload_type_local >= fmt)
|
||||
fmt = c->payload_type_local + 1;
|
||||
}
|
||||
codec->payload_type_local = fmt;
|
||||
}
|
||||
}
|
||||
codec->encoder = encoder;
|
||||
codec->decoder = decoder;
|
||||
codecp = &codec->media->codec_list;
|
||||
while (*codecp)
|
||||
codecp = &((*codecp)->next);
|
||||
*codecp = codec;
|
||||
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, "Adding session codec.\n");
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> payload type = %d\n", codec->payload_type_local);
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> payload name = %s\n", codec->payload_name);
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
|
||||
if (debug) LOGP(DCC, LOGL_DEBUG, " -> payload channels = %d\n", codec->payload_channels);
|
||||
|
||||
return codec;
|
||||
}
|
||||
|
||||
void osmo_cc_free_codec(osmo_cc_session_codec_t *codec)
|
||||
{
|
||||
osmo_cc_session_codec_t **codecp;
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Free session codec.\n");
|
||||
|
||||
free((char *)codec->payload_name);
|
||||
codecp = &codec->media->codec_list;
|
||||
while (*codecp != codec)
|
||||
codecp = &((*codecp)->next);
|
||||
*codecp = codec->next;
|
||||
free(codec);
|
||||
}
|
||||
|
||||
int osmo_cc_session_check(osmo_cc_session_t *session, int remote)
|
||||
{
|
||||
struct osmo_cc_session_origin *orig;
|
||||
struct osmo_cc_session_media *media;
|
||||
struct osmo_cc_session_connection_data *cd;
|
||||
struct osmo_cc_session_media_description *md;
|
||||
struct osmo_cc_session_codec *codec;
|
||||
int i, j;
|
||||
|
||||
if (remote)
|
||||
orig = &session->origin_remote;
|
||||
else
|
||||
orig = &session->origin_local;
|
||||
if (!orig->username
|
||||
|| !orig->sess_id
|
||||
|| !orig->sess_version
|
||||
|| !orig->nettype
|
||||
|| !orig->addrtype
|
||||
|| !orig->unicast_address) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Missing data in session origin\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!session->name) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Missing data in session origin\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!session->media_list) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Missing media session\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
i = 0;
|
||||
osmo_cc_session_for_each_media(session->media_list, media) {
|
||||
i++;
|
||||
if (remote)
|
||||
cd = &media->connection_data_remote;
|
||||
else
|
||||
cd = &media->connection_data_local;
|
||||
if (!cd->nettype && !cd->nettype_name) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Session with media #%d is missing connection network type\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!cd->addrtype && !cd->addrtype_name) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Session with media #%d is missing connection address type\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!cd->address) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Session with media #%d is missing connection address\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
md = &media->description;
|
||||
if (!md->type && !md->type_name) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Session with media #%d is missing media type\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!md->proto && !md->proto_name) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Session with media #%d is missing protocol\n", i);
|
||||
return -EINVAL;
|
||||
}
|
||||
j = 0;
|
||||
osmo_cc_session_for_each_codec(media->codec_list, codec) {
|
||||
j++;
|
||||
if (!codec->payload_name) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Session with media #%d, codec #%d is missing name\n", i, j);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!codec->payload_rate) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Session with media #%d, codec #%d is missing rate\n", i, j);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (!codec->payload_channels) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Session with media #%d, codec #%d is missing channel count\n", i, j);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* check session description and generate SDP */
|
||||
const char *osmo_cc_session_send_offer(osmo_cc_session_t *session)
|
||||
{
|
||||
const char *sdp;
|
||||
int rc;
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Generating session offer and opening RTP stream.\n");
|
||||
|
||||
rc = osmo_cc_session_check(session, 0);
|
||||
if (rc < 0) {
|
||||
LOGP(DCC, LOGL_ERROR, "Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
sdp = osmo_cc_session_gensdp(session, 0);
|
||||
osmo_cc_debug_sdp(sdp);
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf, void *priv, const char *sdp)
|
||||
{
|
||||
osmo_cc_session_t *session;
|
||||
int rc;
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Parsing session offer.\n");
|
||||
|
||||
osmo_cc_debug_sdp(sdp);
|
||||
session = osmo_cc_session_parsesdp(conf, priv, sdp);
|
||||
if (!session)
|
||||
return NULL;
|
||||
|
||||
rc = osmo_cc_session_check(session, 0);
|
||||
if (rc < 0) {
|
||||
osmo_cc_free_session(session);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len))
|
||||
{
|
||||
osmo_cc_session_config_t *conf = media->session->config;
|
||||
|
||||
media->accepted = 1;
|
||||
if (nettype)
|
||||
media->connection_data_local.nettype = nettype;
|
||||
else
|
||||
media->connection_data_local.nettype = conf->default_nettype;
|
||||
if (addrtype)
|
||||
media->connection_data_local.addrtype = addrtype;
|
||||
else
|
||||
media->connection_data_local.addrtype = conf->default_addrtype;
|
||||
free((char *)media->connection_data_local.address);
|
||||
if (address)
|
||||
media->connection_data_local.address = strdup(address);
|
||||
else
|
||||
media->connection_data_local.address = strdup(conf->default_unicast_address);
|
||||
media->send = send;
|
||||
media->receive = receive;
|
||||
media->receiver = receiver;
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Accepting session media.\n");
|
||||
LOGP(DCC, LOGL_DEBUG, " -> network type = %s\n", osmo_cc_session_nettype2string(media->connection_data_local.nettype));
|
||||
LOGP(DCC, LOGL_DEBUG, " -> address type = %s\n", osmo_cc_session_addrtype2string(media->connection_data_local.addrtype));
|
||||
LOGP(DCC, LOGL_DEBUG, " -> address = %s\n", media->connection_data_local.address);
|
||||
}
|
||||
|
||||
|
||||
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 *priv), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv))
|
||||
{
|
||||
codec->accepted = 1;
|
||||
codec->encoder = encoder;
|
||||
codec->decoder = decoder;
|
||||
/* when we accept a codec, we just use the same payload type as the remote */
|
||||
codec->payload_type_local = codec->payload_type_remote;
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Accepting session codec.\n");
|
||||
LOGP(DCC, LOGL_DEBUG, " -> payload type = %d\n", codec->payload_type_local);
|
||||
LOGP(DCC, LOGL_DEBUG, " -> payload name = %s\n", codec->payload_name);
|
||||
LOGP(DCC, LOGL_DEBUG, " -> payload rate = %d\n", codec->payload_rate);
|
||||
LOGP(DCC, LOGL_DEBUG, " -> payload channels = %d\n", codec->payload_channels);
|
||||
}
|
||||
|
||||
const char *osmo_cc_session_send_answer(osmo_cc_session_t *session)
|
||||
{
|
||||
const char *sdp;
|
||||
int rc;
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Generating session answer.\n");
|
||||
|
||||
rc = osmo_cc_session_check(session, 0);
|
||||
if (rc < 0) {
|
||||
LOGP(DCC, LOGL_ERROR, "Please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
sdp = osmo_cc_session_gensdp(session, 1);
|
||||
osmo_cc_debug_sdp(sdp);
|
||||
|
||||
return sdp;
|
||||
}
|
||||
|
||||
/* Apply remote session description to local session description.
|
||||
* If remote media's port is 0, remove from local session description.
|
||||
* If codecs in the remote session description are missing, remove from local session description.
|
||||
*/
|
||||
static int osmo_cc_session_negotiate(osmo_cc_session_t *session_local, struct osmo_cc_session *session_remote)
|
||||
{
|
||||
osmo_cc_session_media_t *media_local, *media_remote, **media_local_p;
|
||||
osmo_cc_session_codec_t *codec_local, *codec_remote, **codec_local_p;
|
||||
int rc;
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Negotiating session.\n");
|
||||
|
||||
/* copy remote session information */
|
||||
session_local->origin_remote.username = strdup(session_remote->origin_remote.username);
|
||||
session_local->origin_remote.sess_id = strdup(session_remote->origin_remote.sess_id);
|
||||
session_local->origin_remote.sess_version = strdup(session_remote->origin_remote.sess_version);
|
||||
session_local->origin_remote.nettype = strdup(session_remote->origin_remote.nettype);
|
||||
session_local->origin_remote.addrtype = strdup(session_remote->origin_remote.addrtype);
|
||||
session_local->origin_remote.unicast_address = strdup(session_remote->origin_remote.unicast_address);
|
||||
|
||||
/* loop all media */
|
||||
for (media_local = session_local->media_list, media_remote = session_remote->media_list; media_local && media_remote; media_local = media_local->next, media_remote = media_remote->next) {
|
||||
/* copy remote media information */
|
||||
media_local->connection_data_remote.nettype = media_remote->connection_data_remote.nettype;
|
||||
if (media_remote->connection_data_remote.nettype_name)
|
||||
media_local->connection_data_remote.nettype_name = strdup(media_remote->connection_data_remote.nettype_name);
|
||||
media_local->connection_data_remote.addrtype = media_remote->connection_data_remote.addrtype;
|
||||
if (media_remote->connection_data_remote.addrtype_name)
|
||||
media_local->connection_data_remote.addrtype_name = strdup(media_remote->connection_data_remote.addrtype_name);
|
||||
if (media_remote->connection_data_remote.address)
|
||||
media_local->connection_data_remote.address = strdup(media_remote->connection_data_remote.address);
|
||||
media_local->description.port_remote = media_remote->description.port_remote;
|
||||
media_local->send = media_remote->send;
|
||||
media_local->receive = media_remote->receive;
|
||||
/* loop all codecs and remove if they are not found in local session description */
|
||||
codec_local_p = &media_local->codec_list;
|
||||
codec_local = *codec_local_p;
|
||||
while (codec_local) {
|
||||
/* search for equal codec, payload type may differe for each direction */
|
||||
osmo_cc_session_for_each_codec(media_remote->codec_list, codec_remote) {
|
||||
if (!strcmp(codec_local->payload_name, codec_remote->payload_name)
|
||||
&& codec_local->payload_rate == codec_remote->payload_rate
|
||||
&& codec_local->payload_channels == codec_remote->payload_channels)
|
||||
break;
|
||||
}
|
||||
if (codec_remote) {
|
||||
/* mark as accepted, copy remote codec information */
|
||||
codec_local->accepted = 1;
|
||||
codec_local->payload_type_remote = codec_remote->payload_type_remote;
|
||||
} else {
|
||||
/* mark as not accepted, in case it was accepted in earlier response */
|
||||
codec_local->accepted = 0;
|
||||
}
|
||||
codec_local_p = &codec_local->next;
|
||||
codec_local = *codec_local_p;
|
||||
}
|
||||
}
|
||||
if (media_local) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Negotiation failed, because remote endpoint returns less media streams than we offered.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
if (media_remote) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Negotiation failed, because remote endpoint returns more media streams than we offered.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* mark media as accepted, if there is a remote port and there is at least one codec */
|
||||
media_local_p = &session_local->media_list;
|
||||
media_local = *media_local_p;
|
||||
while (media_local) {
|
||||
if (media_local->description.port_remote && media_local->codec_list) {
|
||||
/* mark as accepted */
|
||||
media_local->accepted = 1;
|
||||
} else {
|
||||
/* mark as not accepted, in case it was accepted in earlier response */
|
||||
media_local->accepted = 0;
|
||||
}
|
||||
media_local_p = &media_local->next;
|
||||
media_local = *media_local_p;
|
||||
}
|
||||
|
||||
rc = osmo_cc_session_check(session_local, 1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int osmo_cc_session_receive_answer(osmo_cc_session_t *session, const char *sdp)
|
||||
{
|
||||
osmo_cc_session_t *session_remote;
|
||||
int rc;
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Parsing session answer.\n");
|
||||
|
||||
osmo_cc_debug_sdp(sdp);
|
||||
session_remote = osmo_cc_session_parsesdp(session->config, NULL, sdp);
|
||||
if (!session_remote)
|
||||
return -EINVAL;
|
||||
|
||||
rc = osmo_cc_session_check(session_remote, 1);
|
||||
if (rc < 0) {
|
||||
osmo_cc_free_session(session_remote);
|
||||
return rc;
|
||||
}
|
||||
rc = osmo_cc_session_negotiate(session, session_remote);
|
||||
if (rc < 0) {
|
||||
osmo_cc_free_session(session_remote);
|
||||
return rc;
|
||||
}
|
||||
osmo_cc_free_session(session_remote);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *osmo_cc_session_nettype2string(enum osmo_cc_session_nettype nettype)
|
||||
{
|
||||
switch (nettype) {
|
||||
case osmo_cc_session_nettype_inet:
|
||||
return "IN";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *osmo_cc_session_addrtype2string(enum osmo_cc_session_addrtype addrtype)
|
||||
{
|
||||
switch (addrtype) {
|
||||
case osmo_cc_session_addrtype_ipv4:
|
||||
return "IP4";
|
||||
case osmo_cc_session_addrtype_ipv6:
|
||||
return "IP6";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *osmo_cc_session_media_type2string(enum osmo_cc_session_media_type media_type)
|
||||
{
|
||||
switch (media_type) {
|
||||
case osmo_cc_session_media_type_audio:
|
||||
return "audio";
|
||||
case osmo_cc_session_media_type_video:
|
||||
return "video";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *osmo_cc_session_media_proto2string(enum osmo_cc_session_media_proto media_proto)
|
||||
{
|
||||
switch (media_proto) {
|
||||
case osmo_cc_session_media_proto_rtp:
|
||||
return "RTP/AVP";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int osmo_cc_session_if_codec(osmo_cc_session_codec_t *codec, const char *name, uint32_t rate, int channels)
|
||||
{
|
||||
return (!strcmp(codec->payload_name, name)
|
||||
&& codec->payload_rate == rate
|
||||
&& codec->payload_channels == channels);
|
||||
}
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
#include <sys/socket.h>
|
||||
|
||||
/* configuration */
|
||||
|
||||
enum osmo_cc_session_nettype {
|
||||
osmo_cc_session_nettype_unknown = 0,
|
||||
osmo_cc_session_nettype_inet,
|
||||
};
|
||||
|
||||
enum osmo_cc_session_addrtype {
|
||||
osmo_cc_session_addrtype_unknown = 0,
|
||||
osmo_cc_session_addrtype_ipv4,
|
||||
osmo_cc_session_addrtype_ipv6,
|
||||
};
|
||||
|
||||
typedef struct osmo_cc_session_config {
|
||||
enum osmo_cc_session_nettype default_nettype;
|
||||
enum osmo_cc_session_addrtype default_addrtype;
|
||||
const char *default_unicast_address;
|
||||
uint16_t rtp_port_next;
|
||||
uint16_t rtp_port_from;
|
||||
uint16_t rtp_port_to;
|
||||
} osmo_cc_session_config_t;
|
||||
|
||||
/* session description, global part: */
|
||||
|
||||
typedef struct osmo_cc_session_origin {
|
||||
const char *username;
|
||||
const char *sess_id;
|
||||
const char *sess_version;
|
||||
const char *nettype;
|
||||
const char *addrtype;
|
||||
const char *unicast_address;
|
||||
} osmo_cc_session_origin_t;
|
||||
|
||||
/* session instance */
|
||||
typedef struct osmo_cc_session {
|
||||
osmo_cc_session_config_t *config;
|
||||
void *priv;
|
||||
osmo_cc_session_origin_t origin_local, origin_remote;
|
||||
const char *name;
|
||||
struct osmo_cc_session_media *media_list;
|
||||
} osmo_cc_session_t;
|
||||
|
||||
/* connection description: */
|
||||
|
||||
typedef struct osmo_cc_session_connection_data {
|
||||
enum osmo_cc_session_nettype nettype;
|
||||
const char *nettype_name;
|
||||
enum osmo_cc_session_addrtype addrtype;
|
||||
const char *addrtype_name;
|
||||
const char *address;
|
||||
} osmo_cc_session_connection_data_t;
|
||||
|
||||
/* one media of session description: */
|
||||
|
||||
enum osmo_cc_session_media_type {
|
||||
osmo_cc_session_media_type_unknown,
|
||||
osmo_cc_session_media_type_audio,
|
||||
osmo_cc_session_media_type_video,
|
||||
};
|
||||
|
||||
enum osmo_cc_session_media_proto {
|
||||
osmo_cc_session_media_proto_unknown,
|
||||
osmo_cc_session_media_proto_rtp,
|
||||
};
|
||||
|
||||
typedef struct osmo_cc_session_media_description {
|
||||
enum osmo_cc_session_media_type type;
|
||||
const char *type_name;
|
||||
uint16_t port_local, port_remote;
|
||||
enum osmo_cc_session_media_proto proto;
|
||||
const char *proto_name;
|
||||
} osmo_cc_session_media_description_t;
|
||||
|
||||
/* media entry */
|
||||
typedef struct osmo_cc_session_media {
|
||||
struct osmo_cc_session_media *next;
|
||||
osmo_cc_session_t *session;
|
||||
osmo_cc_session_media_description_t description;
|
||||
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, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
|
||||
struct sockaddr_storage rtp_sa, rtcp_sa;
|
||||
socklen_t rtp_slen, rtcp_slen;
|
||||
uint16_t *rtp_sport, *rtcp_sport; // pointers to the port inside sa sockaddr
|
||||
struct osmo_fd rtp_ofd;
|
||||
struct osmo_fd rtcp_ofd;
|
||||
uint32_t tx_ssrc, rx_ssrc;
|
||||
uint16_t tx_sequence, rx_sequence;
|
||||
uint32_t tx_timestamp, rx_timestamp;
|
||||
int accepted;
|
||||
} osmo_cc_session_media_t;
|
||||
|
||||
/* codec entry */
|
||||
typedef struct osmo_cc_session_codec {
|
||||
struct osmo_cc_session_codec *next;
|
||||
osmo_cc_session_media_t *media;
|
||||
uint8_t payload_type_local, payload_type_remote; /* local = towards local, remote = toward remote */
|
||||
const char *payload_name;
|
||||
uint32_t payload_rate;
|
||||
int payload_channels;
|
||||
void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
|
||||
int accepted;
|
||||
} osmo_cc_session_codec_t;
|
||||
|
||||
#define osmo_cc_session_for_each_media(head, m) \
|
||||
for (m = (head); m; m = m->next)
|
||||
|
||||
#define osmo_cc_session_for_each_codec(head, c) \
|
||||
for (c = (head); c; c = c->next)
|
||||
|
||||
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, uint8_t marker, 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 *priv), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), 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, uint8_t marker, 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 *priv), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv));
|
||||
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);
|
||||
const char *osmo_cc_session_nettype2string(enum osmo_cc_session_nettype nettype);
|
||||
const char *osmo_cc_session_addrtype2string(enum osmo_cc_session_addrtype addrtype);
|
||||
const char *osmo_cc_session_media_type2string(enum osmo_cc_session_media_type media_type);
|
||||
const char *osmo_cc_session_media_proto2string(enum osmo_cc_session_media_proto media_proto);
|
||||
int osmo_cc_session_if_codec(osmo_cc_session_codec_t *codec, const char *name, uint32_t rate, int channels);
|
||||
|
|
@ -1,619 +0,0 @@
|
|||
/* Osmo-CC: Socket handling
|
||||
*
|
||||
* (C) 2016 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../libtimer/timer.h"
|
||||
#include "../libselect/select.h"
|
||||
#include "message.h"
|
||||
#include "cause.h"
|
||||
#include "socket.h"
|
||||
|
||||
static const char version_string[] = OSMO_CC_VERSION;
|
||||
|
||||
static int _getaddrinfo(const char *host, uint16_t port, struct addrinfo **result)
|
||||
{
|
||||
char portstr[8];
|
||||
struct addrinfo hints;
|
||||
int rc;
|
||||
|
||||
sprintf(portstr, "%d", port);
|
||||
|
||||
/* bind socket */
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
hints.ai_protocol = 0;
|
||||
hints.ai_canonname = NULL;
|
||||
hints.ai_addr = NULL;
|
||||
hints.ai_next = NULL;
|
||||
|
||||
rc = getaddrinfo(host, portstr, &hints, result);
|
||||
if (rc < 0) {
|
||||
LOGP(DCC, LOGL_ERROR, "Failed to create socket for host '%s', port '%d': %s.\n", host, port, gai_strerror(rc));
|
||||
return rc;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* send a reject message toward CC process.
|
||||
* the CC process will change the reject message to a release message when not in INIT_IN state
|
||||
*/
|
||||
static void rej_msg(osmo_cc_socket_t *os, uint32_t callref, uint8_t socket_cause, uint8_t isdn_cause, uint16_t sip_cause)
|
||||
{
|
||||
osmo_cc_msg_t *msg;
|
||||
|
||||
/* create message */
|
||||
msg = osmo_cc_new_msg(OSMO_CC_MSG_REJ_REQ);
|
||||
if (!msg)
|
||||
abort();
|
||||
|
||||
/* add cause */
|
||||
osmo_cc_add_ie_cause(msg, os->location, isdn_cause, sip_cause, socket_cause);
|
||||
osmo_cc_convert_cause_msg(msg);
|
||||
|
||||
/* message down */
|
||||
os->recv_msg_cb(os->priv, callref, msg);
|
||||
}
|
||||
|
||||
static void tx_keepalive_timeout(void *data)
|
||||
{
|
||||
osmo_cc_conn_t *conn = data;
|
||||
osmo_cc_msg_t *msg;
|
||||
|
||||
/* send keepalive message */
|
||||
msg = osmo_cc_new_msg(OSMO_CC_MSG_DUMMY_REQ);
|
||||
osmo_cc_msg_list_enqueue(&conn->os->write_list, msg, conn->callref);
|
||||
timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
|
||||
}
|
||||
|
||||
static void close_conn(osmo_cc_conn_t *conn, uint8_t socket_cause);
|
||||
|
||||
static void rx_keepalive_timeout(void *data)
|
||||
{
|
||||
osmo_cc_conn_t *conn = data;
|
||||
|
||||
LOGP(DCC, LOGL_ERROR, "OsmoCC-Socket failed due to timeout.\n");
|
||||
close_conn(conn, OSMO_CC_SOCKET_CAUSE_TIMEOUT);
|
||||
}
|
||||
|
||||
static int socket_listen_cb(struct osmo_fd *ofd, unsigned int when);
|
||||
|
||||
/* create socket process and bind socket */
|
||||
int osmo_cc_open_socket(osmo_cc_socket_t *os, const char *host, uint16_t port, void *priv, void (*recv_msg_cb)(void *priv, uint32_t callref, osmo_cc_msg_t *msg), uint8_t location)
|
||||
{
|
||||
int try = 0, auto_port = 0;
|
||||
struct addrinfo *result, *rp;
|
||||
int rc, sock;
|
||||
|
||||
memset(os, 0, sizeof(*os));
|
||||
|
||||
try_again:
|
||||
/* check for given port, if NULL, autoselect port */
|
||||
if (!port || auto_port) {
|
||||
port = OSMO_CC_DEFAULT_PORT + try;
|
||||
try++;
|
||||
auto_port = 1;
|
||||
}
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Create socket for host %s port %d.\n", host, port);
|
||||
|
||||
rc = _getaddrinfo(host, port, &result);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
for (rp = result; rp; rp = rp->ai_next) {
|
||||
int on = 1;
|
||||
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (sock < 0)
|
||||
continue;
|
||||
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (unsigned char *)&on, sizeof(on));
|
||||
rc = bind(sock, rp->ai_addr, rp->ai_addrlen);
|
||||
if (rc == 0)
|
||||
break;
|
||||
close(sock);
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
if (rp == NULL) {
|
||||
if (auto_port && port < OSMO_CC_DEFAULT_PORT_MAX) {
|
||||
LOGP(DCC, LOGL_DEBUG, "Failed to bind host %s port %d, trying again.\n", host, port);
|
||||
goto try_again;
|
||||
}
|
||||
LOGP(DCC, LOGL_ERROR, "Failed to bind given host %s port %d.\n", host, port);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* listen to socket */
|
||||
rc = listen(sock, 10);
|
||||
if (rc < 0) {
|
||||
LOGP(DCC, LOGL_ERROR, "Failed to listen on socket.\n");
|
||||
close(sock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* register */
|
||||
os->ofd.fd = sock;
|
||||
os->ofd.cb = socket_listen_cb;
|
||||
os->ofd.data = os;
|
||||
os->ofd.when = OSMO_FD_READ;
|
||||
osmo_fd_register(&os->ofd);
|
||||
os->recv_msg_cb = recv_msg_cb;
|
||||
os->priv = priv;
|
||||
os->location = location;
|
||||
|
||||
return port;
|
||||
}
|
||||
|
||||
static int socket_conn_cb(struct osmo_fd *ofd, unsigned int when);
|
||||
|
||||
/* create a connection */
|
||||
static osmo_cc_conn_t *open_conn(osmo_cc_socket_t *os, int sock, uint32_t callref, int read_setup)
|
||||
{
|
||||
osmo_cc_conn_t *conn, **connp;
|
||||
|
||||
/* create connection */
|
||||
conn = calloc(1, sizeof(*conn));
|
||||
if (!conn) {
|
||||
LOGP(DCC, LOGL_ERROR, "No mem!\n");
|
||||
abort();
|
||||
}
|
||||
conn->os = os;
|
||||
conn->ofd.fd = sock;
|
||||
conn->ofd.cb = socket_conn_cb;
|
||||
conn->ofd.data = conn;
|
||||
conn->ofd.when = OSMO_FD_READ;
|
||||
osmo_fd_register(&conn->ofd);
|
||||
conn->read_version = 1;
|
||||
conn->write_version = 1;
|
||||
conn->read_setup = read_setup;
|
||||
if (callref)
|
||||
conn->callref = callref;
|
||||
else
|
||||
conn->callref = osmo_cc_new_callref();
|
||||
|
||||
timer_init(&conn->tx_keepalive_timer, tx_keepalive_timeout, conn);
|
||||
timer_init(&conn->rx_keepalive_timer, rx_keepalive_timeout, conn);
|
||||
timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
|
||||
timer_start(&conn->rx_keepalive_timer, OSMO_CC_SOCKET_RX_KEEPALIVE);
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "New socket connection (callref %d).\n", conn->callref);
|
||||
|
||||
/* attach to list */
|
||||
connp = &os->conn_list;
|
||||
while (*connp)
|
||||
connp = &((*connp)->next);
|
||||
*connp = conn;
|
||||
|
||||
return conn;
|
||||
}
|
||||
|
||||
/* remove a connection */
|
||||
static void close_conn(osmo_cc_conn_t *conn, uint8_t socket_cause)
|
||||
{
|
||||
osmo_cc_conn_t **connp;
|
||||
osmo_cc_msg_list_t *ml;
|
||||
|
||||
/* detach connection first, to prevent a destruction during message handling (double free) */
|
||||
connp = &conn->os->conn_list;
|
||||
while (*connp != conn)
|
||||
connp = &((*connp)->next);
|
||||
*connp = conn->next;
|
||||
/* send reject message, if socket_cause is set */
|
||||
if (socket_cause && !conn->read_setup) {
|
||||
/* receive a release or reject (depending on state), but only if we sent a setup */
|
||||
rej_msg(conn->os, conn->callref, socket_cause, 0, 0);
|
||||
}
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Destroy socket connection (callref %d).\n", conn->callref);
|
||||
|
||||
/* close socket */
|
||||
if (conn->ofd.fd) {
|
||||
osmo_fd_unregister(&conn->ofd);
|
||||
close(conn->ofd.fd);
|
||||
}
|
||||
/* free partly received message */
|
||||
if (conn->read_msg)
|
||||
osmo_cc_free_msg(conn->read_msg);
|
||||
/* free send queue */
|
||||
while ((ml = conn->write_list)) {
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
conn->write_list = ml->next;
|
||||
free(ml);
|
||||
}
|
||||
/* free timers */
|
||||
timer_exit(&conn->tx_keepalive_timer);
|
||||
timer_exit(&conn->rx_keepalive_timer);
|
||||
/* free connection (already detached above) */
|
||||
free(conn);
|
||||
}
|
||||
|
||||
/* close socket and remove */
|
||||
void osmo_cc_close_socket(osmo_cc_socket_t *os)
|
||||
{
|
||||
osmo_cc_msg_list_t *ml;
|
||||
|
||||
LOGP(DCC, LOGL_DEBUG, "Destroy socket.\n");
|
||||
|
||||
/* free all connections */
|
||||
while (os->conn_list)
|
||||
close_conn(os->conn_list, 0);
|
||||
/* close socket */
|
||||
if (os->ofd.fd > 0) {
|
||||
osmo_fd_unregister(&os->ofd);
|
||||
close(os->ofd.fd);
|
||||
os->ofd.fd = 0;
|
||||
}
|
||||
/* free send queue */
|
||||
while ((ml = os->write_list)) {
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
os->write_list = ml->next;
|
||||
free(ml);
|
||||
}
|
||||
}
|
||||
|
||||
/* send message to send_queue of sock instance */
|
||||
int osmo_cc_sock_send_msg(osmo_cc_socket_t *os, uint32_t callref, osmo_cc_msg_t *msg, const char *host, uint16_t port)
|
||||
{
|
||||
osmo_cc_msg_list_t *ml;
|
||||
|
||||
/* turn _IND into _REQ and _CNF into _RSP */
|
||||
msg->type &= ~1;
|
||||
|
||||
/* create list entry */
|
||||
ml = osmo_cc_msg_list_enqueue(&os->write_list, msg, callref);
|
||||
if (host)
|
||||
strncpy(ml->host, host, sizeof(ml->host) - 1);
|
||||
ml->port = port;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* receive message
|
||||
* return 1 if work was done.
|
||||
*/
|
||||
static int receive_conn(osmo_cc_conn_t *conn)
|
||||
{
|
||||
uint8_t socket_cause = OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE;
|
||||
int rc;
|
||||
osmo_cc_msg_t *msg;
|
||||
uint8_t msg_type;
|
||||
int len;
|
||||
int work = 0;
|
||||
|
||||
/* get version from remote */
|
||||
if (conn->read_version) {
|
||||
rc = recv(conn->ofd.fd, conn->read_version_string + conn->read_version_pos, strlen(version_string) - conn->read_version_pos, 0);
|
||||
if (rc < 0 && errno == EAGAIN)
|
||||
return work;
|
||||
work = 1;
|
||||
if (rc <= 0) {
|
||||
goto close;
|
||||
}
|
||||
conn->read_version_pos += rc;
|
||||
if (conn->read_version_pos == strlen(version_string)) {
|
||||
conn->read_version = 0;
|
||||
if (!!memcmp(conn->read_version_string, version_string, strlen(version_string) - 1)) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Remote does not seem to be an Osmo-CC socket, rejecting!\n");
|
||||
socket_cause = OSMO_CC_SOCKET_CAUSE_FAILED;
|
||||
goto close;
|
||||
}
|
||||
if (conn->read_version_string[strlen(version_string) - 1] != version_string[strlen(version_string) - 1]) {
|
||||
LOGP(DCC, LOGL_NOTICE, "Remote Osmo-CC socket has wrong version (local=%s, remote=%s), rejecting!\n", version_string, conn->read_version_string);
|
||||
socket_cause = OSMO_CC_SOCKET_CAUSE_VERSION_MISMATCH;
|
||||
goto close;
|
||||
}
|
||||
} else
|
||||
return work;
|
||||
}
|
||||
|
||||
try_next_message:
|
||||
/* read message header from remote */
|
||||
if (!conn->read_msg) {
|
||||
rc = recv(conn->ofd.fd, ((uint8_t *)&conn->read_hdr) + conn->read_pos, sizeof(conn->read_hdr) - conn->read_pos, 0);
|
||||
if (rc < 0 && errno == EAGAIN)
|
||||
return work;
|
||||
work = 1;
|
||||
if (rc <= 0) {
|
||||
goto close;
|
||||
}
|
||||
conn->read_pos += rc;
|
||||
if (conn->read_pos == sizeof(conn->read_hdr)) {
|
||||
conn->read_msg = osmo_cc_new_msg(conn->read_hdr.type);
|
||||
if (!conn->read_msg)
|
||||
abort();
|
||||
conn->read_msg->length_networkorder = conn->read_hdr.length_networkorder;
|
||||
/* prepare for reading message */
|
||||
conn->read_pos = 0;
|
||||
} else
|
||||
return work;
|
||||
}
|
||||
|
||||
/* read message data from remote */
|
||||
msg = conn->read_msg;
|
||||
len = ntohs(msg->length_networkorder);
|
||||
if (len == 0)
|
||||
goto empty_message;
|
||||
rc = recv(conn->ofd.fd, msg->data + conn->read_pos, len - conn->read_pos, 0);
|
||||
if (rc < 0 && errno == EAGAIN)
|
||||
return work;
|
||||
work = 1;
|
||||
if (rc <= 0) {
|
||||
goto close;
|
||||
}
|
||||
conn->read_pos += rc;
|
||||
if (conn->read_pos == len) {
|
||||
empty_message:
|
||||
/* start RX keepalive timeer, if not already */
|
||||
timer_start(&conn->rx_keepalive_timer, OSMO_CC_SOCKET_RX_KEEPALIVE);
|
||||
/* we got our setup message, so we clear the flag */
|
||||
conn->read_setup = 0;
|
||||
/* prepare for reading header */
|
||||
conn->read_pos = 0;
|
||||
/* detach message first, because the connection might be destroyed during message handling */
|
||||
msg_type = conn->read_msg->type;
|
||||
conn->read_msg = NULL;
|
||||
/* drop dummy or forward message */
|
||||
if (msg_type == OSMO_CC_MSG_DUMMY_REQ)
|
||||
osmo_cc_free_msg(msg);
|
||||
else
|
||||
conn->os->recv_msg_cb(conn->os->priv, conn->callref, msg);
|
||||
if (msg_type == OSMO_CC_MSG_REL_REQ || msg_type == OSMO_CC_MSG_REJ_REQ) {
|
||||
LOGP(DCC, LOGL_DEBUG, "closing socket because we received a release or reject message.\n");
|
||||
close_conn(conn, 0);
|
||||
return 1; /* conn removed */
|
||||
}
|
||||
goto try_next_message;
|
||||
}
|
||||
return work;
|
||||
|
||||
close:
|
||||
LOGP(DCC, LOGL_ERROR, "OsmoCC-Socket failed, socket cause %d.\n", socket_cause);
|
||||
close_conn(conn, socket_cause);
|
||||
return work; /* conn removed */
|
||||
}
|
||||
|
||||
/* transmit message
|
||||
* return 1 if work was done.
|
||||
*/
|
||||
static int transmit_conn(osmo_cc_conn_t *conn)
|
||||
{
|
||||
uint8_t socket_cause = OSMO_CC_SOCKET_CAUSE_BROKEN_PIPE;
|
||||
int rc;
|
||||
osmo_cc_msg_t *msg;
|
||||
int len;
|
||||
osmo_cc_msg_list_t *ml;
|
||||
int work = 0;
|
||||
|
||||
/* send socket version to remote */
|
||||
if (conn->write_version) {
|
||||
rc = write(conn->ofd.fd, version_string, strlen(version_string));
|
||||
if (rc < 0 && errno == EAGAIN)
|
||||
return work;
|
||||
work = 1;
|
||||
if (rc <= 0) {
|
||||
goto close;
|
||||
}
|
||||
if (rc != strlen(version_string)) {
|
||||
LOGP(DCC, LOGL_ERROR, "short write, please fix handling!\n");
|
||||
abort();
|
||||
}
|
||||
conn->write_version = 0;
|
||||
}
|
||||
|
||||
/* send message to remote */
|
||||
while (conn->write_list) {
|
||||
timer_stop(&conn->tx_keepalive_timer);
|
||||
msg = conn->write_list->msg;
|
||||
len = sizeof(*msg) + ntohs(msg->length_networkorder);
|
||||
rc = write(conn->ofd.fd, msg, len);
|
||||
if (rc < 0 && errno == EAGAIN)
|
||||
return work;
|
||||
work = 1;
|
||||
if (rc <= 0) {
|
||||
goto close;
|
||||
}
|
||||
if (rc != len) {
|
||||
LOGP(DCC, LOGL_ERROR, "short write, please fix handling!\n");
|
||||
abort();
|
||||
}
|
||||
/* close socket after sending release/reject message */
|
||||
if (msg->type == OSMO_CC_MSG_REL_REQ || msg->type == OSMO_CC_MSG_REJ_REQ) {
|
||||
LOGP(DCC, LOGL_DEBUG, "closing socket because we sent a release or reject message.\n");
|
||||
close_conn(conn, 0);
|
||||
return work; /* conn removed */
|
||||
}
|
||||
/* free message after sending */
|
||||
ml = conn->write_list;
|
||||
conn->write_list = ml->next;
|
||||
osmo_cc_free_msg(msg);
|
||||
free(ml);
|
||||
}
|
||||
|
||||
/* start TX keepalive timeer, if not already
|
||||
* because we stop at every message above, we actually restart the timer here.
|
||||
* only if there is no message for the amount of time, the timer fires.
|
||||
*/
|
||||
if (!timer_running(&conn->tx_keepalive_timer))
|
||||
timer_start(&conn->tx_keepalive_timer, OSMO_CC_SOCKET_TX_KEEPALIVE);
|
||||
|
||||
return work;
|
||||
|
||||
close:
|
||||
LOGP(DCC, LOGL_NOTICE, "OsmoCC-Socket failed.\n");
|
||||
close_conn(conn, socket_cause);
|
||||
return work; /* conn removed */
|
||||
}
|
||||
|
||||
/* handle all sockets of a socket interface
|
||||
* return 1 if work was done.
|
||||
*/
|
||||
int osmo_cc_handle_socket(osmo_cc_socket_t *os)
|
||||
{
|
||||
int sock;
|
||||
osmo_cc_conn_t *conn;
|
||||
osmo_cc_msg_list_t *ml, **mlp;
|
||||
int flags;
|
||||
struct addrinfo *result, *rp;
|
||||
int rc;
|
||||
int work = 0;
|
||||
|
||||
/* handle messages in send queue */
|
||||
while ((ml = os->write_list)) {
|
||||
work = 1;
|
||||
/* detach list entry */
|
||||
os->write_list = ml->next;
|
||||
ml->next = NULL;
|
||||
/* search for socket connection */
|
||||
for (conn = os->conn_list; conn; conn=conn->next) {
|
||||
if (conn->callref == ml->callref)
|
||||
break;
|
||||
}
|
||||
if (conn) {
|
||||
/* attach to list */
|
||||
mlp = &conn->write_list;
|
||||
while (*mlp)
|
||||
mlp = &((*mlp)->next);
|
||||
*mlp = ml;
|
||||
conn->ofd.when |= OSMO_FD_WRITE;
|
||||
/* done with message */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* reject and release are ignored */
|
||||
if (ml->msg->type == OSMO_CC_MSG_REJ_REQ
|
||||
|| ml->msg->type == OSMO_CC_MSG_REL_REQ) {
|
||||
/* drop message */
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
free(ml);
|
||||
/* done with message */
|
||||
continue;
|
||||
}
|
||||
|
||||
/* reject, if this is not a setup message */
|
||||
if (ml->msg->type != OSMO_CC_MSG_SETUP_REQ
|
||||
&& ml->msg->type != OSMO_CC_MSG_ATTACH_REQ) {
|
||||
LOGP(DCC, LOGL_ERROR, "Message with unknown callref.\n");
|
||||
rej_msg(os, ml->callref, 0, OSMO_CC_ISDN_CAUSE_INVAL_CALLREF, 0);
|
||||
/* drop message */
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
free(ml);
|
||||
/* done with message */
|
||||
continue;
|
||||
}
|
||||
/* connect to remote */
|
||||
rc = _getaddrinfo(ml->host, ml->port, &result);
|
||||
if (rc < 0) {
|
||||
rej_msg(os, ml->callref, OSMO_CC_SOCKET_CAUSE_FAILED, 0, 0);
|
||||
/* drop message */
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
free(ml);
|
||||
/* done with message */
|
||||
continue;
|
||||
}
|
||||
for (rp = result; rp; rp = rp->ai_next) {
|
||||
sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (sock < 0)
|
||||
continue;
|
||||
/* set nonblocking io, to prevent connect() and subsequent reads from blocking */
|
||||
flags = fcntl(sock, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(sock, F_SETFL, flags);
|
||||
/* connect */
|
||||
rc = connect(sock, rp->ai_addr, rp->ai_addrlen);
|
||||
if (rc == 0 || errno == EINPROGRESS)
|
||||
break;
|
||||
close(sock);
|
||||
}
|
||||
freeaddrinfo(result);
|
||||
if (rp == NULL) {
|
||||
LOGP(DCC, LOGL_ERROR, "Failed to connect to given host %s port %d.\n", ml->host, ml->port);
|
||||
rej_msg(os, ml->callref, OSMO_CC_SOCKET_CAUSE_FAILED, 0, 0);
|
||||
/* drop message */
|
||||
osmo_cc_free_msg(ml->msg);
|
||||
free(ml);
|
||||
/* done with message */
|
||||
continue;
|
||||
}
|
||||
/* create connection */
|
||||
conn = open_conn(os, sock, ml->callref, 0);
|
||||
/* attach to list */
|
||||
conn->write_list = ml;
|
||||
conn->ofd.when |= OSMO_FD_WRITE;
|
||||
/* done with (setup) message */
|
||||
}
|
||||
|
||||
return work;
|
||||
}
|
||||
|
||||
static int socket_listen_cb(struct osmo_fd *ofd, unsigned int when)
|
||||
{
|
||||
osmo_cc_socket_t *os = ofd->data;
|
||||
struct sockaddr_storage sa;
|
||||
socklen_t slen = sizeof(sa);
|
||||
int sock;
|
||||
int flags;
|
||||
|
||||
if (when & OSMO_FD_READ) {
|
||||
/* handle new socket connection */
|
||||
if ((sock = accept(os->ofd.fd, (struct sockaddr *)&sa, &slen)) > 0) {
|
||||
/* set nonblocking io, to prevent subsequent reads from blocking */
|
||||
flags = fcntl(sock, F_GETFL);
|
||||
flags |= O_NONBLOCK;
|
||||
fcntl(sock, F_SETFL, flags);
|
||||
/* create connection */
|
||||
open_conn(os, sock, 0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int socket_conn_cb(struct osmo_fd *ofd, unsigned int when)
|
||||
{
|
||||
osmo_cc_conn_t *conn = ofd->data;
|
||||
int work;
|
||||
|
||||
if (when & OSMO_FD_READ) {
|
||||
/* check for rx */
|
||||
work = receive_conn(conn);
|
||||
/* if "change" is set, connection list might have changed, so we restart processing the list */
|
||||
if (work)
|
||||
return 0;
|
||||
}
|
||||
if (when & OSMO_FD_WRITE) {
|
||||
/* check for tx */
|
||||
work = transmit_conn(conn);
|
||||
/* if "change" is set, connection list might have changed, so we restart processing the list */
|
||||
if (work)
|
||||
return 0;
|
||||
else
|
||||
conn->ofd.when &= ~OSMO_FD_WRITE;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
#ifndef OSMO_CC_SOCKET_H
|
||||
#define OSMO_CC_SOCKET_H
|
||||
|
||||
#define OSMO_CC_DEFAULT_PORT 4200
|
||||
#define OSMO_CC_DEFAULT_PORT_MAX 4219
|
||||
|
||||
#define OSMO_CC_SOCKET_TX_KEEPALIVE 10.0
|
||||
#define OSMO_CC_SOCKET_RX_KEEPALIVE 20.0
|
||||
|
||||
struct osmo_cc_socket;
|
||||
|
||||
typedef struct osmo_cc_conn {
|
||||
struct osmo_cc_conn *next;
|
||||
struct osmo_cc_socket *os;
|
||||
struct osmo_fd ofd;
|
||||
uint32_t callref;
|
||||
int read_setup;
|
||||
int read_version;
|
||||
char read_version_string[sizeof(OSMO_CC_VERSION)]; /* must include 0-termination */
|
||||
int read_version_pos;
|
||||
int write_version;
|
||||
osmo_cc_msg_t read_hdr;
|
||||
osmo_cc_msg_t *read_msg;
|
||||
int read_pos;
|
||||
osmo_cc_msg_list_t *write_list;
|
||||
struct timer tx_keepalive_timer;
|
||||
struct timer rx_keepalive_timer;
|
||||
} osmo_cc_conn_t;
|
||||
|
||||
typedef struct osmo_cc_socket {
|
||||
struct osmo_fd ofd;
|
||||
osmo_cc_conn_t *conn_list;
|
||||
osmo_cc_msg_list_t *write_list;
|
||||
void (*recv_msg_cb)(void *priv, uint32_t callref, osmo_cc_msg_t *msg);
|
||||
void *priv;
|
||||
uint8_t location;
|
||||
} osmo_cc_socket_t;
|
||||
|
||||
int osmo_cc_open_socket(osmo_cc_socket_t *os, const char *host, uint16_t port, void *priv, void (*recv_msg_cb)(void *priv, uint32_t callref, osmo_cc_msg_t *msg), uint8_t location);
|
||||
void osmo_cc_close_socket(osmo_cc_socket_t *os);
|
||||
int osmo_cc_sock_send_msg(osmo_cc_socket_t *os, uint32_t callref, osmo_cc_msg_t *msg, const char *host, uint16_t port);
|
||||
int osmo_cc_handle_socket(osmo_cc_socket_t *os);
|
||||
|
||||
#endif /* OSMO_CC_SOCKET_H */
|
|
@ -1,6 +0,0 @@
|
|||
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||
|
||||
noinst_LIBRARIES = libselect.a
|
||||
|
||||
libselect_a_SOURCES = \
|
||||
select.c
|
|
@ -1,168 +0,0 @@
|
|||
/* Timer handling
|
||||
*
|
||||
* (C) 2023 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Inspired by libosmocore
|
||||
*
|
||||
* 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 <math.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <sys/select.h>
|
||||
#include <sys/time.h>
|
||||
#include "select.h"
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#define MAX_OFD 1024
|
||||
|
||||
struct osmo_fd *ofd_list = NULL;
|
||||
int ofd_changed = 0;
|
||||
|
||||
int osmo_fd_register(struct osmo_fd *ofd)
|
||||
{
|
||||
struct osmo_fd **ofdp;
|
||||
|
||||
/* attach to list, if not already */
|
||||
ofdp = &ofd_list;
|
||||
while (*ofdp) {
|
||||
if (*ofdp == ofd)
|
||||
break;
|
||||
ofdp = &((*ofdp)->next);
|
||||
}
|
||||
if (!*ofdp) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: ofd=%p fd=%d registers.\n", __func__, ofd, ofd->fd);
|
||||
#endif
|
||||
ofd->next = NULL;
|
||||
*ofdp = ofd;
|
||||
ofd_changed = 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void osmo_fd_unregister(struct osmo_fd *ofd)
|
||||
{
|
||||
struct osmo_fd **ofdp;
|
||||
|
||||
/* detach from list, if not already */
|
||||
ofdp = &ofd_list;
|
||||
while (*ofdp) {
|
||||
if (*ofdp == ofd)
|
||||
break;
|
||||
ofdp = &((*ofdp)->next);
|
||||
}
|
||||
if (*ofdp) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: ofd=%p fd=%d unregisters.\n", __func__, ofd, ofd->fd);
|
||||
#endif
|
||||
*ofdp = ofd->next;
|
||||
ofd->next = NULL;
|
||||
ofd_changed = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int osmo_fd_select(double timeout)
|
||||
{
|
||||
fd_set readset;
|
||||
fd_set writeset;
|
||||
fd_set exceptset;
|
||||
struct osmo_fd *ofd;
|
||||
struct timeval tv;
|
||||
int max_fd;
|
||||
unsigned int what;
|
||||
int work = 0;
|
||||
int rc;
|
||||
|
||||
/* init event sets */
|
||||
FD_ZERO(&readset);
|
||||
FD_ZERO(&writeset);
|
||||
FD_ZERO(&exceptset);
|
||||
|
||||
/* populate event set with all file descriptios */
|
||||
ofd = ofd_list;
|
||||
max_fd = 0;
|
||||
while (ofd) {
|
||||
if (ofd->fd > max_fd)
|
||||
max_fd = ofd->fd;
|
||||
if (ofd->when & OSMO_FD_READ)
|
||||
FD_SET(ofd->fd, &readset);
|
||||
if (ofd->when & OSMO_FD_WRITE)
|
||||
FD_SET(ofd->fd, &writeset);
|
||||
if (ofd->when & OSMO_FD_EXCEPT)
|
||||
FD_SET(ofd->fd, &exceptset);
|
||||
ofd = ofd->next;
|
||||
}
|
||||
|
||||
if (timeout >= 0) {
|
||||
/* prepare timeout */
|
||||
tv.tv_sec = floor(timeout);
|
||||
tv.tv_usec = (timeout - tv.tv_sec) * 1000000.0;
|
||||
/* wait for event or timeout */
|
||||
rc = select(max_fd + 1, &readset, &writeset, &exceptset, &tv);
|
||||
} else {
|
||||
/* wait for event */
|
||||
rc = select(max_fd + 1, &readset, &writeset, &exceptset, NULL);
|
||||
}
|
||||
if (rc < 0) {
|
||||
if (errno != EINTR)
|
||||
fprintf(stderr, "%s: select() failed: '%d' with errno %d (%s) Please fix!\n", __func__, rc, errno, strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
again:
|
||||
/* check the result and call handler */
|
||||
ofd_changed = 0;
|
||||
ofd = ofd_list;
|
||||
while (ofd) {
|
||||
what = 0;
|
||||
if (FD_ISSET(ofd->fd, &readset)) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: ofd=%p fd=%d get READ event.\n", __func__, ofd, ofd->fd);
|
||||
#endif
|
||||
what |= OSMO_FD_READ;
|
||||
FD_CLR(ofd->fd, &readset);
|
||||
}
|
||||
if (FD_ISSET(ofd->fd, &writeset)) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: ofd=%p fd=%d get WRITE event.\n", __func__, ofd, ofd->fd);
|
||||
#endif
|
||||
what |= OSMO_FD_WRITE;
|
||||
FD_CLR(ofd->fd, &writeset);
|
||||
}
|
||||
if (FD_ISSET(ofd->fd, &exceptset)) {
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: ofd=%p fd=%d get EXCEPTION event.\n", __func__, ofd, ofd->fd);
|
||||
#endif
|
||||
what |= OSMO_FD_EXCEPT;
|
||||
FD_CLR(ofd->fd, &exceptset);
|
||||
}
|
||||
if (what) {
|
||||
work = 1;
|
||||
ofd->cb(ofd, what);
|
||||
/* list has changed */
|
||||
if (ofd_changed)
|
||||
goto again;
|
||||
}
|
||||
ofd = ofd->next;
|
||||
}
|
||||
|
||||
return work;
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
|
||||
#define OSMO_FD_READ 0x0001
|
||||
#define OSMO_FD_WRITE 0x0002
|
||||
#define OSMO_FD_EXCEPT 0x0004
|
||||
#define BSC_FD_READ 0x0001
|
||||
#define BSC_FD_WRITE 0x0002
|
||||
#define BSC_FD_EXCEPT 0x0004
|
||||
|
||||
struct osmo_fd {
|
||||
struct osmo_fd *next;
|
||||
int fd;
|
||||
unsigned int when;
|
||||
int (*cb)(struct osmo_fd *fd, unsigned int what);
|
||||
void *data;
|
||||
};
|
||||
|
||||
int osmo_fd_register(struct osmo_fd *ofd);
|
||||
void osmo_fd_unregister(struct osmo_fd *ofd);
|
||||
int osmo_fd_select(double timeout);
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||
|
||||
noinst_LIBRARIES = libtimer.a
|
||||
|
||||
libtimer_a_SOURCES = \
|
||||
timer.c
|
|
@ -1,165 +0,0 @@
|
|||
/* Timer handling
|
||||
*
|
||||
* (C) 2016 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 <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
//#include <sys/time.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include "timer.h"
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
static struct timer *timer_head = NULL;
|
||||
static struct timer **timer_tail_p = &timer_head;
|
||||
|
||||
double get_time(void)
|
||||
{
|
||||
static struct timespec tv;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &tv);
|
||||
|
||||
return (double)tv.tv_sec + (double)tv.tv_nsec / 1000000000.0;
|
||||
}
|
||||
|
||||
void timer_init(struct timer *timer, void (*fn)(void *data), void *priv)
|
||||
{
|
||||
if (timer->linked) {
|
||||
fprintf(stderr, "Timer is already initialized, aborting!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
timer->timeout = 0;
|
||||
timer->cb = fn;
|
||||
timer->data = priv;
|
||||
timer->next = NULL;
|
||||
*timer_tail_p = timer;
|
||||
timer_tail_p = &timer->next;
|
||||
timer->linked = 1;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: timer=%p linked.\n", __func__, timer);
|
||||
#endif
|
||||
}
|
||||
|
||||
void timer_exit(struct timer *timer)
|
||||
{
|
||||
timer_tail_p = &timer_head;
|
||||
while (*timer_tail_p) {
|
||||
if (timer == *timer_tail_p)
|
||||
*timer_tail_p = (*timer_tail_p)->next;
|
||||
else
|
||||
timer_tail_p = &((*timer_tail_p)->next);
|
||||
}
|
||||
timer->linked = 0;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: timer=%p unlinked.\n", __func__, timer);
|
||||
#endif
|
||||
}
|
||||
|
||||
void timer_start(struct timer *timer, double duration)
|
||||
{
|
||||
if (!timer->linked) {
|
||||
fprintf(stderr, "Timer is not initialized, aborting!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
timer->duration = duration;
|
||||
timer->timeout = get_time() + duration;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: timer=%p started %.3f seconds.\n", __func__, timer, duration);
|
||||
#endif
|
||||
}
|
||||
|
||||
void timer_stop(struct timer *timer)
|
||||
{
|
||||
if (!timer->linked) {
|
||||
fprintf(stderr, "Timer is not initialized, aborting!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
timer->timeout = 0;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: timer=%p stopped.\n", __func__, timer);
|
||||
#endif
|
||||
}
|
||||
|
||||
int timer_running(struct timer *timer)
|
||||
{
|
||||
if (!timer->linked) {
|
||||
fprintf(stderr, "Timer is not initialized, aborting!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
return (timer->timeout != 0);
|
||||
}
|
||||
|
||||
double process_timer(void)
|
||||
{
|
||||
struct timer *timer;
|
||||
double now, timeout = -1.0;
|
||||
|
||||
now = get_time();
|
||||
|
||||
again:
|
||||
timer = timer_head;
|
||||
while (timer) {
|
||||
if (timer->linked && timer->timeout > 0) {
|
||||
/* timeout, handle it, set timeout to 0 */
|
||||
if (now >= timer->timeout) {
|
||||
timer->timeout = 0;
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: timer=%p fired.\n", __func__, timer);
|
||||
#endif
|
||||
if (!timer->cb)
|
||||
abort();
|
||||
timer->cb(timer->data);
|
||||
timeout = 0.0;
|
||||
goto again;
|
||||
}
|
||||
/* in the future, set timeout to future */
|
||||
if (timeout < 0.0 || (timer->timeout - now) < timeout)
|
||||
timeout = timer->timeout - now;
|
||||
}
|
||||
timer = timer->next;
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
void osmo_timer_schedule(struct osmo_timer_list *ti, time_t sec, suseconds_t usec)
|
||||
{
|
||||
if (!ti->linked)
|
||||
timer_init(ti, ti->cb, ti->data);
|
||||
timer_start(ti, (double)sec + (double)usec / 1000000.0);
|
||||
}
|
||||
|
||||
void osmo_timer_del(struct osmo_timer_list *ti)
|
||||
{
|
||||
timer_exit(ti);
|
||||
}
|
||||
|
||||
int osmo_timer_pending(struct osmo_timer_list *ti)
|
||||
{
|
||||
if (!ti->linked)
|
||||
return 0;
|
||||
return (ti->timeout != 0);
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
|
||||
struct timer {
|
||||
struct timer *next;
|
||||
int linked; /* set is timer is initialized and linked */
|
||||
double duration;
|
||||
double timeout;
|
||||
void (*cb)(void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
double get_time(void);
|
||||
void timer_init(struct timer *timer, void (*fn)(void *data), void *priv);
|
||||
void timer_exit(struct timer *timer);
|
||||
void timer_start(struct timer *timer, double duration);
|
||||
void timer_stop(struct timer *timer);
|
||||
int timer_running(struct timer *timer);
|
||||
double process_timer(void);
|
||||
|
||||
#define osmo_timer_list timer
|
||||
void osmo_timer_schedule(struct osmo_timer_list *ti, time_t sec, long usec);
|
||||
void osmo_timer_del(struct osmo_timer_list *ti);
|
||||
int osmo_timer_pending(struct osmo_timer_list *ti);
|
||||
|
|
@ -25,7 +25,7 @@
|
|||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include "../libsample/sample.h"
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../liblogging/logging.h"
|
||||
#include "wave.h"
|
||||
|
||||
/* NOTE: No locking required for writing and reading buffer pointers, since 'int' is atomic on >=32 bit machines */
|
||||
|
@ -52,7 +52,7 @@ static void *record_child(void *arg)
|
|||
/* quit on error */
|
||||
if (len < 0) {
|
||||
error:
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "Failed to write to recording WAVE file! (errno %d)\n", errno);
|
||||
LOGP(DWAVE, LOGL_ERROR, "Failed to write to recording WAVE file! (errno %d)\n", errno);
|
||||
rec->finish = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -88,7 +88,7 @@ static void *playback_child(void *arg)
|
|||
len = fread(play->buffer + play->buffer_writep, 1, to_read, play->fp);
|
||||
/* quit on error */
|
||||
if (len < 0) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "Failed to read from playback WAVE file! (errno %d)\n", errno);
|
||||
LOGP(DWAVE, LOGL_ERROR, "Failed to read from playback WAVE file! (errno %d)\n", errno);
|
||||
play->finish = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ int wave_create_record(wave_rec_t *rec, const char *filename, int samplerate, in
|
|||
|
||||
rec->fp = fopen(filename, "w");
|
||||
if (!rec->fp) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "Failed to open recording file '%s'! (errno %d)\n", filename, errno);
|
||||
LOGP(DWAVE, LOGL_ERROR, "Failed to open recording file '%s'! (errno %d)\n", filename, errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
|
@ -139,18 +139,18 @@ int wave_create_record(wave_rec_t *rec, const char *filename, int samplerate, in
|
|||
rec->buffer_size = samplerate * 2 * channels;
|
||||
rec->buffer = calloc(rec->buffer_size, 1);
|
||||
if (!rec->buffer) {
|
||||
PDEBUG(DWAVE, DEBUG_NOTICE, "No mem!\n");
|
||||
LOGP(DWAVE, LOGL_NOTICE, "No mem!\n");
|
||||
rc = ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = pthread_create(&rec->tid, NULL, record_child, rec);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "Failed to create thread to record WAVE file! (errno %d)\n", errno);
|
||||
LOGP(DWAVE, LOGL_ERROR, "Failed to create thread to record WAVE file! (errno %d)\n", errno);
|
||||
goto error;
|
||||
}
|
||||
|
||||
PDEBUG(DWAVE, DEBUG_NOTICE, "*** Writing WAVE file to %s.\n", filename);
|
||||
LOGP(DWAVE, LOGL_NOTICE, "*** Writing WAVE file to %s.\n", filename);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -180,50 +180,50 @@ int wave_create_playback(wave_play_t *play, const char *filename, int *samplerat
|
|||
|
||||
play->fp = fopen(filename, "r");
|
||||
if (!play->fp) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "Failed to open playback file '%s'! (errno %d)\n", filename, errno);
|
||||
LOGP(DWAVE, LOGL_ERROR, "Failed to open playback file '%s'! (errno %d)\n", filename, errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
len = fread(buffer, 1, 12, play->fp);
|
||||
if (len != 12) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "Failed to read RIFF header!\n");
|
||||
LOGP(DWAVE, LOGL_ERROR, "Failed to read RIFF header!\n");
|
||||
rc = -EIO;
|
||||
goto error;
|
||||
}
|
||||
if (!!strncmp((char *)buffer, "RIFF", 4)) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "Missing RIFF header, seems that this is no WAVE file!\n");
|
||||
LOGP(DWAVE, LOGL_ERROR, "Missing RIFF header, seems that this is no WAVE file!\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
size = buffer[4] + (buffer[5] << 8) + (buffer[6] << 16) + (buffer[7] << 24);
|
||||
if (!!strncmp((char *)buffer + 8, "WAVE", 4)) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "Missing WAVE header, seems that this is no WAVE file!\n");
|
||||
LOGP(DWAVE, LOGL_ERROR, "Missing WAVE header, seems that this is no WAVE file!\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
size -= 4;
|
||||
while (size) {
|
||||
if (size < 8) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "Short read of WAVE file!\n");
|
||||
LOGP(DWAVE, LOGL_ERROR, "Short read of WAVE file!\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
len = fread(buffer, 1, 8, play->fp);
|
||||
if (len != 8) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "Failed to read chunk of WAVE file!\n");
|
||||
LOGP(DWAVE, LOGL_ERROR, "Failed to read chunk of WAVE file!\n");
|
||||
rc = -EIO;
|
||||
goto error;
|
||||
}
|
||||
chunk = buffer[4] + (buffer[5] << 8) + (buffer[6] << 16) + (buffer[7] << 24);
|
||||
size -= 8 + chunk;
|
||||
if (size < 0) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "WAVE error: Chunk '%c%c%c%c' overflows file size!\n", buffer[4], buffer[5], buffer[6], buffer[7]);
|
||||
LOGP(DWAVE, LOGL_ERROR, "WAVE error: Chunk '%c%c%c%c' overflows file size!\n", buffer[4], buffer[5], buffer[6], buffer[7]);
|
||||
rc = -EIO;
|
||||
goto error;
|
||||
}
|
||||
if (!strncmp((char *)buffer, "fmt ", 4)) {
|
||||
if (chunk < 16 || chunk > (int)sizeof(buffer)) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "WAVE error: Short or corrupt 'fmt' chunk!\n");
|
||||
LOGP(DWAVE, LOGL_ERROR, "WAVE error: Short or corrupt 'fmt' chunk!\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ int wave_create_playback(wave_play_t *play, const char *filename, int *samplerat
|
|||
} else
|
||||
if (!strncmp((char *)buffer, "data", 4)) {
|
||||
if (!gotfmt) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "WAVE error: 'data' without 'fmt' chunk!\n");
|
||||
LOGP(DWAVE, LOGL_ERROR, "WAVE error: 'data' without 'fmt' chunk!\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
@ -255,42 +255,42 @@ int wave_create_playback(wave_play_t *play, const char *filename, int *samplerat
|
|||
}
|
||||
|
||||
if (!gotfmt || !gotdata) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "WAVE error: Missing 'data' or 'fmt' chunk!\n");
|
||||
LOGP(DWAVE, LOGL_ERROR, "WAVE error: Missing 'data' or 'fmt' chunk!\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (fmt.format != 1) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "WAVE error: We support only PCM files!\n");
|
||||
LOGP(DWAVE, LOGL_ERROR, "WAVE error: We support only PCM files!\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
if (*channels_p == 0)
|
||||
*channels_p = fmt.channels;
|
||||
if (fmt.channels != *channels_p) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "WAVE error: We expect %d cannel(s), but wave file only has %d channel(s)\n", *channels_p, fmt.channels);
|
||||
LOGP(DWAVE, LOGL_ERROR, "WAVE error: We expect %d cannel(s), but wave file only has %d channel(s)\n", *channels_p, fmt.channels);
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
if (*samplerate_p == 0)
|
||||
*samplerate_p = fmt.sample_rate;
|
||||
if ((int)fmt.sample_rate != *samplerate_p) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "WAVE error: The WAVE file's sample rate (%d) does not match our sample rate (%d)!\n", fmt.sample_rate, *samplerate_p);
|
||||
LOGP(DWAVE, LOGL_ERROR, "WAVE error: The WAVE file's sample rate (%d) does not match our sample rate (%d)!\n", fmt.sample_rate, *samplerate_p);
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
if ((int)fmt.data_rate != 2 * *channels_p * *samplerate_p) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "WAVE error: The WAVE file's data rate is only %d bytes per second, but we expect %d bytes per second (2 bytes per sample * channels * samplerate)!\n", fmt.data_rate, 2 * *channels_p * *samplerate_p);
|
||||
LOGP(DWAVE, LOGL_ERROR, "WAVE error: The WAVE file's data rate is only %d bytes per second, but we expect %d bytes per second (2 bytes per sample * channels * samplerate)!\n", fmt.data_rate, 2 * *channels_p * *samplerate_p);
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
if (fmt.bytes_sample != 2 * *channels_p) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "WAVE error: The WAVE file's bytes per sample is only %d, but we expect %d bytes sample (2 bytes per sample * channels)!\n", fmt.bytes_sample, 2 * *channels_p);
|
||||
LOGP(DWAVE, LOGL_ERROR, "WAVE error: The WAVE file's bytes per sample is only %d, but we expect %d bytes sample (2 bytes per sample * channels)!\n", fmt.bytes_sample, 2 * *channels_p);
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
if (fmt.bits_sample != 16) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "WAVE error: We support only 16 bit files!\n");
|
||||
LOGP(DWAVE, LOGL_ERROR, "WAVE error: We support only 16 bit files!\n");
|
||||
rc = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
@ -301,18 +301,18 @@ int wave_create_playback(wave_play_t *play, const char *filename, int *samplerat
|
|||
play->buffer_size = *samplerate_p * 2 * *channels_p;
|
||||
play->buffer = calloc(play->buffer_size, 1);
|
||||
if (!play->buffer) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "No mem!\n");
|
||||
LOGP(DWAVE, LOGL_ERROR, "No mem!\n");
|
||||
rc = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = pthread_create(&play->tid, NULL, playback_child, play);
|
||||
if (rc < 0) {
|
||||
PDEBUG(DWAVE, DEBUG_ERROR, "Failed to create thread to playback WAVE file! (errno %d)\n", errno);
|
||||
LOGP(DWAVE, LOGL_ERROR, "Failed to create thread to playback WAVE file! (errno %d)\n", errno);
|
||||
goto error;
|
||||
}
|
||||
|
||||
PDEBUG(DWAVE, DEBUG_NOTICE, "*** Reading WAVE file from %s.\n", filename);
|
||||
LOGP(DWAVE, LOGL_NOTICE, "*** Reading WAVE file from %s.\n", filename);
|
||||
|
||||
return 0;
|
||||
|
||||
|
@ -344,7 +344,7 @@ int wave_write(wave_rec_t *rec, sample_t **samples, int length)
|
|||
to_write = (rec->buffer_size + rec->buffer_readp - rec->buffer_writep - 1) % rec->buffer_size;
|
||||
to_write /= 2 * rec->channels;
|
||||
if (to_write < length)
|
||||
PDEBUG(DWAVE, DEBUG_NOTICE, "Record WAVE buffer overflow.\n");
|
||||
LOGP(DWAVE, LOGL_NOTICE, "Record WAVE buffer overflow.\n");
|
||||
else
|
||||
to_write = length;
|
||||
if (to_write == 0)
|
||||
|
@ -398,7 +398,7 @@ read_empty:
|
|||
|
||||
if (to_read == 0 && play->finish) {
|
||||
if (play->left) {
|
||||
PDEBUG(DWAVE, DEBUG_NOTICE, "*** Finished reading WAVE file. (short read)\n");
|
||||
LOGP(DWAVE, LOGL_NOTICE, "*** Finished reading WAVE file. (short read)\n");
|
||||
play->left = 0;
|
||||
}
|
||||
goto read_empty;
|
||||
|
@ -418,7 +418,7 @@ read_empty:
|
|||
play->left -= to_read;
|
||||
|
||||
if (!play->left)
|
||||
PDEBUG(DWAVE, DEBUG_NOTICE, "*** Finished reading WAVE file.\n");
|
||||
LOGP(DWAVE, LOGL_NOTICE, "*** Finished reading WAVE file.\n");
|
||||
|
||||
if (to_read < length)
|
||||
goto read_empty;
|
||||
|
@ -499,7 +499,7 @@ void wave_destroy_record(wave_rec_t *rec)
|
|||
fclose(rec->fp);
|
||||
rec->fp = NULL;
|
||||
|
||||
PDEBUG(DWAVE, DEBUG_NOTICE, "*** WAVE file written.\n");
|
||||
LOGP(DWAVE, LOGL_NOTICE, "*** WAVE file written.\n");
|
||||
}
|
||||
|
||||
void wave_destroy_playback(wave_play_t *play)
|
||||
|
|
|
@ -12,19 +12,17 @@ osmo_cc_router_SOURCES = \
|
|||
|
||||
osmo_cc_router_LDADD = \
|
||||
$(COMMON_LA) \
|
||||
../libdebug/libdebug.a \
|
||||
../liboptions/liboptions.a \
|
||||
../libsample/libsample.a \
|
||||
../libtimer/libtimer.a \
|
||||
../libselect/libselect.a \
|
||||
../libjitter/libjitter.a \
|
||||
../libosmocc/libosmocc.a \
|
||||
../libg711/libg711.a \
|
||||
../libdtmf/libdtmf.a \
|
||||
../libfm/libfm.a \
|
||||
../libfilter/libfilter.a \
|
||||
../libsendevolumenregler/libsendevolumenregler.a \
|
||||
../libwave/libwave.a
|
||||
../libwave/libwave.a \
|
||||
../liblogging/liblogging.a \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOCC_LIBS)
|
||||
|
||||
if HAVE_GSM
|
||||
AM_CPPFLAGS += -DHAVE_GSM
|
||||
|
|
|
@ -100,7 +100,10 @@
|
|||
#include <math.h>
|
||||
#include <sys/types.h>
|
||||
#include <arpa/inet.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/cc/session.h>
|
||||
#include <osmocom/cc/rtp.h>
|
||||
#include "../liblogging/logging.h"
|
||||
#include "call.h"
|
||||
#include "audio.h"
|
||||
|
||||
|
@ -193,7 +196,7 @@ void receive_terminator(struct osmo_cc_session_codec *codec, uint8_t __attribute
|
|||
return;
|
||||
|
||||
if (codec->decoder == decode_te) {
|
||||
PDEBUG(DROUTER, DEBUG_NOTICE, "Ignoring received telephony-events from terminator.\n");
|
||||
LOGP(DROUTER, LOGL_NOTICE, "Ignoring received telephony-events from terminator.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,8 +70,8 @@
|
|||
#include <math.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../libg711/g711.h"
|
||||
#include "../liblogging/logging.h"
|
||||
#include <osmocom/cc/g711.h>
|
||||
#include "call.h"
|
||||
#include "audio.h"
|
||||
#include "display.h"
|
||||
|
@ -80,7 +80,7 @@
|
|||
#endif
|
||||
|
||||
call_t *call_list = NULL;
|
||||
struct timer status_timer;
|
||||
struct osmo_timer_list status_timer;
|
||||
static osmo_cc_endpoint_t *cc_ep_list[3];
|
||||
static const char *routing_script, *routing_shell;
|
||||
|
||||
|
@ -162,7 +162,7 @@ static void status_timeout(void __attribute__((unused)) *data)
|
|||
status_needs_update = 0;
|
||||
}
|
||||
|
||||
timer_start(&status_timer, 0.1);
|
||||
osmo_timer_schedule(&status_timer, 0,100000);
|
||||
}
|
||||
|
||||
static const char *state_names[] = {
|
||||
|
@ -178,8 +178,8 @@ static const char *state_names[] = {
|
|||
|
||||
static void new_state(call_t *call, enum call_state state)
|
||||
{
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "(call #%d) Changing state %s -> %s.\n", call->num, state_names[call->state], state_names[state]);
|
||||
call->state = state;
|
||||
LOGP(DROUTER, LOGL_DEBUG, "(call #%d) Changing state %s -> %s.\n", call->num, state_names[call->state], state_names[state]);
|
||||
call->state = state;
|
||||
}
|
||||
|
||||
static const char *relation_name(call_relation_t *relation)
|
||||
|
@ -202,7 +202,7 @@ static call_t *call_create(void)
|
|||
|
||||
call = calloc(1, sizeof(*call));
|
||||
if (!call) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "No memory!\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "No memory!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
|
@ -223,7 +223,7 @@ static call_t *call_create(void)
|
|||
call_p = &((*call_p)->next);
|
||||
*call_p = call;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "(call #%d) Created new call instance.\n", call->num);
|
||||
LOGP(DROUTER, LOGL_DEBUG, "(call #%d) Created new call instance.\n", call->num);
|
||||
|
||||
return call;
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ static void call_destroy(call_t *call)
|
|||
}
|
||||
*call_p = call->next;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "(call #%d) Destroyed call instance.\n", call->num);
|
||||
LOGP(DROUTER, LOGL_DEBUG, "(call #%d) Destroyed call instance.\n", call->num);
|
||||
|
||||
free(call);
|
||||
}
|
||||
|
@ -275,7 +275,7 @@ static call_relation_t *relation_create(call_t *call)
|
|||
|
||||
relation = calloc(1, sizeof(*relation));
|
||||
if (!relation) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "No memory!\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "No memory!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
|
@ -293,7 +293,7 @@ static call_relation_t *relation_create(call_t *call)
|
|||
gsm_fr_create(relation);
|
||||
#endif
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s Created new endpoint relation instance.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s Created new endpoint relation instance.\n", relation_name(relation));
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
@ -328,7 +328,7 @@ static void relation_destroy(call_relation_t *relation)
|
|||
gsm_fr_destroy(relation);
|
||||
#endif
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s Destroyed endpoint relation instance.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s Destroyed endpoint relation instance.\n", relation_name(relation));
|
||||
|
||||
free(relation);
|
||||
|
||||
|
@ -343,14 +343,14 @@ int call_init(osmo_cc_endpoint_t *cc_ep1, osmo_cc_endpoint_t *cc_ep2, const char
|
|||
cc_ep_list[2] = NULL;
|
||||
routing_script = _routing_script;
|
||||
routing_shell = _routing_shell;
|
||||
timer_init(&status_timer, status_timeout, NULL);
|
||||
osmo_timer_setup(&status_timer, status_timeout, NULL);
|
||||
status_timeout(NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void call_exit(void)
|
||||
{
|
||||
timer_exit(&status_timer);
|
||||
osmo_timer_del(&status_timer);
|
||||
|
||||
/* destroy all calls */
|
||||
while (call_list)
|
||||
|
@ -374,7 +374,7 @@ int call_handle(void)
|
|||
|
||||
/* eat zombies */
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Script child %d terminated.\n", pid);
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Script child %d terminated.\n", pid);
|
||||
for (call = call_list; call; call = call->next) {
|
||||
if (call->routing.script_pid == pid) {
|
||||
/* tell routing that script has terminated */
|
||||
|
@ -406,7 +406,7 @@ static void proxy_send_sdp_answer(call_relation_t *relation, osmo_cc_msg_t *msg)
|
|||
if (msg->type != OSMO_CC_MSG_SETUP_CNF
|
||||
&& (msg->type == OSMO_CC_MSG_DISC_IND || !relation->codec_negotiated)) {
|
||||
/* process */
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Sending progress indicator 8 to allow audio.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Sending progress indicator 8 to allow audio.\n");
|
||||
osmo_cc_add_ie_progress(msg, OSMO_CC_CODING_ITU_T, OSMO_CC_LOCATION_BEYOND_INTERWORKING, OSMO_CC_PROGRESS_INBAND_INFO_AVAILABLE);
|
||||
}
|
||||
|
||||
|
@ -415,12 +415,12 @@ static void proxy_send_sdp_answer(call_relation_t *relation, osmo_cc_msg_t *msg)
|
|||
sdp = osmo_cc_helper_audio_accept_te(&relation->cc_ep->session_config, relation, relation->orig_codecs, receive_originator, relation->call->setup_msg, &relation->cc_session, &relation->codec, &relation->telephone_event, 0);
|
||||
if (sdp) {
|
||||
relation->codec_negotiated = 1;
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Sending SDP answer to originator with supported codecs.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Sending SDP answer to originator with supported codecs.\n");
|
||||
/* SDP */
|
||||
osmo_cc_add_ie_sdp(msg, sdp);
|
||||
} else {
|
||||
relation->call->rtp_proxy = 0;
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Originator's setup message does not contain a codec we support, cannot use RTP-Proxy!\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "Originator's setup message does not contain a codec we support, cannot use RTP-Proxy!\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -442,7 +442,7 @@ static void orig_setup(osmo_cc_endpoint_t *cc_ep, uint32_t callref, osmo_cc_msg_
|
|||
/* creating call instance, transparent until setup with hdlc */
|
||||
call = call_create();
|
||||
if (!call) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Cannot create calll instance.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "Cannot create calll instance.\n");
|
||||
abort();
|
||||
}
|
||||
relation = relation_create(call);
|
||||
|
@ -451,7 +451,7 @@ static void orig_setup(osmo_cc_endpoint_t *cc_ep, uint32_t callref, osmo_cc_msg_
|
|||
relation->cc_ep = cc_ep;
|
||||
relation->cc_callref = callref;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-SETUP-REQ from originator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-SETUP-REQ from originator.\n", relation_name(relation));
|
||||
|
||||
/* store setup message in call structure */
|
||||
call->setup_msg = osmo_cc_clone_msg(old_msg);
|
||||
|
@ -511,7 +511,7 @@ static void orig_info(call_t *call, osmo_cc_msg_t *old_msg)
|
|||
int rc;
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-INFO-REQ from originator.\n", relation_name(call->relation_list));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-INFO-REQ from originator.\n", relation_name(call->relation_list));
|
||||
|
||||
/* add called number to dial string */
|
||||
rc = osmo_cc_get_ie_called(old_msg, 0, &type, &plan, number, sizeof(number));
|
||||
|
@ -593,7 +593,7 @@ static void orig_disc(call_t *call, osmo_cc_msg_t *old_msg)
|
|||
osmo_cc_msg_t *new_msg;
|
||||
call_relation_t *relation;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-DISC-REQ from originator.\n", relation_name(call->relation_list));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-DISC-REQ from originator.\n", relation_name(call->relation_list));
|
||||
|
||||
/* stop routing, if originator hangs up */
|
||||
if (call->routing.routing) {
|
||||
|
@ -642,7 +642,7 @@ static void orig_rel(call_t *call, osmo_cc_msg_t *old_msg)
|
|||
osmo_cc_msg_t *new_msg;
|
||||
call_relation_t *relation;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-REL-REQ from originator.\n", relation_name(call->relation_list));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-REL-REQ from originator.\n", relation_name(call->relation_list));
|
||||
|
||||
/* stop routing, if originator hangs up */
|
||||
if (call->routing.routing) {
|
||||
|
@ -667,7 +667,7 @@ static void orig_other(call_t *call, osmo_cc_msg_t *old_msg)
|
|||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s Other CC message from originator.\n", relation_name(call->relation_list));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s Other CC message from originator.\n", relation_name(call->relation_list));
|
||||
|
||||
/* if there is one terminating call, forward the message */
|
||||
if (call->relation_list->next && !call->forking) {
|
||||
|
@ -687,7 +687,7 @@ static void term_progress(call_t *call, call_relation_t *relation, osmo_cc_msg_t
|
|||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-PROGRESS-REQ from terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-PROGRESS-REQ from terminator.\n", relation_name(relation));
|
||||
|
||||
/* if single call exists, forward progress to originator */
|
||||
if (!call->forking) {
|
||||
|
@ -705,7 +705,7 @@ static void term_overlap(call_t *call, call_relation_t *relation, osmo_cc_msg_t
|
|||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-SETUP-ACK-REQ from terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-SETUP-ACK-REQ from terminator.\n", relation_name(relation));
|
||||
|
||||
/* update call state for status display */
|
||||
relation->state = CALL_STATE_OVERLAP;
|
||||
|
@ -740,7 +740,7 @@ static void term_proc(call_t *call, call_relation_t *relation, osmo_cc_msg_t *ol
|
|||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-PROC-REQ from terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-PROC-REQ from terminator.\n", relation_name(relation));
|
||||
|
||||
/* update call state for status display */
|
||||
relation->state = CALL_STATE_PROCEEDING;
|
||||
|
@ -776,7 +776,7 @@ static void term_alert(call_t *call, call_relation_t *relation, osmo_cc_msg_t *o
|
|||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-ALERT-REQ from terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-ALERT-REQ from terminator.\n", relation_name(relation));
|
||||
|
||||
/* update call state for status display */
|
||||
relation->state = CALL_STATE_ALERTING;
|
||||
|
@ -815,7 +815,7 @@ static void term_connect(call_t *call, call_relation_t *relation, osmo_cc_msg_t
|
|||
char sdp[65536];
|
||||
int rc;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-SETUP-RSP (connect) from terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-SETUP-RSP (connect) from terminator.\n", relation_name(relation));
|
||||
|
||||
/* update call state for status display */
|
||||
relation->state = CALL_STATE_CONNECT;
|
||||
|
@ -830,7 +830,7 @@ static void term_connect(call_t *call, call_relation_t *relation, osmo_cc_msg_t
|
|||
&& call->state != CALL_STATE_OVERLAP
|
||||
&& call->state != CALL_STATE_PROCEEDING
|
||||
&& call->state != CALL_STATE_ALERTING) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Connect message from terminating call now allowed in state '%s'.\n", state_names[call->state]);
|
||||
LOGP(DROUTER, LOGL_ERROR, "Connect message from terminating call now allowed in state '%s'.\n", state_names[call->state]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -845,7 +845,7 @@ static void term_connect(call_t *call, call_relation_t *relation, osmo_cc_msg_t
|
|||
other = call->relation_list->next->next;
|
||||
else
|
||||
other = call->relation_list->next;
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Sending 'non-selected user clearing' to other terminator.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Sending 'non-selected user clearing' to other terminator.\n");
|
||||
/* send release message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||
osmo_cc_add_ie_cause(new_msg, other->cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NONSE_USER_CLR, 0, 0);
|
||||
|
@ -883,7 +883,7 @@ static void term_disc(call_t *call, call_relation_t *relation, osmo_cc_msg_t *ol
|
|||
uint8_t coding, location, progress;
|
||||
int rc;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-DISC-REQ from terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-DISC-REQ from terminator.\n", relation_name(relation));
|
||||
|
||||
/* update call state for status display */
|
||||
relation->state = CALL_STATE_DISC_FROM_TERM;
|
||||
|
@ -902,10 +902,10 @@ static void term_disc(call_t *call, call_relation_t *relation, osmo_cc_msg_t *ol
|
|||
uint16_t sip_cause;
|
||||
int rc;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Got a disconnect before connect.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Got a disconnect before connect.\n");
|
||||
/* if there is only one terminating call, forward that disconnect */
|
||||
if (!call->forking) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Call is not forking, so we directly forward this disconnect.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Call is not forking, so we directly forward this disconnect.\n");
|
||||
/* notify routing */
|
||||
if (call->routing.routing)
|
||||
routing_send(&call->routing, "call-disconnect");
|
||||
|
@ -923,7 +923,7 @@ static void term_disc(call_t *call, call_relation_t *relation, osmo_cc_msg_t *ol
|
|||
}
|
||||
|
||||
/* collect cause */
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Call is forking, so we collect ISDN cause and destroy relation.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Call is forking, so we collect ISDN cause and destroy relation.\n");
|
||||
rc = osmo_cc_get_ie_cause(old_msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
|
||||
if (rc >= 0)
|
||||
call->collect_cause = osmo_cc_collect_cause(call->collect_cause, isdn_cause);
|
||||
|
@ -938,7 +938,7 @@ static void term_disc(call_t *call, call_relation_t *relation, osmo_cc_msg_t *ol
|
|||
|
||||
/* if all terminating calls have been released, release the originator and destroy call */
|
||||
if (!call->relation_list->next) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "All terminators have disconnected, so we forward a release with the collected cause.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "All terminators have disconnected, so we forward a release with the collected cause.\n");
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||
osmo_cc_add_ie_cause(new_msg, call->relation_list->cc_ep->serving_location, call->collect_cause, 0, 0);
|
||||
osmo_cc_ll_msg(call->relation_list->cc_ep, call->relation_list->cc_callref, new_msg);
|
||||
|
@ -951,7 +951,7 @@ static void term_disc(call_t *call, call_relation_t *relation, osmo_cc_msg_t *ol
|
|||
/* this is connect or disconnect collision. the state implies that there is only one terminating call */
|
||||
if (call->state == CALL_STATE_CONNECT
|
||||
|| call->state == CALL_STATE_DISC_FROM_ORIG) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Got a disconnect after connect, so we directly forward this disconnect.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Got a disconnect after connect, so we directly forward this disconnect.\n");
|
||||
new_msg = osmo_cc_clone_msg(old_msg);
|
||||
new_msg->type = OSMO_CC_MSG_REL_IND;
|
||||
osmo_cc_ll_msg(call->relation_list->cc_ep, call->relation_list->cc_callref, new_msg);
|
||||
|
@ -963,7 +963,7 @@ static void term_disc(call_t *call, call_relation_t *relation, osmo_cc_msg_t *ol
|
|||
return;
|
||||
}
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Disconnect message from terminating call now allowed in state '%s'.\n", state_names[call->state]);
|
||||
LOGP(DROUTER, LOGL_ERROR, "Disconnect message from terminating call now allowed in state '%s'.\n", state_names[call->state]);
|
||||
}
|
||||
|
||||
/* rel is received from terminating side */
|
||||
|
@ -971,7 +971,7 @@ static void term_rel(call_t *call, call_relation_t *relation, osmo_cc_msg_t *old
|
|||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-REL-REQ from terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-REL-REQ from terminator.\n", relation_name(relation));
|
||||
|
||||
/* if we have not yet connected a call */
|
||||
if (call->state == CALL_STATE_SETUP
|
||||
|
@ -982,10 +982,10 @@ static void term_rel(call_t *call, call_relation_t *relation, osmo_cc_msg_t *old
|
|||
uint16_t sip_cause;
|
||||
int rc;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Got a release before connect.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Got a release before connect.\n");
|
||||
/* if there is only one terminating call, forward that disconnect */
|
||||
if (!call->forking) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Call is not forking, so we directly forward this release.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Call is not forking, so we directly forward this release.\n");
|
||||
/* forward message to originator */
|
||||
new_msg = osmo_cc_clone_msg(old_msg);
|
||||
new_msg->type = OSMO_CC_MSG_REL_IND;
|
||||
|
@ -1000,7 +1000,7 @@ static void term_rel(call_t *call, call_relation_t *relation, osmo_cc_msg_t *old
|
|||
}
|
||||
|
||||
/* collect cause */
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Call is forking, so we collect ISDN cause and destroy relation.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Call is forking, so we collect ISDN cause and destroy relation.\n");
|
||||
rc = osmo_cc_get_ie_cause(old_msg, 0, &location, &isdn_cause, &sip_cause, &socket_cause);
|
||||
if (rc >= 0)
|
||||
call->collect_cause = osmo_cc_collect_cause(call->collect_cause, isdn_cause);
|
||||
|
@ -1015,7 +1015,7 @@ static void term_rel(call_t *call, call_relation_t *relation, osmo_cc_msg_t *old
|
|||
|
||||
/* if all terminating calls have been released, release the originator and destroy call */
|
||||
if (!call->relation_list->next) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "All terminators have released, so we forward it with the collected cause.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "All terminators have released, so we forward it with the collected cause.\n");
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||
osmo_cc_add_ie_cause(new_msg, call->relation_list->cc_ep->serving_location, call->collect_cause, 0, 0);
|
||||
osmo_cc_ll_msg(call->relation_list->cc_ep, call->relation_list->cc_callref, new_msg);
|
||||
|
@ -1028,7 +1028,7 @@ static void term_rel(call_t *call, call_relation_t *relation, osmo_cc_msg_t *old
|
|||
/* forward release to originator and confirm to terminator. the state implies that there is only one terminating call */
|
||||
if (call->state == CALL_STATE_CONNECT
|
||||
|| call->state == CALL_STATE_DISC_FROM_ORIG) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Got a release after connect, so we directly forward this release.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Got a release after connect, so we directly forward this release.\n");
|
||||
new_msg = osmo_cc_clone_msg(old_msg);
|
||||
new_msg->type = OSMO_CC_MSG_REL_IND;
|
||||
osmo_cc_ll_msg(call->relation_list->cc_ep, call->relation_list->cc_callref, new_msg);
|
||||
|
@ -1040,7 +1040,7 @@ static void term_rel(call_t *call, call_relation_t *relation, osmo_cc_msg_t *old
|
|||
return;
|
||||
}
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Release message from terminating call now allowed in state '%s'.\n", state_names[call->state]);
|
||||
LOGP(DROUTER, LOGL_ERROR, "Release message from terminating call now allowed in state '%s'.\n", state_names[call->state]);
|
||||
}
|
||||
|
||||
/* other message is received from terminating side */
|
||||
|
@ -1048,7 +1048,7 @@ static void term_other(call_t *call, call_relation_t *relation, osmo_cc_msg_t *o
|
|||
{
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s Other CC message from terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s Other CC message from terminator.\n", relation_name(relation));
|
||||
|
||||
/* if there is one terminating call, forward the message */
|
||||
if (!call->relation_list->next->next) {
|
||||
|
@ -1084,7 +1084,7 @@ void cc_message(osmo_cc_endpoint_t *cc_ep, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
/* process SETUP (new call) */
|
||||
if (!call) {
|
||||
if (msg->type != OSMO_CC_MSG_SETUP_REQ) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Received message without call instance, please fix!\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "Received message without call instance, please fix!\n");
|
||||
return;
|
||||
}
|
||||
orig_setup(cc_ep, callref, msg);
|
||||
|
@ -1230,17 +1230,17 @@ static void add_codecs_by_names(struct osmo_cc_helper_audio_codecs *codecs, char
|
|||
for (i = 0; codecs_def[i].payload_name; i++) {
|
||||
if (!strcasecmp(name, codecs_def[i].payload_name)) {
|
||||
if (c == MAX_CODECS) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Maximum of %d codecs are reached. Ignoring codec '%s'.\n", c, name);
|
||||
LOGP(DROUTER, LOGL_ERROR, "Maximum of %d codecs are reached. Ignoring codec '%s'.\n", c, name);
|
||||
break;
|
||||
}
|
||||
memcpy(&codecs[c], &codecs_def[i], sizeof(*codecs));
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "Adding given codec '%s'.\n", codecs[c].payload_name);
|
||||
LOGP(DROUTER, LOGL_INFO, "Adding given codec '%s'.\n", codecs[c].payload_name);
|
||||
c++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!codecs_def[i].payload_name) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Given codec '%s' not supported!\n", name);
|
||||
LOGP(DROUTER, LOGL_ERROR, "Given codec '%s' not supported!\n", name);
|
||||
}
|
||||
}
|
||||
memset(&codecs[c], 0, sizeof(*codecs));
|
||||
|
@ -1289,12 +1289,12 @@ static int parse_params(int argc, char *argv[], struct command_def *command_def)
|
|||
}
|
||||
}
|
||||
if (!param_def[j].n) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Unknown '%s' parameter '%s' from routing.\n", command_def->name, argv[i]);
|
||||
LOGP(DROUTER, LOGL_ERROR, "Unknown '%s' parameter '%s' from routing.\n", command_def->name, argv[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg[0] && param_def[j].no_value) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'%s' parameter '%s' has no value, ignoring.\n", command_def->name, argv[i]);
|
||||
LOGP(DROUTER, LOGL_ERROR, "'%s' parameter '%s' has no value, ignoring.\n", command_def->name, argv[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -1312,12 +1312,12 @@ static int parse_params(int argc, char *argv[], struct command_def *command_def)
|
|||
if (param_def[j].s)
|
||||
*param_def[j].s = arg;
|
||||
if (param_def[j].o) {
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "Originating codecs given: '%s'.\n", arg);
|
||||
LOGP(DROUTER, LOGL_INFO, "Originating codecs given: '%s'.\n", arg);
|
||||
add_codecs_by_names(param_def[j].o, arg);
|
||||
param.orig_given = 1;
|
||||
}
|
||||
if (param_def[j].t) {
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "Terminating codecs given: '%s'.\n", arg);
|
||||
LOGP(DROUTER, LOGL_INFO, "Terminating codecs given: '%s'.\n", arg);
|
||||
add_codecs_by_names(param_def[j].t, arg);
|
||||
param.term_given = 1;
|
||||
}
|
||||
|
@ -1339,7 +1339,7 @@ static void routing_rtp_proxy(call_t *call, int argc, char *argv[], struct comma
|
|||
|
||||
/* ignore, if already enabled */
|
||||
if (call->rtp_proxy) {
|
||||
PDEBUG(DROUTER, DEBUG_NOTICE, "RTP proxy is already enabled!.\n");
|
||||
LOGP(DROUTER, LOGL_NOTICE, "RTP proxy is already enabled!.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1350,7 +1350,7 @@ static void routing_rtp_proxy(call_t *call, int argc, char *argv[], struct comma
|
|||
memcpy(&relation->orig_codecs[1], &codecs_def[1], sizeof(relation->orig_codecs[1]));
|
||||
memcpy(&relation->orig_codecs[2], &codecs_def[2], sizeof(relation->orig_codecs[2]));
|
||||
memset(&relation->orig_codecs[3], 0, sizeof(relation->orig_codecs[3]));
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "No originating codeds given, using default '%s' and '%s' and '%s'.\n", relation->orig_codecs[0].payload_name, relation->orig_codecs[1].payload_name, relation->orig_codecs[2].payload_name);
|
||||
LOGP(DROUTER, LOGL_INFO, "No originating codeds given, using default '%s' and '%s' and '%s'.\n", relation->orig_codecs[0].payload_name, relation->orig_codecs[1].payload_name, relation->orig_codecs[2].payload_name);
|
||||
} else
|
||||
memcpy(relation->orig_codecs, param.orig_codecs, sizeof(param.orig_codecs));
|
||||
|
||||
|
@ -1359,19 +1359,19 @@ static void routing_rtp_proxy(call_t *call, int argc, char *argv[], struct comma
|
|||
memcpy(&relation->term_codecs[1], &codecs_def[1], sizeof(relation->term_codecs[1]));
|
||||
memcpy(&relation->term_codecs[2], &codecs_def[2], sizeof(relation->term_codecs[2]));
|
||||
memset(&relation->term_codecs[3], 0, sizeof(relation->term_codecs[3]));
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "No terminating codeds given, using default '%s' and '%s' and '%s'.\n", relation->term_codecs[0].payload_name, relation->term_codecs[1].payload_name, relation->term_codecs[2].payload_name);
|
||||
LOGP(DROUTER, LOGL_INFO, "No terminating codeds given, using default '%s' and '%s' and '%s'.\n", relation->term_codecs[0].payload_name, relation->term_codecs[1].payload_name, relation->term_codecs[2].payload_name);
|
||||
} else
|
||||
memcpy(relation->term_codecs, param.term_codecs, sizeof(param.term_codecs));
|
||||
|
||||
/* error, if we already negotiated */
|
||||
if (call->sdp_forwarded) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "RTP-Proxy cannot be enabled now, because we already forwarded a call.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "RTP-Proxy cannot be enabled now, because we already forwarded a call.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* no SDP */
|
||||
if (!relation->sdp) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Originator's setup message does not contain SDP, please fix!.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "Originator's setup message does not contain SDP, please fix!.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1395,14 +1395,14 @@ static void routing_play(call_t *call, int argc, char *argv[], struct command_de
|
|||
wave_destroy_playback(&call->play);
|
||||
|
||||
if (!call->rtp_proxy) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "RTP-Proxy must be enabled to play a file!.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "RTP-Proxy must be enabled to play a file!.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
parse_params(argc, argv, command_def);
|
||||
|
||||
if (!param.filename) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'play' command reqires a file name as parameter.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "'play' command reqires a file name as parameter.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1419,7 +1419,7 @@ static void routing_play(call_t *call, int argc, char *argv[], struct command_de
|
|||
|
||||
if (channels != 1 && channels != 2) {
|
||||
wave_destroy_playback(&call->play);
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'play' command reqires a wave file that has 1 or 2 channels only.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "'play' command reqires a wave file that has 1 or 2 channels only.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1448,14 +1448,14 @@ static void routing_record(call_t *call, int argc, char *argv[], struct command_
|
|||
wave_destroy_record(&call->rec);
|
||||
|
||||
if (!call->rtp_proxy) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "RTP-Proxy must be enabled to record a file!.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "RTP-Proxy must be enabled to record a file!.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
parse_params(argc, argv, command_def);
|
||||
|
||||
if (!param.filename) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'record' command reqires a file name as parameter.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "'record' command reqires a file name as parameter.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1482,14 +1482,14 @@ struct param_def param_gain[] = {
|
|||
static void routing_tx_gain(call_t *call, int argc, char *argv[], struct command_def *command_def)
|
||||
{
|
||||
if (!call->rtp_proxy) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "RTP-Proxy must be enabled to record a file!\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "RTP-Proxy must be enabled to record a file!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
parse_params(argc, argv, command_def);
|
||||
|
||||
if (!param.gain) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'tx-gain' command reqires a gain value as parameter.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "'tx-gain' command reqires a gain value as parameter.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1499,14 +1499,14 @@ static void routing_tx_gain(call_t *call, int argc, char *argv[], struct command
|
|||
static void routing_rx_gain(call_t *call, int argc, char *argv[], struct command_def *command_def)
|
||||
{
|
||||
if (!call->rtp_proxy) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "RTP-Proxy must be enabled to record a file!\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "RTP-Proxy must be enabled to record a file!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
parse_params(argc, argv, command_def);
|
||||
|
||||
if (!param.gain) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'rx-gain' command reqires a gain value as parameter.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "'rx-gain' command reqires a gain value as parameter.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1529,7 +1529,7 @@ struct param_def param_compress[] = {
|
|||
static void routing_tx_compress(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def)
|
||||
{
|
||||
if (!call->rtp_proxy) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "RTP-Proxy must be enabled to record a file!\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "RTP-Proxy must be enabled to record a file!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1542,7 +1542,7 @@ static void routing_tx_compress(call_t *call, int __attribute__((unused)) argc,
|
|||
static void routing_rx_compress(call_t *call, int __attribute__((unused)) argc, char __attribute__((unused)) *argv[], struct command_def __attribute__((unused)) *command_def)
|
||||
{
|
||||
if (!call->rtp_proxy) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "RTP-Proxy must be enabled to record a file!\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "RTP-Proxy must be enabled to record a file!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1596,7 +1596,7 @@ static void routing_call(call_t *call, int argc, char *argv[], struct command_de
|
|||
|
||||
/* if we have a call, we don't add more terminators */
|
||||
if (call->relation_list->next) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Multiple call commands from routing are not allowed.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "Multiple call commands from routing are not allowed.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1613,7 +1613,7 @@ next_call:
|
|||
argc = 0;
|
||||
|
||||
if (!param.interface) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'call' command without 'interface' parameter.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "'call' command without 'interface' parameter.\n");
|
||||
call->collect_cause = osmo_cc_collect_cause(call->collect_cause, OSMO_CC_ISDN_CAUSE_NETWORK_OOO);
|
||||
goto try_next;
|
||||
}
|
||||
|
@ -1623,7 +1623,7 @@ next_call:
|
|||
break;
|
||||
}
|
||||
if (!att) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'call' command with 'interface' parameter '%s' which is not attached.\n", param.interface);
|
||||
LOGP(DROUTER, LOGL_ERROR, "'call' command with 'interface' parameter '%s' which is not attached.\n", param.interface);
|
||||
call->collect_cause = osmo_cc_collect_cause(call->collect_cause, OSMO_CC_ISDN_CAUSE_DEST_OOO);
|
||||
goto try_next;
|
||||
}
|
||||
|
@ -1643,7 +1643,7 @@ next_call:
|
|||
relation->cc_ep = att->ep;
|
||||
relation->cc_callref = cc_call->callref;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-SETUP-IND to terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-SETUP-IND to terminator.\n", relation_name(relation));
|
||||
|
||||
/* create setup message */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_IND);
|
||||
|
@ -1778,7 +1778,7 @@ next_call:
|
|||
|
||||
/* only if RTP-Proxy is used */
|
||||
if (call->rtp_proxy) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Sending our codecs to the terminator.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Sending our codecs to the terminator.\n");
|
||||
relation->cc_session = osmo_cc_helper_audio_offer(&relation->cc_ep->session_config, relation, call->relation_list->term_codecs, receive_terminator, new_msg, 1);
|
||||
} else
|
||||
/* sdp from originator's setup message */
|
||||
|
@ -1806,7 +1806,7 @@ try_next:
|
|||
|
||||
/* if there is no terminator (interface not attached), release the originator and destroy call */
|
||||
if (!call->relation_list->next) {
|
||||
PDEBUG(DROUTER, DEBUG_NOTICE, "No interace to be called to, so we forward a release with the collected cause.\n");
|
||||
LOGP(DROUTER, LOGL_NOTICE, "No interace to be called to, so we forward a release with the collected cause.\n");
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||
osmo_cc_add_ie_cause(new_msg, call->relation_list->cc_ep->serving_location, call->collect_cause, 0, 0);
|
||||
osmo_cc_ll_msg(call->relation_list->cc_ep, call->relation_list->cc_callref, new_msg);
|
||||
|
@ -1824,7 +1824,7 @@ static void routing_call_stop(call_t *call, int __attribute__((unused)) argc, ch
|
|||
/* send message to all terminators, if any */
|
||||
while (call->relation_list->next) {
|
||||
relation = call->relation_list->next;
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-REL-IND to terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-REL-IND to terminator.\n", relation_name(relation));
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||
osmo_cc_add_ie_cause(new_msg, relation->cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR, 0, 0);
|
||||
osmo_cc_ll_msg(relation->cc_ep, relation->cc_callref, new_msg);
|
||||
|
@ -1843,7 +1843,7 @@ static void routing_overlap(call_t *call, int __attribute__((unused)) argc, char
|
|||
|
||||
new_state(call, CALL_STATE_OVERLAP);
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-SETUP-ACK-IND to originator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-SETUP-ACK-IND to originator.\n", relation_name(relation));
|
||||
|
||||
/* send message to originator */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_SETUP_ACK_IND);
|
||||
|
@ -1866,7 +1866,7 @@ static void routing_proceeding(call_t *call, int __attribute__((unused)) argc, c
|
|||
|
||||
new_state(call, CALL_STATE_PROCEEDING);
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-PROC-IND to originator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-PROC-IND to originator.\n", relation_name(relation));
|
||||
|
||||
/* send message to originator */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_PROC_IND);
|
||||
|
@ -1890,7 +1890,7 @@ static void routing_alerting(call_t *call, int __attribute__((unused)) argc, cha
|
|||
|
||||
new_state(call, CALL_STATE_ALERTING);
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-ALERT-IND to originator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-ALERT-IND to originator.\n", relation_name(relation));
|
||||
|
||||
/* send message to originator */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_ALERT_IND);
|
||||
|
@ -1913,7 +1913,7 @@ static void routing_answer(call_t *call, int __attribute__((unused)) argc, char
|
|||
&& call->state != CALL_STATE_ALERTING)
|
||||
return;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-SETUP-CNF to originator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-SETUP-CNF to originator.\n", relation_name(relation));
|
||||
|
||||
new_state(call, CALL_STATE_CONNECT);
|
||||
|
||||
|
@ -1940,7 +1940,7 @@ static void routing_disconnect(call_t *call, int argc, char *argv[], struct comm
|
|||
|
||||
parse_params(argc, argv, command_def);
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-DISC-IND to originator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-DISC-IND to originator.\n", relation_name(relation));
|
||||
|
||||
/* send message to originator */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_DISC_IND);
|
||||
|
@ -1952,7 +1952,7 @@ static void routing_disconnect(call_t *call, int argc, char *argv[], struct comm
|
|||
|
||||
/* send message to all terminators, if any */
|
||||
for (relation = relation->next; relation; relation = relation->next) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-REL-IND to terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-REL-IND to terminator.\n", relation_name(relation));
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||
/* add cause */
|
||||
osmo_cc_add_ie_cause(new_msg, relation->cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR, 0, 0);
|
||||
|
@ -1967,7 +1967,7 @@ static void routing_release(call_t *call, int argc, char *argv[], struct command
|
|||
|
||||
parse_params(argc, argv, command_def);
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-REL-IND to originator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-REL-IND to originator.\n", relation_name(relation));
|
||||
|
||||
/* send message to originator */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||
|
@ -1977,7 +1977,7 @@ static void routing_release(call_t *call, int argc, char *argv[], struct command
|
|||
|
||||
/* send message to all terminators, if any */
|
||||
for (relation = relation->next; relation; relation = relation->next) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "%s CC-REL-IND to terminator.\n", relation_name(relation));
|
||||
LOGP(DROUTER, LOGL_DEBUG, "%s CC-REL-IND to terminator.\n", relation_name(relation));
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||
/* add cause */
|
||||
osmo_cc_add_ie_cause(new_msg, relation->cc_ep->serving_location, OSMO_CC_ISDN_CAUSE_NORM_CALL_CLEAR, 0, 0);
|
||||
|
@ -2014,7 +2014,7 @@ static void routing_dtmf(call_t *call, int __attribute__((unused)) argc, char __
|
|||
relation = relation->next;
|
||||
|
||||
if (!relation || relation->next) {
|
||||
PDEBUG(DROUTER, DEBUG_NOTICE, "Cannot enable DTMF detection on the called side, because there is no single terminating callee.\n");
|
||||
LOGP(DROUTER, LOGL_NOTICE, "Cannot enable DTMF detection on the called side, because there is no single terminating callee.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2060,20 +2060,20 @@ static void routing_te(call_t *call, int __attribute__((unused)) argc, char __at
|
|||
relation = relation->next;
|
||||
|
||||
if (!relation || relation->next) {
|
||||
PDEBUG(DROUTER, DEBUG_NOTICE, "Cannot send telephone event, because there is no single terminating callee.\n");
|
||||
LOGP(DROUTER, LOGL_NOTICE, "Cannot send telephone event, because there is no single terminating callee.\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!relation->telephone_event) {
|
||||
PDEBUG(DROUTER, DEBUG_NOTICE, "Cannot send telephone event, because the codec was not negotiated.\n");
|
||||
LOGP(DROUTER, LOGL_NOTICE, "Cannot send telephone event, because the codec was not negotiated.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
parse_params(argc, argv, command_def);
|
||||
|
||||
if (!param.event || !param.event[0] || param.event[1]) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'telephone-event' command reqires an event character as parameter.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "'telephone-event' command reqires an event character as parameter.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2109,7 +2109,7 @@ static void routing_te(call_t *call, int __attribute__((unused)) argc, char __at
|
|||
event = param.event[0] - 'A' + 12;
|
||||
break;
|
||||
default:
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'telephone-event' with illegal event character '%c'.\n", param.event[0]);
|
||||
LOGP(DROUTER, LOGL_ERROR, "'telephone-event' with illegal event character '%c'.\n", param.event[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2137,11 +2137,11 @@ static void routing_error(call_t *call, int __attribute__((unused)) argc, char _
|
|||
parse_params(argc, argv, command_def);
|
||||
|
||||
if (!param.error) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "'error' command reqires an error string as parameter.\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "'error' command reqires an error string as parameter.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Routing script error: '%s'\n", param.error);
|
||||
LOGP(DROUTER, LOGL_ERROR, "Routing script error: '%s'\n", param.error);
|
||||
|
||||
/* send message to originator */
|
||||
new_msg = osmo_cc_new_msg(OSMO_CC_MSG_REL_IND);
|
||||
|
@ -2184,6 +2184,70 @@ struct command_def command_def[] = {
|
|||
{ NULL, NULL, NULL, NULL }
|
||||
};
|
||||
|
||||
static char *strtok_quotes(const char **text_p)
|
||||
{
|
||||
static char token[1024];
|
||||
const char *text = *text_p;
|
||||
int i, quote;
|
||||
|
||||
/* skip spaces */
|
||||
while (*text) {
|
||||
if (*text > 32)
|
||||
break;
|
||||
text++;
|
||||
}
|
||||
|
||||
/* if eol, return NULL */
|
||||
if (!(*text))
|
||||
return NULL;
|
||||
|
||||
i = 0;
|
||||
quote = 0;
|
||||
while (*text) {
|
||||
/* escape allows all following characters */
|
||||
if (*text == '\\') {
|
||||
text++;
|
||||
if (*text)
|
||||
token[i++] = *text++;
|
||||
continue;
|
||||
}
|
||||
/* no quote, check for them or break on white space */
|
||||
if (quote == 0) {
|
||||
if (*text == '\'') {
|
||||
quote = 1;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
if (*text == '\"') {
|
||||
quote = 2;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
if (*text <= ' ')
|
||||
break;
|
||||
}
|
||||
/* single quote, check for unquote */
|
||||
if (quote == 1 && *text == '\'') {
|
||||
quote = 0;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
/* double quote, check for unquote */
|
||||
if (quote == 2 && *text == '\"') {
|
||||
quote = 0;
|
||||
text++;
|
||||
continue;
|
||||
}
|
||||
/* copy character */
|
||||
token[i++] = *text++;
|
||||
}
|
||||
token[i] = '\0';
|
||||
|
||||
*text_p = text;
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
void routing_receive_stdout(routing_t *routing, const char *string)
|
||||
{
|
||||
call_t *call = routing->call;
|
||||
|
@ -2192,12 +2256,12 @@ void routing_receive_stdout(routing_t *routing, const char *string)
|
|||
int i;
|
||||
|
||||
if (routing->call->relation_list)
|
||||
PDEBUG(DSTDERR, DEBUG_INFO, "(call #%d) Command from routing: '%s'\n", routing->call->num, string);
|
||||
LOGP(DSTDERR, LOGL_INFO, "(call #%d) Command from routing: '%s'\n", routing->call->num, string);
|
||||
else
|
||||
PDEBUG(DSTDERR, DEBUG_INFO, "Command from routing: '%s'\n", string);
|
||||
LOGP(DSTDERR, LOGL_INFO, "Command from routing: '%s'\n", string);
|
||||
|
||||
/* convert string into tokens */
|
||||
while ((token = osmo_cc_strtok_quotes(&string)))
|
||||
while ((token = strtok_quotes(&string)))
|
||||
argv[argc++] = strdup(token);
|
||||
if (!argc)
|
||||
return;
|
||||
|
@ -2209,7 +2273,7 @@ void routing_receive_stdout(routing_t *routing, const char *string)
|
|||
}
|
||||
}
|
||||
if (!command_def[i].name)
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Unknown command '%s' from routing.\n", argv[0]);
|
||||
LOGP(DROUTER, LOGL_ERROR, "Unknown command '%s' from routing.\n", argv[0]);
|
||||
|
||||
while (argc)
|
||||
free((char *)argv[--argc]);
|
||||
|
@ -2218,9 +2282,9 @@ void routing_receive_stdout(routing_t *routing, const char *string)
|
|||
void routing_receive_stderr(routing_t *routing, const char *string)
|
||||
{
|
||||
if (routing->call->relation_list)
|
||||
PDEBUG(DSTDERR, DEBUG_NOTICE, "(call #%d) Routing STDERR: %s\n", routing->call->num, string);
|
||||
LOGP(DSTDERR, LOGL_NOTICE, "(call #%d) Routing STDERR: %s\n", routing->call->num, string);
|
||||
else
|
||||
PDEBUG(DSTDERR, DEBUG_NOTICE, "Routing STDERR: %s\n", string);
|
||||
LOGP(DSTDERR, LOGL_NOTICE, "Routing STDERR: %s\n", string);
|
||||
}
|
||||
|
||||
void routing_close(routing_t *routing)
|
||||
|
@ -2229,7 +2293,7 @@ void routing_close(routing_t *routing)
|
|||
|
||||
osmo_cc_msg_t *new_msg;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "(call #%d) Routing script exitted.\n", call->num);
|
||||
LOGP(DROUTER, LOGL_INFO, "(call #%d) Routing script exitted.\n", call->num);
|
||||
|
||||
/* if we have a terminating call, it is fine to continue without routing process */
|
||||
if (call->relation_list->next)
|
||||
|
@ -2266,25 +2330,25 @@ void rx_telephone_event(call_relation_t *relation, uint8_t marker, struct teleph
|
|||
if (te->event < 16) {
|
||||
if (marker && te->volume <= 36) {
|
||||
if (!te->e) {
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "Received start of Telephone-Event '%d' duration=%d ms (marker set)\n", te->event, te->duration / 8);
|
||||
LOGP(DROUTER, LOGL_INFO, "Received start of Telephone-Event '%d' duration=%d ms (marker set)\n", te->event, te->duration / 8);
|
||||
relation->te_started = 1;
|
||||
} else
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "Received start and end of Telephone-Event '%d' duration=%d ms (marker set)\n", te->event, te->duration / 8);
|
||||
LOGP(DROUTER, LOGL_INFO, "Received start and end of Telephone-Event '%d' duration=%d ms (marker set)\n", te->event, te->duration / 8);
|
||||
digit_string[5] = "0123456789*#ABCD"[te->event];
|
||||
} else if (!relation->te_started && !te->e && te->volume <= 36) {
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "Received start of Telephone-Event '%d' duration=%d ms (marker not set, this is wrong!)\n", te->event, te->duration / 8);
|
||||
LOGP(DROUTER, LOGL_INFO, "Received start of Telephone-Event '%d' duration=%d ms (marker not set, this is wrong!)\n", te->event, te->duration / 8);
|
||||
relation->te_started = 1;
|
||||
digit_string[5] = "0123456789*#ABCD"[te->event];
|
||||
} else if (relation->te_started && !te->e && te->volume <= 36) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Received subsequent Telephone-Event '%d' duration=%d ms\n", te->event, te->duration / 8);
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Received subsequent Telephone-Event '%d' duration=%d ms\n", te->event, te->duration / 8);
|
||||
} else if (relation->te_started && te->e) {
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "Received end of Telephone-Event '%d'\n", te->event);
|
||||
LOGP(DROUTER, LOGL_INFO, "Received end of Telephone-Event '%d'\n", te->event);
|
||||
relation->te_started = 0;
|
||||
} else if (!relation->te_started && te->e) {
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Received subsequent end of Telephone-Event '%d'\n", te->event);
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Received subsequent end of Telephone-Event '%d'\n", te->event);
|
||||
}
|
||||
} else
|
||||
PDEBUG(DROUTER, DEBUG_INFO, "Received unsupported Telephone-Event '%d'\n", te->event);
|
||||
LOGP(DROUTER, LOGL_INFO, "Received unsupported Telephone-Event '%d'\n", te->event);
|
||||
|
||||
if (!relation->call->routing.routing)
|
||||
return;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
#include "../libtimer/timer.h"
|
||||
#include "../libselect/select.h"
|
||||
#include "../libosmocc/endpoint.h"
|
||||
#include "../libosmocc/helper.h"
|
||||
#include <osmocom/core/timer.h>
|
||||
#include <osmocom/core/select.h>
|
||||
#include <osmocom/cc/endpoint.h>
|
||||
#include <osmocom/cc/helper.h>
|
||||
#include "../libsample/sample.h"
|
||||
#include "../libjitter/jitter.h"
|
||||
#include "../libsendevolumenregler/sendevolumenregler.h"
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../liblogging/logging.h"
|
||||
#include "call.h"
|
||||
#include "display.h"
|
||||
|
||||
|
@ -47,6 +47,8 @@ static void print_status(int on)
|
|||
if (h > lines_total)
|
||||
h = lines_total;
|
||||
|
||||
lock_logging();
|
||||
enable_limit_scroll(false);
|
||||
printf("\0337\033[H");
|
||||
for (i = 0; i < h; i++) {
|
||||
j = 0;
|
||||
|
@ -66,6 +68,8 @@ static void print_status(int on)
|
|||
putchar('\n');
|
||||
}
|
||||
printf("\033[0;39m\0338"); fflush(stdout);
|
||||
enable_limit_scroll(true);
|
||||
unlock_logging();
|
||||
}
|
||||
|
||||
void display_status_on(int on)
|
||||
|
@ -82,9 +86,9 @@ void display_status_on(int on)
|
|||
print_status(1);
|
||||
|
||||
if (status_on)
|
||||
debug_limit_scroll = lines_total;
|
||||
logging_limit_scroll_top(lines_total);
|
||||
else
|
||||
debug_limit_scroll = 0;
|
||||
logging_limit_scroll_top(0);
|
||||
}
|
||||
|
||||
/* start status display */
|
||||
|
@ -229,7 +233,7 @@ void display_status_end(void)
|
|||
/* set new total lines */
|
||||
lines_total = line_count;
|
||||
if (status_on)
|
||||
debug_limit_scroll = lines_total;
|
||||
logging_limit_scroll_top(lines_total);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
#include <gsm.h>
|
||||
#include "call.h"
|
||||
#include "gsm_codec.h"
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../liblogging/logging.h"
|
||||
|
||||
|
||||
/* create gsm instance */
|
||||
|
|
|
@ -25,9 +25,9 @@
|
|||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <termios.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../liblogging/logging.h"
|
||||
#include "../liboptions/options.h"
|
||||
#include "../libg711/g711.h"
|
||||
#include <osmocom/cc/g711.h>
|
||||
#include "call.h"
|
||||
#include "audio.h"
|
||||
#include "display.h"
|
||||
|
@ -55,7 +55,7 @@ static void print_help()
|
|||
printf(" --config [~/]<path to config file>\n");
|
||||
printf(" Give a config file to use. If it starts with '~/', path is at home dir.\n");
|
||||
printf(" Each line in config file is one option, '-' or '--' must not be given!\n");
|
||||
debug_print_help();
|
||||
logging_print_help();
|
||||
printf(" -r --routing-script <script>\n");
|
||||
printf(" Define the script/executable that is executed to perform routing\n");
|
||||
printf(" decision for each call. (default = %s)\n", routing_script);
|
||||
|
@ -89,11 +89,9 @@ static int handle_options(int short_option, int argi, char **argv)
|
|||
show_help = 1;
|
||||
break;
|
||||
case 'v':
|
||||
if (!strcasecmp(argv[argi], "list")) {
|
||||
debug_list_cat();
|
||||
rc = parse_logging_opt(argv[argi]);
|
||||
if (rc > 0)
|
||||
return 0;
|
||||
}
|
||||
rc = parse_debug_opt(argv[argi]);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "Failed to parse debug option, please use -h for help.\n");
|
||||
return rc;
|
||||
|
@ -161,9 +159,18 @@ static int get_char()
|
|||
return -1;
|
||||
}
|
||||
|
||||
struct timer clock_timer;
|
||||
struct osmo_timer_list clock_timer;
|
||||
double last_time_clock = 0;
|
||||
|
||||
static double get_time(void)
|
||||
{
|
||||
static struct timespec tv;
|
||||
|
||||
clock_gettime(CLOCK_REALTIME, &tv);
|
||||
|
||||
return (double)tv.tv_sec + (double)tv.tv_nsec / 1000000000.0;
|
||||
}
|
||||
|
||||
static void clock_timeout(void __attribute__((unused)) *data)
|
||||
{
|
||||
double now;
|
||||
|
@ -176,7 +183,7 @@ static void clock_timeout(void __attribute__((unused)) *data)
|
|||
last_time_clock += 0.020;
|
||||
if (last_time_clock < now)
|
||||
last_time_clock = now;
|
||||
timer_start(&clock_timer, last_time_clock - now);
|
||||
osmo_timer_schedule(&clock_timer, 0,(last_time_clock - now) * 1000000);
|
||||
|
||||
/* call call-clock every 20ms */
|
||||
call_clock(160);
|
||||
|
@ -198,6 +205,9 @@ int main(int argc, char *argv[])
|
|||
int argi, rc;
|
||||
struct termios term, term_orig;
|
||||
|
||||
/* init logging */
|
||||
logging_init();
|
||||
|
||||
/* init FM */
|
||||
fm_init(0);
|
||||
|
||||
|
@ -246,8 +256,8 @@ int main(int argc, char *argv[])
|
|||
call_init(cc_ep1, cc_ep2, routing_script, routing_shell);
|
||||
|
||||
/* init clock timer for clocking call */
|
||||
timer_init(&clock_timer, clock_timeout, NULL);
|
||||
timer_start(&clock_timer, 0.020);
|
||||
osmo_timer_setup(&clock_timer, clock_timeout, NULL);
|
||||
osmo_timer_schedule(&clock_timer, 0,20000);
|
||||
|
||||
/* prepare terminal */
|
||||
tcgetattr(0, &term_orig);
|
||||
|
@ -267,22 +277,12 @@ int main(int argc, char *argv[])
|
|||
|
||||
while (!quit) {
|
||||
int work;
|
||||
double timeout;
|
||||
|
||||
do {
|
||||
work = 0;
|
||||
work |= osmo_cc_handle();
|
||||
work |= call_handle();
|
||||
} while (work);
|
||||
/* handle all timers
|
||||
* timeout is 0, if there was an event
|
||||
* -> handle FDs without waiting, continue this loop
|
||||
* timeout is not 0, if there was no event
|
||||
* -> wait until FD or timeout
|
||||
*/
|
||||
timeout = process_timer();
|
||||
/* wait for FD event until given timeout */
|
||||
osmo_fd_select(timeout);
|
||||
osmo_select_main(0);
|
||||
}
|
||||
|
||||
/* reset signals */
|
||||
|
@ -297,7 +297,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
|
||||
error:
|
||||
timer_exit(&clock_timer);
|
||||
osmo_timer_del(&clock_timer);
|
||||
|
||||
if (cc_ep1) {
|
||||
/* exit call handling */
|
||||
|
@ -319,6 +319,8 @@ error:
|
|||
/* exit FM */
|
||||
fm_exit();
|
||||
|
||||
enable_limit_scroll(false);
|
||||
|
||||
options_free();
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#include <sys/resource.h>
|
||||
#include <sys/types.h>
|
||||
#include <signal.h>
|
||||
#include "../libdebug/debug.h"
|
||||
#include "../liblogging/logging.h"
|
||||
#include "call.h"
|
||||
|
||||
extern char **environ;
|
||||
|
@ -100,7 +100,7 @@ static int env_set(routing_t *routing, const char *name, int index)
|
|||
break;
|
||||
}
|
||||
if (!env_def[i].n) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Software error: Environment variable '%s' does not exist in definition, please fix!\n", name);
|
||||
LOGP(DROUTER, LOGL_ERROR, "Software error: Environment variable '%s' does not exist in definition, please fix!\n", name);
|
||||
return index;
|
||||
}
|
||||
|
||||
|
@ -327,7 +327,7 @@ void routing_start(routing_t *routing, const char *script, const char *shell)
|
|||
rc = pipe(in_pipe);
|
||||
if (rc < 0) {
|
||||
epipe:
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "pipe() failed: errno=%d\n", errno);
|
||||
LOGP(DROUTER, LOGL_ERROR, "pipe() failed: errno=%d\n", errno);
|
||||
abort();
|
||||
}
|
||||
rc = pipe(out_pipe);
|
||||
|
@ -339,7 +339,7 @@ void routing_start(routing_t *routing, const char *script, const char *shell)
|
|||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "fork() failed: errno=%d\n", errno);
|
||||
LOGP(DROUTER, LOGL_ERROR, "fork() failed: errno=%d\n", errno);
|
||||
abort();
|
||||
}
|
||||
if (pid == 0) {
|
||||
|
@ -375,12 +375,12 @@ void routing_start(routing_t *routing, const char *script, const char *shell)
|
|||
}
|
||||
|
||||
/* PARENT */
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Running script '%s' as child %d\n", script, pid);
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Running script '%s' as child %d\n", script, pid);
|
||||
|
||||
/* wait for clild to complete closing file descriptors */
|
||||
rc = read(out_pipe[0], &x, 1);
|
||||
if (rc != 1 || x != 'x') {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "communication with child failed!\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "communication with child failed!\n");
|
||||
kill(pid, SIGKILL);
|
||||
abort();
|
||||
}
|
||||
|
@ -413,7 +413,7 @@ void routing_start(routing_t *routing, const char *script, const char *shell)
|
|||
|
||||
routing->routing = 1;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Routing script started.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Routing script started.\n");
|
||||
}
|
||||
|
||||
|
||||
|
@ -466,13 +466,13 @@ void routing_stop(routing_t *routing)
|
|||
|
||||
routing->routing = 0;
|
||||
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Routing script stopped.\n");
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Routing script stopped.\n");
|
||||
}
|
||||
|
||||
|
||||
void routing_send(routing_t *routing, const char *string)
|
||||
{
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Sending line to routing script: '%s'\n", string);
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Sending line to routing script: '%s'\n", string);
|
||||
enqueue_string(&routing->stdin_queue, string, "\n");
|
||||
routing->script_stdin.when |= OSMO_FD_WRITE;
|
||||
}
|
||||
|
@ -534,7 +534,7 @@ static int routing_handle_stdout(struct osmo_fd *ofd, unsigned int what)
|
|||
i = 0;
|
||||
}
|
||||
if (i == sizeof(routing->stdout_buffer)) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Output line from script too long, please fix!\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "Output line from script too long, please fix!\n");
|
||||
routing->stdout_pos = 0;
|
||||
}
|
||||
}
|
||||
|
@ -572,7 +572,7 @@ static int routing_handle_stderr(struct osmo_fd *ofd, unsigned int what)
|
|||
i = 0;
|
||||
}
|
||||
if (i == sizeof(routing->stderr_buffer)) {
|
||||
PDEBUG(DROUTER, DEBUG_ERROR, "Output line from script too long, please fix!\n");
|
||||
LOGP(DROUTER, LOGL_ERROR, "Output line from script too long, please fix!\n");
|
||||
routing->stderr_pos = 0;
|
||||
}
|
||||
}
|
||||
|
@ -587,7 +587,7 @@ int routing_handle(routing_t *routing)
|
|||
|
||||
if (routing->stdout_queue) {
|
||||
string = dequeue_string(&routing->stdout_queue);
|
||||
PDEBUG(DROUTER, DEBUG_DEBUG, "Routing script returned line from stdout: '%s'\n", string);
|
||||
LOGP(DROUTER, LOGL_DEBUG, "Routing script returned line from stdout: '%s'\n", string);
|
||||
routing_receive_stdout(routing, string);
|
||||
free(string);
|
||||
return 1;
|
||||
|
|
Loading…
Reference in New Issue