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.
This commit is contained in:
Andreas Eversberg 2024-03-10 13:45:08 +01:00
parent e7efcee289
commit a20637825a
40 changed files with 865 additions and 572 deletions

View File

@ -23,7 +23,7 @@ AC_CHECK_LIB([m], [main], [], [echo "Failed to find lib!" ; exit -1])
AC_CHECK_LIB([pthread], [main], [], [echo "Failed to find lib!" ; exit -1])
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOCC, libosmocc >= 1.0.0)
PKG_CHECK_MODULES(LIBOSMOCC, libosmocc >= 2.0.0)
with_sdr=no
soapy_0_8_0_or_higher=

View File

@ -1080,28 +1080,6 @@ void call_down_release(int callref, int cause)
}
}
/* Receive audio from call instance. */
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
amps_t *amps;
for (sender = sender_head; sender; sender = sender->next) {
amps = (amps_t *) sender;
if (amps->trans_list && amps->trans_list->callref == callref)
break;
}
if (!sender)
return;
if (amps->dsp_mode == DSP_MODE_AUDIO_RX_AUDIO_TX) {
compress_audio(&amps->cstate, samples, count);
jitter_save(&amps->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
}
void call_down_clock(void) {}
/* Timeout handling */
void transaction_timeout(void *data)
{

View File

@ -488,7 +488,12 @@ again:
case DSP_MODE_AUDIO_RX_AUDIO_TX:
memset(power, 1, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
{
int16_t spl[input_num];
jitter_load_samples(&sender->dejitter, (uint8_t *)spl, input_num, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(samples, spl, input_num);
}
compress_audio(&amps->cstate, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
/* pre-emphasis */
if (amps->pre_emphasis)
@ -956,6 +961,8 @@ void amps_set_dsp_mode(amps_t *amps, enum dsp_mode mode, int frame_length)
sat_reset(amps, "Disable FVC");
LOGP_CHAN(DDSP, LOGL_INFO, "Change mode from FVC to OFF\n");
}
if (mode == DSP_MODE_AUDIO_RX_AUDIO_TX && amps->dsp_mode != mode)
jitter_reset(&amps->sender.dejitter);
LOGP_CHAN(DDSP, LOGL_DEBUG, "Reset FSK frame transmitter, due to setting dsp mode.\n");
@ -974,3 +981,27 @@ void amps_set_dsp_mode(amps_t *amps, enum dsp_mode mode, int frame_length)
amps->fsk_tx_frame_pos = 0;
}
/* Receive audio from call instance. */
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
sender_t *sender;
amps_t *amps;
for (sender = sender_head; sender; sender = sender->next) {
amps = (amps_t *) sender;
if (amps->trans_list && amps->trans_list->callref == callref)
break;
}
if (!sender)
return;
if (amps->dsp_mode == DSP_MODE_AUDIO_RX_AUDIO_TX) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(decoder, decoder_priv, payload, payload_len, marker, sequence, timestamp, ssrc);
if (jf)
jitter_save(&amps->sender.dejitter, jf);
}
}
void call_down_clock(void) {}

View File

@ -2,6 +2,7 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include "../libsample/sample.h"
#include "amps.h"

View File

@ -512,25 +512,5 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
}
}
/* Receive audio from call instance. */
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
anetz_t *anetz;
for (sender = sender_head; sender; sender = sender->next) {
anetz = (anetz_t *) sender;
if (anetz->callref == callref)
break;
}
if (!sender)
return;
if (anetz->dsp_mode == DSP_MODE_AUDIO)
jitter_save(&anetz->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
void call_down_clock(void) {}
void dump_info(void) {}

View File

@ -367,7 +367,11 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
break;
case DSP_MODE_AUDIO:
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
{
int16_t spl[input_num];
jitter_load_samples(&sender->dejitter, (uint8_t *)spl, input_num, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(samples, spl, input_num);
}
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
break;
case DSP_MODE_TONE:
@ -404,6 +408,9 @@ const char *anetz_dsp_mode_name(enum dsp_mode mode)
void anetz_set_dsp_mode(anetz_t *anetz, enum dsp_mode mode, int detect_reset)
{
LOGP_CHAN(DDSP, LOGL_DEBUG, "DSP mode %s -> %s\n", anetz_dsp_mode_name(anetz->dsp_mode), anetz_dsp_mode_name(mode));
if (mode == DSP_MODE_AUDIO && anetz->dsp_mode != mode)
jitter_reset(&anetz->sender.dejitter);
anetz->dsp_mode = mode;
/* reset sequence paging */
anetz->paging_tone = 0;
@ -414,3 +421,27 @@ void anetz_set_dsp_mode(anetz_t *anetz, enum dsp_mode mode, int detect_reset)
anetz->tone_detected = -1;
}
/* Receive audio from call instance. */
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
sender_t *sender;
anetz_t *anetz;
for (sender = sender_head; sender; sender = sender->next) {
anetz = (anetz_t *) sender;
if (anetz->callref == callref)
break;
}
if (!sender)
return;
if (anetz->dsp_mode == DSP_MODE_AUDIO) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(decoder, decoder_priv, payload, payload_len, marker, sequence, timestamp, ssrc);
if (jf)
jitter_save(&anetz->sender.dejitter, jf);
}
}
void call_down_clock(void) {}

View File

@ -857,27 +857,5 @@ void call_down_release(int callref, int __attribute__((unused)) cause)
}
}
/* Receive audio from call instance. */
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
bnetz_t *bnetz;
for (sender = sender_head; sender; sender = sender->next) {
bnetz = (bnetz_t *) sender;
if (bnetz->callref == callref)
break;
}
if (!sender)
return;
if (bnetz->dsp_mode == DSP_MODE_AUDIO
|| bnetz->dsp_mode == DSP_MODE_AUDIO_METER) {
jitter_save(&bnetz->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
}
void call_down_clock(void) {}
void dump_info(void) {}

View File

@ -373,7 +373,11 @@ again:
case DSP_MODE_AUDIO:
case DSP_MODE_AUDIO_METER:
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
{
int16_t spl[input_num];
jitter_load_samples(&sender->dejitter, (uint8_t *)spl, input_num, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(samples, spl, input_num);
}
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
if (bnetz->dsp_mode == DSP_MODE_AUDIO_METER)
metering_tone(bnetz, samples, length);
@ -422,8 +426,35 @@ void bnetz_set_dsp_mode(bnetz_t *bnetz, enum dsp_mode mode)
bnetz->tx_telegramm = 0;
fsk_mod_reset(&bnetz->fsk_mod);
}
if ((mode == DSP_MODE_AUDIO || mode == DSP_MODE_AUDIO_METER) && (bnetz->dsp_mode != DSP_MODE_AUDIO && bnetz->dsp_mode != DSP_MODE_AUDIO_METER))
jitter_reset(&bnetz->sender.dejitter);
LOGP_CHAN(DDSP, LOGL_DEBUG, "DSP mode %s -> %s\n", bnetz_dsp_mode_name(bnetz->dsp_mode), bnetz_dsp_mode_name(mode));
bnetz->dsp_mode = mode;
}
/* Receive audio from call instance. */
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
sender_t *sender;
bnetz_t *bnetz;
for (sender = sender_head; sender; sender = sender->next) {
bnetz = (bnetz_t *) sender;
if (bnetz->callref == callref)
break;
}
if (!sender)
return;
if (bnetz->dsp_mode == DSP_MODE_AUDIO
|| bnetz->dsp_mode == DSP_MODE_AUDIO_METER) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(decoder, decoder_priv, payload, payload_len, marker, sequence, timestamp, ssrc);
if (jf)
jitter_save(&bnetz->sender.dejitter, jf);
}
}
void call_down_clock(void) {}

View File

@ -579,28 +579,6 @@ static void cnetz_release(transaction_t *trans, uint8_t cause)
osmo_timer_del(&trans->timer);
}
/* Receive audio from call instance. */
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
cnetz_t *cnetz;
for (sender = sender_head; sender; sender = sender->next) {
cnetz = (cnetz_t *) sender;
if (cnetz->trans_list && cnetz->trans_list->callref == callref)
break;
}
if (!sender)
return;
if (cnetz->dsp_mode == DSP_MODE_SPK_V) {
/* store as is, since we convert rate when processing FSK frames */
jitter_save(&cnetz->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
}
void call_down_clock(void) {}
int call_down_setup(int callref, const char __attribute__((unused)) *caller_id, enum number_type __attribute__((unused)) caller_type, const char *dialing)
{
cnetz_t *ogk, *spk;

View File

@ -49,7 +49,7 @@
#define BLOCK_BITS 198 /* duration of one time slot including pause at beginning and end */
#ifdef TEST_SCRAMBLE
jitter_t scrambler_test_jb;
test_echo_t scrambler_test_echo = {};
scrambler_t scrambler_test_scrambler1;
scrambler_t scrambler_test_scrambler2;
#endif
@ -168,9 +168,9 @@ int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], en
cnetz->offset_range = ceil(cnetz->fsk_bitduration);
#ifdef TEST_SCRAMBLE
rc = jitter_create(&scrambler_test_jb, "scramble", cnetz->sender.samplerate, sizeof(sample_t), JITTER_AUDIO);
rc = test_echo_alloc(&scrambler_test_echo, cnetz->sender.samplerate / 20);
if (rc < 0) {
LOGP_CHAN(DDSP, LOGL_ERROR, "Failed to init jitter buffer for scrambler test!\n");
LOGP_CHAN(DDSP, LOGL_ERROR, "Failed to init echo buffer for scrambler test!\n");
exit(0);
}
scrambler_setup(&scrambler_test_scrambler1, cnetz->sender.samplerate);
@ -573,7 +573,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double rf_l
#ifdef TEST_UNSCRAMBLE
scrambler(&scrambler_test_scrambler1, samples, length);
#endif
jitter_save(&scrambler_test_jb, samples, length, 0, 0, 0, 0);
test_echo_store(&scrambler_test_echo, samples, length);
return;
#endif
@ -589,8 +589,10 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double rf_l
static int shrink_speech(cnetz_t *cnetz, sample_t *speech_buffer)
{
int speech_length;
int16_t spl[100];
jitter_load(&cnetz->sender.dejitter, speech_buffer, 100);
jitter_load_samples(&cnetz->sender.dejitter, (uint8_t *)spl, 100, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(speech_buffer, spl, 100);
/* 1. compress dynamics */
compress_audio(&cnetz->cstate, speech_buffer, 100);
/* 2. upsample */
@ -795,7 +797,7 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
calc_clock_speed(cnetz, length, 1, 0);
#ifdef TEST_SCRAMBLE
jitter_load(&scrambler_test_jb, samples, length);
test_echo_load(&scrambler_test_echo, samples, length);
scrambler(&scrambler_test_scrambler2, samples, length);
return;
#endif
@ -864,6 +866,9 @@ void cnetz_set_dsp_mode(cnetz_t *cnetz, enum dsp_mode mode)
LOGP_CHAN(DDSP, LOGL_INFO, "DSP mode %s -> %s\n", cnetz_dsp_mode_name(cnetz->dsp_mode), cnetz_dsp_mode_name(mode));
cnetz->dsp_mode = mode;
}
if (mode == DSP_MODE_SPK_V && cnetz->dsp_mode != mode)
jitter_reset(&cnetz->sender.dejitter);
/* we must get rid of partly received frame */
fsk_demod_reset(&cnetz->fsk_demod);
}
@ -879,3 +884,27 @@ void cnetz_set_sched_dsp_mode(cnetz_t *cnetz, enum dsp_mode mode, int timeslot)
cnetz->sched_dsp_mode_ts = timeslot;
}
/* Receive audio from call instance. */
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
sender_t *sender;
cnetz_t *cnetz;
for (sender = sender_head; sender; sender = sender->next) {
cnetz = (cnetz_t *) sender;
if (cnetz->trans_list && cnetz->trans_list->callref == callref)
break;
}
if (!sender)
return;
if (cnetz->dsp_mode == DSP_MODE_SPK_V) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(decoder, decoder_priv, payload, payload_len, marker, sequence, timestamp, ssrc);
if (jf)
jitter_save(&cnetz->sender.dejitter, jf);
}
}
void call_down_clock(void) {}

View File

@ -760,7 +760,7 @@ void call_down_release(int callref, int cause)
}
/* Receive audio from call instance. */
void call_down_audio(int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
void call_down_audio(void __attribute__((unused)) *decoder, void __attribute__((unused)) *decoder_priv, int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint8_t __attribute__((unused)) marker, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, uint8_t __attribute__((unused)) *payload, int __attribute__((unused)) payload_len)
{
}

View File

@ -604,7 +604,11 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
if (fuenf->state == FUENF_STATE_DURCHSAGE && fuenf->callref) {
memset(power, 1, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
{
int16_t spl[input_num];
jitter_load_samples(&sender->dejitter, (uint8_t *)spl, input_num, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(samples, spl, input_num);
}
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
} else {
/* send if something has to be sent. else turn transmitter off */
@ -646,3 +650,27 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
}
/* Receive audio from call instance. */
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
sender_t *sender;
fuenf_t *fuenf;
for (sender = sender_head; sender; sender = sender->next) {
fuenf = (fuenf_t *) sender;
if (fuenf->callref == callref)
break;
}
if (!sender)
return;
if (fuenf->state == FUENF_STATE_DURCHSAGE) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(decoder, decoder_priv, payload, payload_len, marker, sequence, timestamp, ssrc);
if (jf)
jitter_save(&fuenf->sender.dejitter, jf);
}
}
void call_down_clock(void) {}

View File

@ -270,10 +270,6 @@ void fuenf_rx_function(fuenf_t *fuenf, enum fuenf_funktion funktion)
LOGP_CHAN(DFUENF, LOGL_INFO, "Received function '%s'.\n", fuenf_funktion_name[funktion]);
}
void call_down_clock(void)
{
}
/* Call control starts call towards transmitter. */
int call_down_setup(int callref, const char __attribute__((unused)) *caller_id, enum number_type __attribute__((unused)) caller_type, const char *dialing)
{
@ -389,23 +385,5 @@ void call_down_release(int callref, int cause)
_release(callref, cause);
}
/* Receive audio from call instance. */
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
fuenf_t *fuenf;
for (sender = sender_head; sender; sender = sender->next) {
fuenf = (fuenf_t *) sender;
if (fuenf->callref == callref)
break;
}
if (!sender)
return;
if (fuenf->state == FUENF_STATE_DURCHSAGE)
jitter_save(&fuenf->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
void dump_info(void) {}

View File

@ -1259,7 +1259,11 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
v27_modem_send(&fuvst->modem, samples, length);
else {
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
{
int16_t spl[input_num];
jitter_load_samples(&sender->dejitter, (uint8_t *)spl, input_num, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(samples, spl, input_num);
}
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
}
}
@ -1295,7 +1299,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
}
/* Receive audio from call instance. */
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
sender_t *sender;
fuvst_t *fuvst;
@ -1308,8 +1312,12 @@ void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_
if (!sender)
return;
if (fuvst->callref)
jitter_save(&fuvst->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
if (fuvst->callref) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(decoder, decoder_priv, payload, payload_len, marker, sequence, timestamp, ssrc);
if (jf)
jitter_save(&fuvst->sender.dejitter, jf);
}
}
void call_down_clock(void) {}

View File

@ -257,7 +257,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
v27_modem_receive(&sniffer->modem, samples, length);
}
void call_down_audio(int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count) { }
void call_down_audio(void __attribute__((unused)) *decoder, void __attribute__((unused)) *decoder_priv, int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint8_t __attribute__((unused)) marker, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, uint8_t __attribute__((unused)) *payload, int __attribute__((unused)) payload_len) { }
void call_down_clock(void) {}

View File

@ -869,7 +869,7 @@ void call_down_release(int callref, int cause)
}
/* Receive audio from call instance. */
void call_down_audio(int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
void call_down_audio(void __attribute__((unused)) *decoder, void __attribute__((unused)) *decoder_priv, int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint8_t __attribute__((unused)) marker, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, uint8_t __attribute__((unused)) *payload, int __attribute__((unused)) payload_len)
{
}

View File

@ -297,7 +297,11 @@ again:
case DSP_MODE_AUDIO:
memset(power, 1, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
{
int16_t spl[input_num];
jitter_load_samples(&sender->dejitter, (uint8_t *)spl, input_num, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(samples, spl, input_num);
}
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
if (imts->pre_emphasis)
pre_emphasis(&imts->estate, samples, length);
@ -549,6 +553,8 @@ void imts_set_dsp_mode(imts_t *imts, enum dsp_mode mode, int tone, double durati
imts->demod_duration = 0.0;
}
if (mode == DSP_MODE_AUDIO && imts->dsp_mode != mode)
jitter_reset(&imts->sender.dejitter);
if (imts->dsp_mode != mode) {
LOGP_CHAN(DDSP, LOGL_DEBUG, "DSP mode %s -> %s\n", imts_dsp_mode_name(imts->dsp_mode), imts_dsp_mode_name(mode));
imts->dsp_mode = mode;
@ -564,3 +570,27 @@ void imts_set_dsp_mode(imts_t *imts, enum dsp_mode mode, int tone, double durati
}
}
/* Receive audio from call instance. */
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
sender_t *sender;
imts_t *imts;
for (sender = sender_head; sender; sender = sender->next) {
imts = (imts_t *) sender;
if (imts->callref == callref)
break;
}
if (!sender)
return;
if (imts->dsp_mode == DSP_MODE_AUDIO) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(decoder, decoder_priv, payload, payload_len, marker, sequence, timestamp, ssrc);
if (jf)
jitter_save(&imts->sender.dejitter, jf);
}
}
void call_down_clock(void) {}

View File

@ -1290,26 +1290,5 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
}
}
/* Receive audio from call instance. */
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
imts_t *imts;
for (sender = sender_head; sender; sender = sender->next) {
imts = (imts_t *) sender;
if (imts->callref == callref)
break;
}
if (!sender)
return;
if (imts->dsp_mode == DSP_MODE_AUDIO) {
jitter_save(&imts->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
}
void call_down_clock(void) {}
void dump_info(void) {}

View File

@ -113,7 +113,7 @@ int dsp_init_sender(jolly_t *jolly, int nbfm, double squelch_db, int repeater)
/* repeater */
jolly->repeater = repeater;
jolly->repeater_max = (int)((double)jolly->sender.samplerate * REPEATER_TIME);
rc = jitter_create(&jolly->repeater_dejitter, "repeater", jolly->sender.samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
rc = jitter_create(&jolly->repeater_dejitter, "repeater", jolly->sender.samplerate, 0.050, 0.500, JITTER_FLAG_NONE);
if (rc < 0) {
LOGP(DDSP, LOGL_ERROR, "Failed to create and init repeater buffer!\n");
goto error;
@ -318,8 +318,14 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double rf_l
}
/* if repeater mode, store sample in jitter buffer */
if (jolly->repeater)
jitter_save(&jolly->repeater_dejitter, samples, length, 0, 0, 0, 0);
if (jolly->repeater) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(NULL, NULL, (uint8_t *)samples, length * sizeof(*samples), 0, jolly->repeater_sequence, jolly->repeater_timestamp, 123);
if (jf)
jitter_save(&jolly->repeater_dejitter, jf);
jolly->repeater_sequence += 1;
jolly->repeater_timestamp += length;
}
/* downsample, decode DTMF */
count = samplerate_downsample(&jolly->sender.srstate, samples, length);
@ -369,7 +375,11 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
case STATE_CALL_DIALING:
memset(power, 1, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
{
int16_t spl[input_num];
jitter_load_samples(&sender->dejitter, (uint8_t *)spl, input_num, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(samples, spl, input_num);
}
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
break;
case STATE_OUT_VERIFY:
@ -394,9 +404,33 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
if (jolly->repeater) {
sample_t uplink[length];
int i;
jitter_load(&jolly->repeater_dejitter, uplink, length);
jitter_load_samples(&jolly->repeater_dejitter, (uint8_t *)uplink, length, sizeof(*uplink), NULL, NULL);
for (i = 0; i < length; i++)
samples[i] += uplink[i];
}
}
/* Receive audio from call instance. */
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
sender_t *sender;
jolly_t *jolly;
for (sender = sender_head; sender; sender = sender->next) {
jolly = (jolly_t *) sender;
if (jolly->callref == callref)
break;
}
if (!sender)
return;
if (jolly->state == STATE_CALL || jolly->state == STATE_CALL_DIALING) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(decoder, decoder_priv, payload, payload_len, marker, sequence, timestamp, ssrc);
if (jf)
jitter_save(&jolly->sender.dejitter, jf);
}
}
void call_down_clock(void) {}

View File

@ -593,25 +593,5 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
}
}
/* Receive audio from call instance. */
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
jolly_t *jolly;
for (sender = sender_head; sender; sender = sender->next) {
jolly = (jolly_t *) sender;
if (jolly->callref == callref)
break;
}
if (!sender)
return;
if (jolly->state == STATE_CALL || jolly->state == STATE_CALL_DIALING)
jitter_save(&jolly->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
void call_down_clock(void) {}
void dump_info(void) {}

View File

@ -30,6 +30,8 @@ typedef struct jolly {
/* dsp states */
int repeater; /* mix audio of RX signal to TX signal */
jitter_t repeater_dejitter; /* forwarding audio */
uint16_t repeater_sequence; /* sequence & ts for jitter buffer */
uint32_t repeater_timestamp;
int repeater_count; /* counter to count down repeater's "transmitter on" */
int repeater_max; /* duration in samples */
squelch_t squelch; /* squelch detection process */

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.
*
*/
@ -82,33 +82,19 @@
#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) {
LOGP(DJITTER, LOGL_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);
LOGP(DJITTER, LOGL_INFO, "%sCreated jitter buffer. (samplerate=%.0f, target_window=%.0fms, max_window=%.0fms, flag:latency=%s flag:repeat=%s)\n", jb->name, samplerate, target_window_duration * 1000.0, max_window_duration * 1000.0, (window_flags & JITTER_FLAG_LATENCY) ? "true" : "false", (window_flags & JITTER_FLAG_REPEAT) ? "true" : "false");
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;
LOGP(DJITTER, LOGL_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,401 @@ 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);
LOGP(DJITTER, LOGL_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
LOGP(DJITTER, LOGL_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
LOGP(DJITTER, LOGL_DEBUG, "%sSave frame of %d samples (seqence=%u timestamp=%u ssrc=0x%02x).\n", jb->name, length, sequence, timestamp, ssrc);
#endif
jb->next_sequence = sequence + 1;
jb->next_timestamp = timestamp + length;
}
/* 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)
LOGP(DJITTER, LOGL_DEBUG, "%s Initial frame after init or reset.\n", jb->name);
else
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) {
LOGP(DJITTER, LOGL_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
LOGP(DJITTER, LOGL_DEBUG, "%sFrame has offset of %.0fms in jitter buffer.\n", jb->name, (double)offset_timestamp * jb->sample_duration * 1000.0);
#endif
/* measure delay */
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)) {
LOGP(DJITTER, LOGL_DEBUG, "%s Frame too early: Shift jitter buffer to the future, to make the frame fit to the end. (offset_timestamp(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
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 {
LOGP(DJITTER, LOGL_DEBUG, "%s Frame too early: Shift jitter buffer to the future, to make the frame fit to the target delay. (offset_timestamp(%d) > max_window_size(%d))\n", jb->name, offset_timestamp, jb->max_window_size);
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)) {
LOGP(DJITTER, LOGL_DEBUG, "%s Frame too late: Shift jitter buffer to the past, and add target window size. (offset_timestamp(%d) < 0)\n", jb->name, offset_timestamp);
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 {
LOGP(DJITTER, LOGL_DEBUG, "%s Frame too late: Shift jitter buffer to the past, and add half target window size. (offset_timestamp(%d) < 0)\n", jb->name, offset_timestamp);
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
LOGP(DJITTER, LOGL_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) {
LOGP(DJITTER, LOGL_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 */
while ((jf = jb->frame_list)) {
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
LOGP(DJITTER, LOGL_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)
LOGP(DJITTER, LOGL_DEBUG, "%s Statistics: target_window_delay=%.0fms max_window_delay=%.0fms current min_delay=%.0fms\n", jb->name, (double)jb->target_window_size * jb->sample_duration * 1000.0, (double)jb->max_window_size * jb->sample_duration * 1000.0, (double)jb->min_delay_value * jb->sample_duration * 1000.0);
/* delay reduction, if maximum delay is greater than target jitter window size */
if ((jb->window_flags & JITTER_FLAG_LATENCY) && jb->min_delay_value > jb->target_window_size) {
LOGP(DJITTER, LOGL_DEBUG, "%s Reducing current minimum delay of %.0fms, because maximum delay is greater than target window size of %.0fms.\n", jb->name, (double)jb->min_delay_value * jb->sample_duration * 1000.0, (double)jb->target_window_size * jb->sample_duration * 1000.0);
/* only reduce delay to half of the target window size */
jb->window_timestamp += jb->min_delay_value - jb->target_window_size / 2;
}
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)
LOGP(DJITTER, LOGL_DEBUG, "%s There is a frame ahead in buffer after %d samples. Interpolating gap.\n", jb->name, jf->timestamp - jb->window_timestamp);
else
LOGP(DJITTER, LOGL_DEBUG, "%s There is no frame ahead in buffer. Interpolating gap.\n", jb->name);
#endif
/* extrapolate by playing the extrapolation buffer */
while (count) {
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
LOGP(DJITTER, LOGL_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
LOGP(DJITTER, LOGL_DEBUG, "%s Copy data (offset=%u count=%u) from frame (sequence=%u timestamp=%u length=%u).\n", jb->name, index, count, jf->sequence, jf->timestamp, jf->length);
#endif
memcpy(samples, (uint8_t *)jf->samples + index * jb->sample_size, count * jb->sample_size);
samples = (uint8_t *)samples + count * jb->sample_size;
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;
/* 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;
/* 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;
}
/* 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);
if (!jb->spl_buf) {
jitter_frame_free(jf);
return;
}
} else {
/* no decoder, so just copy as it is */
jb->spl_buf = malloc(payload_len);
if (!jb->spl_buf) {
jitter_frame_free(jf);
return;
}
memcpy(jb->spl_buf, payload, payload_len);
jb->spl_len = payload_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

@ -390,20 +390,22 @@ static void process_timeout(void *data)
}
}
void down_audio(struct osmo_cc_session_codec *codec, uint8_t __attribute__((unused)) marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len)
static void down_audio(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
process_t *process = codec->media->session->priv;
sample_t samples[len / 2];
// sample_t samples[len / 2];
/* if we are disconnected, ignore audio */
if (!process || process->pattern != PATTERN_NONE)
return;
#if 0
int16_to_samples_speech(samples, (int16_t *)data, len / 2);
#ifdef DEBUG_LEVEL
double lev = level_of(samples, len / 2);
printf("festnetz-level: %s %.4f\n", debug_db(lev), (20 * log10(lev)));
#endif
call_down_audio(process->callref, sequence_number, timestamp, ssrc, samples, len / 2);
#endif
call_down_audio(codec->decoder, process, process->callref, marker, sequence_number, timestamp, ssrc, payload, payload_len);
}
static void indicate_setup(process_t *process, const char *callerid, const char *dialing, uint8_t network_type, const char *network_id)
@ -602,12 +604,14 @@ void call_tone_recall(int callref, int on)
}
/* forward audio to OSMO-CC or call instance */
void call_up_audio(int callref, sample_t *samples, int count)
void call_up_audio(int callref, sample_t *samples, int len)
{
process_t *process;
int16_t data[count];
int16_t spl[len];
uint8_t *payload;
int payload_len;
if (count != 160) {
if (len != 160) {
fprintf(stderr, "Samples must be 160, please fix!\n");
abort();
}
@ -619,13 +623,21 @@ void call_up_audio(int callref, sample_t *samples, int count)
if (!process || process->pattern != PATTERN_NONE)
return;
/* no codec negotiated (yet) */
if (!process->codec)
return;
/* forward audio */
#ifdef DEBUG_LEVEL
double lev = level_of(samples, count);
double lev = level_of(samples, len);
printf(" mobil-level: %s%.4f\n", debug_db(lev), (20 * log10(lev)));
#endif
samples_to_int16_speech(data, samples, count);
osmo_cc_rtp_send(process->codec, (uint8_t *)data, count * 2, 0, 1, count, process);
/* real to integer */
samples_to_int16_speech(spl, samples, len);
/* encode and send via RTP */
process->codec->encoder((uint8_t *)spl, len * 2, &payload, &payload_len, process);
osmo_cc_rtp_send(process->codec, payload, payload_len, 0, 1, len);
free(payload);
/* don't destroy process here in case of an error */
}
@ -638,17 +650,21 @@ void call_clock(void)
while(process) {
if (process->pattern != PATTERN_NONE) {
int16_t data[160];
int16_t spl[160];
uint8_t *payload;
int payload_len;
/* try to get patterns, else copy the samples we got */
get_process_patterns(process, data, 160);
get_process_patterns(process, spl, 160);
#ifdef DEBUG_LEVEL
sample_t samples[160];
int16_to_samples(samples, (int16_t *)data->data, 160);
int16_to_samples(samples, (int16_t *)spl->data, 160);
double lev = level_of(samples, 160);
printf(" mobil-level: %s%.4f\n", debug_db(lev), (20 * log10(lev)));
samples_to_int16(data, samples, 160);
samples_to_int16(spl, samples, 160);
#endif
osmo_cc_rtp_send(process->codec, (uint8_t *)data, 160 * 2, 0, 1, 160, process);
/* encode and send via RTP */
process->codec->encoder((uint8_t *)spl, 160 * 2, &payload, &payload_len, process);
osmo_cc_rtp_send(process->codec, (uint8_t *)spl, 160 * 2, 0, 1, 160);
/* don't destroy process here in case of an error */
}
process = process->next;
@ -660,7 +676,7 @@ void ll_msg_cb(osmo_cc_endpoint_t __attribute__((unused)) *ep, uint32_t callref,
{
process_t *process;
uint8_t coding, location, progress, isdn_cause, socket_cause;
uint16_t sip_cause, metering_connect_units, metering_unit_period_decisecs;
uint16_t sip_cause, metering_connect_units;
uint8_t type, plan, present, screen, caller_type;
char caller_id[33], number[33];
struct timeval tv_meter = {};
@ -705,12 +721,8 @@ void ll_msg_cb(osmo_cc_endpoint_t __attribute__((unused)) *ep, uint32_t callref,
return;
}
/* get metering information */
rc = osmo_cc_get_ie_metering(msg, 0, &metering_connect_units, &metering_unit_period_decisecs);
if (rc >= 0) {
tv_meter.tv_sec = metering_unit_period_decisecs / 10;
tv_meter.tv_usec = (metering_unit_period_decisecs % 10) / 10;
}
/* get metering information, tv_meter elements are 0, if no metering info available */
osmo_cc_get_ie_metering(msg, 0, &metering_connect_units, &tv_meter);
switch(msg->type) {
case OSMO_CC_MSG_SETUP_REQ:

View File

@ -37,7 +37,7 @@ void call_down_release(int callref, int cause);
/* send and receive audio */
void call_up_audio(int callref, sample_t *samples, int count);
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count);
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len);
/* clock to transmit to */
void call_clock(void); /* from main loop */

View File

@ -22,6 +22,7 @@
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/param.h>
@ -143,26 +144,31 @@ static void free_console(void)
console.callref = 0;
}
void up_audio(struct osmo_cc_session_codec *codec, uint8_t __attribute__((unused)) marker, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len)
static void up_audio(struct osmo_cc_session_codec *codec, uint8_t marker, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
int count = len / 2;
sample_t samples[count];
/* save audio from transceiver to jitter buffer */
if (console.sound) {
int16_to_samples_speech(samples, (int16_t *)data, count);
jitter_save(&console.dejitter, samples, count, 1, sequence_number, timestamp, ssrc);
jitter_frame_t *jf;
jf = jitter_frame_alloc(codec->decoder, &console, payload, payload_len, marker, sequence, timestamp, ssrc);
if (!jf)
return;
jitter_save(&console.dejitter, jf);
return;
}
/* if echo test is used, send echo back to mobile */
if (console.echo_test) {
osmo_cc_rtp_send(codec, (uint8_t *)data, count * 2, 0, 1, count, &console);
osmo_cc_rtp_send_ts(codec, payload, payload_len, marker, sequence, timestamp);
return;
}
/* if no sound is used, send test tone to mobile */
if (console.state == CONSOLE_CONNECT) {
get_test_patterns((int16_t *)data, count);
osmo_cc_rtp_send(codec, (uint8_t *)data, count * 2, 0, 1, count, &console);
int16_t spl[160];
uint8_t *payload;
int payload_len;
get_test_patterns(spl, 160);
codec->encoder((uint8_t *)spl, 160 * 2, &payload, &payload_len, &console);
osmo_cc_rtp_send(codec, payload, payload_len, 0, 1, 160);
free(payload);
return;
}
}
@ -377,7 +383,7 @@ int console_init(const char *audiodev, int samplerate, int buffer, int loopback,
goto error;
}
rc = jitter_create(&console.dejitter, "console", 8000, sizeof(sample_t), 0.050, 0.200, JITTER_FLAG_NONE);
rc = jitter_create(&console.dejitter, "console", 8000, 0.040, 0.200, JITTER_FLAG_NONE);
if (rc < 0) {
LOGP(DSENDER, LOGL_ERROR, "Failed to create and init dejitter buffer!\n");
goto error;
@ -583,7 +589,11 @@ void process_console(int c)
if (count > 0) {
/* load and upsample */
input_num = samplerate_upsample_input_num(&console.srstate, count);
jitter_load(&console.dejitter, samples, input_num);
{
int16_t spl[input_num];
jitter_load_samples(&console.dejitter, (uint8_t *)spl, input_num, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(samples, spl, input_num);
}
samplerate_upsample(&console.srstate, samples, input_num, samples, count);
/* write to sound device */
samples_list[0] = samples;
@ -608,8 +618,6 @@ void process_console(int c)
int i;
count = samplerate_downsample(&console.srstate, samples, count);
if (console.loopback == 3)
jitter_save(&console.dejitter, samples, count, 0, 0, 0, 0);
/* put samples into ring buffer */
for (i = 0; i < count; i++) {
console.tx_buffer[console.tx_buffer_pos] = samples[i];
@ -618,9 +626,12 @@ void process_console(int c)
console.tx_buffer_pos = 0;
/* only if we have a call */
if (console.callref && console.codec) {
int16_t data[160];
samples_to_int16_speech(data, console.tx_buffer, 160);
osmo_cc_rtp_send(console.codec, (uint8_t *)data, 160 * 2, 0, 1, 160, &console);
int16_t spl[160];
uint8_t *payload;
int payload_len;
samples_to_int16_speech(spl, console.tx_buffer, 160);
console.codec->encoder((uint8_t *)spl, 160 * 2, &payload, &payload_len, &console);
osmo_cc_rtp_send(console.codec, payload, payload_len, 0, 1, 160);
}
}
}

View File

@ -32,8 +32,8 @@
#include "sender.h"
#include <osmocom/core/timer.h>
#include <osmocom/core/select.h>
#include "call.h"
#include <osmocom/cc/endpoint.h>
#include "call.h"
#include "console.h"
#include "get_time.h"
#ifdef HAVE_SDR

View File

@ -149,15 +149,15 @@ int sender_create(sender_t *sender, const char *kanal, double sendefrequenz, dou
goto error;
}
rc = jitter_create(&sender->dejitter, sender->kanal, 8000, sizeof(sample_t), JITTER_AUDIO);
rc = jitter_create(&sender->dejitter, sender->kanal, 8000, JITTER_AUDIO);
if (rc < 0) {
LOGP(DSENDER, LOGL_ERROR, "Failed to create and init audio buffer!\n");
goto error;
}
rc = jitter_create(&sender->loop_dejitter, sender->kanal, samplerate, sizeof(sample_t), JITTER_AUDIO);
rc = jitter_create(&sender->loop_dejitter, sender->kanal, samplerate, JITTER_AUDIO);
if (rc < 0) {
LOGP(DSENDER, LOGL_ERROR, "Failed to create and init audio buffer!\n");
LOGP(DSENDER, LOGL_ERROR, "Failed to create and init loop audio buffer!\n");
goto error;
}
@ -374,7 +374,7 @@ cant_recover:
for (i = 0, inst = sender; inst; i++, inst = inst->slave) {
/* load TX data from audio loop or from sender instance */
if (inst->loopback == 3)
jitter_load(&inst->loop_dejitter, samples[i], count);
jitter_load_samples(&inst->loop_dejitter, (uint8_t *)samples[i], count, sizeof(*(samples[i])), NULL, NULL);
else
sender_send(inst, samples[i], power[i], count);
/* internal loopback: loop back TX audio to RX */
@ -458,8 +458,14 @@ cant_recover:
display_wave(&inst->dispwav, samples[i], count, inst->max_display);
sender_receive(inst, samples[i], count, rf_level_db[i]);
}
if (inst->loopback == 3)
jitter_save(&inst->loop_dejitter, samples[i], count, 0, 0, 0, 0);
if (inst->loopback == 3) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(NULL, NULL, (uint8_t *)samples[i], count * sizeof(*(samples[i])), 0, inst->loop_sequence, inst->loop_timestamp, 123);
if (jf)
jitter_save(&inst->loop_dejitter, jf);
inst->loop_sequence += 1;
inst->loop_timestamp += count;
}
}
}
#ifdef DEBUG_TIME_CONSUMPTION

View File

@ -59,7 +59,7 @@ typedef struct sender {
emphasis_t estate; /* pre and de emphasis */
/* loopback test */
int loopback; /* 0 = off, 1 = internal, 2 = external */
int loopback; /* 0 = off, 1 = internal, 2 = external, 3 = audio loop */
/* record and playback */
const char *write_rx_wave; /* file name pointers */
@ -71,9 +71,11 @@ typedef struct sender {
wave_play_t wave_rx_play; /* wave playback (as rx) */
wave_play_t wave_tx_play; /* wave playback (as tx) */
/* audio buffer for audio to send to transmitter (also used as loopback buffer) */
/* audio buffer for audio to send to transmitter */
jitter_t dejitter;
jitter_t loop_dejitter;
uint16_t loop_sequence; /* sequence + ts for loopback mode */
uint32_t loop_timestamp;
/* audio buffer for audio to send to caller (20ms = 160 samples @ 8000Hz) */
sample_t rxbuf[160];
@ -105,4 +107,5 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int count)
void sender_receive(sender_t *sender, sample_t *samples, int count, double rf_level_db);
void sender_paging(sender_t *sender, int on);
sender_t *get_sender_by_empfangsfrequenz(double freq);
void sender_conceal(uint8_t *_spl, int len, void __attribute__((unused)) *priv);

View File

@ -85,7 +85,7 @@ int dsp_init_sender(mpt1327_t *mpt1327, double squelch_db)
mpt1327->dmp_frame_quality = display_measurements_add(&mpt1327->sender.dispmeas, "Frame Quality", "%.1f %% (last)", DISPLAY_MEAS_LAST, DISPLAY_MEAS_LEFT, 0.0, 100.0, 100.0);
/* repeater */
rc = jitter_create(&mpt1327->repeater_dejitter, "repeater", mpt1327->sender.samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
rc = jitter_create(&mpt1327->repeater_dejitter, "repeater", mpt1327->sender.samplerate, 0.050, 0.500, JITTER_FLAG_NONE);
if (rc < 0) {
LOGP(DDSP, LOGL_ERROR, "Failed to create and init repeater buffer!\n");
goto error;
@ -234,9 +234,14 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
if (mpt1327->dsp_mode == DSP_MODE_TRAFFIC) {
/* if repeater mode, store sample in jitter buffer */
if (mpt1327->repeater)
jitter_save(&mpt1327->repeater_dejitter, samples, length, 0, 0, 0, 0);
if (mpt1327->repeater) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(NULL, NULL, (uint8_t *)samples, length * sizeof(*samples), 0, mpt1327->repeater_sequence, mpt1327->repeater_timestamp, 123);
if (jf)
jitter_save(&mpt1327->repeater_dejitter, jf);
mpt1327->repeater_sequence += 1;
mpt1327->repeater_timestamp += length;
}
if (mpt1327->unit && mpt1327->unit->callref) {
int count;
@ -291,13 +296,17 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
if (mpt1327->dsp_mode == DSP_MODE_TRAFFIC) {
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
{
int16_t spl[input_num];
jitter_load_samples(&sender->dejitter, (uint8_t *)spl, input_num, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(samples, spl, input_num);
}
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
/* if repeater mode, sum samples from jitter buffer to samples */
if (mpt1327->repeater) {
sample_t uplink[length];
int i;
jitter_load(&mpt1327->repeater_dejitter, uplink, length);
jitter_load_samples(&mpt1327->repeater_dejitter, (uint8_t *)uplink, length, sizeof(*uplink), NULL, NULL);
for (i = 0; i < length; i++)
samples[i] += uplink[i];
}
@ -334,9 +343,11 @@ void mpt1327_set_dsp_mode(mpt1327_t *mpt1327, enum dsp_mode mode, int repeater)
mpt1327->sync_word = 0xc4d7;
if (mode == DSP_MODE_TRAFFIC)
mpt1327->sync_word = 0x3b28;
if (mode == DSP_MODE_TRAFFIC && mpt1327->dsp_mode != mode)
jitter_reset(&mpt1327->sender.dejitter);
if (repeater)
jitter_reset(&mpt1327->repeater_dejitter);
jitter_reset(&mpt1327->repeater_dejitter);
mpt1327->repeater = repeater;
LOGP_CHAN(DDSP, LOGL_DEBUG, "DSP mode %s -> %s\n", mpt1327_dsp_mode_name(mpt1327->dsp_mode), mpt1327_dsp_mode_name(mode));
@ -350,3 +361,24 @@ void mpt1327_reset_sync(mpt1327_t *mpt1327)
mpt1327->rx_mute = 0;
}
/* Receive audio from call instance. */
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
mpt1327_unit_t *unit;
unit = find_unit_callref(callref);
if (!unit)
return;
if (!unit->tc)
return;
if (unit->tc->state == STATE_BUSY && unit->tc->dsp_mode == DSP_MODE_TRAFFIC) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(decoder, decoder_priv, payload, payload_len, marker, sequence, timestamp, ssrc);
if (jf)
jitter_save(&unit->tc->sender.dejitter, jf);
}
}
void call_down_clock(void) {}

View File

@ -1648,25 +1648,8 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
unit->callref = 0;
}
/* Receive audio from call instance. */
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
mpt1327_unit_t *unit;
unit = find_unit_callref(callref);
if (!unit)
return;
if (!unit->tc)
return;
if (unit->tc->state == STATE_BUSY && unit->tc->dsp_mode == DSP_MODE_TRAFFIC)
jitter_save(&unit->tc->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
void dump_info(void)
{
dump_units();
}
void call_down_clock(void) {}

View File

@ -118,6 +118,8 @@ typedef struct mpt1327 {
/* dsp states */
int repeater; /* in repeater mode the received audio is repeated */
jitter_t repeater_dejitter; /* forwarding audio */
uint16_t repeater_sequence; /* sequence & ts for jitter buffer */
uint32_t repeater_timestamp;
int pressel_on; /* set if somebody transmitting on TC */
enum dsp_mode dsp_mode; /* current mode: audio, durable tone 0 or 1, paging */
fsk_mod_t fsk_mod; /* fsk processing */
@ -153,4 +155,5 @@ void mpt1327_destroy(sender_t *sender);
void mpt1327_receive_codeword(mpt1327_t *mpt1327, uint64_t bits, double quality, double level);
int mpt1327_send_codeword(mpt1327_t *mpt1327, uint64_t *bits);
void mpt1327_signal_indication(mpt1327_t *mpt1327);
mpt1327_unit_t *find_unit_callref(uint32_t callref);

View File

@ -461,7 +461,13 @@ again:
case DSP_MODE_AUDIO:
case DSP_MODE_DTMF:
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
{
int16_t spl[input_num];
jitter_load_samples(&sender->dejitter, (uint8_t *)spl, input_num, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(samples, spl, input_num);
}
if (nmt->compandor)
compress_audio(&nmt->cstate, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
/* send after dejitter, so audio is flushed */
if (nmt->dms.tx_frame_valid) {
@ -522,6 +528,32 @@ void nmt_set_dsp_mode(nmt_t *nmt, enum dsp_mode mode)
}
LOGP_CHAN(DDSP, LOGL_DEBUG, "DSP mode %s -> %s\n", nmt_dsp_mode_name(nmt->dsp_mode), nmt_dsp_mode_name(mode));
if ((mode == DSP_MODE_AUDIO || mode == DSP_MODE_DTMF) && (nmt->dsp_mode != DSP_MODE_AUDIO && nmt->dsp_mode != DSP_MODE_DTMF))
jitter_reset(&nmt->sender.dejitter);
nmt->dsp_mode = mode;
}
/* Receive audio from call instance. */
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
transaction_t *trans;
nmt_t *nmt;
trans = get_transaction_by_callref(callref);
if (!trans)
return;
nmt = trans->nmt;
if (!nmt)
return;
if (nmt->dsp_mode == DSP_MODE_AUDIO || nmt->dsp_mode == DSP_MODE_DTMF) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(decoder, decoder_priv, payload, payload_len, marker, sequence, timestamp, ssrc);
if (jf)
jitter_save(&nmt->sender.dejitter, jf);
}
}
void call_down_clock(void) {}

View File

@ -1957,28 +1957,6 @@ void call_down_release(int callref, int __attribute__((unused)) cause)
}
}
/* Receive audio from call instance. */
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
transaction_t *trans;
nmt_t *nmt;
trans = get_transaction_by_callref(callref);
if (!trans)
return;
nmt = trans->nmt;
if (!nmt)
return;
if (nmt->dsp_mode == DSP_MODE_AUDIO || nmt->dsp_mode == DSP_MODE_DTMF) {
if (nmt->compandor)
compress_audio(&nmt->cstate, samples, count);
jitter_save(&nmt->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
}
void call_down_clock(void) {}
/*
* SMS layer messages
*/

View File

@ -565,7 +565,7 @@ void call_down_release(int callref, int cause)
}
/* Receive audio from call instance. */
void call_down_audio(int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
void call_down_audio(void __attribute__((unused)) *decoder, void __attribute__((unused)) *decoder_priv, int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint8_t __attribute__((unused)) marker, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, uint8_t __attribute__((unused)) *payload, int __attribute__((unused)) payload_len)
{
}

View File

@ -348,7 +348,13 @@ again:
case DSP_MODE_AUDIO_TX_RX:
memset(power, 1, length);
input_num = samplerate_upsample_input_num(&sender->srstate, length);
jitter_load(&sender->dejitter, samples, input_num);
{
int16_t spl[input_num];
jitter_load_samples(&sender->dejitter, (uint8_t *)spl, input_num, sizeof(*spl), jitter_conceal_s16, NULL);
int16_to_samples_speech(samples, spl, input_num);
}
if (r2000->compandor)
compress_audio(&r2000->cstate, samples, input_num);
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
iir_process(&r2000->super_tx_hp, samples, length);
/* do pre-emphasis */
@ -409,6 +415,7 @@ void r2000_set_dsp_mode(r2000_t *r2000, enum dsp_mode mode, int super)
&& (r2000->dsp_mode != DSP_MODE_AUDIO_TX && r2000->dsp_mode != DSP_MODE_AUDIO_TX_RX)) {
r2000->super_tx_word_length = 0;
fsk_mod_reset(&r2000->super_fsk_mod);
jitter_reset(&r2000->sender.dejitter);
}
if (super >= 0) {
@ -423,3 +430,28 @@ void r2000_set_dsp_mode(r2000_t *r2000, enum dsp_mode mode, int super)
r2000->dsp_mode = mode;
}
/* Receive audio from call instance. */
void call_down_audio(void *decoder, void *decoder_priv, int callref, uint16_t sequence, uint8_t marker, uint32_t timestamp, uint32_t ssrc, uint8_t *payload, int payload_len)
{
sender_t *sender;
r2000_t *r2000;
for (sender = sender_head; sender; sender = sender->next) {
r2000 = (r2000_t *) sender;
if (r2000->callref == callref)
break;
}
if (!sender)
return;
if (r2000->dsp_mode == DSP_MODE_AUDIO_TX
|| r2000->dsp_mode == DSP_MODE_AUDIO_TX_RX) {
jitter_frame_t *jf;
jf = jitter_frame_alloc(decoder, decoder_priv, payload, payload_len, marker, sequence, timestamp, ssrc);
if (jf)
jitter_save(&r2000->sender.dejitter, jf);
}
}
void call_down_clock(void) {}

View File

@ -1530,29 +1530,5 @@ void call_down_release(int callref, int __attribute__((unused)) cause)
}
}
/* Receive audio from call instance. */
void call_down_audio(int callref, uint16_t sequence, uint32_t timestamp, uint32_t ssrc, sample_t *samples, int count)
{
sender_t *sender;
r2000_t *r2000;
for (sender = sender_head; sender; sender = sender->next) {
r2000 = (r2000_t *) sender;
if (r2000->callref == callref)
break;
}
if (!sender)
return;
if (r2000->dsp_mode == DSP_MODE_AUDIO_TX
|| r2000->dsp_mode == DSP_MODE_AUDIO_TX_RX) {
if (r2000->compandor)
compress_audio(&r2000->cstate, samples, count);
jitter_save(&r2000->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
}
}
void call_down_clock(void) {}
void dump_info(void) {}

View File

@ -106,8 +106,8 @@ int radio_init(radio_t *radio, int buffer_size, int samplerate, double frequency
LOGP(DRADIO, LOGL_ERROR, "Failed to open sound device!\n");
goto error;
}
jitter_create(&radio->tx_dejitter[0], "left", radio->tx_audio_samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
jitter_create(&radio->tx_dejitter[1], "right", radio->tx_audio_samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
jitter_create(&radio->tx_dejitter[0], "left", radio->tx_audio_samplerate, 0.050, 0.500, JITTER_FLAG_NONE);
jitter_create(&radio->tx_dejitter[1], "right", radio->tx_audio_samplerate, 0.050, 0.500, JITTER_FLAG_NONE);
radio->tx_audio_mode = AUDIO_MODE_AUDIODEV;
#else
rc = -ENOTSUP;
@ -173,8 +173,8 @@ int radio_init(radio_t *radio, int buffer_size, int samplerate, double frequency
LOGP(DRADIO, LOGL_ERROR, "Failed to open sound device!\n");
goto error;
}
jitter_create(&radio->rx_dejitter[0], "left", radio->rx_audio_samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
jitter_create(&radio->rx_dejitter[1], "right", radio->rx_audio_samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
jitter_create(&radio->rx_dejitter[0], "left", radio->rx_audio_samplerate, 0.050, 0.500, JITTER_FLAG_NONE);
jitter_create(&radio->rx_dejitter[1], "right", radio->rx_audio_samplerate, 0.050, 0.500, JITTER_FLAG_NONE);
radio->rx_audio_mode |= AUDIO_MODE_AUDIODEV;
#else
rc = -ENOTSUP;
@ -442,6 +442,7 @@ int radio_tx(radio_t *radio, float *baseband, int signal_num)
sample_t *audio_samples[2];
sample_t *signal_samples[3];
uint8_t *signal_power;
jitter_frame_t *jf;
if (signal_num > radio->buffer_size) {
LOGP(DRADIO, LOGL_ERROR, "signal_num > buffer_size, please fix!.\n");
@ -494,11 +495,19 @@ int radio_tx(radio_t *radio, float *baseband, int signal_num)
else
return 0;
}
jitter_save(&radio->tx_dejitter[0], audio_samples[0], rc, 0, 0, 0 ,0);
jitter_load(&radio->tx_dejitter[0], audio_samples[0], audio_num);
jf = jitter_frame_alloc(NULL, NULL, (uint8_t *)audio_samples[0], rc * sizeof(*(audio_samples[0])), 0, radio->tx_sequence[0], radio->tx_timestamp[0], 123);
if (jf)
jitter_save(&radio->tx_dejitter[0], jf);
radio->tx_sequence[0] += 1;
radio->tx_timestamp[0] += rc;
jitter_load_samples(&radio->tx_dejitter[0], (uint8_t *)audio_samples[0], audio_num, sizeof(*(audio_samples[0])), NULL, NULL);
if (radio->tx_audio_channels == 2) {
jitter_save(&radio->tx_dejitter[1], audio_samples[1], rc, 0, 0, 0 ,0);
jitter_load(&radio->tx_dejitter[1], audio_samples[1], audio_num);
jf = jitter_frame_alloc(NULL, NULL, (uint8_t *)audio_samples[1], rc * sizeof(*(audio_samples[1])), 0, radio->tx_sequence[1], radio->tx_timestamp[1], 123);
if (jf)
jitter_save(&radio->tx_dejitter[1], jf);
radio->tx_sequence[1] += 1;
radio->tx_timestamp[1] += rc;
jitter_load_samples(&radio->tx_dejitter[1], (uint8_t *)audio_samples[1], audio_num, sizeof(*(audio_samples[1])), NULL, NULL);
}
break;
#endif
@ -616,6 +625,7 @@ int radio_rx(radio_t *radio, float *baseband, int signal_num)
int audio_num;
sample_t *samples[3];
double p;
jitter_frame_t *jf;
if (signal_num > radio->buffer_size) {
LOGP(DRADIO, LOGL_ERROR, "signal_num > buffer_size, please fix!.\n");
@ -728,13 +738,22 @@ int radio_rx(radio_t *radio, float *baseband, int signal_num)
wave_write(&radio->wave_rx_rec, samples, audio_num);
#ifdef HAVE_ALSA
if ((radio->rx_audio_mode & AUDIO_MODE_AUDIODEV)) {
jitter_save(&radio->rx_dejitter[0], samples[0], audio_num, 0, 0, 0 ,0);
if (radio->rx_audio_channels == 2)
jitter_save(&radio->rx_dejitter[1], samples[1], audio_num, 0, 0, 0 ,0);
jf = jitter_frame_alloc(NULL, NULL, (uint8_t *)samples[0], audio_num * sizeof(*(samples[0])), 0, radio->rx_sequence[0], radio->rx_timestamp[0], 123);
if (jf)
jitter_save(&radio->rx_dejitter[0], jf);
radio->rx_sequence[0] += 1;
radio->rx_timestamp[0] += audio_num;
if (radio->rx_audio_channels == 2) {
jf = jitter_frame_alloc(NULL, NULL, (uint8_t *)samples[1], audio_num * sizeof(*(samples[1])), 0, radio->rx_sequence[1], radio->rx_timestamp[1], 123);
if (jf)
jitter_save(&radio->rx_dejitter[1], jf);
radio->rx_sequence[1] += 1;
radio->rx_timestamp[1] += audio_num;
}
audio_num = sound_get_tosend(radio->rx_sound, radio->signal_buffer_size);
jitter_load(&radio->rx_dejitter[0], samples[0], audio_num);
jitter_load_samples(&radio->rx_dejitter[0], (uint8_t *)samples[0], audio_num, sizeof(*samples), NULL, NULL);
if (radio->rx_audio_channels == 2)
jitter_load(&radio->rx_dejitter[1], samples[1], audio_num);
jitter_load_samples(&radio->rx_dejitter[1], (uint8_t *)samples[1], audio_num, sizeof(*samples), NULL, NULL);
audio_num = sound_write(radio->rx_sound, samples, NULL, audio_num, NULL, NULL, radio->rx_audio_channels);
if (audio_num < 0) {
LOGP(DRADIO, LOGL_ERROR, "Failed to write to sound device (rc = %d)!\n", audio_num);

View File

@ -45,7 +45,11 @@ typedef struct radio {
void *tx_sound; /* sound card process */
void *rx_sound; /* sound card process */
jitter_t tx_dejitter[2]; /* jitter buffer when reading from sound card */
uint16_t tx_sequence[2]; /* sequence & ts for jitter buffer */
uint32_t tx_timestamp[2];
jitter_t rx_dejitter[2]; /* jitter buffer when writing to sound card */
uint16_t rx_sequence[2]; /* sequence & ts for jitter buffer */
uint32_t rx_timestamp[2];
sample_t *testtone[2]; /* test tone sample */
int testtone_length;
int testtone_pos;

View File

@ -398,7 +398,7 @@ void call_down_release(int callref, int cause)
}
/* Receive audio from call instance. */
void call_down_audio(int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
void call_down_audio(void __attribute__((unused)) *decoder, void __attribute__((unused)) *decoder_priv, int __attribute__((unused)) callref, uint16_t __attribute__((unused)) sequence, uint8_t __attribute__((unused)) marker, uint32_t __attribute__((unused)) timestamp, uint32_t __attribute__((unused)) ssrc, uint8_t __attribute__((unused)) *payload, int __attribute__((unused)) payload_len)
{
}