Compare commits

...

19 Commits

Author SHA1 Message Date
Andreas Eversberg d49008d2ea Audio rework, new jitter buffer
Jitter buffer is now based on packets, not on samples. The frames are
dejittered in received form. After reading from jitter buffer, they are
decoded in correct order. If a frame is missing, it is concealed by
repeating audio.
2024-03-27 20:51:32 +01:00
Andreas Eversberg e9b0e9481c Updated libs 2024-03-26 22:06:46 +01:00
Dennis Grunert 4ab31aa755 Fix german tone patterns according to 1TR110-1 Chapter 8
- Busy tone: 480ms on / 480ms off
- Hangup tone: 480ms on / 480ms off
- Old german ringing: 1000ms on / 4000ms off
2024-02-03 21:03:04 +01:00
Dennis Grunert 687e52e69a Fix timing of old german busy tone; add morse-a dialtone
New parameter "morsegerman" for option "local-tones" to generate a morse-a dialtone (pre-1979).
Timing of old german busy tone set to 125ms on / 475ms off.
2024-02-03 20:59:51 +01:00
Dennis Grunert 1481cf8d5a Add no-dtmf option to disable DTMF detection when using rotary (pay)phones
If you have a pure rotary dial payphone and don't want users to bypass coin insertion by using a DTMF tone sender,
the option "no-dtmf" disables DTMF detection.
2024-01-31 21:42:21 +01:00
Dennis Grunert d0fd4ca634 Fix local-tones option not accepting any parameter 2024-01-31 21:09:33 +01:00
Dennis Grunert 5f4e1c2dee Fix subscriber busy indication if line active despite no active call
If the called subscriber has no active call, but stays off-hook (e.g. after a previously disconnected call), busy must be indicated to incoming call attempts.
2024-01-30 00:04:53 +01:00
Dennis Grunert 9e51fac285 Fix line reversal on connect in case of incoming calls
In addition to line reversal supervision signals of outgoing (originating) calls, option lr-on-connect now indicates connect and disconnect of incoming (terminating) calls, too.
Line is only reversed on answer of incoming calls if option loop-disconnect is not enabled for that endpoint.
2024-01-29 23:27:22 +01:00
Dennis Grunert b13641ec32 Ensure metering-lr-timer is stopped before issuing V5 disconnect request 2024-01-29 17:42:33 +01:00
Dennis Grunert 6e09a11895 Fix CLIP-type pulse for Nokia EKSOS
Nokia EKSOS rejects V5 pulsed signal initial-ring with suppression bits 0b11; 0b10 is accepted with PSTN mappings DE and UK.
EN 300 324-1 definition for suppression bits:
- 0b11 = Suppression allowed by pre-defined V5.1 SIGNAL message from LE or pre-defined line signal from TE
- 0b10 = Suppression allowed by pre-defined line signal from TE
2024-01-29 12:46:17 +01:00
Dennis Grunert 16cfce2f23 Change IE_METERING timing information from deciseconds to seconds and milliseconds 2024-01-29 11:48:27 +01:00
Dennis Grunert d825843be5 Helptext improvement; Add support for metering using short line reversal pulses
The option "lr-metering" has been added, which produces line reversal pulses for each charged unit. The timings are currently hard-coded in pstn.c (METERING_LR_REV_MILLISECS=150 and METERING_LR_NORM_MILLISECS=150).
This is used by some payphones in the former GDR instead of sinusoidal pulses, sometimes called "Schwellimpulszaehlung".
A new timer metering_lr_timer is used to revert back to normal polarity after each pulse (generated as V5 steady signal), since Nokia EKSOS with UK PSTN mapping does not seem to accept V5 pulsed-signal reverse-battery.
Option "lr-metering" is currently only supported with "pstn-mapping uk". It can be used together with "metering", if both V5 metering signals (usually sinusoidal 12/16 kHz) and line reversal signals are desired.
Option "lr-on-connect" for line reversal on call connect is incompatible with option "lr-metering".
2024-01-29 02:57:13 +01:00
Andreas Eversberg 22ac5a0317 Correctly handle release/reject from upper layer (cc) 2024-01-27 09:36:49 +01:00
Andreas Eversberg f6207a55d0 Updated libs 2024-01-27 09:29:59 +01:00
Andreas Eversberg 9e272ade15 fixup cc 2024-01-27 09:29:19 +01:00
Andreas Eversberg 9af42630f6 fixup cc 2024-01-24 21:05:10 +01:00
Dennis Grunert 64055360b6 CLIP pulse fix for UK PSTN mapping
Fix: EKSOS rejects initial ring with clip-pulse setting.
Note: Not tested with other PSTN mappings than UK, therefore fix applied only there.
2024-01-18 18:39:45 +01:00
Dennis Grunert b0fc8a49c1 Additional line supervision signals on connect and disconnect; metering pulse generation from CC metering information
If enabled within the endpoint configuration, additional line supervision signals can be generated:
 - option 'lr_on_connect': line reversal on connect, revert to normal polarity on disconnect
 - option 'loop_disconnect': upon remote disconnect, loop current (battery) is interrupted for a short period
If metering details via IE_METERING are received from osmo-cc, metering pulses can be generated based on initial call-connect pulse count and unit time period:
 - option 'metering'
Some line signals require a specific national PSTN dialect (or mapping) to be accepted by the EKSOS AN. A new configuration option allows the specification of the configured AN PSTN mapping:
 - option 'pstn_dialect de' (Germany, default setting)
 - option 'pstn_dialect uk' (United Kingdom, allows line reversal and loop disconnect signals)
2024-01-18 18:39:44 +01:00
Andreas Eversberg 7909556b84 Move from local to external osmo* libraries
src/libdebug -> libosmocore
src/libselect -> libosmocore
src/libtimer -> libosmocore
src/libosmocc -> libosmo-cc
src/libg711 -> libosmo-cc
2024-01-18 18:39:41 +01:00
54 changed files with 1383 additions and 9107 deletions

6
.gitignore vendored
View File

@ -33,18 +33,14 @@ Doxyfile
.*.sw?
src/libdebug/libdebug.a
src/libg711/libg711.a
src/liblogging/liblogging.a
src/libjitter/libjitter.a
src/libfm/libfm.a
src/libdtmf/libdtmf.a
src/libfsk/libfsk.a
src/liboptions/liboptions.a
src/libosmocc/libosmocc.a
src/libsample/libsample.a
src/libfilter/libfilter.a
src/libtimer/libtimer.a
src/libselect/libselect.a
src/libph_socket/libph_socket.a
src/pstn/osmo-cc-pstn-endpoint

View File

@ -77,19 +77,18 @@ AM_CONFIG_HEADER(config.h)
AC_CHECK_LIB([m], [main])
AC_CHECK_LIB([pthread], [main])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOCC, libosmocc >= 2.0.0)
AC_OUTPUT(
src/liboptions/Makefile
src/libdebug/Makefile
src/liblogging/Makefile
src/libsample/Makefile
src/libfsk/Makefile
src/libdtmf/Makefile
src/libfm/Makefile
src/libfilter/Makefile
src/libtimer/Makefile
src/libselect/Makefile
src/libjitter/Makefile
src/libosmocc/Makefile
src/libg711/Makefile
src/libph_socket/Makefile
src/pstn/Makefile
src/Makefile

View File

@ -2,17 +2,13 @@ AUTOMAKE_OPTIONS = foreign
SUBDIRS = \
liboptions \
libdebug \
liblogging \
libsample \
libfilter \
libfm \
libdtmf \
libfsk \
libtimer \
libselect \
libjitter \
libosmocc \
libph_socket \
libg711 \
pstn

View File

@ -1,7 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libdebug.a
libdebug_a_SOURCES = \
debug.c

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -24,7 +24,7 @@
#include <errno.h>
#include <math.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../liblogging/logging.h"
#include "fsk.h"
#define PI M_PI
@ -51,7 +51,7 @@ int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int sa
int i;
int rc;
PDEBUG(DDSP, DEBUG_DEBUG, "Setup FSK for Transmitter. (F0 = %.1f, F1 = %.1f, peak = %.1f)\n", f0, f1, level);
LOGP(DDSP, LOGL_DEBUG, "Setup FSK for Transmitter. (F0 = %.1f, F1 = %.1f, peak = %.1f)\n", f0, f1, level);
memset(fsk, 0, sizeof(*fsk));
@ -79,12 +79,12 @@ int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int sa
}
fsk->bits65536_per_sample = (double)bitrate / (double)samplerate * 65536.0;
PDEBUG(DDSP, DEBUG_DEBUG, "Bitduration of %.4f bits per sample @ %d.\n", fsk->bits65536_per_sample / 65536.0, samplerate);
LOGP(DDSP, LOGL_DEBUG, "Bitduration of %.4f bits per sample @ %d.\n", fsk->bits65536_per_sample / 65536.0, samplerate);
fsk->phaseshift65536[0] = f0 / (double)samplerate * 65536.0;
fsk->phaseshift65536[1] = f1 / (double)samplerate * 65536.0;
PDEBUG(DDSP, DEBUG_DEBUG, "F0 = %.0f Hz (phaseshift65536[0] = %.4f)\n", f0, fsk->phaseshift65536[0]);
PDEBUG(DDSP, DEBUG_DEBUG, "F1 = %.0f Hz (phaseshift65536[1] = %.4f)\n", f1, fsk->phaseshift65536[1]);
LOGP(DDSP, LOGL_DEBUG, "F0 = %.0f Hz (phaseshift65536[0] = %.4f)\n", f0, fsk->phaseshift65536[0]);
LOGP(DDSP, LOGL_DEBUG, "F1 = %.0f Hz (phaseshift65536[1] = %.4f)\n", f1, fsk->phaseshift65536[1]);
/* use ffsk modulation, i.e. each bit has an integer number of
* half waves and starts/ends at zero crossing
@ -93,12 +93,12 @@ int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int sa
double waves;
if (filter) {
PDEBUG(DDSP, DEBUG_ERROR, "Cannot use FFSK with filter.\n");
LOGP(DDSP, LOGL_ERROR, "Cannot use FFSK with filter.\n");
rc = -EINVAL;
goto error;
}
PDEBUG(DDSP, DEBUG_DEBUG, "enable FFSK modulation mode\n");
LOGP(DDSP, LOGL_DEBUG, "enable FFSK modulation mode\n");
fsk->ffsk = 1;
waves = (f0 / bitrate);
if (fabs(round(waves * 2) - (waves * 2)) > 0.001) {
@ -116,8 +116,8 @@ int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int sa
fsk->cycles_per_bit65536[0] = f0 / bitrate * 65536.0;
fsk->cycles_per_bit65536[1] = f1 / bitrate * 65536.0;
}
PDEBUG(DDSP, DEBUG_DEBUG, "F0 = %.0f Hz (cycles_per_bit65536[0] = %.4f)\n", f0, fsk->cycles_per_bit65536[0]);
PDEBUG(DDSP, DEBUG_DEBUG, "F1 = %.0f Hz (cycles_per_bit65536[1] = %.4f)\n", f1, fsk->cycles_per_bit65536[1]);
LOGP(DDSP, LOGL_DEBUG, "F0 = %.0f Hz (cycles_per_bit65536[0] = %.4f)\n", f0, fsk->cycles_per_bit65536[0]);
LOGP(DDSP, LOGL_DEBUG, "F1 = %.0f Hz (cycles_per_bit65536[1] = %.4f)\n", f1, fsk->cycles_per_bit65536[1]);
/* if filter is enabled, use a cosine shaped curve to change the phase each sample */
if (filter) {
@ -138,7 +138,7 @@ int fsk_mod_init(fsk_mod_t *fsk, void *inst, int (*send_bit)(void *inst), int sa
#endif
}
PDEBUG(DDSP, DEBUG_DEBUG, "Enable filter to smooth FSK transmission.\n");
LOGP(DDSP, LOGL_DEBUG, "Enable filter to smooth FSK transmission.\n");
fsk->filter = 1;
}
@ -155,7 +155,7 @@ error:
/* Cleanup transceiver instance. */
void fsk_mod_cleanup(fsk_mod_t *fsk)
{
PDEBUG(DDSP, DEBUG_DEBUG, "Cleanup FSK for Transmitter.\n");
LOGP(DDSP, LOGL_DEBUG, "Cleanup FSK for Transmitter.\n");
if (fsk->sin_tab) {
free(fsk->sin_tab);
@ -314,7 +314,7 @@ int fsk_demod_init(fsk_demod_t *fsk, void *inst, void (*receive_bit)(void *inst,
double bandwidth;
int rc;
PDEBUG(DDSP, DEBUG_DEBUG, "Setup FSK for Receiver. (F0 = %.1f, F1 = %.1f)\n", f0, f1);
LOGP(DDSP, LOGL_DEBUG, "Setup FSK for Receiver. (F0 = %.1f, F1 = %.1f)\n", f0, f1);
memset(fsk, 0, sizeof(*fsk));
@ -341,7 +341,7 @@ int fsk_demod_init(fsk_demod_t *fsk, void *inst, void (*receive_bit)(void *inst,
goto error;
fsk->bits_per_sample = (double)bitrate / (double)samplerate;
PDEBUG(DDSP, DEBUG_DEBUG, "Bitduration of %.4f bits per sample @ %d.\n", fsk->bits_per_sample, samplerate);
LOGP(DDSP, LOGL_DEBUG, "Bitduration of %.4f bits per sample @ %d.\n", fsk->bits_per_sample, samplerate);
return 0;
@ -353,7 +353,7 @@ error:
/* Cleanup transceiver instance. */
void fsk_demod_cleanup(fsk_demod_t *fsk)
{
PDEBUG(DDSP, DEBUG_DEBUG, "Cleanup FSK for Receiver.\n");
LOGP(DDSP, LOGL_DEBUG, "Cleanup FSK for Receiver.\n");
fm_demod_exit(&fsk->demod);
}
@ -361,6 +361,8 @@ void fsk_demod_cleanup(fsk_demod_t *fsk)
//#define DEBUG_MODULATOR
//#define DEBUG_FILTER
#define CHUNK 1024
/* Demodulates bits
*
* If bit is received, callback function send_bit() is called.
@ -375,15 +377,21 @@ void fsk_demod_cleanup(fsk_demod_t *fsk)
*/
void fsk_demod_receive(fsk_demod_t *fsk, sample_t *sample, int length)
{
sample_t I[length], Q[length], frequency[length], f;
sample_t I[CHUNK], Q[CHUNK], frequency[CHUNK], f;
int i;
int bit;
double level, quality;
/* demod samples to offset around center frequency */
fm_demodulate_real(&fsk->demod, frequency, length, sample, I, Q);
for (i = 0; i < length; i++) {
/* this is called on first sample and subsequently every sample defined by CHUNK */
if (i % CHUNK == 0) {
length -= i;
i = 0;
/* demod CHUNK samples to offset around center frequency */
fm_demodulate_real(&fsk->demod, frequency, (length > CHUNK) ? CHUNK : length, sample, I, Q);
sample += CHUNK;
}
f = frequency[i];
if (f < 0)
bit = fsk->low_bit;

View File

@ -1,7 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libg711.a
libg711_a_SOURCES = \
g711.c

View File

@ -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_flipped_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_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_to_linear[i] = g711_ulaw_flipped_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_flipped_to_linear[j])
j++;
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = j;
g711_linear_to_ulaw[(i - 32768) & 0xffff] = g711_flip[j];
i++;
}
j = 255;
while(i < 65536) {
if (i - 32768 > g711_alaw_flipped_to_linear[j])
j--;
g711_linear_to_ulaw_flipped[(i - 32768) & 0xffff] = j;
g711_linear_to_ulaw[(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;
}

View File

@ -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);

View File

@ -21,12 +21,13 @@
*
* Storing:
*
* Each saved frame is sorted into the list of packages by their sequence
* number.
* Each saved frame is sorted into the list of packages by their timestamp.
*
* The first packet will be stored with a delay of minimum jitter window size.
* The first packet will be stored with a timestamp offset of minimum jitter
* window size or half of the target size, depending on the adaptive jitter
* buffer flag.
*
* Packets with the same sequence are dropped.
* Packets with the same timestamp are dropped.
*
* Early packts that exceed maximum jitter window size cause jitter
* window to shift into the future.
@ -35,37 +36,36 @@
* delay). Minimum jitter window size is added also, to prevent subsequent
* packets from beeing late too.
*
* If no sequence is provided (autosequence), the sequence number is generated
* by a counter. Also the timestamp is generated by counting the length of each
* frame.
* If adaptive jitter buffer is used, a delay that exceed the target size
* is reduced to the target size.
*
* If ssrc changes, the buffer is reset.
* If ssrc changes, the buffer is reset, but not locked again.
*
*
* Playout:
* Loading:
*
* The caller of the playout function can request any length of samples from
* the packet list. The packt's time stamp and the jitter window time stamp
* indicate what portion of a packet is already provided to the caller.
* Complete packet, sent to the caller, are removed.
* jitter_offset() will return the number of samples between the jitter buffer's head and the first packet afterwards. Packets that already passed the jitter buffer's head are ignored. If no frame is ahead the jitter buffer's head, a negative value is returned.
*
* Missing packets are interpolated by repeating last 20ms of audio (optional)
* or by inserting zeroes (sample size > 1 byte) or by inserting 0xff (sample
* size = 1). In case of repeating audio, the number of turns are limited until
* buffer is reset to silence, if no frames are received for a certain time.
* jitter_load() will remove and return the frame at the jitter buffer's head. Packet that already passed the jitter buffer's head are deleted. If no frame matches the jitter buffer's head, NULL is returned.
*
* Optionally the constant delay will be measured continuously and lowered if
* greater than minimum window size. (adaptive jitter buffer size)
* jitter_advance() will advance the jitter buffer's head by the given number of samples.
*
* Note that the delay is measured with time stamp of frame, no matter what
* the length is. Length is an extra delay, but not considered here.
* jitter_load_samples() will read decoded samples from jitter buffer's frames.
* This means that that the decoder of each frame must generate samples of equal type and size.
* If there is a gap between jitter buffer's head and the next frame, the samples are taken from the last frame.
* The conceal function is called in this case, to extrapolate the missing samples.
* If no conceal function is given, the last frame is repeated.
* If there is no gap between jitter buffer's head and the next frame, the frame is decoded and the samples are taken from that frame.
* After that the jitter buffer's head is advanced by the number of samples read.
*
* *TBD*
*
*
* Unlocking:
*
* If the buffer is created or reset, the buffer is locked, so no packets are
* stored. When the playout routine is called, the buffer is unlocked. This
* prevents from filling the buffer before playout is performed, which would
* stored. When the loading routine is called, the buffer is unlocked. This
* prevents from filling the buffer before loading is performed, which would
* cause high delay.
*
*/
@ -77,38 +77,24 @@
#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
#define REPEAT_DELAY_INTERVAL 3.0
#define EXTRA_BUFFER 0.020 // 20 ms
#define EXTRA_TIMEOUT 0.500 // maximum time to repeat extrapolation buffer
/* uncomment to enable heavy debugging */
//#define HEAVY_DEBUG
//#define VISUAL_DEBUG
static int unnamed_count = 1;
/* create jitter buffer */
int jitter_create(jitter_t *jb, const char *name, double samplerate, int sample_size, double target_window_duration, double max_window_duration, uint32_t window_flags)
int jitter_create(jitter_t *jb, const char *name, double samplerate, double target_window_duration, double max_window_duration, uint32_t window_flags)
{
int rc = 0;
memset(jb, 0, sizeof(*jb));
jb->sample_duration = 1.0 / samplerate;
jb->sample_size = sample_size;
jb->target_window_size = (int)(samplerate * target_window_duration);
jb->max_window_size = (int)(samplerate * max_window_duration);
jb->window_flags = window_flags;
jb->extra_size = (int)(EXTRA_BUFFER * samplerate);
jb->extra_samples = calloc(sample_size, jb->extra_size);
if (!jb->extra_samples) {
PDEBUG(DJITTER, DEBUG_ERROR, "No memory for frame.\n");
rc = -ENOMEM;
goto error;
}
jb->extra_timeout_max = (int)ceil(EXTRA_TIMEOUT / EXTRA_BUFFER);
memset(jb, 0, sizeof(*jb));
/* optionally give a string to be show with the debug */
if (name && *name)
@ -116,36 +102,37 @@ int jitter_create(jitter_t *jb, const char *name, double samplerate, int sample_
else
snprintf(jb->name, sizeof(jb->name) - 1, "(unnamed %d) ", unnamed_count++);
jb->sample_duration = 1.0 / samplerate;
jb->samples_20ms = samplerate / 50;
jb->target_window_size = (int)ceil(target_window_duration / jb->sample_duration);
jb->max_window_size = (int)ceil(max_window_duration / jb->sample_duration);
jb->window_flags = window_flags;
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, "%s Created jitter buffer. (samperate=%.0f, target_window=%.0fms, max_window=%.0fms, flag:latency=%s flag:repeat=%s)\n",
jb->name,
samplerate,
(double)jb->target_window_size * jb->sample_duration * 1000.0,
(double)jb->max_window_size * jb->sample_duration * 1000.0,
(window_flags & JITTER_FLAG_LATENCY) ? "true" : "false",
(window_flags & JITTER_FLAG_REPEAT) ? "true" : "false");
error:
if (rc)
jitter_destroy(jb);
return rc;
}
static void clear_extra_buffer(jitter_t *jb)
{
if (jb->sample_size == 1)
memset(jb->extra_samples, 0xff, jb->sample_size * jb->extra_size);
else
memset(jb->extra_samples, 0, jb->sample_size * jb->extra_size);
}
/* reset jitter buffer */
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, "%s Reset jitter buffer.\n", jb->name);
/* jitter buffer locked */
jb->unlocked = 0;
jb->unlocked = false;
/* window becomes invalid */
jb->window_valid = 0;
jb->window_valid = false;
/* remove all pending frames */
jf = jb->frame_list;
@ -156,269 +143,403 @@ void jitter_reset(jitter_t *jb)
}
jb->frame_list = NULL;
/* clear extrapolation buffer */
if (jb->extra_samples)
clear_extra_buffer(jb);
jb->extra_index = 0;
jb->extra_timeout_count = jb->extra_timeout_max; /* no data in buffer yet, so we set timeout condition */
/* delay measurement and reduction */
jb->delay_counter = 0.0;
jb->delay_interval = INITIAL_DELAY_INTERVAL;
jb->min_delay_value = -1;
/* remove current sample buffer */
free(jb->spl_buf);
jb->spl_buf = NULL;
jb->spl_valid = false;
}
void jitter_destroy(jitter_t *jb)
{
jitter_reset(jb);
PDEBUG(DJITTER, DEBUG_INFO, "%sDestroying jitter buffer.\n", jb->name);
if (jb->extra_samples) {
free(jb->extra_samples);
jb->extra_samples = NULL;
}
LOGP(DJITTER, LOGL_INFO, "%s Destroying jitter buffer.\n", jb->name);
}
/* store audio in jitterbuffer
*
* stop if buffer is completely filled
*/
void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
jitter_frame_t *jitter_frame_alloc(void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void *decoder_priv, uint8_t *data, int size, uint8_t marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
{
jitter_frame_t *jf, **jfp;
int16_t offset_sequence;
jitter_frame_t *jf;
jf = malloc(sizeof(*jf) + size);
if (!jf) {
LOGP(DJITTER, LOGL_ERROR, "No memory for frame.\n");
return NULL;
}
memset(jf, 0, sizeof(*jf)); // note: clear header only
jf->decoder = decoder;
jf->decoder_priv = decoder_priv;
memcpy(jf->data, data, size);
jf->size = size;
jf->marker = marker;
jf->sequence = sequence;
jf->timestamp = timestamp;
jf->ssrc = ssrc;
return jf;
}
void jitter_frame_free(jitter_frame_t *jf)
{
free(jf);
}
void jitter_frame_get(jitter_frame_t *jf, void (**decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void **decoder_priv, uint8_t **data, int *size, uint8_t *marker, uint16_t *sequence, uint32_t *timestamp, uint32_t *ssrc)
{
if (decoder)
*decoder = jf->decoder;
if (decoder_priv)
*decoder_priv = jf->decoder_priv;
if (data)
*data = jf->data;
if (size)
*size = jf->size;
if (marker)
*marker = jf->marker;
if (sequence)
*sequence = jf->sequence;
if (timestamp)
*timestamp = jf->timestamp;
if (ssrc)
*ssrc = jf->ssrc;
}
/* Store frame in jitterbuffer
*
* Use sequence number to order frames.
* Use timestamp to handle delay.
*/
void jitter_save(jitter_t *jb, jitter_frame_t *jf)
{
jitter_frame_t **jfp;
int32_t offset_timestamp;
/* ignore frames until the buffer is unlocked by jitter_load() */
if (!jb->unlocked)
if (!jb->unlocked) {
jitter_frame_free(jf);
return;
/* omit frames with no data */
if (length < 1)
return;
/* generate sequence and timestamp automatically, if enabled */
if (!has_sequence) {
#ifdef DEBUG_JITTER
PDEBUG(DJITTER, DEBUG_DEBUG, "%sSave frame of %d samples (no seqence).\n", jb->name, length);
#endif
sequence = jb->next_sequence;
jb->next_sequence++;
timestamp = jb->next_timestamp;
jb->next_timestamp += length;
ssrc = jb->window_ssrc;
} else {
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%sSave frame of %d samples (seqence=%u timestamp=%u ssrc=0x%02x).\n", jb->name, length, sequence, timestamp, ssrc);
#endif
jb->next_sequence = sequence + 1;
jb->next_timestamp = timestamp + length;
}
/* first packet (with this ssrc) sets window size to target_window_size */
if (!jb->window_valid || jb->window_ssrc != ssrc) {
if (!jb->window_valid || jb->window_ssrc != jf->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;
jb->unlocked = true;
/* when using dynamic jitter buffer, we use half of the target delay */
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
jb->window_timestamp = timestamp - (uint32_t)jb->target_window_size / 2;
jb->window_timestamp = jf->timestamp - (uint32_t)jb->target_window_size / 2;
} else {
jb->window_timestamp = timestamp - (uint32_t)jb->target_window_size;
jb->window_timestamp = jf->timestamp - (uint32_t)jb->target_window_size;
}
jb->window_valid = 1;
jb->window_ssrc = ssrc;
jb->window_valid = true;
jb->window_ssrc = jf->ssrc;
jb->min_delay = -1;
jb->delay_counter = 0.0;
jb->delay_interval = INITIAL_DELAY_INTERVAL;
}
/* reduce delay */
if (jb->delay_counter >= jb->delay_interval) {
if (jb->min_delay >= 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 * jb->sample_duration * 1000.0);
/* delay reduction, if minimum delay is greater than target jitter window size */
if ((jb->window_flags & JITTER_FLAG_LATENCY) && jb->min_delay > jb->target_window_size) {
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 * 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 - jb->target_window_size / 2;
}
jb->delay_counter -= jb->delay_interval;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
jb->min_delay = -1;
}
/* find location where to put frame into the list, depending on sequence number */
jfp = &jb->frame_list;
while(*jfp) {
offset_sequence = (int16_t)(sequence - (*jfp)->sequence);
offset_timestamp = (int16_t)(jf->timestamp - (*jfp)->timestamp);
/* found double entry */
if (offset_sequence == 0) {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Dropping double packet (sequence = %d)\n", jb->name, sequence);
if (offset_timestamp == 0) {
LOGP(DJITTER, LOGL_DEBUG, "%s Dropping double packet (timestamp = %u)\n", jb->name, jf->timestamp);
jitter_frame_free(jf);
return;
}
/* offset is negative, so we found the position to insert frame */
if (offset_sequence < 0)
if (offset_timestamp < 0)
break;
jfp = &((*jfp)->next);
}
offset_timestamp = timestamp - jb->window_timestamp;
offset_timestamp = jf->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, "%s Frame has offset of %.0fms in jitter buffer.\n", jb->name, (double)offset_timestamp * jb->sample_duration * 1000.0);
#endif
/* measure delay */
if (jb->min_delay_value < 0 || offset_timestamp < jb->min_delay_value)
jb->min_delay_value = offset_timestamp;
if (jb->min_delay < 0 || offset_timestamp < jb->min_delay)
jb->min_delay = offset_timestamp;
/* if frame is too early (delay ceases), shift window to the future */
if (offset_timestamp > jb->max_window_size) {
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too early: Shift jitter buffer to the future, 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_sequence(%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;
jb->window_timestamp = jf->timestamp - jb->max_window_size;
jb->min_delay = -1;
jb->delay_counter = 0.0;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
} 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_sequence(%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);
jb->window_timestamp = jf->timestamp - jb->target_window_size;
jb->min_delay = -1;
jb->delay_counter = 0.0;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
}
}
/* 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_sequence(%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;
jb->window_timestamp = jf->timestamp - jb->target_window_size / 2;
jb->min_delay = -1;
jb->delay_counter = 0.0;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
} 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_sequence(%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);
jb->window_timestamp = jf->timestamp - jb->target_window_size;
jb->min_delay = -1;
jb->delay_counter = 0.0;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
}
}
/* insert or append frame */
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Store frame\n", jb->name);
#include <time.h>
static struct timespec tv;
clock_gettime(CLOCK_REALTIME, &tv);
LOGP(DJITTER, LOGL_DEBUG, "%s Store frame. %ld.%04ld\n", jb->name, tv.tv_sec, tv.tv_nsec / 1000000);
#endif
jf = malloc(sizeof(*jf) + length * jb->sample_size);
if (!jf) {
PDEBUG(DJITTER, DEBUG_ERROR, "No memory for frame.\n");
return;
}
memset(jf, 0, sizeof(*jf)); // note: clear header only
jf->sequence = sequence;
jf->timestamp = timestamp;
memcpy(jf->samples, samples, length * jb->sample_size);
jf->length = length;
jf->next = *jfp;
*jfp = jf;
}
/* get audio from jitterbuffer
*/
void jitter_load(jitter_t *jb, void *samples, int length)
/* get offset to next chunk, return -1, if there is no */
int32_t jitter_offset(jitter_t *jb)
{
jitter_frame_t *jf;
int32_t count, count2, index;
int16_t offset_timestamp = 0;
/* now unlock jitter buffer */
jb->unlocked = true;
/* get timestamp of chunk that is not in the past */
for (jf = jb->frame_list; jf; jf = jf->next) {
offset_timestamp = jf->timestamp - jb->window_timestamp;
if (offset_timestamp >= 0)
break;
}
return (jf) ? offset_timestamp : -1;
}
/* get next data chunk from jitterbuffer */
jitter_frame_t *jitter_load(jitter_t *jb)
{
jitter_frame_t *jf;
int32_t offset_timestamp;
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%sLoad chunk of %d samples.\n", jb->name, length);
static struct timespec tv;
clock_gettime(CLOCK_REALTIME, &tv);
LOGP(DJITTER, LOGL_DEBUG, "%s Load frame. %ld.%04ld\n", jb->name, tv.tv_sec, tv.tv_nsec / 1000000);
#endif
/* now unlock jitter buffer */
jb->unlocked = 1;
jb->unlocked = true;
/* reduce delay */
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);
/* 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);
/* only reduce delay to half of the target window size */
jb->window_timestamp += jb->min_delay_value - jb->target_window_size / 2;
}
jb->delay_counter -= jb->delay_interval;
jb->delay_interval = REPEAT_DELAY_INTERVAL;
jb->min_delay_value = -1;
/* get current chunk, free all chunks that are in the past */
while ((jf = jb->frame_list)) {
offset_timestamp = jf->timestamp - jb->window_timestamp;
if (offset_timestamp >= 0)
break;
/* detach and free */
jb->frame_list = jf->next;
jitter_frame_free(jf);
}
/* process all frames until output buffer is loaded */
while (length) {
/* always get frame with the lowest sequence number (1st frame) */
jf = jb->frame_list;
/* next frame in the future */
if (jf && jf->timestamp != jb->window_timestamp)
return NULL;
if (jf) {
count = jf->timestamp - jb->window_timestamp;
if (count > length)
count = length;
} else
count = length;
/* if there is no frame or we have not reached frame's time stamp, extrapolate */
if (count > 0) {
#ifdef HEAVY_DEBUG
if (jf)
PDEBUG(DJITTER, DEBUG_DEBUG, "%s There is a frame ahead in buffer after %d samples. Interpolating gap.\n", jb->name, jf->timestamp - jb->window_timestamp);
else
PDEBUG(DJITTER, DEBUG_DEBUG, "%s There is no frame ahead in buffer. Interpolating gap.\n", jb->name);
#endif
/* extrapolate by playing the extrapolation buffer */
while (count) {
count2 = count;
if (count2 > jb->extra_size - jb->extra_index)
count2 = jb->extra_size - jb->extra_index;
memcpy(samples, (uint8_t *)jb->extra_samples + jb->extra_index * jb->sample_size, count2 * jb->sample_size);
jb->extra_index += count2;
if (jb->extra_index == jb->extra_size) {
jb->extra_index = 0;
if ((jb->window_flags & JITTER_FLAG_REPEAT) && jb->extra_timeout_count < jb->extra_timeout_max) {
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);
#endif
clear_extra_buffer(jb);
}
}
}
samples = (uint8_t *)samples + count2 * jb->sample_size;
length -= count2;
jb->window_timestamp += count2;
count -= count2;
}
if (length == 0)
return;
}
/* copy samples from frame (what is not in the past) */
index = jb->window_timestamp - jf->timestamp;
while (index < jf->length) {
/* use the lowest value of 'playout length' or 'remaining packet length' */
count = length;
if (jf->length - index < count)
count = jf->length - index;
/* if extrapolation is to be written, limit count to what we can store into buffer */
if ((jb->window_flags & JITTER_FLAG_REPEAT) && jb->extra_size - jb->extra_index < count)
count = jb->extra_size - jb->extra_index;
/* copy samples from packet to play out, increment sample pointer and decrement length */
#ifdef HEAVY_DEBUG
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Copy data (offset=%u count=%u) from frame (sequence=%u timestamp=%u length=%u).\n", jb->name, index, count, jf->sequence, jf->timestamp, jf->length);
#endif
memcpy(samples, (uint8_t *)jf->samples + index * jb->sample_size, count * jb->sample_size);
samples = (uint8_t *)samples + count * jb->sample_size;
length -= count;
/* copy frame data to extrapolation buffer also, increment index */
if ((jb->window_flags & JITTER_FLAG_REPEAT)) {
memcpy((uint8_t *)jb->extra_samples + jb->extra_index * jb->sample_size, (uint8_t *)jf->samples + index * jb->sample_size, count * jb->sample_size);
jb->extra_index += count;
if (jb->extra_index == jb->extra_size)
jb->extra_index = 0;
jb->extra_timeout_count = 0; /* now we have new data, we reset timeout condition */
}
/* increment time stamp */
jb->window_timestamp += count;
index += count;
/* if there was enough to play out, we are done */
if (length == 0)
return;
}
/* free frame, because all samples are now in the past */
/* detach, and return */
if (jf)
jb->frame_list = jf->next;
free(jf);
return jf;
}
/* now go for next loop, in case there is still date to play out */
/* advance time stamp of jitter buffer */
void jitter_advance(jitter_t *jb, uint32_t offset)
{
if (!jb->window_valid)
return;
jb->window_timestamp += offset;
/* increment timer to check delay */
jb->delay_counter += jb->sample_duration * (double)offset;
}
/* load samples from jitter buffer
* store in spl_buf until all copied
* conceal, if frame is missing
* ceate silence, if no spl_buf exists in the first place */
void jitter_load_samples(jitter_t *jb, uint8_t *spl, int len, size_t sample_size, void (*conceal)(uint8_t *spl, int len, void *priv), void *conceal_priv)
{
jitter_frame_t *jf;
int32_t offset;
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void *decoder_priv;
uint8_t *payload;
int payload_len;
int tocopy;
#ifdef VISUAL_DEBUG
int32_t offset_timestamp;
char debug[jb->max_window_size + 32];
int last = 0;
memset(debug, ' ', sizeof(debug));
for (jf = jb->frame_list; jf; jf = jf->next) {
offset_timestamp = jf->timestamp - jb->window_timestamp;
if (offset_timestamp < 0)
continue;
offset_timestamp = (int)((double)offset_timestamp * jb->sample_duration * 1000.0);
debug[offset_timestamp] = '0' + jf->sequence % 10;
last = offset_timestamp + 1;
}
debug[last] = '\0';
LOGP(DJITTER, LOGL_DEBUG, "%s:%s\n", jb->name, debug);
#endif
next_chunk:
/* nothing more to return */
if (!len)
return;
copy_chunk:
/* consume from buffer, if valid */
if (jb->spl_buf && jb->spl_valid) {
tocopy = jb->spl_len - jb->spl_pos;
if (tocopy > len)
tocopy = len;
#ifdef HEAVY_DEBUG
LOGP(DJITTER, LOGL_DEBUG, "%s loading %d samples: from valid sample buffer.\n", jb->name, tocopy);
#endif
/* advance jitter buffer */
jitter_advance(jb, tocopy);
memcpy(spl, jb->spl_buf + jb->spl_pos * sample_size, tocopy * sample_size);
spl += tocopy * sample_size;
len -= tocopy;
jb->spl_pos += tocopy;
if (jb->spl_pos == jb->spl_len) {
jb->spl_pos = 0;
jb->spl_valid = false;
}
goto next_chunk;
}
/* get offset to next frame in jitter buffer */
offset = jitter_offset(jb);
/* jitter buffer is empty, so we must conceal all samples we have */
if (offset < 0)
offset = len;
/* if we have an offset, we need to conceal the samples */
if (offset > 0) {
/* only process as much samples as need */
if (offset > len)
offset = len;
#ifdef HEAVY_DEBUG
LOGP(DJITTER, LOGL_DEBUG, "%s concealing %d samples: from invalid sample buffer.\n", jb->name, offset);
#endif
/* advance jitter buffer */
jitter_advance(jb, offset);
/* if there is no buffer, allocate 20ms, filled with 0 */
if (!jb->spl_buf) {
jb->spl_len = jb->samples_20ms;
jb->spl_buf = calloc(jb->spl_len, sample_size);
}
/* do until all samples are processed */
while (offset) {
tocopy = jb->spl_len - jb->spl_pos;
if (tocopy > offset)
tocopy = offset;
if (conceal)
conceal(jb->spl_buf + jb->spl_pos * sample_size, tocopy, conceal_priv);
memcpy(spl, jb->spl_buf + jb->spl_pos * sample_size, tocopy * sample_size);
spl += tocopy * sample_size;
len -= tocopy;
jb->spl_pos += tocopy;
if (jb->spl_pos == jb->spl_len)
jb->spl_pos = 0;
offset -= tocopy;
}
goto next_chunk;
}
/* load from jitter buffer (it should work, because offset equals 0 */
jf = jitter_load(jb);
if (!jf) {
LOGP(DJITTER, LOGL_ERROR, "%s Failed to get frame from jitter buffer, please fix!\n", jb->name);
jitter_reset(jb);
return;
}
#ifdef HEAVY_DEBUG
LOGP(DJITTER, LOGL_DEBUG, "%s loading new frame to sample buffer.\n", jb->name);
#endif
/* get data from frame */
jitter_frame_get(jf, &decoder, &decoder_priv, &payload, &payload_len, NULL, NULL, NULL, NULL);
/* free previous buffer */
free(jb->spl_buf);
jb->spl_buf = NULL;
jb->spl_pos = 0;
/* decode */
if (decoder) {
decoder(payload, payload_len, &jb->spl_buf, &jb->spl_len, decoder_priv);
} else {
jb->spl_buf = malloc(payload_len);
if (!jb->spl_buf)
return;
jb->spl_len = payload_len / sample_size;
memcpy(jb->spl_buf, payload, payload_len);
}
jb->spl_len = jb->spl_len / sample_size;
jb->spl_valid = true;
/* free jiter frame */
jitter_frame_free(jf);
goto copy_chunk;
}
void jitter_conceal_s16(uint8_t *_spl, int len, void __attribute__((unused)) *priv)
{
int16_t *spl = (int16_t *)_spl;
while (len) {
*spl++ /= 1.5;
len--;
}
}

View File

@ -4,57 +4,64 @@
#define JITTER_FLAG_REPEAT (1 << 1) // repeat audio to extrapolate gaps
/* window settings for low latency audio and extrapolation of gaps */
#define JITTER_AUDIO 0.050, 1.000, JITTER_FLAG_LATENCY | JITTER_FLAG_REPEAT
#define JITTER_AUDIO 0.060, 1.000, JITTER_FLAG_LATENCY | JITTER_FLAG_REPEAT
/* window settings for analog data (fax/modem) or digial data (HDLC) */
#define JITTER_DATA 0.100, 0.200, JITTER_FLAG_NONE
typedef struct jitter_frame {
struct jitter_frame *next;
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv);
void *decoder_priv;
uint8_t marker;
uint16_t sequence;
uint32_t timestamp;
int length;
uint8_t samples[0];
uint32_t ssrc;
int size;
uint8_t data[0];
} jitter_frame_t;
typedef struct jitter {
char name[64];
/* sample properties */
int sample_size;
double sample_duration;
/* automatic sequence generation */
uint16_t next_sequence;
uint32_t next_timestamp;
/* frame properties */
double sample_duration; /* duration of a frame (ms) */
int samples_20ms; /* samples to compensate a gap of unknown size */
/* window properties */
int unlocked;
uint32_t window_flags;
int target_window_size;
int max_window_size;
int window_valid;
uint32_t window_ssrc;
uint32_t window_timestamp;
bool unlocked; /* jitter buffer will be locked until some reads from it */
uint32_t window_flags; /* flags to alter behaviour of jitter buffer */
int target_window_size; /* target size of window (frames) */
int max_window_size; /* maximum size of window (frames) */
bool window_valid; /* set, if first frame has been received */
uint32_t window_ssrc; /* current sync source of window */
uint32_t window_timestamp; /* lowest timestamp number in window */
/* reduction of delay */
double delay_interval;
double delay_counter;
int32_t min_delay_value;
/* extrapolation */
int extra_size;
int extra_index;
void *extra_samples;
int extra_timeout_max;
int extra_timeout_count;
double delay_interval; /* interval for delay measurement (seconds) */
double delay_counter; /* current counter to count interval (seconds) */
int min_delay; /* minimum delay measured during interval (frames) */
/* list of frames */
jitter_frame_t *frame_list;
/* sample buffer (optional) */
uint8_t *spl_buf; /* current samples buffer */
int spl_pos; /* position of in buffer */
int spl_len; /* total buffer size */
bool spl_valid; /* if buffer has valid frame (not repeated) */
} jitter_t;
int jitter_create(jitter_t *jb, const char *name, double samplerate, int sample_size, double target_window_duration, double max_window_duration, uint32_t window_flags);
int jitter_create(jitter_t *jb, const char *name, double samplerate, double target_window_duration, double max_window_duration, uint32_t window_flags);
void jitter_reset(jitter_t *jb);
void jitter_destroy(jitter_t *jb);
void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint16_t sequence, uint32_t timestamp, uint32_t ssrc);
void jitter_load(jitter_t *jb, void *samples, int length);
jitter_frame_t *jitter_frame_alloc(void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void *decoder_priv, uint8_t *data, int size, uint8_t marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc);
void jitter_frame_free(jitter_frame_t *jf);
void jitter_frame_get(jitter_frame_t *jf, void (**decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len, void *priv), void **decoder_priv, uint8_t **data, int *size, uint8_t *marker, uint16_t *sequence, uint32_t *timestamp, uint32_t *ssrc);
void jitter_save(jitter_t *jb, jitter_frame_t *jf);
int32_t jitter_offset(jitter_t *jb);
jitter_frame_t *jitter_load(jitter_t *jb);
void jitter_advance(jitter_t *jb, uint32_t offset);
void jitter_load_samples(jitter_t *jb, uint8_t *spl, int len, size_t sample_size, void (*conceal)(uint8_t *spl, int len, void *priv), void *conceal_priv);
void jitter_conceal_s16(uint8_t *_spl, int len, void __attribute__((unused)) *priv);

View File

@ -0,0 +1,8 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = liblogging.a
liblogging_a_SOURCES = \
logging.c \
categories.c

View File

@ -0,0 +1,42 @@
#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",
},
[DDSP] = {
.name = "DDSP",
.description = "digital signal processing",
.color = "\033[0;31m",
},
[DPH] = {
.name = "DPH",
.description = "PH SAP socket interface",
.color = "\033[0;33m",
},
[DTEL] = {
.name = "DTEL",
.description = "Telephone application",
.color = "\033[1;34m",
},
};
size_t log_categories_size = ARRAY_SIZE(log_categories);

View File

@ -0,0 +1,13 @@
enum {
DLCC,
DOPTIONS,
DJITTER,
DDSP,
DPH,
DTEL,
};
extern struct log_info_cat log_categories[];
extern size_t log_categories_size;

264
src/liblogging/logging.c Normal file
View File

@ -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);
}

21
src/liblogging/logging.h Normal file
View File

@ -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);

View File

@ -23,7 +23,9 @@
#include <stdlib.h>
#include <errno.h>
#include "options.h"
#include "../libdebug/debug.h"
#include "../liblogging/logging.h"
const char *selected_config_file = NULL;
typedef struct option {
struct option *next;
@ -47,7 +49,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;
@ -62,21 +64,21 @@ void option_add(int short_option, const char *long_option, int parameter_count)
option_t *option;
/* check if option already exists or is not allowed */
if (!strcmp(long_option, "config") || !strcmp(long_option, "no-config")) {
LOGP(DOPTIONS, LOGL_ERROR, "Option '%s' is not allowed to add, please fix!\n", option->long_option);
abort();
}
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);
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();
}
@ -87,7 +89,7 @@ void option_add(int short_option, const char *long_option, int parameter_count)
option_tailp = &(option->next);
}
int options_config_file(int argc, char *argv[], const char *config_file, int (*handle_options)(int short_option, int argi, char *argv[]))
int options_config_file(int argc, char *argv[], const char *_config_file, int (*handle_options)(int short_option, int argi, char *argv[]))
{
static const char *home;
char config[256];
@ -101,21 +103,29 @@ int options_config_file(int argc, char *argv[], const char *config_file, int (*h
/* select for alternative config file */
if (argc > 2 && !strcmp(argv[1], "--config"))
config_file = argv[2];
selected_config_file = argv[2];
else
selected_config_file = _config_file;
/* select for alternative config file */
if (argc > 1 && !strcmp(argv[1], "--no-config")) {
selected_config_file = NULL;
return 1;
}
/* add home directory */
if (config_file[0] == '~' && config_file[1] == '/') {
if (selected_config_file[0] == '~' && selected_config_file[1] == '/') {
home = getenv("HOME");
if (home == NULL)
return 1;
sprintf(config, "%s/%s", home, config_file + 2);
sprintf(config, "%s/%s", home, selected_config_file + 2);
} else
strcpy(config, config_file);
strcpy(config, selected_config_file);
/* 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 +211,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, selected_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, selected_config_file, line, option->parameter_count);
return -EINVAL;
}
rc = handle_options(option->short_option, 0, args);
@ -242,20 +252,22 @@ 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;
continue;
}
if (!strcmp(argv[argi], "--no-config"))
continue;
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 +277,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 +291,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 +318,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;
}
}

View File

@ -1,4 +1,5 @@
extern const char *selected_config_file;
char *options_strdup(const char *s);
void option_add(int short_option, const char *long_option, int parameter_count);
int options_config_file(int argc, char *argv[], const char *config_file, int (*handle_options)(int short_option, int argi, char *argv[]));

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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 */

View File

@ -1,194 +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)
{
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;
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)
{
char sdp[65536];
osmo_cc_session_media_t *media;
int rc;
if (!(*session_p)) {
LOGP(DCC, LOGL_ERROR, "Session not set, please fix!\n");
abort();
}
/* once done, just ignore further messages that reply to setup */
if (*codec_p)
return 0;
/* 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) {
/* only audio */
if (media->description.type != osmo_cc_session_media_type_audio)
continue;
/* select first codec, if one was accpeted */
if (media->codec_list)
*codec_p = media->codec_list;
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;
}

View File

@ -1,13 +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);
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p);

File diff suppressed because it is too large Load Diff

View File

@ -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 */

View File

@ -1,484 +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);
}
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;
}
}

View File

@ -1,7 +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_close(osmo_cc_session_media_t *media);

View File

@ -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;
}

View File

@ -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);

View File

@ -1,545 +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)
{
/* 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 (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 (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) {
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)
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) {
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++;
}
}

View File

@ -1,6 +0,0 @@
char *osmo_cc_session_gensdp(struct osmo_cc_session *session);
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);

View File

@ -1,625 +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);
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);
}
/* remove codecs/media that have not been accepted and generate SDP */
const char *osmo_cc_session_send_answer(osmo_cc_session_t *session)
{
osmo_cc_session_media_t *media;
osmo_cc_session_codec_t *codec, **codec_p;
const char *sdp;
int rc;
LOGP(DCC, LOGL_DEBUG, "Generating session answer.\n");
/* loop all media */
osmo_cc_session_for_each_media(session->media_list, media) {
/* remove unaccepted codecs */
codec_p = &media->codec_list;
codec = *codec_p;
while (codec) {
if (!codec->accepted) {
osmo_cc_free_codec(codec);
codec = *codec_p;
continue;
}
codec_p = &codec->next;
codec = *codec_p;
}
/* mark media as unused, if no codec or not accepted */
if (!media->accepted || !media->codec_list)
media->description.port_local = 0;
}
rc = osmo_cc_session_check(session, 0);
if (rc < 0) {
LOGP(DCC, LOGL_ERROR, "Please fix!\n");
abort();
}
sdp = osmo_cc_session_gensdp(session);
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) {
osmo_cc_free_codec(codec_local);
codec_local = *codec_local_p;
continue;
}
/* copy remote codec information */
codec_local->payload_type_remote = codec_remote->payload_type_remote;
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;
}
/* remove media with port == 0 or no codec at all */
media_local_p = &session_local->media_list;
media_local = *media_local_p;
while (media_local) {
if (media_local->description.port_remote == 0 || !media_local->codec_list) {
osmo_cc_free_media(media_local);
media_local = *media_local_p;
continue;
}
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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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 */

View File

@ -3,9 +3,11 @@
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
* All Rights Reserved
*
* This program is free software: you can redistribute it and/or modify
* SPDX-License-Identifier: GPL-2.0+
*
* 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
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
@ -13,8 +15,10 @@
* 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/>.
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <stdio.h>
@ -26,9 +30,10 @@
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "../libtimer/timer.h"
#include "../libselect/select.h"
#include "../libdebug/debug.h"
#include <osmocom/core/utils.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include "../liblogging/logging.h"
#include "ph_socket.h"
static int ph_socket_listen_cb(struct osmo_fd *ofd, unsigned int __attribute__((unused)) what);

View File

@ -4,12 +4,12 @@
/*
* Procedure:
*
* If socket connection is establised, a PH_PRIM_CTRL_REQ message with
* If socket connection is established, a PH_PRIM_CTRL_REQ message with
* PH_CTRL_ENABLE information is received by the socket server user.
* If the socket connection is lost, a PH_PRIM_CTRL_REQ message with
* PH_CTRL_DISABLE information is received by the user.
*
* If socket connection is establised, a PH_PRIM_CTRL_IND message with
*
* If socket connection is established, a PH_PRIM_CTRL_IND message with
* PH_CTRL_ENABLE information is received by the socket client user.
* If the socket connection is lost, a PH_PRIM_CTRL_IND message with
* PH_CTRL_DISABLE information is received by the user.
@ -24,14 +24,14 @@
* PH_PRIM_DACT_IND, if the socket is currently unavailable.
*
* PH_PRIM_CTRL_REQ and PH_PRIM_CTRL_IND messages with PH_CTRL_ENABLE
* and PH_CTRL_DISABLE informations are not assoicated with a channel
* and PH_CTRL_DISABLE information are not associated with a channel
* number. The socket sender shall set it to 0, the receiver shall
* ignore it.
*
* A missing MODE in PH_PRIM_ACT_REQ is interepreted as default:
* A missing MODE in PH_PRIM_ACT_REQ is interpreted as default:
* HDLC on D-channel, TRANS on B-channel.
*
* Each packet on the socket shall have the follwoing header:
* Each packet on the socket shall have the following header:
* uint8_t channel;
* uint8_t prim;
* uint16_t length;
@ -65,8 +65,8 @@
#define PH_CTRL_BLOCK 0x00 /* disable (block) interface, when socket is disconnected */
#define PH_CTRL_UNBLOCK 0x01 /* enable (unblock) interface, when socket is connected */
#define PH_CTRL_LOOP_DISABLE 0x04 /* disable loopback */
#define PH_CTRL_LOOP1_ENABLE 0x05 /* enable LT transceier loopback */
#define PH_CTRL_LOOP2_ENABLE 0x06 /* enable NT transceier loopback */
#define PH_CTRL_LOOP1_ENABLE 0x05 /* enable LT transceiver loopback */
#define PH_CTRL_LOOP2_ENABLE 0x06 /* enable NT transceiver loopback */
#define PH_CTRL_LOOP_ERROR 0x10 /* frame error report (loopback test) */
#define PH_CTRL_VIOLATION_LT 0x11 /* code violation received by LT */
#define PH_CTRL_VIOLATION_NT 0x12 /* code violation received by NT */

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libselect.a
libselect_a_SOURCES = \
select.c

View File

@ -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;
}

View File

@ -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);

View File

@ -1,6 +0,0 @@
AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
noinst_LIBRARIES = libtimer.a
libtimer_a_SOURCES = \
timer.c

View File

@ -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);
}

View File

@ -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);

View File

@ -11,7 +11,6 @@ osmo_cc_pstn_endpoint_SOURCES = \
osmo_cc_pstn_endpoint_LDADD = \
$(COMMON_LA) \
../libdebug/libdebug.a \
../liboptions/liboptions.a \
../libsample/libsample.a \
../libfilter/libfilter.a \
@ -19,11 +18,10 @@ osmo_cc_pstn_endpoint_LDADD = \
../libfsk/libfsk.a \
../libfm/libfm.a \
../libfilter/libfilter.a \
../libtimer/libtimer.a \
../libselect/libselect.a \
../libjitter/libjitter.a \
../libosmocc/libosmocc.a \
../libg711/libg711.a \
../libph_socket/libph_socket.a \
../liblogging/liblogging.a \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCC_LIBS) \
-lm

View File

@ -30,9 +30,9 @@
#include <errno.h>
#include <time.h>
#include "../libsample/sample.h"
#include "../libdebug/debug.h"
#include "../libtimer/timer.h"
#include "../libosmocc/message.h"
#include "../liblogging/logging.h"
#include <osmocom/core/timer.h>
#include <osmocom/cc/message.h>
#include "callerid.h"
#define db2level(db) pow(10, (double)(db) / 20.0)
@ -138,7 +138,7 @@ int callerid_init(callerid_t *cid, int samplerate, int bell, char dtmf)
}
rc = fsk_mod_init(&cid->fsk, cid, fsk_send_bit, samplerate, FSK_BAUD_RATE, f0, f1, db2level(FSK_TX_DBV + DBV_TO_DBM), 0, 1);
if (rc < 0) {
PDEBUG(DDSP, DEBUG_ERROR, "FSK init failed!\n");
LOGP(DDSP, LOGL_ERROR, "FSK init failed!\n");
return rc;
}
} else {
@ -172,12 +172,21 @@ static uint8_t *add_ie(uint8_t *p, uint8_t type, int len, const uint8_t *val)
return p + len;
}
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;
}
/* set caller ID and start transmission */
int callerid_set(callerid_t *cid, int cw, int dt_as, const char *callerid, uint8_t caller_type, int use_date)
{
int clen = strlen(callerid);
if (clen > 32) {
PDEBUG(DDSP, DEBUG_ERROR, "Callerid too long!\n");
LOGP(DDSP, LOGL_ERROR, "Callerid too long!\n");
return -EINVAL;
}
@ -192,11 +201,11 @@ int callerid_set(callerid_t *cid, int cw, int dt_as, const char *callerid, uint8
uint8_t data[8];
if (caller_type == OSMO_CC_PRESENT_RESTRICTED)
PDEBUG(DDSP, DEBUG_INFO, "Sending restricted caller ID reason.\n");
LOGP(DDSP, LOGL_INFO, "Sending restricted caller ID reason.\n");
else if (caller_type == OSMO_CC_PRESENT_ALLOWED && callerid[0])
PDEBUG(DDSP, DEBUG_INFO, "Sending caller ID '%s' via FSK.\n", callerid);
LOGP(DDSP, LOGL_INFO, "Sending caller ID '%s' via FSK.\n", callerid);
else
PDEBUG(DDSP, DEBUG_INFO, "Sending unavailable caller ID reason.\n");
LOGP(DDSP, LOGL_INFO, "Sending unavailable caller ID reason.\n");
p = cid->data + 2;
if (use_date) {
@ -243,20 +252,20 @@ int callerid_set(callerid_t *cid, int cw, int dt_as, const char *callerid, uint8
if (dt_as) {
cid->state = CID_STATE_WAIT_DTAS;
cid->wait = (int)(WAIT_CW_DTAS * (double)cid->samplerate);
PDEBUG(DDSP, DEBUG_DEBUG, "Start sending callerid '%s' via FSK, waiting to start DT-AS transmission.\n", callerid);
LOGP(DDSP, LOGL_DEBUG, "Start sending callerid '%s' via FSK, waiting to start DT-AS transmission.\n", callerid);
} else {
cid->state = CID_STATE_WAIT_FSK;
cid->wait = (int)(WAIT_RING_FSK * (double)cid->samplerate);
PDEBUG(DDSP, DEBUG_DEBUG, "Start sending callerid '%s' via FSK, waiting to start FSK transmission.\n", callerid);
LOGP(DDSP, LOGL_DEBUG, "Start sending callerid '%s' via FSK, waiting to start FSK transmission.\n", callerid);
}
} else {
if (caller_type == OSMO_CC_PRESENT_RESTRICTED) {
PDEBUG(DDSP, DEBUG_INFO, "Not sending restricted caller ID.\n");
LOGP(DDSP, LOGL_INFO, "Not sending restricted caller ID.\n");
return 0;
} else if (caller_type == OSMO_CC_PRESENT_ALLOWED && callerid[0]) {
PDEBUG(DDSP, DEBUG_INFO, "Sending caller ID '%s' via DTMF.\n", callerid);
LOGP(DDSP, LOGL_INFO, "Sending caller ID '%s' via DTMF.\n", callerid);
} else {
PDEBUG(DDSP, DEBUG_INFO, "Not sending unavailable caller ID.\n");
LOGP(DDSP, LOGL_INFO, "Not sending unavailable caller ID.\n");
return 0;
}
cid->data[0] = cid->use_dtmf;
@ -265,7 +274,7 @@ int callerid_set(callerid_t *cid, int cw, int dt_as, const char *callerid, uint8
cid->pos = 0;
cid->wait = (int)(WAIT_RING_DTMF * (double)cid->samplerate);
cid->state = CID_STATE_WAIT_DTMF;
PDEBUG(DDSP, DEBUG_DEBUG, "Start sending callerid '%s' via DTMF, waiting to start DTMF transmission.\n", callerid);
LOGP(DDSP, LOGL_DEBUG, "Start sending callerid '%s' via DTMF, waiting to start DTMF transmission.\n", callerid);
}
return 0;
@ -278,11 +287,11 @@ void callerid_te_ack(callerid_t *cid, char digit)
return;
if (digit < 'A' || digit > 'D') {
PDEBUG(DDSP, DEBUG_DEBUG, "Ignoring digit '%c', this is not a valid TE-ACK signal.\n", digit);
LOGP(DDSP, LOGL_DEBUG, "Ignoring digit '%c', this is not a valid TE-ACK signal.\n", digit);
return;
}
PDEBUG(DDSP, DEBUG_DEBUG, "Received valid TE-ACK digit '%c', wait to send FSK transmission.\n", digit);
LOGP(DDSP, LOGL_DEBUG, "Received valid TE-ACK digit '%c', wait to send FSK transmission.\n", digit);
/* wait for FSK */
cid->state = CID_STATE_WAIT_FSK;
cid->wait = (int)(WAIT_TEACK_FSK * (double)cid->samplerate);
@ -324,23 +333,23 @@ again:
cid->dtas_phase65536[0] = 0.0;
cid->dtas_phase65536[1] = 0.0;
cid->dt_as_count = (int)(((cid->cw) ? DTAS_CW_DURATION : DTAS_DURATION) * (double)cid->samplerate);
PDEBUG(DDSP, DEBUG_DEBUG, "Now start DT_AS transmission.\n");
LOGP(DDSP, LOGL_DEBUG, "Now start DT_AS transmission.\n");
break;
case CID_STATE_WAIT_DTMF:
cid->state = CID_STATE_SEND_DTMF;
PDEBUG(DDSP, DEBUG_DEBUG, "Now start DTMF transmission.\n");
LOGP(DDSP, LOGL_DEBUG, "Now start DTMF transmission.\n");
dtmf_encode_set_tone(&cid->dtmf, cid->data[cid->pos++], DTMF_TONE_ON, DTMF_TONE_OFF);
break;
case CID_STATE_WAIT_FSK:
cid->state = CID_STATE_SEND_FSK;
PDEBUG(DDSP, DEBUG_DEBUG, "Now start FSK transmission.\n");
LOGP(DDSP, LOGL_DEBUG, "Now start FSK transmission.\n");
break;
case CID_STATE_WAIT_TE_ACK:
PDEBUG(DDSP, DEBUG_INFO, "No TE-ACK received. Phone does not seem to support off-hook caller ID.\n");
LOGP(DDSP, LOGL_INFO, "No TE-ACK received. Phone does not seem to support off-hook caller ID.\n");
cid->state = CID_STATE_NULL;
break;
case CID_STATE_WAIT_END:
PDEBUG(DDSP, DEBUG_DEBUG, "Reestablish audio.\n");
LOGP(DDSP, LOGL_DEBUG, "Reestablish audio.\n");
cid->state = CID_STATE_NULL;
break;
default:
@ -367,12 +376,12 @@ again:
if (cid->dt_as_count == 0) {
if (cid->cw) {
/* wait for TE ACK */
PDEBUG(DDSP, DEBUG_DEBUG, "DT-AS transmission done, waiting for TE-ACK.\n");
LOGP(DDSP, LOGL_DEBUG, "DT-AS transmission done, waiting for TE-ACK.\n");
cid->state = CID_STATE_WAIT_TE_ACK;
cid->wait = (int)(WAIT_DTAS_TEACK * (double)cid->samplerate);
} else {
/* wait for FSK */
PDEBUG(DDSP, DEBUG_DEBUG, "DT-AS transmission done, waiting for FSK.\n");
LOGP(DDSP, LOGL_DEBUG, "DT-AS transmission done, waiting for FSK.\n");
cid->state = CID_STATE_WAIT_FSK;
cid->wait = (int)(WAIT_DTAS_FSK * (double)cid->samplerate);
}
@ -387,7 +396,7 @@ again:
if (!length)
break;
if (!cid->data[cid->pos]) {
PDEBUG(DDSP, DEBUG_DEBUG, "DTMF transmission done, waiting to re-establish audio.\n");
LOGP(DDSP, LOGL_DEBUG, "DTMF transmission done, waiting to re-establish audio.\n");
cid->wait = (int)(WAIT_DTMF_END * (double)cid->samplerate);
cid->state = CID_STATE_WAIT_END;
break;
@ -401,9 +410,9 @@ again:
length -= ret;
if (length) {
if (cid->cw)
PDEBUG(DDSP, DEBUG_DEBUG, "FSK transmission done, waiting to re-establish audio.\n");
LOGP(DDSP, LOGL_DEBUG, "FSK transmission done, waiting to re-establish audio.\n");
else
PDEBUG(DDSP, DEBUG_DEBUG, "FSK transmission done, waiting to send ringing.\n");
LOGP(DDSP, LOGL_DEBUG, "FSK transmission done, waiting to send ringing.\n");
cid->wait = (int)(WAIT_FSK_END * (double)cid->samplerate);
cid->state = CID_STATE_WAIT_END;
break;

View File

@ -26,9 +26,9 @@
#include <stdlib.h>
#include <termios.h>
#include <sched.h>
#include "../libdebug/debug.h"
#include "../liblogging/logging.h"
#include "../liboptions/options.h"
#include "../libg711/g711.h"
#include <osmocom/cc/g711.h>
#include "pstn.h"
pstn_t *pstn_ep = NULL;
@ -47,7 +47,13 @@ static enum pstn_cid_method clip = CID_METHOD_NONE;
static int cid_bell = 0;
static int cid_dtmf = 0;
static int clip_date = 0;
static int no_dtmf = 0;
static int enblock = 4;
static int loop_disconnect = 0;
static int lr_on_connect = 0;
static int metering = 0;
static int lr_metering = 0;
static enum pstn_dialect pstn_dialect = PSTN_DIALECT_GERMAN;
static int recall = 0;
static int ringing_types_incoming[SUBSCRIBER_MAX] = { 0 };
static int ringing_type_incoming_num = 0;
@ -71,7 +77,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(" -s --socket <name>\n");
printf(" Abstract UNIX socket that provides layer 1 connection to a PSTN device\n");
printf(" -n --name <interface name>\n");
@ -101,6 +107,8 @@ static void print_help()
printf(" --cid-dtmf D\n");
printf(" Use DTMF with given start digit 'D' for default, rather than FSK.\n");
printf(" 'D' is used in Taiwan, 'A' in Brazil...\n");
printf(" --no-dtmf\n");
printf(" Disable DTMF dialing if you have a rotary dial (pay)phone.\n");
printf(" --enblock off | <seconds>\n");
printf(" Enable en-block dialing, to collect number before setup. The value\n");
printf(" given is the number of seconds to wait for more digits to be dialed.\n");
@ -111,13 +119,26 @@ static void print_help()
printf(" and yyyy must have equal length. If one of the given numbers are dialed,\n");
printf(" the en-block dialing is complete and there is no need to wait for the\n");
printf(" timeout.\n");
printf(" --loop-disconnect\n");
printf(" Enable open loop (battery off) pulse on disconnect.\n");
printf(" --lr-on-connect\n");
printf(" Enable line polarity reversal on connect, restore normal polarity on disconnect.\n");
printf(" Excludes option --lr-metering.\n");
printf(" --metering\n");
printf(" Enable metering pulses using V5 metering pulsed signal (usually 12/16 kHz).\n");
printf(" --lr-metering\n");
printf(" Enable metering pulses using V5 reversed battery (line reversal) steady signal.\n");
printf(" Excludes option --lr-on-connect.\n");
printf(" --pstn-dialect de | uk\n");
printf(" V5 PSTN protocol dialect (default = de).\n");
printf(" Dialect uk is required for options --lr-metering, --lr-on-connect and --loop-disconnect.\n");
printf(" --recall\n");
printf(" Enable recall / call waiting. (disabled by default)\n");
printf(" -R --ringing-type-incoming <type>\n");
printf(" Cadenced ringing for incoming call. (default = %d)\n", ringing_types_incoming[0]);
printf(" --ringing-type-hold <type>\n");
printf(" Cadenced ringing for call on hold. (default = %d)\n", ringing_type_hold);
printf(" -T --local-tones german | oldgerman | american\n");
printf(" -T --local-tones german | oldgerman | morsegerman | american\n");
printf(" Send locally generated tones, if not provided by remote interface.\n");
printf(" DTMF may not work with old German tones, because they might interfer!\n");
printf(" --ulaw\n");
@ -129,7 +150,7 @@ static void print_help()
printf(" digit '0', two pulses are digit '1', and so on.\n");
printf(" --new-zealand\n");
printf(" Translate pulses from New Zealand rotary phone into digits. Ten pulses\n");
printf(" area '0', nine pulses digit '9', and so on.\n");
printf(" are digit '0', nine pulses are digit '1', and so on.\n");
printf(" -r --realtime <prio>\n");
printf(" Set prio: 0 to disable, 99 for maximum (default = %d)\n", rt_prio);
printf(" -C --cc \"<osmo-cc arg>\" [--cc ...]\n");
@ -167,6 +188,12 @@ static void print_recall()
#define OPT_SERVING 266
#define OPT_SWEDEN 267
#define OPT_NEWZEALAND 268
#define OPT_PSTN_DIALECT 269
#define OPT_METERING 270
#define OPT_LOOP_DISCONNECT 271
#define OPT_LR_ON_CONNECT 272
#define OPT_LR_METERING 273
#define OPT_NO_DTMF 274
static void add_options(void)
{
@ -182,10 +209,16 @@ static void add_options(void)
option_add(OPT_TX_CID_DTMF, "cid-dtmf", 1);
option_add(OPT_TX_ENBLOCK, "enblock", 1);
option_add('D', "dial-hint", 1);
option_add(OPT_NO_DTMF, "no-dtmf", 0);
option_add(OPT_LOOP_DISCONNECT, "loop-disconnect", 0);
option_add(OPT_LR_ON_CONNECT, "lr-on-connect", 0);
option_add(OPT_METERING, "metering", 0);
option_add(OPT_LR_METERING, "lr-metering", 0);
option_add(OPT_PSTN_DIALECT, "pstn-dialect", 1);
option_add(OPT_TX_RECALL, "recall", 0);
option_add(OPT_TX_RING_I, "ringing-type-incoming", 1);
option_add(OPT_TX_RING_H, "ringing-type-hold", 1);
option_add('T', "local-tones", 0);
option_add('T', "local-tones", 1);
option_add(OPT_ULAW, "ulaw", 0);
option_add(OPT_SERVING, "serving-location", 1);
option_add(OPT_SWEDEN, "sweden", 0);
@ -204,11 +237,9 @@ static int handle_options(int short_option, int argi, char **argv)
print_help();
return 0;
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;
@ -260,6 +291,31 @@ static int handle_options(int short_option, int argi, char **argv)
case OPT_TX_ENBLOCK:
enblock = atoi(argv[argi]);
break;
case OPT_LOOP_DISCONNECT:
loop_disconnect = 1;
break;
case OPT_LR_ON_CONNECT:
lr_on_connect = 1;
break;
case OPT_METERING:
metering = 1;
break;
case OPT_LR_METERING:
lr_metering = 1;
break;
case OPT_PSTN_DIALECT:
if (!strcasecmp(argv[argi], "de"))
pstn_dialect = PSTN_DIALECT_GERMAN;
else if (!strcasecmp(argv[argi], "uk"))
pstn_dialect = PSTN_DIALECT_UK;
else {
fprintf(stderr, "Invalid PSTN dialect.\n");
return -EINVAL;
}
break;
case OPT_NO_DTMF:
no_dtmf = 1;
break;
case OPT_TX_RECALL:
recall = 1;
break;
@ -287,6 +343,8 @@ static int handle_options(int short_option, int argi, char **argv)
tones_type = TONES_TYPE_GERMAN;
else if (!strcasecmp(argv[argi], "oldgerman"))
tones_type = TONES_TYPE_OLDGERMAN;
else if (!strcasecmp(argv[argi], "morsegerman"))
tones_type = TONES_TYPE_MORSEGERMAN;
else {
fprintf(stderr, "Invalid tones type given!\n");
return -EINVAL;
@ -339,6 +397,8 @@ int main(int argc, char *argv[])
{
int argi, rc;
logging_init();
/* init codecs */
g711_init();
@ -361,10 +421,23 @@ int main(int argc, char *argv[])
goto error;
}
if (lr_metering && lr_on_connect) {
fprintf(stderr, "You cannot use lr-metering and lr-on-connect together.\n");
goto error;
}
if((lr_metering || lr_on_connect || loop_disconnect) && pstn_dialect != PSTN_DIALECT_UK) {
fprintf(stderr, "Options lr-metering, lr-on-connect and loop-disconnect require pstn-dialect uk.\n");
goto error;
}
/* change tones to ulaw */
if (law == 'u')
isdn_tone_generate_ulaw_samples();
/* generate conealment table */
init_law(law);
/* check subscribers and their ringing types */
if (subscriber_num == 0)
subscriber_num = 1;
@ -379,9 +452,9 @@ int main(int argc, char *argv[])
if (!pstn_ep)
goto error;
rc = pstn_init(pstn_ep, name, socketname, subscribers, subscriber_num, serving_location, tx_delay, clip, cid_bell, cid_dtmf, clip_date, enblock, recall, ringing_types_incoming, ringing_type_hold, tones_type, law, pulse_coding);
rc = pstn_init(pstn_ep, name, socketname, subscribers, subscriber_num, serving_location, tx_delay, clip, cid_bell, cid_dtmf, clip_date, enblock, recall, ringing_types_incoming, ringing_type_hold, tones_type, law, pulse_coding, pstn_dialect, metering, lr_on_connect, loop_disconnect, lr_metering, no_dtmf);
if (rc) {
PDEBUG(DTEL, DEBUG_ERROR, "Endpoint initializing failed!\n");
LOGP(DTEL, LOGL_ERROR, "Endpoint initializing failed!\n");
goto error;
}
@ -418,21 +491,12 @@ int main(int argc, char *argv[])
while (!quit) {
int work;
double timeout;
/* handle all handlers until done */
do {
work = 0;
work |= osmo_cc_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 */
work = osmo_fd_select(timeout);
work = osmo_select_main(0);
}
signal(SIGINT, SIG_DFL);

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
#include "../libtimer/timer.h"
#include "../libselect/select.h"
#include "../libosmocc/endpoint.h"
#include <osmocom/core/timer.h>
#include <osmocom/core/select.h>
#include <osmocom/cc/endpoint.h>
#include "../libsample/sample.h"
#include "../libjitter/jitter.h"
#include "../libdtmf/dtmf_decode.h"
@ -44,10 +44,14 @@ enum pstn_v5_signal {
PSTN_V5_STEADY_SIGNAL_REVERSED = 0x01,
PSTN_V5_STEADY_SIGNAL_OFF_HOOK = 0x04,
PSTN_V5_STEADY_SIGNAL_ON_HOOK = 0x05,
PSTN_V5_STEADY_SIGNAL_NOBAT = 0x0b,
PSTN_V5_STEADY_SIGNAL_STOP_RING = 0x0e,
PSTN_V5_PULSED_SIGNAL_INIT_RING = 0x79,
PSTN_V5_PULSED_SIGNAL_REG_RECAL = 0x76,
PSTN_V5_PULSED_SIGNAL_ON_HOOK = 0x7c,
PSTN_V5_PULSED_SIGNAL_METERING = 0x78,
PSTN_V5_PULSED_SIGNAL_NOBAT = 0x7a,
PSTN_V5_PULSED_SIGNAL_REVBAT = 0x7e,
};
enum timer_ident {
@ -90,6 +94,7 @@ enum tones_type {
TONES_TYPE_AMERICAN,
TONES_TYPE_GERMAN,
TONES_TYPE_OLDGERMAN,
TONES_TYPE_MORSEGERMAN,
};
enum pulse_coding {
@ -98,6 +103,11 @@ enum pulse_coding {
PULSE_NEWZEALAND,
};
enum pstn_dialect {
PSTN_DIALECT_GERMAN,
PSTN_DIALECT_UK,
};
struct pstn;
struct call {
@ -109,6 +119,9 @@ struct call {
osmo_cc_session_t *cc_session;
osmo_cc_session_codec_t *codec;
int on_hold; /* track hold/retrieval notification */
uint16_t metering_connect_units;
struct timeval metering_unit_period;
struct osmo_timer_list metering_timer;
};
struct dial_hint {
@ -131,6 +144,7 @@ typedef struct pstn {
int clip_date; /* send date with caller ID */
int cid_bell; /* use V.23 or Bell 202 FSK tones */
int cid_dtmf; /* use DTMF instead of FSK caller ID */
int no_dtmf; /* disable dtmf dial detection */
int enblock; /* receive digits before transmitting them via SETUP message (timeout as given) */
struct dial_hint dial_hints; /* list of numbers that are complete */
int recall; /* support recall / waiting call */
@ -138,10 +152,15 @@ typedef struct pstn {
int ringing_type_hold; /* cadenced rining on waiting call */
enum tones_type tones_type; /* what tone set to use */
enum pulse_coding pulse_coding; /* pulse mapping */
enum pstn_dialect pstn_dialect; /* V5 national PSTN dialect */
int metering; /* metering pulses */
int lr_metering; /* use line reversal pulses instead of sinusoidal pulses */
int lr_on_connect; /* line reversal on connect */
int loop_disconnect; /* Loop current off-pulse on disconnect */
/* states */
enum pstn_state state;
struct timer timer; /* timer for enblock dialing */
struct osmo_timer_list timer; /* timer for enblock dialing */
enum timer_ident timer_ident; /* why is the timer running */
char dialing[33]; /* register for en-block dialing */
int reversed; /* if polarity is reversed */
@ -151,6 +170,9 @@ typedef struct pstn {
int callerid_wait1; /* wait (number of samples) until transmitting caller ID */
int callerid_wait2; /* wait (number of samples) after transmitting caller ID */
int b_transmitting; /* transmitting on b-channel */
struct osmo_timer_list metering_lr_timer; /* line reversal timer for metering pulse generation */
int metering_units_charged; /* amount of charged line reversal metering pulses */
int metering_units_sent; /* amount of transmitted line reversal metering pulses */
/* v5 interface */
ph_socket_t ph_socket;
@ -168,10 +190,11 @@ typedef struct pstn {
int tx_buffer_pos; /* current position in transmit audio buffer */
} pstn_t;
void init_law(char law);
int add_dial_hint(const char *arg);
void purge_dial_hints(void);
void pstn_destroy(pstn_t *pstn_ep);
pstn_t *pstn_create(void);
int pstn_init(pstn_t *pstn, const char *name, const char *socketname, const char **subscribers, int subscriber_num, uint8_t serving_location, int tx_delay, enum pstn_cid_method clip, int cid_bell, int cid_dtmf, int clip_date, int enblock, int recall, int *ringing_types_incoming, int ringing_type_hold, enum tones_type tones_type, char law, enum pulse_coding pulse_coding);
int pstn_init(pstn_t *pstn, const char *name, const char *socketname, const char **subscribers, int subscriber_num, uint8_t serving_location, int tx_delay, enum pstn_cid_method clip, int cid_bell, int cid_dtmf, int clip_date, int enblock, int recall, int *ringing_types_incoming, int ringing_type_hold, enum tones_type tones_type, char law, enum pulse_coding pulse_coding, enum pstn_dialect pstn_dialect, int metering, int lr_on_connect, int loop_disconnect, int lr_metering, int no_dtmf);
void cc_message(osmo_cc_endpoint_t *ep, uint32_t callref, osmo_cc_msg_t *msg);
void rtp_work(pstn_t *pstn_ep);

View File

@ -17,8 +17,8 @@
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stddef.h>
#include "../libdebug/debug.h"
#include "../libg711/g711.h"
#include "../liblogging/logging.h"
#include <osmocom/cc/g711.h>
#include "tones.h"
@ -259,6 +259,11 @@ static struct pattern {
{SIZE_GO, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{7956, 0, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_MORSEDIALTONE,
{DATA_GO, DATA_S, DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL},
{1600, 2400, 5600, 6400, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_DIALTONE,
{DATA_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_DT, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
@ -293,7 +298,7 @@ static struct pattern {
{TONE_GERMAN_OLDRINGING,
{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{8000, 40000, 0, 0, 0, 0, 0, 0, 0, 0} },
{8000, 32000, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_RINGING,
{DATA_RI, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
@ -318,12 +323,12 @@ static struct pattern {
{TONE_GERMAN_BUSY,
{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
{3840, 3840, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDBUSY,
{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
{1000, 3800, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_BUSY,
{DATA_BU, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
@ -333,12 +338,12 @@ static struct pattern {
{TONE_GERMAN_HANGUP,
{DATA_GA, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GA, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{4000, 4000, 0, 0, 0, 0, 0, 0, 0, 0} },
{3840, 3840, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_GERMAN_OLDHANGUP,
{DATA_GO, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{SIZE_GO, SIZE_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
{1000, 5000, 0, 0, 0, 0, 0, 0, 0, 0} },
{1000, 3800, 0, 0, 0, 0, 0, 0, 0, 0} },
{TONE_AMERICAN_HANGUP,
{DATA_BU, DATA_S, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
@ -464,10 +469,10 @@ int isdn_tone_set(struct isdn_tone *t, int tone)
i++;
}
if (!pat) {
PDEBUG(DDSP, DEBUG_ERROR, "given tone 0x%x is invalid\n", tone);
LOGP(DDSP, LOGL_ERROR, "given tone 0x%x is invalid\n", tone);
return -EINVAL;
}
PDEBUG(DDSP, DEBUG_DEBUG, "playing given tone 0x%x\n", tone);
LOGP(DDSP, LOGL_DEBUG, "playing given tone 0x%x\n", tone);
t->tone = tone;
t->pattern = pat;
t->index = 0;

View File

@ -3,6 +3,7 @@ enum {
TONE_OFF = 0,
TONE_GERMAN_DIALTONE,
TONE_GERMAN_OLDDIALTONE,
TONE_GERMAN_MORSEDIALTONE,
TONE_AMERICAN_DIALTONE,
TONE_GERMAN_DIALPBX,
TONE_GERMAN_OLDDIALPBX,