updated libs
This commit is contained in:
parent
94a08803a5
commit
56a9d512ef
|
@ -57,6 +57,8 @@ struct debug_cat {
|
|||
{ "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" },
|
||||
|
@ -89,12 +91,16 @@ struct debug_cat {
|
|||
{ "dss1", "\033[1;34m" },
|
||||
{ "sip", "\033[1;35m" },
|
||||
{ "telephone", "\033[1;34m" },
|
||||
{ "uk0", "\033[1;34m" },
|
||||
{ "ph", "\033[0;33m" },
|
||||
{ "dcf77", "\033[1;34m" },
|
||||
{ "jitter", "\033[0;36m" },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
int debuglevel = DEBUG_INFO;
|
||||
int debug_date = 0;
|
||||
uint64_t debug_mask = ~0;
|
||||
uint64_t debug_mask[2] = { ~0, ~0 };
|
||||
extern int num_kanal;
|
||||
|
||||
void (*clear_console_text)(void) = NULL;
|
||||
|
@ -105,6 +111,25 @@ int debug_limit_scroll = 0;
|
|||
static int lock_initialized = 0;
|
||||
static pthread_mutex_t debug_mutex;
|
||||
|
||||
void lock_debug(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!lock_initialized) {
|
||||
rc = pthread_mutex_init(&debug_mutex, NULL);
|
||||
if (rc == 0)
|
||||
lock_initialized = 1;
|
||||
}
|
||||
if (lock_initialized)
|
||||
pthread_mutex_lock(&debug_mutex);
|
||||
}
|
||||
|
||||
void unlock_debug(void)
|
||||
{
|
||||
if (lock_initialized)
|
||||
pthread_mutex_unlock(&debug_mutex);
|
||||
}
|
||||
|
||||
void get_win_size(int *w, int *h)
|
||||
{
|
||||
struct winsize win;
|
||||
|
@ -127,22 +152,15 @@ void _printdebug(const char *file, const char __attribute__((unused)) *function,
|
|||
int s = sizeof(buffer) - 1;
|
||||
const char *p;
|
||||
va_list args;
|
||||
int w, h;
|
||||
int rc;
|
||||
int w, h = 0; // make GCC happy
|
||||
|
||||
if (debuglevel > level)
|
||||
return;
|
||||
|
||||
if (!(debug_mask & ((uint64_t)1 << cat)))
|
||||
if (!(debug_mask[cat >> 6] & ((uint64_t)1 << (cat & 63))))
|
||||
return;
|
||||
|
||||
if (!lock_initialized) {
|
||||
rc = pthread_mutex_init(&debug_mutex, NULL);
|
||||
if (rc == 0)
|
||||
lock_initialized = 1;
|
||||
}
|
||||
if (lock_initialized)
|
||||
pthread_mutex_lock(&debug_mutex);
|
||||
lock_debug();
|
||||
|
||||
buffer[sizeof(buffer) - 1] = '\0';
|
||||
|
||||
|
@ -174,15 +192,14 @@ void _printdebug(const char *file, const char __attribute__((unused)) *function,
|
|||
|
||||
printf("%04d-%02d-%02d %02d:%02d:%02d.%03d ", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 10000.0));
|
||||
}
|
||||
printf("%s%s:%4d %s: %s\033[0;39m", debug_cat[cat].color, file, line, debug_level[level], buffer);
|
||||
printf("%s%s %4d %s-%s: %s\033[0;39m", debug_cat[cat].color, file, line, debug_cat[cat].name, debug_level[level], buffer);
|
||||
if (debug_limit_scroll)
|
||||
printf("\0337\033[%d;%dr\0338", 1, h);
|
||||
if (print_console_text)
|
||||
print_console_text();
|
||||
fflush(stdout);
|
||||
|
||||
if (lock_initialized)
|
||||
pthread_mutex_unlock(&debug_mutex);
|
||||
unlock_debug();
|
||||
}
|
||||
|
||||
const char *debug_amplitude(double level)
|
||||
|
@ -274,7 +291,7 @@ int parse_debug_opt(const char *optarg)
|
|||
return -EINVAL;
|
||||
}
|
||||
if (dstring)
|
||||
debug_mask = 0;
|
||||
memset(debug_mask, 0, sizeof(debug_mask));
|
||||
while((p = strsep(&dstring, ","))) {
|
||||
for (i = 0; debug_cat[i].name; i++) {
|
||||
if (!strcasecmp(p, debug_cat[i].name))
|
||||
|
@ -285,7 +302,7 @@ int parse_debug_opt(const char *optarg)
|
|||
free(dup);
|
||||
return -EINVAL;
|
||||
}
|
||||
debug_mask |= ((uint64_t)1 << i);
|
||||
debug_mask[i >> 6] |= ((uint64_t)1 << (i & 63));
|
||||
}
|
||||
|
||||
free(dup);
|
||||
|
|
|
@ -19,38 +19,48 @@
|
|||
#define DJOLLY 12
|
||||
#define DEURO 13
|
||||
#define DPOCSAG 14
|
||||
#define DFRAME 15
|
||||
#define DCALL 16
|
||||
#define DCC 17
|
||||
#define DDB 18
|
||||
#define DTRANS 19
|
||||
#define DDMS 20
|
||||
#define DSMS 21
|
||||
#define DSDR 22
|
||||
#define DUHD 23
|
||||
#define DSOAPY 24
|
||||
#define DWAVE 25
|
||||
#define DRADIO 26
|
||||
#define DAM791X 27
|
||||
#define DUART 28
|
||||
#define DDEVICE 29
|
||||
#define DDATENKLO 30
|
||||
#define DZEIT 31
|
||||
#define DSIM1 32
|
||||
#define DSIM2 33
|
||||
#define DSIMI 34
|
||||
#define DSIM7 35
|
||||
#define DMTP2 36
|
||||
#define DMTP3 37
|
||||
#define DMUP 38
|
||||
#define DROUTER 39
|
||||
#define DSTDERR 40
|
||||
#define DSS5 41
|
||||
#define DISDN 42
|
||||
#define DMISDN 43
|
||||
#define DDSS1 44
|
||||
#define DSIP 45
|
||||
#define DTEL 46
|
||||
#define 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 DISDN 44
|
||||
#define DMISDN 45
|
||||
#define DDSS1 46
|
||||
#define DSIP 47
|
||||
#define DTEL 48
|
||||
#define DUK0 49
|
||||
#define DPH 50
|
||||
#define DDCF77 51
|
||||
#define DJITTER 52
|
||||
//NOTE: increment mask array, if 127 is exceeded
|
||||
|
||||
void lock_debug(void);
|
||||
void unlock_debug(void);
|
||||
|
||||
void get_win_size(int *w, int *h);
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* Jitter buffering functions
|
||||
*
|
||||
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
|
@ -17,6 +17,60 @@
|
|||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* How does it work:
|
||||
*
|
||||
* Storing:
|
||||
*
|
||||
* Each saved frame is sorted into the list of packages by their sequence
|
||||
* number.
|
||||
*
|
||||
* The first packet will be stored with a delay of minimum jitter window size.
|
||||
*
|
||||
* Packets with the same sequence are dropped.
|
||||
*
|
||||
* Early packts that exceed maximum jitter window size cause jitter
|
||||
* window to shift into the future.
|
||||
*
|
||||
* Late packets cause jitter window to shift into the past (allowing more
|
||||
* delay). Minimum jitter window size is added also, to prevent subsequent
|
||||
* packets from beeing late too.
|
||||
*
|
||||
* If no sequence is provided (autosequence), the sequence number is generated
|
||||
* by a counter. Also the timestamp is generated by counting the length of each
|
||||
* frame.
|
||||
*
|
||||
* If ssrc changes, the buffer is reset.
|
||||
*
|
||||
*
|
||||
* Playout:
|
||||
*
|
||||
* The caller of the playout function can request any length of samples from
|
||||
* the packet list. The packt's time stamp and the jitter window time stamp
|
||||
* indicate what portion of a packet is already provided to the caller.
|
||||
* Complete packet, sent to the caller, are removed.
|
||||
*
|
||||
* Missing packets are interpolated by repeating last 20ms of audio (optional)
|
||||
* or by inserting zeroes (sample size > 1 byte) or by inserting 0xff (sample
|
||||
* size = 1). 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.
|
||||
*
|
||||
* Optionally the constant delay will be measured continuously and lowered if
|
||||
* greater than minimum window size. (adaptive jitter buffer size)
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
* 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
|
||||
* cause high delay.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
@ -26,35 +80,103 @@
|
|||
#include "../libdebug/debug.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
|
||||
|
||||
static int unnamed_count = 1;
|
||||
|
||||
/* create jitter buffer */
|
||||
int jitter_create(jitter_t *jitter, int length)
|
||||
int jitter_create(jitter_t *jb, const char *name, double samplerate, int sample_size, double target_window_duration, double max_window_duration, uint32_t window_flags)
|
||||
{
|
||||
memset(jitter, 0, sizeof(*jitter));
|
||||
jitter->spl = malloc(length * sizeof(sample_t));
|
||||
if (!jitter->spl) {
|
||||
PDEBUG(DDSP, DEBUG_ERROR, "No memory for jitter buffer.\n");
|
||||
return -ENOMEM;
|
||||
int rc = 0;
|
||||
memset(jb, 0, sizeof(*jb));
|
||||
jb->sample_duration = 1.0 / samplerate;
|
||||
jb->sample_size = sample_size;
|
||||
jb->target_window_size = (int)(samplerate * target_window_duration);
|
||||
jb->max_window_size = (int)(samplerate * max_window_duration);
|
||||
jb->window_flags = window_flags;
|
||||
|
||||
jb->extra_size = (int)(EXTRA_BUFFER * samplerate);
|
||||
jb->extra_samples = calloc(sample_size, jb->extra_size);
|
||||
if (!jb->extra_samples) {
|
||||
PDEBUG(DJITTER, DEBUG_ERROR, "No memory for frame.\n");
|
||||
rc = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
jitter->len = length;
|
||||
jb->extra_timeout_max = (int)ceil(EXTRA_TIMEOUT / EXTRA_BUFFER);
|
||||
|
||||
jitter_reset(jitter);
|
||||
/* optionally give a string to be show with the debug */
|
||||
if (name && *name)
|
||||
snprintf(jb->name, sizeof(jb->name) - 1, "(%s) ", name);
|
||||
else
|
||||
snprintf(jb->name, sizeof(jb->name) - 1, "(unnamed %d) ", unnamed_count++);
|
||||
|
||||
return 0;
|
||||
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");
|
||||
|
||||
error:
|
||||
if (rc)
|
||||
jitter_destroy(jb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void jitter_reset(jitter_t *jitter)
|
||||
static void clear_extra_buffer(jitter_t *jb)
|
||||
{
|
||||
memset(jitter->spl, 0, jitter->len * sizeof(sample_t));
|
||||
|
||||
/* put write pointer ahead by half of the buffer length */
|
||||
jitter->inptr = jitter->len / 2;
|
||||
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);
|
||||
}
|
||||
|
||||
void jitter_destroy(jitter_t *jitter)
|
||||
/* reset jitter buffer */
|
||||
void jitter_reset(jitter_t *jb)
|
||||
{
|
||||
if (jitter->spl) {
|
||||
free(jitter->spl);
|
||||
jitter->spl = NULL;
|
||||
jitter_frame_t *jf, *temp;
|
||||
|
||||
PDEBUG(DJITTER, DEBUG_INFO, "%sReset jitter buffer.\n", jb->name);
|
||||
|
||||
/* jitter buffer locked */
|
||||
jb->unlocked = 0;
|
||||
|
||||
/* window becomes invalid */
|
||||
jb->window_valid = 0;
|
||||
|
||||
/* remove all pending frames */
|
||||
jf = jb->frame_list;
|
||||
while(jf) {
|
||||
temp = jf;
|
||||
jf = jf->next;
|
||||
free(temp);
|
||||
}
|
||||
jb->frame_list = NULL;
|
||||
|
||||
/* clear extrapolation buffer */
|
||||
if (jb->extra_samples)
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,64 +184,241 @@ void jitter_destroy(jitter_t *jitter)
|
|||
*
|
||||
* stop if buffer is completely filled
|
||||
*/
|
||||
void jitter_save(jitter_t *jb, sample_t *samples, int length)
|
||||
void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
|
||||
{
|
||||
sample_t *spl;
|
||||
int inptr, outptr, len, space;
|
||||
int i;
|
||||
jitter_frame_t *jf, **jfp;
|
||||
int16_t offset_sequence;
|
||||
int32_t offset_timestamp;
|
||||
|
||||
spl = jb->spl;
|
||||
inptr = jb->inptr;
|
||||
outptr = jb->outptr;
|
||||
len = jb->len;
|
||||
space = (outptr - inptr + len - 1) % len;
|
||||
/* ignore frames until the buffer is unlocked by jitter_load() */
|
||||
if (!jb->unlocked)
|
||||
return;
|
||||
|
||||
if (space < length)
|
||||
length = space;
|
||||
for (i = 0; i < length; i++) {
|
||||
spl[inptr++] = *samples++;
|
||||
if (inptr == len)
|
||||
inptr = 0;
|
||||
/* omit frames with no data */
|
||||
if (length < 1)
|
||||
return;
|
||||
|
||||
/* generate sequence and timestamp automatically, if enabled */
|
||||
if (!has_sequence) {
|
||||
#ifdef DEBUG_JITTER
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%sSave frame of %d samples (no seqence).\n", jb->name, length);
|
||||
#endif
|
||||
sequence = jb->next_sequence;
|
||||
jb->next_sequence++;
|
||||
timestamp = jb->next_timestamp;
|
||||
jb->next_timestamp += length;
|
||||
ssrc = jb->window_ssrc;
|
||||
} else {
|
||||
#ifdef HEAVY_DEBUG
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%sSave frame of %d samples (seqence=%u timestamp=%u ssrc=0x%02x).\n", jb->name, length, sequence, timestamp, ssrc);
|
||||
#endif
|
||||
jb->next_sequence = sequence + 1;
|
||||
jb->next_timestamp = timestamp + length;
|
||||
}
|
||||
|
||||
jb->inptr = inptr;
|
||||
/* first packet (with this ssrc) sets window size to target_window_size */
|
||||
if (!jb->window_valid || jb->window_ssrc != ssrc) {
|
||||
if (!jb->window_valid)
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Initial frame after init or reset.\n", jb->name);
|
||||
else
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s SSRC changed.\n", jb->name);
|
||||
// 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;
|
||||
/* 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;
|
||||
} else {
|
||||
jb->window_timestamp = timestamp - (uint32_t)jb->target_window_size;
|
||||
}
|
||||
jb->window_valid = 1;
|
||||
jb->window_ssrc = ssrc;
|
||||
}
|
||||
|
||||
/* find location where to put frame into the list, depending on sequence number */
|
||||
jfp = &jb->frame_list;
|
||||
while(*jfp) {
|
||||
offset_sequence = (int16_t)(sequence - (*jfp)->sequence);
|
||||
/* found double entry */
|
||||
if (offset_sequence == 0) {
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Dropping double packet (sequence = %d)\n", jb->name, sequence);
|
||||
return;
|
||||
}
|
||||
/* offset is negative, so we found the position to insert frame */
|
||||
if (offset_sequence < 0)
|
||||
break;
|
||||
jfp = &((*jfp)->next);
|
||||
}
|
||||
|
||||
offset_timestamp = timestamp - jb->window_timestamp;
|
||||
#ifdef HEAVY_DEBUG
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%sFrame has offset of %.0fms in jitter buffer.\n", jb->name, (double)offset_timestamp * jb->sample_duration * 1000.0);
|
||||
#endif
|
||||
|
||||
/* measure delay */
|
||||
if (jb->min_delay_value < 0 || offset_timestamp < jb->min_delay_value)
|
||||
jb->min_delay_value = offset_timestamp;
|
||||
|
||||
/* if frame is too early (delay ceases), shift window to the future */
|
||||
if (offset_timestamp > jb->max_window_size) {
|
||||
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too early: Shift jitter buffer to the future, to make the frame fit to the end. (offset_timestamp(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
|
||||
/* shift window so it fits to the end of window */
|
||||
jb->window_timestamp = timestamp - jb->max_window_size;
|
||||
} else {
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too early: Shift jitter buffer to the future, to make the frame fit to the target delay. (offset_timestamp(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
|
||||
/* shift window so frame fits to the start of window + target delay */
|
||||
jb->window_timestamp = timestamp - (uint32_t)(jb->target_window_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* is frame is too late, shift window to the past. */
|
||||
if (offset_timestamp < 0) {
|
||||
if ((jb->window_flags & JITTER_FLAG_LATENCY)) {
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too late: Shift jitter buffer to the past, and add target window size. (offset_timestamp(%d) < 0)\n", jb->name, offset_timestamp);
|
||||
/* shift window so frame fits to the start of window + half of target delay */
|
||||
jb->window_timestamp = timestamp - (uint32_t)(jb->target_window_size) / 2;
|
||||
} else {
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Frame too late: Shift jitter buffer to the past, and add half target window size. (offset_timestamp(%d) < 0)\n", jb->name, offset_timestamp);
|
||||
/* shift window so frame fits to the start of window + target delay */
|
||||
jb->window_timestamp = timestamp - (uint32_t)(jb->target_window_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* insert or append frame */
|
||||
#ifdef HEAVY_DEBUG
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Store frame\n", jb->name);
|
||||
#endif
|
||||
jf = malloc(sizeof(*jf) + length * jb->sample_size);
|
||||
if (!jf) {
|
||||
PDEBUG(DJITTER, DEBUG_ERROR, "No memory for frame.\n");
|
||||
return;
|
||||
}
|
||||
memset(jf, 0, sizeof(*jf)); // note: clear header only
|
||||
jf->sequence = sequence;
|
||||
jf->timestamp = timestamp;
|
||||
memcpy(jf->samples, samples, length * jb->sample_size);
|
||||
jf->length = length;
|
||||
jf->next = *jfp;
|
||||
*jfp = jf;
|
||||
}
|
||||
|
||||
/* get audio from jitterbuffer
|
||||
*/
|
||||
void jitter_load(jitter_t *jb, sample_t *samples, int length)
|
||||
void jitter_load(jitter_t *jb, void *samples, int length)
|
||||
{
|
||||
sample_t *spl;
|
||||
int inptr, outptr, len, fill;
|
||||
int i, ii;
|
||||
jitter_frame_t *jf;
|
||||
int32_t count, count2, index;
|
||||
|
||||
spl = jb->spl;
|
||||
inptr = jb->inptr;
|
||||
outptr = jb->outptr;
|
||||
len = jb->len;
|
||||
fill = (inptr - outptr + len) % len;
|
||||
#ifdef HEAVY_DEBUG
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%sLoad chunk of %d samples.\n", jb->name, length);
|
||||
#endif
|
||||
|
||||
if (fill < length)
|
||||
ii = fill;
|
||||
else
|
||||
ii = length;
|
||||
/* now unlock jitter buffer */
|
||||
jb->unlocked = 1;
|
||||
|
||||
/* fill what we got */
|
||||
for (i = 0; i < ii; i++) {
|
||||
*samples++ = spl[outptr++];
|
||||
if (outptr == len)
|
||||
outptr = 0;
|
||||
}
|
||||
/* on underrun, fill with silence */
|
||||
for (; i < length; i++) {
|
||||
*samples++ = 0;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
jb->outptr = outptr;
|
||||
}
|
||||
|
||||
void jitter_clear(jitter_t *jb)
|
||||
{
|
||||
jb->inptr = jb->outptr = 0;
|
||||
/* process all frames until output buffer is loaded */
|
||||
while (length) {
|
||||
/* always get frame with the lowest sequence number (1st frame) */
|
||||
jf = jb->frame_list;
|
||||
|
||||
if (jf) {
|
||||
count = jf->timestamp - jb->window_timestamp;
|
||||
if (count > length)
|
||||
count = length;
|
||||
} else
|
||||
count = length;
|
||||
/* if there is no frame or we have not reached frame's time stamp, extrapolate */
|
||||
if (count > 0) {
|
||||
#ifdef HEAVY_DEBUG
|
||||
if (jf)
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s There is a frame ahead in buffer after %d samples. Interpolating gap.\n", jb->name, jf->timestamp - jb->window_timestamp);
|
||||
else
|
||||
PDEBUG(DJITTER, DEBUG_DEBUG, "%s There is no frame ahead in buffer. Interpolating gap.\n", jb->name);
|
||||
#endif
|
||||
/* extrapolate by playing the extrapolation buffer */
|
||||
while (count) {
|
||||
count2 = count;
|
||||
if (count2 > jb->extra_size - jb->extra_index)
|
||||
count2 = jb->extra_size - jb->extra_index;
|
||||
memcpy(samples, (uint8_t *)jb->extra_samples + jb->extra_index * jb->sample_size, count2 * jb->sample_size);
|
||||
jb->extra_index += count2;
|
||||
if (jb->extra_index == jb->extra_size) {
|
||||
jb->extra_index = 0;
|
||||
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 */
|
||||
jb->frame_list = jf->next;
|
||||
free(jf);
|
||||
|
||||
/* now go for next loop, in case there is still date to play out */
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,60 @@
|
|||
|
||||
#define JITTER_FLAG_NONE 0 // no flags at all
|
||||
#define JITTER_FLAG_LATENCY (1 << 0) // keep latency close to target_window_duration
|
||||
#define JITTER_FLAG_REPEAT (1 << 1) // repeat audio to extrapolate gaps
|
||||
|
||||
/* window settings for low latency audio and extrapolation of gaps */
|
||||
#define JITTER_AUDIO 0.050, 1.000, JITTER_FLAG_LATENCY | JITTER_FLAG_REPEAT
|
||||
/* window settings for analog data (fax/modem) or digial data (HDLC) */
|
||||
#define JITTER_DATA 0.100, 0.200, JITTER_FLAG_NONE
|
||||
|
||||
typedef struct jitter_frame {
|
||||
struct jitter_frame *next;
|
||||
uint16_t sequence;
|
||||
uint32_t timestamp;
|
||||
int length;
|
||||
uint8_t samples[0];
|
||||
} jitter_frame_t;
|
||||
|
||||
typedef struct jitter {
|
||||
sample_t *spl; /* pointer to sample buffer */
|
||||
int len; /* buffer size: number of samples */
|
||||
int inptr, outptr; /* write pointer and read pointer */
|
||||
char name[64];
|
||||
|
||||
/* sample properties */
|
||||
int sample_size;
|
||||
double sample_duration;
|
||||
|
||||
/* automatic sequence generation */
|
||||
uint16_t next_sequence;
|
||||
uint32_t next_timestamp;
|
||||
|
||||
/* window properties */
|
||||
int unlocked;
|
||||
uint32_t window_flags;
|
||||
int target_window_size;
|
||||
int max_window_size;
|
||||
int window_valid;
|
||||
uint32_t window_ssrc;
|
||||
uint32_t window_timestamp;
|
||||
|
||||
/* reduction of delay */
|
||||
double delay_interval;
|
||||
double delay_counter;
|
||||
int32_t min_delay_value;
|
||||
|
||||
/* extrapolation */
|
||||
int extra_size;
|
||||
int extra_index;
|
||||
void *extra_samples;
|
||||
int extra_timeout_max;
|
||||
int extra_timeout_count;
|
||||
|
||||
/* list of frames */
|
||||
jitter_frame_t *frame_list;
|
||||
} jitter_t;
|
||||
|
||||
int jitter_create(jitter_t *jitter, int length);
|
||||
void jitter_reset(jitter_t *jitter);
|
||||
void jitter_destroy(jitter_t *jitter);
|
||||
void jitter_save(jitter_t *jb, sample_t *samples, int length);
|
||||
void jitter_load(jitter_t *jb, sample_t *samples, int length);
|
||||
void jitter_clear(jitter_t *jb);
|
||||
int jitter_create(jitter_t *jb, const char *name, double samplerate, int sample_size, double target_window_duration, double max_window_duration, uint32_t window_flags);
|
||||
void jitter_reset(jitter_t *jb);
|
||||
void jitter_destroy(jitter_t *jb);
|
||||
void jitter_save(jitter_t *jb, void *samples, int length, int has_sequence, uint16_t sequence, uint32_t timestamp, uint32_t ssrc);
|
||||
void jitter_load(jitter_t *jb, void *samples, int length);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "message.h"
|
||||
#include "cause.h"
|
||||
|
||||
/* stolen from freeswitch */
|
||||
/* stolen from freeswitch, did some corrections */
|
||||
/* map sip responses to QSIG cause codes ala RFC4497 section 8.4.4 */
|
||||
static uint8_t status2isdn_cause(uint16_t status)
|
||||
{
|
||||
|
@ -36,10 +36,9 @@ static uint8_t status2isdn_cause(uint16_t status)
|
|||
case 603:
|
||||
return 21; //SWITCH_CAUSE_CALL_REJECTED;
|
||||
case 404:
|
||||
return 1; //SWITCH_CAUSE_UNALLOCATED_NUMBER;
|
||||
case 485:
|
||||
case 604:
|
||||
return 3; //SWITCH_CAUSE_NO_ROUTE_DESTINATION;
|
||||
return 1; //SWITCH_CAUSE_UNALLOCATED_NUMBER;
|
||||
case 408:
|
||||
case 504:
|
||||
return 102; //SWITCH_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
|
||||
|
@ -55,7 +54,7 @@ static uint8_t status2isdn_cause(uint16_t status)
|
|||
case 513:
|
||||
return 127; //SWITCH_CAUSE_INTERWORKING;
|
||||
case 480:
|
||||
return 180; //SWITCH_CAUSE_NO_USER_RESPONSE;
|
||||
return 18; //SWITCH_CAUSE_NO_USER_RESPONSE;
|
||||
case 400:
|
||||
case 481:
|
||||
case 500:
|
||||
|
@ -68,7 +67,7 @@ static uint8_t status2isdn_cause(uint16_t status)
|
|||
return 28; //SWITCH_CAUSE_INVALID_NUMBER_FORMAT;
|
||||
case 488:
|
||||
case 606:
|
||||
return 88; //SWITCH_CAUSE_INCOMPATIBLE_DESTINATION;
|
||||
return 65; //SWITCH_CAUSE_BERER_CAPABILITY_NOT_IMPLEMENTED;
|
||||
case 502:
|
||||
return 38; //SWITCH_CAUSE_NETWORK_OUT_OF_ORDER;
|
||||
case 405:
|
||||
|
@ -81,7 +80,7 @@ static uint8_t status2isdn_cause(uint16_t status)
|
|||
case 483:
|
||||
return 25; //SWITCH_CAUSE_EXCHANGE_ROUTING_ERROR;
|
||||
case 487:
|
||||
return 31; //??? SWITCH_CAUSE_ORIGINATOR_CANCEL;
|
||||
return 31; //??? SWITCH_CAUSE_ORIGINATOR_CANCEL; (not specified)
|
||||
default:
|
||||
return 31; //SWITCH_CAUSE_NORMAL_UNSPECIFIED;
|
||||
}
|
||||
|
|
|
@ -568,6 +568,18 @@ static void notify_ind(osmo_cc_call_t *call, osmo_cc_msg_t *msg)
|
|||
forward_to_ul(call, msg);
|
||||
}
|
||||
|
||||
static void update_req(osmo_cc_call_t *call, osmo_cc_msg_t *msg)
|
||||
{
|
||||
/* to lower layer */
|
||||
forward_to_ll(call, msg);
|
||||
}
|
||||
|
||||
static void update_cnf(osmo_cc_call_t *call, osmo_cc_msg_t *msg)
|
||||
{
|
||||
/* to upper layer */
|
||||
forward_to_ul(call, msg);
|
||||
}
|
||||
|
||||
static void disc_req(osmo_cc_call_t *call, osmo_cc_msg_t *msg)
|
||||
{
|
||||
/* change state */
|
||||
|
@ -853,6 +865,10 @@ static struct statemachine {
|
|||
OSMO_CC_MSG_INFO_IND, info_ind},
|
||||
{SBIT(OSMO_CC_STATE_ACTIVE),
|
||||
OSMO_CC_MSG_INFO_REQ, info_req},
|
||||
{SBIT(OSMO_CC_STATE_ACTIVE),
|
||||
OSMO_CC_MSG_UPDATE_REQ, update_req},
|
||||
{SBIT(OSMO_CC_STATE_ACTIVE),
|
||||
OSMO_CC_MSG_UPDATE_CNF, update_cnf},
|
||||
|
||||
/* call release */
|
||||
{SBIT(OSMO_CC_STATE_INIT_OUT) | SBIT(OSMO_CC_STATE_INIT_IN) |
|
||||
|
@ -1072,6 +1088,36 @@ void osmo_cc_ul_msg(void *priv, uint32_t callref, osmo_cc_msg_t *msg)
|
|||
osmo_cc_msg_list_enqueue(&call->sock_queue, msg, call->callref);
|
||||
}
|
||||
|
||||
static void osmo_cc_help_name(void)
|
||||
{
|
||||
printf("Name options:\n\n");
|
||||
|
||||
printf("name <name>\n");
|
||||
|
||||
printf("Allows to override endpoint name given by application.\n");
|
||||
}
|
||||
|
||||
static int osmo_cc_set_name(osmo_cc_endpoint_t *ep, const char *text)
|
||||
{
|
||||
if (!strncasecmp(text, "name", 4)) {
|
||||
text += 4;
|
||||
/* remove spaces after keyword */
|
||||
while (*text) {
|
||||
if (*text > 32)
|
||||
break;
|
||||
text++;
|
||||
}
|
||||
} else {
|
||||
PDEBUG(DCC, DEBUG_ERROR, "Invalid name definition '%s'\n", text);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
free((char *)ep->local_name);
|
||||
ep->local_name = strdup(text);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void osmo_cc_help_address(void)
|
||||
{
|
||||
printf("Address options:\n\n");
|
||||
|
@ -1080,6 +1126,8 @@ static void osmo_cc_help_address(void)
|
|||
printf("local [<IPv6 address>]:<port>\n");
|
||||
printf("remote <IPv4 address>:<port>\n");
|
||||
printf("remote [<IPv6 address>]:<port>\n\n");
|
||||
printf("remote auto\n\n");
|
||||
printf("remote none\n\n");
|
||||
|
||||
printf("These options can be used to define local and remote IP and port for the socket\n");
|
||||
printf("interface. Note that IPv6 addresses must be enclosed by '[' and ']'.\n\n");
|
||||
|
@ -1090,6 +1138,9 @@ static void osmo_cc_help_address(void)
|
|||
printf("If no remote address is given, the local IP is used. If the local port is %d,\n", OSMO_CC_DEFAULT_PORT);
|
||||
printf("the remote port will be %d. If not, the remote port will be %d. This way it is\n", OSMO_CC_DEFAULT_PORT + 1, OSMO_CC_DEFAULT_PORT);
|
||||
printf("possible to link two interfaces without any IP configuration required.\n\n");
|
||||
|
||||
printf("Use 'remote auto' to enable and 'remote none' to disable. This can be useful to\n");
|
||||
printf("override application default.\n\n");
|
||||
}
|
||||
|
||||
static int osmo_cc_set_address(osmo_cc_endpoint_t *ep, const char *text)
|
||||
|
@ -1124,6 +1175,11 @@ static int osmo_cc_set_address(osmo_cc_endpoint_t *ep, const char *text)
|
|||
ep->remote_auto = 1;
|
||||
return 0;
|
||||
}
|
||||
if (!strcasecmp(text, "none")) {
|
||||
PDEBUG(DCC, DEBUG_DEBUG, "disable automatic remote peer selection\n");
|
||||
ep->remote_auto = 0;
|
||||
return 0;
|
||||
}
|
||||
ep->remote_auto = 0;
|
||||
address_p = &ep->remote_address;
|
||||
host_p = &ep->remote_host;
|
||||
|
@ -1220,6 +1276,7 @@ static int osmo_cc_set_rtp(osmo_cc_endpoint_t *ep, const char *text)
|
|||
return -EINVAL;
|
||||
}
|
||||
from = from * 10 + *text - '0';
|
||||
text++;
|
||||
}
|
||||
|
||||
/* remove spaces after keyword */
|
||||
|
@ -1235,7 +1292,8 @@ static int osmo_cc_set_rtp(osmo_cc_endpoint_t *ep, const char *text)
|
|||
PDEBUG(DCC, DEBUG_ERROR, "Given 'to' port in '%s' is invalid.\n", text);
|
||||
return -EINVAL;
|
||||
}
|
||||
from = from * 10 + *text - '0';
|
||||
to = to * 10 + *text - '0';
|
||||
text++;
|
||||
}
|
||||
|
||||
osmo_cc_set_rtp_ports(&ep->session_config, from, to);
|
||||
|
@ -1247,9 +1305,10 @@ static int osmo_cc_set_rtp(osmo_cc_endpoint_t *ep, const char *text)
|
|||
|
||||
void osmo_cc_help(void)
|
||||
{
|
||||
osmo_cc_help_screen();
|
||||
osmo_cc_help_name();
|
||||
osmo_cc_help_address();
|
||||
osmo_cc_help_rtp();
|
||||
osmo_cc_help_screen();
|
||||
}
|
||||
|
||||
/* create a new endpoint instance */
|
||||
|
@ -1286,6 +1345,12 @@ int osmo_cc_new(osmo_cc_endpoint_t *ep, const char *version, const char *name, u
|
|||
|
||||
/* apply args */
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (!strncasecmp(argv[i], "name", 4)) {
|
||||
rc = osmo_cc_set_name(ep, argv[i]);
|
||||
if (rc < 0) {
|
||||
return rc;
|
||||
}
|
||||
} else
|
||||
if (!strncasecmp(argv[i], "local", 5)) {
|
||||
rc = osmo_cc_set_address(ep, argv[i]);
|
||||
if (rc < 0) {
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#include "endpoint.h"
|
||||
#include "helper.h"
|
||||
|
||||
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug)
|
||||
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, 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;
|
||||
|
@ -52,7 +52,7 @@ osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, vo
|
|||
return session;
|
||||
}
|
||||
|
||||
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec)
|
||||
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, 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;
|
||||
|
|
|
@ -7,7 +7,7 @@ struct osmo_cc_helper_audio_codecs {
|
|||
void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len);
|
||||
};
|
||||
|
||||
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug);
|
||||
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec);
|
||||
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, 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);
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@
|
|||
}
|
||||
|
||||
#define _OSMO_CC_NAME2VALUE(array) { \
|
||||
for (int value = 0; (size_t)value < (sizeof(array) / sizeof(array[0])); value++) { \
|
||||
int value; \
|
||||
for (value = 0; (size_t)value < (sizeof(array) / sizeof(array[0])); value++) { \
|
||||
if (!strcasecmp(array[value], name)) \
|
||||
return value; \
|
||||
} \
|
||||
|
|
|
@ -30,6 +30,8 @@ enum osmo_cc_msg_type {
|
|||
OSMO_CC_MSG_NOTIFY_IND = 0x85,
|
||||
OSMO_CC_MSG_INFO_REQ = 0x88,
|
||||
OSMO_CC_MSG_INFO_IND = 0x89,
|
||||
OSMO_CC_MSG_UPDATE_REQ = 0x91,
|
||||
OSMO_CC_MSG_UPDATE_CNF = 0x93,
|
||||
OSMO_CC_MSG_ATTACH_REQ = 0xf8,
|
||||
OSMO_CC_MSG_ATTACH_IND = 0xf9,
|
||||
OSMO_CC_MSG_ATTACH_RSP = 0xfa,
|
||||
|
|
|
@ -52,7 +52,7 @@ struct rtp_x_hdr {
|
|||
uint16_t length;
|
||||
} __attribute__((packed));
|
||||
|
||||
static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_t *marker_p, uint8_t *pt_p, uint16_t *sequence_p, uint32_t *timestamp_p)
|
||||
static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_t *marker_p, uint8_t *pt_p, uint16_t *sequence_p, uint32_t *timestamp_p, uint32_t *ssrc_p)
|
||||
{
|
||||
static uint8_t data[2048];
|
||||
int len;
|
||||
|
@ -83,6 +83,7 @@ static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_
|
|||
payload_type = rtph->byte1 & 0x7f;
|
||||
*sequence_p = ntohs(rtph->sequence);
|
||||
*timestamp_p = ntohl(rtph->timestamp);
|
||||
*ssrc_p = ntohl(rtph->ssrc);
|
||||
|
||||
if (version != RTP_VERSION) {
|
||||
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP version %d not supported.\n", version);
|
||||
|
@ -131,7 +132,7 @@ static int rtp_receive(int sock, uint8_t **payload_p, int *payload_len_p, uint8_
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void rtp_send(int sock, uint8_t *payload, int payload_len, uint8_t pt, uint16_t sequence, uint32_t timestamp, uint32_t ssrc)
|
||||
static void rtp_send(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];
|
||||
|
@ -140,7 +141,7 @@ static void rtp_send(int sock, uint8_t *payload, int payload_len, uint8_t pt, ui
|
|||
rtph = (struct rtp_hdr *)data;
|
||||
len = sizeof(*rtph);
|
||||
rtph->byte0 = RTP_VERSION << 6;
|
||||
rtph->byte1 = pt;
|
||||
rtph->byte1 = pt | (marker << 7);
|
||||
rtph->sequence = htons(sequence);
|
||||
rtph->timestamp = htonl(timestamp);
|
||||
rtph->ssrc = htonl(ssrc);
|
||||
|
@ -172,7 +173,7 @@ int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
|
|||
int flags;
|
||||
int rc;
|
||||
|
||||
media->rtp_ssrc = rand();
|
||||
media->tx_ssrc = rand();
|
||||
|
||||
osmo_cc_rtp_close(media);
|
||||
|
||||
|
@ -182,7 +183,12 @@ int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
|
|||
memset(&sa, 0, sizeof(sa));
|
||||
sa4 = (struct sockaddr_in *)&sa;
|
||||
sa4->sin_family = domain;
|
||||
sa4->sin_addr.s_addr = INADDR_ANY;
|
||||
rc = inet_pton(AF_INET, media->connection_data_local.address, &sa4->sin_addr);
|
||||
if (rc < 1) {
|
||||
pton_error:
|
||||
PDEBUG(DCC, DEBUG_NOTICE, "Cannot bind to address '%s'.\n", media->connection_data_local.address);
|
||||
return -EINVAL;
|
||||
}
|
||||
sport = &sa4->sin_port;
|
||||
slen = sizeof(*sa4);
|
||||
break;
|
||||
|
@ -191,7 +197,9 @@ int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
|
|||
memset(&sa, 0, sizeof(sa));
|
||||
sa6 = (struct sockaddr_in6 *)&sa;
|
||||
sa6->sin6_family = domain;
|
||||
sa6->sin6_addr = in6addr_any;
|
||||
rc = inet_pton(AF_INET6, media->connection_data_local.address, &sa6->sin6_addr);
|
||||
if (rc < 1)
|
||||
goto pton_error;
|
||||
sport = &sa6->sin6_port;
|
||||
slen = sizeof(*sa6);
|
||||
break;
|
||||
|
@ -313,7 +321,7 @@ connect_error:
|
|||
}
|
||||
|
||||
/* send rtp data with given codec */
|
||||
void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, int inc_sequence, int inc_timestamp)
|
||||
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)
|
||||
{
|
||||
uint8_t *payload = NULL;
|
||||
int payload_len = 0;
|
||||
|
@ -328,7 +336,7 @@ void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, in
|
|||
payload_len = len;
|
||||
}
|
||||
|
||||
rtp_send(codec->media->rtp_socket, payload, payload_len, codec->payload_type_remote, codec->media->tx_sequence, codec->media->tx_timestamp, codec->media->rtp_ssrc);
|
||||
rtp_send(codec->media->rtp_socket, payload, payload_len, 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;
|
||||
|
||||
|
@ -351,7 +359,7 @@ int osmo_cc_rtp_receive(osmo_cc_session_media_t *media)
|
|||
if (!media || media->rtp_socket <= 0)
|
||||
return -EIO;
|
||||
|
||||
rc = rtp_receive(media->rtp_socket, &payload, &payload_len, &marker, &payload_type, &media->rx_sequence, &media->rx_timestamp);
|
||||
rc = rtp_receive(media->rtp_socket, &payload, &payload_len, &marker, &payload_type, &media->rx_sequence, &media->rx_timestamp, &media->rx_ssrc);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
|
@ -374,7 +382,7 @@ int osmo_cc_rtp_receive(osmo_cc_session_media_t *media)
|
|||
}
|
||||
|
||||
if (codec->media->receive)
|
||||
codec->media->receiver(codec, media->rx_sequence, media->rx_timestamp, data, len);
|
||||
codec->media->receiver(codec, marker, media->rx_sequence, media->rx_timestamp, media->rx_ssrc, data, len);
|
||||
|
||||
if (codec->decoder)
|
||||
free(data);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
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, int inc_sequence, int inc_timestamp);
|
||||
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);
|
||||
int osmo_cc_rtp_receive(osmo_cc_session_media_t *media);
|
||||
void osmo_cc_rtp_close(osmo_cc_session_media_t *media);
|
||||
|
||||
|
|
|
@ -58,6 +58,10 @@ void osmo_cc_help_screen(void)
|
|||
printf("to allow any suffix to match from now on. The new caller ID or dialed number\n");
|
||||
printf("may contain a '*', to append the suffix from the current caller ID or dialed\n");
|
||||
printf("number.\n\n");
|
||||
|
||||
printf("When screening an incoming caller ID or dialed number, the '@' can be appended\n");
|
||||
printf("to the 'new caller ID', followed by a 'host:port', to route call to a special\n");
|
||||
printf("Osmo-CC endpoint. This way it is possible to do simple routing.\n\n");
|
||||
}
|
||||
|
||||
char *osmo_cc_strtok_quotes(const char **text_p)
|
||||
|
@ -139,6 +143,7 @@ int osmo_cc_add_screen(osmo_cc_endpoint_t *ep, const char *text)
|
|||
} else if (!strncasecmp(text, "screen-called-in", 16)) {
|
||||
text += 16;
|
||||
list_p = &ep->screen_called_in;
|
||||
calling_in = 1;
|
||||
} else if (!strncasecmp(text, "screen-calling-out", 18)) {
|
||||
text += 18;
|
||||
list_p = &ep->screen_calling_out;
|
||||
|
@ -218,6 +223,7 @@ no_present_error:
|
|||
list->from_present = OSMO_CC_PRESENT_RESTRICTED;
|
||||
goto next_from;
|
||||
} else {
|
||||
star_used = 0;
|
||||
for (i = j = 0; token[i] && j < (int)sizeof(list->from) - 1; i++, j++) {
|
||||
if (token[i] == '?')
|
||||
list->from[j] = SCREEN_QUESTIONMARK;
|
||||
|
@ -240,7 +246,6 @@ no_present_error:
|
|||
list->from[j] = '\0';
|
||||
}
|
||||
|
||||
star_used = 0;
|
||||
next_to:
|
||||
token = osmo_cc_strtok_quotes(&text);
|
||||
if (!token) {
|
||||
|
@ -293,6 +298,7 @@ next_to:
|
|||
list->to_present = OSMO_CC_PRESENT_RESTRICTED;
|
||||
goto next_to;
|
||||
} else {
|
||||
at_used = star_used = 0;
|
||||
for (i = j = 0; token[i] && j < (int)sizeof(list->to) - 1; i++, j++) {
|
||||
if (token[i] == '*') {
|
||||
if (star_used) {
|
||||
|
@ -506,7 +512,8 @@ static int osmo_cc_screen(const char *what, osmo_cc_screen_list_t *list, uint8_t
|
|||
continue;
|
||||
/* '@' means to stop and return routing also */
|
||||
} else if (list->to[i] == SCREEN_AT) {
|
||||
*routing_p = &list->to[i];
|
||||
if (routing_p)
|
||||
*routing_p = &list->to[i + 1];
|
||||
break;
|
||||
}
|
||||
/* copy output digit */
|
||||
|
@ -543,6 +550,8 @@ static int osmo_cc_screen(const char *what, osmo_cc_screen_list_t *list, uint8_t
|
|||
PDEBUG(DCC, DEBUG_INFO, " -> present = restricted\n");
|
||||
break;
|
||||
}
|
||||
if (routing_p && *routing_p)
|
||||
PDEBUG(DCC, DEBUG_INFO, " -> remote = %s\n", *routing_p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -586,13 +595,13 @@ osmo_cc_msg_t *osmo_cc_screen_msg(osmo_cc_endpoint_t *ep, osmo_cc_msg_t *old_msg
|
|||
if (in && ep->screen_called_in) {
|
||||
rc = osmo_cc_get_ie_called(old_msg, 0, &called_type, &called_plan, id, sizeof(id));
|
||||
if (rc >= 0) {
|
||||
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), id, NULL);
|
||||
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), id, routing_p);
|
||||
if (rc >= 0)
|
||||
called_status = 1;
|
||||
} else {
|
||||
called_type = OSMO_CC_TYPE_UNKNOWN;
|
||||
called_plan = OSMO_CC_PLAN_TELEPHONY;
|
||||
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), "", NULL);
|
||||
rc = osmo_cc_screen("incoming dialed number", ep->screen_called_in, &called_type, NULL, called, sizeof(called), "", routing_p);
|
||||
if (rc >= 0)
|
||||
called_status = 1;
|
||||
}
|
||||
|
|
|
@ -260,7 +260,7 @@ struct osmo_cc_session *osmo_cc_session_parsesdp(osmo_cc_session_config_t *conf,
|
|||
memset(&ccd, 0, sizeof(ccd));
|
||||
|
||||
/* create SDP session description */
|
||||
session = osmo_cc_new_session(conf, priv, NULL, NULL, NULL, osmo_cc_session_nettype_inet, osmo_cc_session_addrtype_ipv4, "127.0.0.1", NULL, 0); // values will be replaced by local definitions during negotiation
|
||||
session = osmo_cc_new_session(conf, priv, NULL, NULL, NULL, 0, 0, NULL, NULL, 0);
|
||||
|
||||
/* check every line of SDP and parse its data */
|
||||
while(*sdp) {
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#include "../liboptions/options.h"
|
||||
#include "endpoint.h"
|
||||
|
||||
#define NTP_OFFSET 2208988800
|
||||
#define NTP_OFFSET 2208988800U
|
||||
|
||||
void osmo_cc_set_local_peer(osmo_cc_session_config_t *conf, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address)
|
||||
{
|
||||
|
@ -127,7 +127,7 @@ void osmo_cc_free_session(osmo_cc_session_t *session)
|
|||
free(session);
|
||||
}
|
||||
|
||||
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), int debug)
|
||||
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, 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;
|
||||
|
@ -374,7 +374,7 @@ osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf,
|
|||
return session;
|
||||
}
|
||||
|
||||
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len))
|
||||
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, 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;
|
||||
|
||||
|
|
|
@ -79,10 +79,10 @@ typedef struct osmo_cc_session_media {
|
|||
osmo_cc_session_connection_data_t connection_data_local, connection_data_remote;
|
||||
struct osmo_cc_session_codec *codec_list;
|
||||
int send, receive;
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len);
|
||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
|
||||
int rtp_socket;
|
||||
int rtcp_socket;
|
||||
uint32_t rtp_ssrc;
|
||||
uint32_t tx_ssrc, rx_ssrc;
|
||||
uint16_t tx_sequence, rx_sequence;
|
||||
uint32_t tx_timestamp, rx_timestamp;
|
||||
int accepted;
|
||||
|
@ -110,14 +110,14 @@ typedef struct osmo_cc_session_codec {
|
|||
void osmo_cc_set_local_peer(osmo_cc_session_config_t *conf, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address);
|
||||
osmo_cc_session_t *osmo_cc_new_session(osmo_cc_session_config_t *conf, void *priv, const char *username, const char *sess_id, const char *sess_version, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *unicast_address, const char *session_name, int debug);
|
||||
void osmo_cc_free_session(osmo_cc_session_t *session);
|
||||
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), int debug);
|
||||
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, 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 (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), int debug);
|
||||
void osmo_cc_free_codec(osmo_cc_session_codec_t *codec);
|
||||
int osmo_cc_session_check(struct osmo_cc_session *session, int remote);
|
||||
const char *osmo_cc_session_send_offer(osmo_cc_session_t *session);
|
||||
osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf, void *priv, const char *sdp);
|
||||
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len));
|
||||
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, 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 (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len));
|
||||
const char *osmo_cc_session_send_answer(osmo_cc_session_t *session);
|
||||
int osmo_cc_session_receive_answer(osmo_cc_session_t *session, const char *sdp);
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
* of 16 bits signed value:
|
||||
*/
|
||||
static double int_16_speech_level = SPEECH_LEVEL * 0.7079; /* 16 dBm below dBm0, which is about 3dBm below full 16 bit range */
|
||||
static double int_16_1mw_level = 0.7079; /* dBm0, 3dBm below full 16 bit range */
|
||||
|
||||
/* A sample_t is a value that has virtually infinite precision but will also
|
||||
* support high numbers. 'double' or 'float' types are sufficient.
|
||||
|
@ -40,7 +41,8 @@ static double int_16_speech_level = SPEECH_LEVEL * 0.7079; /* 16 dBm below dBm0,
|
|||
* envelope is network dependent.
|
||||
*/
|
||||
|
||||
void samples_to_int16(int16_t *spl, sample_t *samples, int length)
|
||||
/* sample conversion relative to SPEECH level */
|
||||
void samples_to_int16_speech(int16_t *spl, sample_t *samples, int length)
|
||||
{
|
||||
int32_t value;
|
||||
|
||||
|
@ -55,10 +57,33 @@ void samples_to_int16(int16_t *spl, sample_t *samples, int length)
|
|||
}
|
||||
}
|
||||
|
||||
void int16_to_samples(sample_t *samples, int16_t *spl, int length)
|
||||
void int16_to_samples_speech(sample_t *samples, int16_t *spl, int length)
|
||||
{
|
||||
while (length--) {
|
||||
*samples++ = (double)(*spl++) / 32767.0 / int_16_speech_level;
|
||||
}
|
||||
}
|
||||
|
||||
/* sample conversion relative to 1mW level */
|
||||
void samples_to_int16_1mw(int16_t *spl, sample_t *samples, int length)
|
||||
{
|
||||
int32_t value;
|
||||
|
||||
while (length--) {
|
||||
value = *samples++ * int_16_1mw_level * 32768.0;
|
||||
if (value > 32767.0)
|
||||
*spl++ = 32767;
|
||||
else if (value < -32767.0)
|
||||
*spl++ = -32767;
|
||||
else
|
||||
*spl++ = (uint16_t)value;
|
||||
}
|
||||
}
|
||||
|
||||
void int16_to_samples_1mw(sample_t *samples, int16_t *spl, int length)
|
||||
{
|
||||
while (length--) {
|
||||
*samples++ = (double)(*spl++) / 32767.0 / int_16_1mw_level;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ typedef double sample_t;
|
|||
|
||||
#define SPEECH_LEVEL 0.1585
|
||||
|
||||
void samples_to_int16(int16_t *spl, sample_t *samples, int length);
|
||||
void int16_to_samples(sample_t *samples, int16_t *spl, int length);
|
||||
void samples_to_int16_speech(int16_t *spl, sample_t *samples, int length);
|
||||
void int16_to_samples_speech(sample_t *samples, int16_t *spl, int length);
|
||||
void samples_to_int16_1mw(int16_t *spl, sample_t *samples, int length);
|
||||
void int16_to_samples_1mw(sample_t *samples, int16_t *spl, int length);
|
||||
|
||||
|
|
|
@ -94,15 +94,60 @@ int samplerate_downsample(samplerate_t *state, sample_t *samples, int input_num)
|
|||
return output_num;
|
||||
}
|
||||
|
||||
/* convert low sample rate to high sample rate */
|
||||
int samplerate_upsample(samplerate_t *state, sample_t *input, int input_num, sample_t *output)
|
||||
int samplerate_upsample_input_num(samplerate_t *state, int output_num)
|
||||
{
|
||||
int output_num = 0, i, idx;
|
||||
double factor = 1.0 / state->factor, in_index, diff;
|
||||
sample_t buff[(int)((double)input_num / factor + 0.5) + 10]; /* add some safety */
|
||||
sample_t *samples, last_sample;
|
||||
double factor = 1.0 / state->factor, in_index;
|
||||
int idx = 0;
|
||||
|
||||
/* count output */
|
||||
in_index = state->up.in_index;
|
||||
|
||||
while (output_num--) {
|
||||
/* increment input index */
|
||||
in_index += factor;
|
||||
/* count index on overflow */
|
||||
if (in_index >= 1.0) {
|
||||
idx++;
|
||||
in_index -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
int samplerate_upsample_output_num(samplerate_t *state, int input_num)
|
||||
{
|
||||
double factor = 1.0 / state->factor, in_index;
|
||||
int output_num = 0, idx = 0;
|
||||
|
||||
/* count output */
|
||||
in_index = state->up.in_index;
|
||||
|
||||
while (idx < input_num) {
|
||||
/* count output number */
|
||||
output_num++;
|
||||
/* increment input index */
|
||||
in_index += factor;
|
||||
/* count index on overflow */
|
||||
if (in_index >= 1.0) {
|
||||
idx++;
|
||||
in_index -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
return output_num;
|
||||
}
|
||||
|
||||
/* convert low sample rate to high sample rate */
|
||||
void samplerate_upsample(samplerate_t *state, sample_t *input, int input_num, sample_t *output, int output_num)
|
||||
{
|
||||
int i, idx;
|
||||
double factor = 1.0 / state->factor, in_index;
|
||||
sample_t buff[output_num];
|
||||
sample_t *samples, current_sample, last_sample;
|
||||
|
||||
/* get last sample for interpolation */
|
||||
current_sample = state->up.current_sample;
|
||||
last_sample = state->up.last_sample;
|
||||
|
||||
if (input == output)
|
||||
|
@ -112,35 +157,31 @@ int samplerate_upsample(samplerate_t *state, sample_t *input, int input_num, sam
|
|||
|
||||
/* resample input */
|
||||
in_index = state->up.in_index;
|
||||
idx = 0;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
/* convert index to int */
|
||||
idx = (int)in_index;
|
||||
/* if index is outside input sample range, we are done */
|
||||
if (idx >= input_num)
|
||||
break;
|
||||
for (i = 0; i < output_num; i++) {
|
||||
/* linear interpolation */
|
||||
diff = in_index - (double)idx;
|
||||
if (idx)
|
||||
samples[i] = input[idx - 1] * (1.0 - diff) + input[idx] * diff;
|
||||
else
|
||||
samples[i] = last_sample * (1.0 - diff) + input[idx] * diff;
|
||||
/* count output number */
|
||||
output_num++;
|
||||
samples[i] = last_sample * (1.0 - in_index) + current_sample * in_index;
|
||||
/* increment input index */
|
||||
in_index += factor;
|
||||
/* get next sample on overflow */
|
||||
if (in_index >= 1.0) {
|
||||
if (idx == input_num) {
|
||||
fprintf(stderr, "Given input_num is too small, please fix!\n");
|
||||
}
|
||||
last_sample = current_sample;
|
||||
current_sample = input[idx++];
|
||||
in_index -= 1.0;
|
||||
}
|
||||
}
|
||||
if (idx < input_num) {
|
||||
fprintf(stderr, "Given input_num is too large, please fix!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
/* store last sample for interpolation */
|
||||
if (input_num)
|
||||
state->up.last_sample = input[input_num - 1];
|
||||
|
||||
/* remove number of input samples from index */
|
||||
in_index -= (double)input_num;
|
||||
/* in_index cannot be negative, except due to rounding error, so... */
|
||||
if ((int)in_index < 0)
|
||||
in_index = 0.0;
|
||||
|
||||
state->up.last_sample = last_sample;
|
||||
state->up.current_sample = current_sample;
|
||||
state->up.in_index = in_index;
|
||||
|
||||
/* filter up */
|
||||
|
@ -151,7 +192,5 @@ int samplerate_upsample(samplerate_t *state, sample_t *input, int input_num, sam
|
|||
for (i = 0; i < output_num; i++)
|
||||
*output++ = samples[i];
|
||||
}
|
||||
|
||||
return output_num;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ typedef struct samplerate {
|
|||
} down;
|
||||
struct {
|
||||
iir_filter_t lp;
|
||||
sample_t current_sample;
|
||||
sample_t last_sample;
|
||||
double in_index;
|
||||
} up;
|
||||
|
@ -16,4 +17,6 @@ typedef struct samplerate {
|
|||
|
||||
int init_samplerate(samplerate_t *state, double low_samplerate, double high_samplerate, double filter_cutoff);
|
||||
int samplerate_downsample(samplerate_t *state, sample_t *samples, int input_num);
|
||||
int samplerate_upsample(samplerate_t *state, sample_t *input, int input_num, sample_t *output);
|
||||
int samplerate_upsample_input_num(samplerate_t *state, int output_num);
|
||||
int samplerate_upsample_output_num(samplerate_t *state, int input_num);
|
||||
void samplerate_upsample(samplerate_t *state, sample_t *input, int input_num, sample_t *output, int output_num);
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "sound.h"
|
||||
#endif
|
||||
|
||||
static int KEEP_FRAMES=8; /* minimum frames not to read, to prevent reading from buffer before data has been received (seems to be a bug in ALSA) */
|
||||
|
||||
typedef struct sound {
|
||||
snd_pcm_t *phandle, *chandle;
|
||||
int pchannels, cchannels;
|
||||
|
@ -190,6 +192,7 @@ static void dev_close(sound_t *sound)
|
|||
void *sound_open(const char *audiodev, double __attribute__((unused)) *tx_frequency, double __attribute__((unused)) *rx_frequency, int __attribute__((unused)) *am, int channels, double __attribute__((unused)) paging_frequency, int samplerate, int __attribute((unused)) buffer_size, double __attribute__((unused)) interval, double max_deviation, double __attribute__((unused)) max_modulation, double __attribute__((unused)) modulation_index)
|
||||
{
|
||||
sound_t *sound;
|
||||
const char *env;
|
||||
int rc;
|
||||
|
||||
if (channels < 1 || channels > 2) {
|
||||
|
@ -229,6 +232,11 @@ void *sound_open(const char *audiodev, double __attribute__((unused)) *tx_freque
|
|||
}
|
||||
#endif
|
||||
|
||||
if ((env = getenv("KEEP_FRAMES"))) {
|
||||
KEEP_FRAMES = atoi(env);
|
||||
PDEBUG(DSOUND, DEBUG_NOTICE, "KEEP %d samples in RX buffer, to prevent corrupt read.\n", KEEP_FRAMES);
|
||||
}
|
||||
|
||||
return sound;
|
||||
|
||||
error:
|
||||
|
@ -387,9 +395,7 @@ int sound_write(void *inst, sample_t **samples, uint8_t __attribute__((unused))
|
|||
return rc;
|
||||
}
|
||||
|
||||
#define KEEP_FRAMES 8 /* minimum frames not to read, due to bug in ALSA */
|
||||
|
||||
int sound_read(void *inst, sample_t **samples, int num, int channels, double __attribute__((unused)) *rf_level_db)
|
||||
int sound_read(void *inst, sample_t **samples, int num, int channels, double *rf_level_db)
|
||||
{
|
||||
sound_t *sound = (sound_t *)inst;
|
||||
double spl_deviation = sound->spl_deviation;
|
||||
|
@ -399,9 +405,6 @@ int sound_read(void *inst, sample_t **samples, int num, int channels, double __a
|
|||
int in, rc;
|
||||
int i, ii;
|
||||
|
||||
/* make valgrind happy, because snd_pcm_readi() does not seem to initially fill buffer with values */
|
||||
memset(buff, 0, sizeof(buff));
|
||||
|
||||
/* get samples in rx buffer */
|
||||
in = snd_pcm_avail(sound->chandle);
|
||||
/* if not more than KEEP_FRAMES frames available, try next time */
|
||||
|
@ -413,8 +416,10 @@ int sound_read(void *inst, sample_t **samples, int num, int channels, double __a
|
|||
if (in > num)
|
||||
in = num;
|
||||
|
||||
rc = snd_pcm_readi(sound->chandle, buff, in);
|
||||
/* make valgrind happy, because snd_pcm_readi() does not seem to initially fill buffer with values */
|
||||
memset(buff, 0, sizeof(*buff) * sound->cchannels * in);
|
||||
|
||||
rc = snd_pcm_readi(sound->chandle, buff, in);
|
||||
if (rc < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return 0;
|
||||
|
@ -430,10 +435,8 @@ int sound_read(void *inst, sample_t **samples, int num, int channels, double __a
|
|||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (rc == 0)
|
||||
return rc;
|
||||
|
||||
if (sound->cchannels == 2) {
|
||||
if (channels < 2) {
|
||||
for (i = 0, ii = 0; i < rc; i++) {
|
||||
|
@ -471,12 +474,17 @@ int sound_read(void *inst, sample_t **samples, int num, int channels, double __a
|
|||
#ifdef HAVE_MOBILE
|
||||
sender_t *sender;
|
||||
for (i = 0; i < channels; i++) {
|
||||
if (rf_level_db)
|
||||
rf_level_db[i] = NAN;
|
||||
sender = get_sender_by_empfangsfrequenz(sound->rx_frequency[i]);
|
||||
if (!sender)
|
||||
continue;
|
||||
display_measurements_update(sound->dmp[i], log10((double)max[i] / 32768.0) * 20, 0.0);
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < channels; i++) {
|
||||
if (rf_level_db)
|
||||
rf_level_db[i] = 0.0;
|
||||
rf_level_db[i] = NAN;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -512,6 +520,8 @@ int sound_get_tosend(void *inst, int buffer_size)
|
|||
}
|
||||
|
||||
tosend = buffer_size - delay;
|
||||
if (tosend < 0)
|
||||
tosend = 0;
|
||||
return tosend;
|
||||
}
|
||||
|
||||
|
|
|
@ -269,17 +269,15 @@ static void call_destroy(call_t *call)
|
|||
*/
|
||||
|
||||
/* take audio from CC and store in jitter buffer */
|
||||
void down_audio(struct osmo_cc_session_codec *codec, uint16_t __attribute__((unused)) sequence_number, uint32_t __attribute__((unused)) timestamp, uint8_t *data, int len)
|
||||
void down_audio(struct osmo_cc_session_codec *codec, uint8_t __attribute__((unused)) marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len)
|
||||
{
|
||||
call_t *call = codec->media->session->priv;
|
||||
int count = len / 2;
|
||||
sample_t samples[count];
|
||||
|
||||
if (call->telephone_ep->loopback != 3) {
|
||||
sample_t up[(int)((double)count * call->srstate.factor + 0.5) + 10];
|
||||
int16_to_samples(samples, (int16_t *)data, count);
|
||||
count = samplerate_upsample(&call->srstate, samples, count, up);
|
||||
jitter_save(&call->dejitter, up, count);
|
||||
int16_to_samples_speech(samples, (int16_t *)data, count);
|
||||
jitter_save(&call->tx_dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,8 +343,8 @@ void alsa_work(telephone_t *telephone_ep)
|
|||
/* only if we have a call */
|
||||
if (call->cc_callref && call->codec) {
|
||||
int16_t data[160];
|
||||
samples_to_int16(data, call->tx_buffer, 160);
|
||||
osmo_cc_rtp_send(call->codec, (uint8_t *)data, 160 * 2, 1, 160);
|
||||
samples_to_int16_speech(data, call->tx_buffer, 160);
|
||||
osmo_cc_rtp_send(call->codec, (uint8_t *)data, 160 * 2, 0, 1, 160);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue