Refactoring jitter buffer
Features are: * Packet based buffer * Random in, first out * Adaptive delay compensation (voice) * Fixed delay (data, optionally MODEM/FAX) * Interpolation of missing frames * Any sample size
This commit is contained in:
parent
4fc92eba45
commit
2b7efedc48
|
@ -1065,7 +1065,7 @@ void call_down_release(int callref, int cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_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)
|
||||||
{
|
{
|
||||||
sender_t *sender;
|
sender_t *sender;
|
||||||
amps_t *amps;
|
amps_t *amps;
|
||||||
|
@ -1079,10 +1079,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (amps->dsp_mode == DSP_MODE_AUDIO_RX_AUDIO_TX) {
|
if (amps->dsp_mode == DSP_MODE_AUDIO_RX_AUDIO_TX) {
|
||||||
sample_t up[(int)((double)count * amps->sender.srstate.factor + 0.5) + 10];
|
|
||||||
compress_audio(&s->cstate, samples, count);
|
compress_audio(&s->cstate, samples, count);
|
||||||
count = samplerate_upsample(&s->sender.srstate, samples, count, up);
|
jitter_save(&s->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||||
jitter_save(&s->sender.dejitter, up, count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -473,7 +473,7 @@ static void sat_encode(amps_t *amps, sample_t *samples, int length)
|
||||||
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
||||||
{
|
{
|
||||||
amps_t *amps = (amps_t *) sender;
|
amps_t *amps = (amps_t *) sender;
|
||||||
int count;
|
int count, input_num;
|
||||||
|
|
||||||
again:
|
again:
|
||||||
switch (amps->dsp_mode) {
|
switch (amps->dsp_mode) {
|
||||||
|
@ -483,7 +483,9 @@ again:
|
||||||
break;
|
break;
|
||||||
case DSP_MODE_AUDIO_RX_AUDIO_TX:
|
case DSP_MODE_AUDIO_RX_AUDIO_TX:
|
||||||
memset(power, 1, length);
|
memset(power, 1, length);
|
||||||
jitter_load(&s->sender.dejitter, samples, length);
|
input_num = samplerate_upsample_input_num(&sender->srstate, length);
|
||||||
|
jitter_load(&sender->dejitter, samples, input_num);
|
||||||
|
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
|
||||||
/* pre-emphasis */
|
/* pre-emphasis */
|
||||||
if (amps->pre_emphasis)
|
if (amps->pre_emphasis)
|
||||||
pre_emphasis(&s->estate, samples, length);
|
pre_emphasis(&s->estate, samples, length);
|
||||||
|
|
|
@ -513,7 +513,7 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_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)
|
||||||
{
|
{
|
||||||
sender_t *sender;
|
sender_t *sender;
|
||||||
anetz_t *anetz;
|
anetz_t *anetz;
|
||||||
|
@ -526,11 +526,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
|
||||||
if (!sender)
|
if (!sender)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (anetz->dsp_mode == DSP_MODE_AUDIO) {
|
if (anetz->dsp_mode == DSP_MODE_AUDIO)
|
||||||
sample_t up[(int)((double)count * anetz->sender.srstate.factor + 0.5) + 10];
|
jitter_save(&anetz->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||||
count = samplerate_upsample(&anetz->sender.srstate, samples, count, up);
|
|
||||||
jitter_save(&anetz->sender.dejitter, up, count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_down_clock(void) {}
|
void call_down_clock(void) {}
|
||||||
|
|
|
@ -357,6 +357,7 @@ static void fsk_tone(anetz_t *anetz, sample_t *samples, int length)
|
||||||
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
||||||
{
|
{
|
||||||
anetz_t *anetz = (anetz_t *) sender;
|
anetz_t *anetz = (anetz_t *) sender;
|
||||||
|
int input_num;
|
||||||
|
|
||||||
memset(power, 1, length);
|
memset(power, 1, length);
|
||||||
|
|
||||||
|
@ -365,7 +366,9 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
|
||||||
memset(samples, 0, length * sizeof(*samples));
|
memset(samples, 0, length * sizeof(*samples));
|
||||||
break;
|
break;
|
||||||
case DSP_MODE_AUDIO:
|
case DSP_MODE_AUDIO:
|
||||||
jitter_load(&anetz->sender.dejitter, samples, length);
|
input_num = samplerate_upsample_input_num(&sender->srstate, length);
|
||||||
|
jitter_load(&sender->dejitter, samples, input_num);
|
||||||
|
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
|
||||||
break;
|
break;
|
||||||
case DSP_MODE_TONE:
|
case DSP_MODE_TONE:
|
||||||
fsk_tone(anetz, samples, length);
|
fsk_tone(anetz, samples, length);
|
||||||
|
|
|
@ -816,7 +816,7 @@ void call_down_release(int callref, int __attribute__((unused)) cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_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)
|
||||||
{
|
{
|
||||||
sender_t *sender;
|
sender_t *sender;
|
||||||
bnetz_t *bnetz;
|
bnetz_t *bnetz;
|
||||||
|
@ -831,9 +831,7 @@ void call_down_audio(int callref, sample_t *samples, int count)
|
||||||
|
|
||||||
if (bnetz->dsp_mode == DSP_MODE_AUDIO
|
if (bnetz->dsp_mode == DSP_MODE_AUDIO
|
||||||
|| bnetz->dsp_mode == DSP_MODE_AUDIO_METER) {
|
|| bnetz->dsp_mode == DSP_MODE_AUDIO_METER) {
|
||||||
sample_t up[(int)((double)count * bnetz->sender.srstate.factor + 0.5) + 10];
|
jitter_save(&bnetz->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||||
count = samplerate_upsample(&bnetz->sender.srstate, samples, count, up);
|
|
||||||
jitter_save(&bnetz->sender.dejitter, up, count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -361,7 +361,7 @@ static void metering_tone(bnetz_t *bnetz, sample_t *samples, int length)
|
||||||
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
||||||
{
|
{
|
||||||
bnetz_t *bnetz = (bnetz_t *) sender;
|
bnetz_t *bnetz = (bnetz_t *) sender;
|
||||||
int count;
|
int count, input_num;
|
||||||
|
|
||||||
memset(power, 1, length);
|
memset(power, 1, length);
|
||||||
|
|
||||||
|
@ -372,7 +372,9 @@ again:
|
||||||
break;
|
break;
|
||||||
case DSP_MODE_AUDIO:
|
case DSP_MODE_AUDIO:
|
||||||
case DSP_MODE_AUDIO_METER:
|
case DSP_MODE_AUDIO_METER:
|
||||||
jitter_load(&bnetz->sender.dejitter, samples, length);
|
input_num = samplerate_upsample_input_num(&sender->srstate, length);
|
||||||
|
jitter_load(&sender->dejitter, samples, input_num);
|
||||||
|
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
|
||||||
if (bnetz->dsp_mode == DSP_MODE_AUDIO_METER)
|
if (bnetz->dsp_mode == DSP_MODE_AUDIO_METER)
|
||||||
metering_tone(bnetz, samples, length);
|
metering_tone(bnetz, samples, length);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -576,7 +576,7 @@ static void cnetz_release(transaction_t *trans, uint8_t cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_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)
|
||||||
{
|
{
|
||||||
sender_t *sender;
|
sender_t *sender;
|
||||||
cnetz_t *cnetz;
|
cnetz_t *cnetz;
|
||||||
|
@ -591,7 +591,7 @@ void call_down_audio(int callref, sample_t *samples, int count)
|
||||||
|
|
||||||
if (cnetz->dsp_mode == DSP_MODE_SPK_V) {
|
if (cnetz->dsp_mode == DSP_MODE_SPK_V) {
|
||||||
/* store as is, since we convert rate when processing FSK frames */
|
/* store as is, since we convert rate when processing FSK frames */
|
||||||
jitter_save(&cnetz->sender.dejitter, samples, count);
|
jitter_save(&cnetz->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -158,12 +158,6 @@ int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], en
|
||||||
scrambler_setup(&cnetz->scrambler_tx, (double)cnetz->sender.samplerate / 1.1);
|
scrambler_setup(&cnetz->scrambler_tx, (double)cnetz->sender.samplerate / 1.1);
|
||||||
scrambler_setup(&cnetz->scrambler_rx, (double)cnetz->sender.samplerate / 1.1);
|
scrambler_setup(&cnetz->scrambler_rx, (double)cnetz->sender.samplerate / 1.1);
|
||||||
|
|
||||||
/* reinit jitter buffer for 8000 kHz */
|
|
||||||
jitter_destroy(&cnetz->sender.dejitter);
|
|
||||||
rc = jitter_create(&cnetz->sender.dejitter, 8000 / 5);
|
|
||||||
if (rc < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
/* init compandor, according to C-Netz specs, attack and recovery time
|
/* init compandor, according to C-Netz specs, attack and recovery time
|
||||||
* shall not exceed according to ITU G.162 */
|
* shall not exceed according to ITU G.162 */
|
||||||
init_compandor(&cnetz->cstate, 8000, 5.0, 22.5);
|
init_compandor(&cnetz->cstate, 8000, 5.0, 22.5);
|
||||||
|
@ -172,7 +166,7 @@ int dsp_init_sender(cnetz_t *cnetz, int measure_speed, double clock_speed[2], en
|
||||||
cnetz->offset_range = ceil(cnetz->fsk_bitduration);
|
cnetz->offset_range = ceil(cnetz->fsk_bitduration);
|
||||||
|
|
||||||
#ifdef TEST_SCRAMBLE
|
#ifdef TEST_SCRAMBLE
|
||||||
rc = jitter_create(&scrambler_test_jb, cnetz->sender.samplerate / 5);
|
rc = jitter_create(&scrambler_test_jb, "scramble", cnetz->sender.samplerate, sizeof(sample_t), JITTER_AUDIO);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
PDEBUG_CHAN(DDSP, DEBUG_ERROR, "Failed to init jitter buffer for scrambler test!\n");
|
PDEBUG_CHAN(DDSP, DEBUG_ERROR, "Failed to init jitter buffer for scrambler test!\n");
|
||||||
exit(0);
|
exit(0);
|
||||||
|
@ -577,7 +571,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double rf_l
|
||||||
#ifdef TEST_UNSCRAMBLE
|
#ifdef TEST_UNSCRAMBLE
|
||||||
scrambler(&scrambler_test_scrambler1, samples, length);
|
scrambler(&scrambler_test_scrambler1, samples, length);
|
||||||
#endif
|
#endif
|
||||||
jitter_save(&scrambler_test_jb, samples, length);
|
jitter_save(&scrambler_test_jb, samples, length, 0, 0, 0, 0);
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -598,7 +592,8 @@ static int shrink_speech(cnetz_t *cnetz, sample_t *speech_buffer)
|
||||||
/* 1. compress dynamics */
|
/* 1. compress dynamics */
|
||||||
compress_audio(&cnetz->cstate, speech_buffer, 100);
|
compress_audio(&cnetz->cstate, speech_buffer, 100);
|
||||||
/* 2. upsample */
|
/* 2. upsample */
|
||||||
speech_length = samplerate_upsample(&cnetz->sender.srstate, speech_buffer, 100, speech_buffer);
|
speech_length = samplerate_upsample_output_num(&cnetz->sender.srstate, 100);
|
||||||
|
samplerate_upsample(&cnetz->sender.srstate, speech_buffer, 100, speech_buffer, speech_length);
|
||||||
/* 3. scramble */
|
/* 3. scramble */
|
||||||
if (cnetz->scrambler)
|
if (cnetz->scrambler)
|
||||||
scrambler(&cnetz->scrambler_tx, speech_buffer, speech_length);
|
scrambler(&cnetz->scrambler_tx, speech_buffer, speech_length);
|
||||||
|
|
|
@ -760,7 +760,7 @@ void call_down_release(int callref, int cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_audio(int __attribute__((unused)) callref, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
|
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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -595,15 +595,17 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
|
||||||
fuenf_t *fuenf = (fuenf_t *) sender;
|
fuenf_t *fuenf = (fuenf_t *) sender;
|
||||||
sample_t *orig_samples = samples;
|
sample_t *orig_samples = samples;
|
||||||
int orig_length = length;
|
int orig_length = length;
|
||||||
int count;
|
int count, input_num;
|
||||||
sample_t *spl;
|
sample_t *spl;
|
||||||
int pos;
|
int pos;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* speak through */
|
/* speak through */
|
||||||
if (fuenf->state == FUENF_STATE_DURCHSAGE && fuenf->callref) {
|
if (fuenf->state == FUENF_STATE_DURCHSAGE && fuenf->callref) {
|
||||||
jitter_load(&fuenf->sender.dejitter, samples, length);
|
|
||||||
memset(power, 1, length);
|
memset(power, 1, length);
|
||||||
|
input_num = samplerate_upsample_input_num(&sender->srstate, length);
|
||||||
|
jitter_load(&sender->dejitter, samples, input_num);
|
||||||
|
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
|
||||||
} else {
|
} else {
|
||||||
/* send if something has to be sent. else turn transmitter off */
|
/* send if something has to be sent. else turn transmitter off */
|
||||||
while ((count = encode(fuenf, samples, length))) {
|
while ((count = encode(fuenf, samples, length))) {
|
||||||
|
|
|
@ -390,7 +390,7 @@ void call_down_release(int callref, int cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_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)
|
||||||
{
|
{
|
||||||
sender_t *sender;
|
sender_t *sender;
|
||||||
fuenf_t *fuenf;
|
fuenf_t *fuenf;
|
||||||
|
@ -403,11 +403,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
|
||||||
if (!sender)
|
if (!sender)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (fuenf->state == FUENF_STATE_DURCHSAGE) {
|
if (fuenf->state == FUENF_STATE_DURCHSAGE)
|
||||||
sample_t up[(int)((double)count * fuenf->sender.srstate.factor + 0.5) + 10];
|
jitter_save(&fuenf->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||||
count = samplerate_upsample(&fuenf->sender.srstate, samples, count, up);
|
|
||||||
jitter_save(&fuenf->sender.dejitter, up, count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_info(void) {}
|
void dump_info(void) {}
|
||||||
|
|
|
@ -1240,13 +1240,17 @@ void fuvst_destroy(sender_t *sender)
|
||||||
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
||||||
{
|
{
|
||||||
fuvst_t *fuvst = (fuvst_t *) sender;
|
fuvst_t *fuvst = (fuvst_t *) sender;
|
||||||
|
int input_num;
|
||||||
|
|
||||||
memset(power, 1, length);
|
memset(power, 1, length);
|
||||||
|
|
||||||
if (fuvst->chan_type == CHAN_TYPE_ZZK)
|
if (fuvst->chan_type == CHAN_TYPE_ZZK)
|
||||||
v27_modem_send(&fuvst->modem, samples, length);
|
v27_modem_send(&fuvst->modem, samples, length);
|
||||||
else
|
else {
|
||||||
jitter_load(&fuvst->sender.dejitter, samples, length);
|
input_num = samplerate_upsample_input_num(&sender->srstate, length);
|
||||||
|
jitter_load(&sender->dejitter, samples, input_num);
|
||||||
|
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sender_receive(sender_t *sender, sample_t *samples, int length, double __attribute__((unused)) rf_level_db)
|
void sender_receive(sender_t *sender, sample_t *samples, int length, double __attribute__((unused)) rf_level_db)
|
||||||
|
@ -1280,7 +1284,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_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)
|
||||||
{
|
{
|
||||||
sender_t *sender;
|
sender_t *sender;
|
||||||
fuvst_t *fuvst;
|
fuvst_t *fuvst;
|
||||||
|
@ -1293,11 +1297,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
|
||||||
if (!sender)
|
if (!sender)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (fuvst->callref) {
|
if (fuvst->callref)
|
||||||
sample_t up[(int)((double)count * fuvst->sender.srstate.factor + 0.5) + 10];
|
jitter_save(&fuvst->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||||
count = samplerate_upsample(&fuvst->sender.srstate, samples, count, up);
|
|
||||||
jitter_save(&fuvst->sender.dejitter, up, count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_down_clock(void) {}
|
void call_down_clock(void) {}
|
||||||
|
|
|
@ -255,7 +255,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
|
||||||
v27_modem_receive(&sniffer->modem, samples, length);
|
v27_modem_receive(&sniffer->modem, samples, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_down_audio(int __attribute__((unused)) callref, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count) { }
|
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_clock(void) {}
|
void call_down_clock(void) {}
|
||||||
|
|
||||||
|
|
|
@ -276,7 +276,7 @@ static int generate_tone(imts_t *imts, sample_t *samples, int length)
|
||||||
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
||||||
{
|
{
|
||||||
imts_t *imts = (imts_t *) sender;
|
imts_t *imts = (imts_t *) sender;
|
||||||
int count;
|
int count, input_num;
|
||||||
|
|
||||||
memset(power, 1, length);
|
memset(power, 1, length);
|
||||||
|
|
||||||
|
@ -296,7 +296,9 @@ again:
|
||||||
break;
|
break;
|
||||||
case DSP_MODE_AUDIO:
|
case DSP_MODE_AUDIO:
|
||||||
memset(power, 1, length);
|
memset(power, 1, length);
|
||||||
jitter_load(&imts->sender.dejitter, samples, length);
|
input_num = samplerate_upsample_input_num(&sender->srstate, length);
|
||||||
|
jitter_load(&sender->dejitter, samples, input_num);
|
||||||
|
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
|
||||||
if (imts->pre_emphasis)
|
if (imts->pre_emphasis)
|
||||||
pre_emphasis(&imts->estate, samples, length);
|
pre_emphasis(&imts->estate, samples, length);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1284,7 +1284,7 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_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)
|
||||||
{
|
{
|
||||||
sender_t *sender;
|
sender_t *sender;
|
||||||
imts_t *imts;
|
imts_t *imts;
|
||||||
|
@ -1298,9 +1298,7 @@ void call_down_audio(int callref, sample_t *samples, int count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (imts->dsp_mode == DSP_MODE_AUDIO) {
|
if (imts->dsp_mode == DSP_MODE_AUDIO) {
|
||||||
sample_t up[(int)((double)count * imts->sender.srstate.factor + 0.5) + 10];
|
jitter_save(&imts->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||||
count = samplerate_upsample(&imts->sender.srstate, samples, count, up);
|
|
||||||
jitter_save(&imts->sender.dejitter, up, count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ int dsp_init_sender(jolly_t *jolly, int nbfm, double squelch_db, int repeater)
|
||||||
/* repeater */
|
/* repeater */
|
||||||
jolly->repeater = repeater;
|
jolly->repeater = repeater;
|
||||||
jolly->repeater_max = (int)((double)jolly->sender.samplerate * REPEATER_TIME);
|
jolly->repeater_max = (int)((double)jolly->sender.samplerate * REPEATER_TIME);
|
||||||
rc = jitter_create(&jolly->repeater_dejitter, jolly->sender.samplerate / 5);
|
rc = jitter_create(&jolly->repeater_dejitter, "repeater", jolly->sender.samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
PDEBUG(DDSP, DEBUG_ERROR, "Failed to create and init repeater buffer!\n");
|
PDEBUG(DDSP, DEBUG_ERROR, "Failed to create and init repeater buffer!\n");
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -319,7 +319,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double rf_l
|
||||||
|
|
||||||
/* if repeater mode, store sample in jitter buffer */
|
/* if repeater mode, store sample in jitter buffer */
|
||||||
if (jolly->repeater)
|
if (jolly->repeater)
|
||||||
jitter_save(&jolly->repeater_dejitter, samples, length);
|
jitter_save(&jolly->repeater_dejitter, samples, length, 0, 0, 0, 0);
|
||||||
|
|
||||||
/* downsample, decode DTMF */
|
/* downsample, decode DTMF */
|
||||||
count = samplerate_downsample(&jolly->sender.srstate, samples, length);
|
count = samplerate_downsample(&jolly->sender.srstate, samples, length);
|
||||||
|
@ -346,7 +346,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double rf_l
|
||||||
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
||||||
{
|
{
|
||||||
jolly_t *jolly = (jolly_t *) sender;
|
jolly_t *jolly = (jolly_t *) sender;
|
||||||
int count;
|
int count, input_num;
|
||||||
|
|
||||||
switch (jolly->state) {
|
switch (jolly->state) {
|
||||||
case STATE_IDLE:
|
case STATE_IDLE:
|
||||||
|
@ -368,7 +368,9 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
|
||||||
case STATE_CALL:
|
case STATE_CALL:
|
||||||
case STATE_CALL_DIALING:
|
case STATE_CALL_DIALING:
|
||||||
memset(power, 1, length);
|
memset(power, 1, length);
|
||||||
jitter_load(&jolly->sender.dejitter, samples, length);
|
input_num = samplerate_upsample_input_num(&sender->srstate, length);
|
||||||
|
jitter_load(&sender->dejitter, samples, input_num);
|
||||||
|
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
|
||||||
break;
|
break;
|
||||||
case STATE_OUT_VERIFY:
|
case STATE_OUT_VERIFY:
|
||||||
case STATE_IN_PAGING:
|
case STATE_IN_PAGING:
|
||||||
|
|
|
@ -594,7 +594,7 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_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)
|
||||||
{
|
{
|
||||||
sender_t *sender;
|
sender_t *sender;
|
||||||
jolly_t *jolly;
|
jolly_t *jolly;
|
||||||
|
@ -607,11 +607,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
|
||||||
if (!sender)
|
if (!sender)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (jolly->state == STATE_CALL || jolly->state == STATE_CALL_DIALING) {
|
if (jolly->state == STATE_CALL || jolly->state == STATE_CALL_DIALING)
|
||||||
sample_t up[(int)((double)count * jolly->sender.srstate.factor + 0.5) + 10];
|
jitter_save(&jolly->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||||
count = samplerate_upsample(&jolly->sender.srstate, samples, count, up);
|
|
||||||
jitter_save(&jolly->sender.dejitter, up, count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void call_down_clock(void) {}
|
void call_down_clock(void) {}
|
||||||
|
|
|
@ -11072,7 +11072,7 @@ int init_voice(int samplerate)
|
||||||
{
|
{
|
||||||
samplerate_t srstate;
|
samplerate_t srstate;
|
||||||
sample_t spl_in[CHUNK], *spl_out;
|
sample_t spl_in[CHUNK], *spl_out;
|
||||||
int i, s, j, chunk, count;
|
int i, s, j, chunk, count, output_num;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
jolly_voice.spl[0] = (sample_t *)digit_0;
|
jolly_voice.spl[0] = (sample_t *)digit_0;
|
||||||
|
@ -11109,7 +11109,8 @@ int init_voice(int samplerate)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 13; i++) {
|
for (i = 0; i < 13; i++) {
|
||||||
spl_out = calloc(((double)jolly_voice.size[i] * srstate.factor + 0.5) + 10, sizeof(*spl_out));
|
output_num = samplerate_upsample_output_num(&srstate, jolly_voice.size[i]);
|
||||||
|
spl_out = calloc(output_num, sizeof(*spl_out));
|
||||||
count = 0;
|
count = 0;
|
||||||
for (s = 0; s < jolly_voice.size[i]; s += CHUNK) {
|
for (s = 0; s < jolly_voice.size[i]; s += CHUNK) {
|
||||||
chunk = jolly_voice.size[i] - s;
|
chunk = jolly_voice.size[i] - s;
|
||||||
|
@ -11117,7 +11118,8 @@ int init_voice(int samplerate)
|
||||||
chunk = CHUNK;
|
chunk = CHUNK;
|
||||||
for (j = 0; j < chunk; j++)
|
for (j = 0; j < chunk; j++)
|
||||||
spl_in[j] = (double)(((int16_t *)(jolly_voice.spl[i]))[s + j]) / 32767.0 * GAIN;
|
spl_in[j] = (double)(((int16_t *)(jolly_voice.spl[i]))[s + j]) / 32767.0 * GAIN;
|
||||||
count += samplerate_upsample(&srstate, spl_in, chunk, spl_out + count);
|
samplerate_upsample(&srstate, spl_in, chunk, spl_out + count, output_num);
|
||||||
|
count += output_num;
|
||||||
}
|
}
|
||||||
jolly_voice.spl[i] = spl_out;
|
jolly_voice.spl[i] = spl_out;
|
||||||
jolly_voice.size[i] = count;
|
jolly_voice.size[i] = count;
|
||||||
|
|
|
@ -93,12 +93,13 @@ struct debug_cat {
|
||||||
{ "uk0", "\033[1;34m" },
|
{ "uk0", "\033[1;34m" },
|
||||||
{ "ph", "\033[0;33m" },
|
{ "ph", "\033[0;33m" },
|
||||||
{ "dcf77", "\033[1;34m" },
|
{ "dcf77", "\033[1;34m" },
|
||||||
|
{ "jitter", "\033[0;36m" },
|
||||||
{ NULL, NULL }
|
{ NULL, NULL }
|
||||||
};
|
};
|
||||||
|
|
||||||
int debuglevel = DEBUG_INFO;
|
int debuglevel = DEBUG_INFO;
|
||||||
int debug_date = 0;
|
int debug_date = 0;
|
||||||
uint64_t debug_mask = ~0;
|
uint64_t debug_mask[2] = { ~0, ~0 };
|
||||||
extern int num_kanal;
|
extern int num_kanal;
|
||||||
|
|
||||||
void (*clear_console_text)(void) = NULL;
|
void (*clear_console_text)(void) = NULL;
|
||||||
|
@ -155,7 +156,7 @@ void _printdebug(const char *file, const char __attribute__((unused)) *function,
|
||||||
if (debuglevel > level)
|
if (debuglevel > level)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!(debug_mask & ((uint64_t)1 << cat)))
|
if (!(debug_mask[cat >> 6] & ((uint64_t)1 << (cat & 63))))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
lock_debug();
|
lock_debug();
|
||||||
|
@ -289,7 +290,7 @@ int parse_debug_opt(const char *optarg)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
if (dstring)
|
if (dstring)
|
||||||
debug_mask = 0;
|
memset(debug_mask, 0, sizeof(debug_mask));
|
||||||
while((p = strsep(&dstring, ","))) {
|
while((p = strsep(&dstring, ","))) {
|
||||||
for (i = 0; debug_cat[i].name; i++) {
|
for (i = 0; debug_cat[i].name; i++) {
|
||||||
if (!strcasecmp(p, debug_cat[i].name))
|
if (!strcasecmp(p, debug_cat[i].name))
|
||||||
|
@ -300,7 +301,7 @@ int parse_debug_opt(const char *optarg)
|
||||||
free(dup);
|
free(dup);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
debug_mask |= ((uint64_t)1 << i);
|
debug_mask[i >> 6] |= ((uint64_t)1 << (i & 63));
|
||||||
}
|
}
|
||||||
|
|
||||||
free(dup);
|
free(dup);
|
||||||
|
|
|
@ -55,6 +55,8 @@
|
||||||
#define DUK0 48
|
#define DUK0 48
|
||||||
#define DPH 49
|
#define DPH 49
|
||||||
#define DDCF77 50
|
#define DDCF77 50
|
||||||
|
#define DJITTER 51
|
||||||
|
//NOTE: increment mask array, if 127 is exceeded
|
||||||
|
|
||||||
void lock_debug(void);
|
void lock_debug(void);
|
||||||
void unlock_debug(void);
|
void unlock_debug(void);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* Jitter buffering functions
|
/* Jitter buffering functions
|
||||||
*
|
*
|
||||||
* (C) 2016 by Andreas Eversberg <jolly@eversberg.eu>
|
* (C) 2022 by Andreas Eversberg <jolly@eversberg.eu>
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
@ -17,6 +17,59 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* 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).
|
||||||
|
*
|
||||||
|
* 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 <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -26,35 +79,97 @@
|
||||||
#include "../libdebug/debug.h"
|
#include "../libdebug/debug.h"
|
||||||
#include "jitter.h"
|
#include "jitter.h"
|
||||||
|
|
||||||
|
#define INITIAL_DELAY_INTERVAL 0.5
|
||||||
|
#define REPEAT_DELAY_INTERVAL 3.0
|
||||||
|
#define EXTRA_BUFFER 0.020 // 20 ms
|
||||||
|
|
||||||
|
/* uncomment to enable heavy debugging */
|
||||||
|
//#define HEAVY_DEBUG
|
||||||
|
|
||||||
|
static int unnamed_count = 1;
|
||||||
|
|
||||||
/* create jitter buffer */
|
/* 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));
|
int rc = 0;
|
||||||
jitter->spl = malloc(length * sizeof(sample_t));
|
memset(jb, 0, sizeof(*jb));
|
||||||
if (!jitter->spl) {
|
jb->sample_duration = 1.0 / samplerate;
|
||||||
PDEBUG(DDSP, DEBUG_ERROR, "No memory for jitter buffer.\n");
|
jb->sample_size = sample_size;
|
||||||
return -ENOMEM;
|
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;
|
|
||||||
|
|
||||||
jitter_reset(jitter);
|
|
||||||
|
|
||||||
return 0;
|
/* optionally give a string to be show with the debug */
|
||||||
|
if (name && *name)
|
||||||
|
snprintf(jb->name, sizeof(jb->name) - 1, "(%s) ", name);
|
||||||
|
else
|
||||||
|
snprintf(jb->name, sizeof(jb->name) - 1, "(unnamed %d) ", unnamed_count++);
|
||||||
|
|
||||||
|
jitter_reset(jb);
|
||||||
|
|
||||||
|
PDEBUG(DJITTER, DEBUG_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)
|
/* reset jitter buffer */
|
||||||
|
void jitter_reset(jitter_t *jb)
|
||||||
{
|
{
|
||||||
memset(jitter->spl, 0, jitter->len * sizeof(sample_t));
|
jitter_frame_t *jf, *temp;
|
||||||
|
|
||||||
/* put write pointer ahead by half of the buffer length */
|
PDEBUG(DJITTER, DEBUG_INFO, "%sReset jitter buffer.\n", jb->name);
|
||||||
jitter->inptr = jitter->len / 2;
|
|
||||||
|
/* jitter buffer locked */
|
||||||
|
jb->unlocked = 0;
|
||||||
|
|
||||||
|
/* window becomes invalid */
|
||||||
|
jb->window_valid = 0;
|
||||||
|
|
||||||
|
/* remove all pending frames */
|
||||||
|
jf = jb->frame_list;
|
||||||
|
while(jf) {
|
||||||
|
temp = jf;
|
||||||
|
jf = jf->next;
|
||||||
|
free(temp);
|
||||||
|
}
|
||||||
|
jb->frame_list = NULL;
|
||||||
|
|
||||||
|
/* clear extrapolation buffer */
|
||||||
|
if (jb->extra_samples) {
|
||||||
|
if (jb->sample_size == 1)
|
||||||
|
memset(jb->extra_samples, 0xff, jb->sample_size * jb->extra_size);
|
||||||
|
else
|
||||||
|
memset(jb->extra_samples, 0, jb->sample_size * jb->extra_size);
|
||||||
|
}
|
||||||
|
jb->extra_index = 0;
|
||||||
|
|
||||||
|
/* 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 *jitter)
|
void jitter_destroy(jitter_t *jb)
|
||||||
{
|
{
|
||||||
if (jitter->spl) {
|
jitter_reset(jb);
|
||||||
free(jitter->spl);
|
|
||||||
jitter->spl = NULL;
|
PDEBUG(DJITTER, DEBUG_INFO, "%sDestroying jitter buffer.\n", jb->name);
|
||||||
|
|
||||||
|
if (jb->extra_samples) {
|
||||||
|
free(jb->extra_samples);
|
||||||
|
jb->extra_samples = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,64 +177,230 @@ void jitter_destroy(jitter_t *jitter)
|
||||||
*
|
*
|
||||||
* stop if buffer is completely filled
|
* 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;
|
jitter_frame_t *jf, **jfp;
|
||||||
int inptr, outptr, len, space;
|
int16_t offset_sequence;
|
||||||
int i;
|
int32_t offset_timestamp;
|
||||||
|
|
||||||
spl = jb->spl;
|
/* ignore frames until the buffer is unlocked by jitter_load() */
|
||||||
inptr = jb->inptr;
|
if (!jb->unlocked)
|
||||||
outptr = jb->outptr;
|
return;
|
||||||
len = jb->len;
|
|
||||||
space = (outptr - inptr + len - 1) % len;
|
|
||||||
|
|
||||||
if (space < length)
|
/* omit frames with no data */
|
||||||
length = space;
|
if (length < 1)
|
||||||
for (i = 0; i < length; i++) {
|
return;
|
||||||
spl[inptr++] = *samples++;
|
|
||||||
if (inptr == len)
|
/* generate sequence and timestamp automatically, if enabled */
|
||||||
inptr = 0;
|
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
|
/* 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;
|
jitter_frame_t *jf;
|
||||||
int inptr, outptr, len, fill;
|
int32_t count, count2, index;
|
||||||
int i, ii;
|
|
||||||
|
|
||||||
spl = jb->spl;
|
#ifdef HEAVY_DEBUG
|
||||||
inptr = jb->inptr;
|
PDEBUG(DJITTER, DEBUG_DEBUG, "%sLoad chunk of %d samples.\n", jb->name, length);
|
||||||
outptr = jb->outptr;
|
#endif
|
||||||
len = jb->len;
|
|
||||||
fill = (inptr - outptr + len) % len;
|
|
||||||
|
|
||||||
if (fill < length)
|
/* now unlock jitter buffer */
|
||||||
ii = fill;
|
jb->unlocked = 1;
|
||||||
else
|
|
||||||
ii = length;
|
|
||||||
|
|
||||||
/* fill what we got */
|
/* reduce delay */
|
||||||
for (i = 0; i < ii; i++) {
|
jb->delay_counter += jb->sample_duration * (double)length;
|
||||||
*samples++ = spl[outptr++];
|
if (jb->delay_counter >= jb->delay_interval) {
|
||||||
if (outptr == len)
|
if (jb->min_delay_value >= 0)
|
||||||
outptr = 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 */
|
||||||
/* on underrun, fill with silence */
|
if ((jb->window_flags & JITTER_FLAG_LATENCY) && jb->min_delay_value > jb->target_window_size) {
|
||||||
for (; i < length; i++) {
|
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);
|
||||||
*samples++ = 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;
|
/* process all frames until output buffer is loaded */
|
||||||
}
|
while (length) {
|
||||||
|
/* always get frame with the lowest sequence number (1st frame) */
|
||||||
void jitter_clear(jitter_t *jb)
|
jf = jb->frame_list;
|
||||||
{
|
|
||||||
jb->inptr = jb->outptr = 0;
|
if (jf) {
|
||||||
|
count = jf->timestamp - jb->window_timestamp;
|
||||||
|
if (count > length)
|
||||||
|
count = length;
|
||||||
|
} else
|
||||||
|
count = length;
|
||||||
|
/* if there is no frame or we have not reached frame's time stamp, extrapolate */
|
||||||
|
if (count > 0) {
|
||||||
|
#ifdef HEAVY_DEBUG
|
||||||
|
if (jf)
|
||||||
|
PDEBUG(DJITTER, DEBUG_DEBUG, "%s There is a frame ahead in buffer after %d samples. Interpolating gap.\n", jb->name, jf->timestamp - jb->window_timestamp);
|
||||||
|
else
|
||||||
|
PDEBUG(DJITTER, DEBUG_DEBUG, "%s There is no frame ahead in buffer. Interpolating gap.\n", jb->name);
|
||||||
|
#endif
|
||||||
|
/* extrapolate by playing the extrapolation buffer */
|
||||||
|
while (count) {
|
||||||
|
count2 = count;
|
||||||
|
if (count2 > jb->extra_size - jb->extra_index)
|
||||||
|
count2 = jb->extra_size - jb->extra_index;
|
||||||
|
memcpy(samples, (uint8_t *)jb->extra_samples + jb->extra_index * jb->sample_size, count2 * jb->sample_size);
|
||||||
|
jb->extra_index += count2;
|
||||||
|
if (jb->extra_index == jb->extra_size)
|
||||||
|
jb->extra_index = 0;
|
||||||
|
samples = (uint8_t *)samples + count2 * jb->sample_size;
|
||||||
|
length -= count2;
|
||||||
|
jb->window_timestamp += count2;
|
||||||
|
count -= count2;
|
||||||
|
}
|
||||||
|
if (length == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* copy samples from frame (what is not in the past) */
|
||||||
|
index = jb->window_timestamp - jf->timestamp;
|
||||||
|
while (index < jf->length) {
|
||||||
|
/* use the lowest value of 'playout length' or 'remaining packet length' */
|
||||||
|
count = length;
|
||||||
|
if (jf->length - index < count)
|
||||||
|
count = jf->length - index;
|
||||||
|
/* if extrapolation is used, limit count to what we can store into buffer */
|
||||||
|
if (jb->extra_samples && jb->extra_size - jb->extra_index < count)
|
||||||
|
count = jb->extra_size - jb->extra_index;
|
||||||
|
/* copy samples from packet to play out, increment sample pointer and decrement length */
|
||||||
|
#ifdef HEAVY_DEBUG
|
||||||
|
PDEBUG(DJITTER, DEBUG_DEBUG, "%s Copy data (offset=%u count=%u) from frame (sequence=%u timestamp=%u length=%u).\n", jb->name, index, count, jf->sequence, jf->timestamp, jf->length);
|
||||||
|
#endif
|
||||||
|
memcpy(samples, (uint8_t *)jf->samples + index * jb->sample_size, count * jb->sample_size);
|
||||||
|
samples = (uint8_t *)samples + count * jb->sample_size;
|
||||||
|
length -= count;
|
||||||
|
/* copy frame data to extrapolation buffer also, increment index */
|
||||||
|
if ((jb->window_flags & JITTER_FLAG_REPEAT)) {
|
||||||
|
memcpy((uint8_t *)jb->extra_samples + jb->extra_index * jb->sample_size, (uint8_t *)jf->samples + index * jb->sample_size, count * jb->sample_size);
|
||||||
|
jb->extra_index += count;
|
||||||
|
if (jb->extra_index == jb->extra_size)
|
||||||
|
jb->extra_index = 0;
|
||||||
|
}
|
||||||
|
/* increment time stamp */
|
||||||
|
jb->window_timestamp += count;
|
||||||
|
index += count;
|
||||||
|
/* if there was enough to play out, we are done */
|
||||||
|
if (length == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* free frame, because all samples are now in the past */
|
||||||
|
jb->frame_list = jf->next;
|
||||||
|
free(jf);
|
||||||
|
|
||||||
|
/* now go for next loop, in case there is still date to play out */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,58 @@
|
||||||
|
|
||||||
|
#define JITTER_FLAG_NONE 0 // no flags at all
|
||||||
|
#define JITTER_FLAG_LATENCY (1 << 0) // keep latency close to target_window_duration
|
||||||
|
#define JITTER_FLAG_REPEAT (1 << 1) // repeat audio to extrapolate gaps
|
||||||
|
|
||||||
|
/* window settings for low latency audio and extrapolation of gaps */
|
||||||
|
#define JITTER_AUDIO 0.050, 1.000, JITTER_FLAG_LATENCY | JITTER_FLAG_REPEAT
|
||||||
|
/* window settings for analog data (fax/modem) or digial data (HDLC) */
|
||||||
|
#define JITTER_DATA 0.100, 0.200, JITTER_FLAG_NONE
|
||||||
|
|
||||||
|
typedef struct jitter_frame {
|
||||||
|
struct jitter_frame *next;
|
||||||
|
uint16_t sequence;
|
||||||
|
uint32_t timestamp;
|
||||||
|
int length;
|
||||||
|
uint8_t samples[0];
|
||||||
|
} jitter_frame_t;
|
||||||
|
|
||||||
typedef struct jitter {
|
typedef struct jitter {
|
||||||
sample_t *spl; /* pointer to sample buffer */
|
char name[64];
|
||||||
int len; /* buffer size: number of samples */
|
|
||||||
int inptr, outptr; /* write pointer and read pointer */
|
/* 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;
|
||||||
|
|
||||||
|
/* list of frames */
|
||||||
|
jitter_frame_t *frame_list;
|
||||||
} jitter_t;
|
} jitter_t;
|
||||||
|
|
||||||
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);
|
||||||
void jitter_reset(jitter_t *jitter);
|
void jitter_reset(jitter_t *jb);
|
||||||
void jitter_destroy(jitter_t *jitter);
|
void jitter_destroy(jitter_t *jb);
|
||||||
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);
|
||||||
void jitter_load(jitter_t *jb, sample_t *samples, int length);
|
void jitter_load(jitter_t *jb, void *samples, int length);
|
||||||
void jitter_clear(jitter_t *jb);
|
|
||||||
|
|
||||||
|
|
|
@ -387,7 +387,7 @@ static void process_timeout(struct timer *timer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len)
|
||||||
{
|
{
|
||||||
process_t *process = codec->media->session->priv;
|
process_t *process = codec->media->session->priv;
|
||||||
sample_t samples[len / 2];
|
sample_t samples[len / 2];
|
||||||
|
@ -400,7 +400,7 @@ void down_audio(struct osmo_cc_session_codec *codec, uint16_t __attribute__((unu
|
||||||
double lev = level_of(samples, len / 2);
|
double lev = level_of(samples, len / 2);
|
||||||
printf("festnetz-level: %s %.4f\n", debug_db(lev), (20 * log10(lev)));
|
printf("festnetz-level: %s %.4f\n", debug_db(lev), (20 * log10(lev)));
|
||||||
#endif
|
#endif
|
||||||
call_down_audio(process->callref, samples, len / 2);
|
call_down_audio(process->callref, sequence_number, timestamp, ssrc, samples, len / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void indicate_setup(process_t *process, const char *callerid, const char *dialing, uint8_t network_type, const char *network_id)
|
static void indicate_setup(process_t *process, const char *callerid, const char *dialing, uint8_t network_type, const char *network_id)
|
||||||
|
|
|
@ -37,7 +37,7 @@ void call_down_release(int callref, int cause);
|
||||||
|
|
||||||
/* send and receive audio */
|
/* send and receive audio */
|
||||||
void call_up_audio(int callref, sample_t *samples, int count);
|
void call_up_audio(int callref, sample_t *samples, int count);
|
||||||
void call_down_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);
|
||||||
|
|
||||||
/* clock to transmit to */
|
/* clock to transmit to */
|
||||||
void call_clock(void); /* from main loop */
|
void call_clock(void); /* from main loop */
|
||||||
|
|
|
@ -141,17 +141,15 @@ static void free_console(void)
|
||||||
console.callref = 0;
|
console.callref = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void up_audio(struct osmo_cc_session_codec *codec, uint16_t __attribute__((unused)) sequence_number, uint32_t __attribute__((unused)) timestamp, uint8_t *data, int len)
|
void up_audio(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len)
|
||||||
{
|
{
|
||||||
int count = len / 2;
|
int count = len / 2;
|
||||||
sample_t samples[count];
|
sample_t samples[count];
|
||||||
|
|
||||||
/* save audio from transceiver to jitter buffer */
|
/* save audio from transceiver to jitter buffer */
|
||||||
if (console.sound) {
|
if (console.sound) {
|
||||||
sample_t up[(int)((double)count * console.srstate.factor + 0.5) + 10];
|
|
||||||
int16_to_samples(samples, (int16_t *)data, count);
|
int16_to_samples(samples, (int16_t *)data, count);
|
||||||
count = samplerate_upsample(&console.srstate, samples, count, up);
|
jitter_save(&console.dejitter, samples, count, 1, sequence_number, timestamp, ssrc);
|
||||||
jitter_save(&console.dejitter, up, count);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
/* if echo test is used, send echo back to mobile */
|
/* if echo test is used, send echo back to mobile */
|
||||||
|
@ -398,7 +396,7 @@ int console_init(const char *audiodev, int samplerate, int buffer, int loopback,
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = jitter_create(&console.dejitter, samplerate / 5);
|
rc = jitter_create(&console.dejitter, "console", 8000, sizeof(sample_t), 0.050, 0.200, JITTER_FLAG_NONE);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init dejitter buffer!\n");
|
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init dejitter buffer!\n");
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -580,7 +578,7 @@ void process_console(int c)
|
||||||
/* handle audio, if sound device is used */
|
/* handle audio, if sound device is used */
|
||||||
sample_t samples[console.buffer_size + 10], *samples_list[1];
|
sample_t samples[console.buffer_size + 10], *samples_list[1];
|
||||||
uint8_t *power_list[1];
|
uint8_t *power_list[1];
|
||||||
int count;
|
int count, input_num;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
count = sound_get_tosend(console.sound, console.buffer_size);
|
count = sound_get_tosend(console.sound, console.buffer_size);
|
||||||
|
@ -591,7 +589,11 @@ void process_console(int c)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
jitter_load(&console.dejitter, samples, count);
|
/* load and upsample */
|
||||||
|
input_num = samplerate_upsample_input_num(&console.srstate, count);
|
||||||
|
jitter_load(&console.dejitter, samples, input_num);
|
||||||
|
samplerate_upsample(&console.srstate, samples, input_num, samples, count);
|
||||||
|
/* write to sound device */
|
||||||
samples_list[0] = samples;
|
samples_list[0] = samples;
|
||||||
power_list[0] = NULL;
|
power_list[0] = NULL;
|
||||||
rc = sound_write(console.sound, samples_list, power_list, count, NULL, NULL, 1);
|
rc = sound_write(console.sound, samples_list, power_list, count, NULL, NULL, 1);
|
||||||
|
@ -613,9 +615,9 @@ void process_console(int c)
|
||||||
if (count) {
|
if (count) {
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (console.loopback == 3)
|
|
||||||
jitter_save(&console.dejitter, samples, count);
|
|
||||||
count = samplerate_downsample(&console.srstate, samples, count);
|
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 */
|
/* put samples into ring buffer */
|
||||||
for (i = 0; i < count; i++) {
|
for (i = 0; i < count; i++) {
|
||||||
console.tx_buffer[console.tx_buffer_pos] = samples[i];
|
console.tx_buffer[console.tx_buffer_pos] = samples[i];
|
||||||
|
|
|
@ -149,7 +149,13 @@ int sender_create(sender_t *sender, const char *kanal, double sendefrequenz, dou
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = jitter_create(&sender->dejitter, samplerate / 5);
|
rc = jitter_create(&sender->dejitter, sender->kanal, 8000, sizeof(sample_t), JITTER_AUDIO);
|
||||||
|
if (rc < 0) {
|
||||||
|
PDEBUG(DSENDER, DEBUG_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);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init audio buffer!\n");
|
PDEBUG(DSENDER, DEBUG_ERROR, "Failed to create and init audio buffer!\n");
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -288,6 +294,7 @@ void sender_destroy(sender_t *sender)
|
||||||
wave_destroy_playback(&sender->wave_tx_play);
|
wave_destroy_playback(&sender->wave_tx_play);
|
||||||
|
|
||||||
jitter_destroy(&sender->dejitter);
|
jitter_destroy(&sender->dejitter);
|
||||||
|
jitter_destroy(&sender->loop_dejitter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* set frequency modulation and parameters */
|
/* set frequency modulation and parameters */
|
||||||
|
@ -373,7 +380,7 @@ cant_recover:
|
||||||
for (i = 0, inst = sender; inst; i++, inst = inst->slave) {
|
for (i = 0, inst = sender; inst; i++, inst = inst->slave) {
|
||||||
/* load TX data from audio loop or from sender instance */
|
/* load TX data from audio loop or from sender instance */
|
||||||
if (inst->loopback == 3)
|
if (inst->loopback == 3)
|
||||||
jitter_load(&inst->dejitter, samples[i], count);
|
jitter_load(&inst->loop_dejitter, samples[i], count);
|
||||||
else
|
else
|
||||||
sender_send(inst, samples[i], power[i], count);
|
sender_send(inst, samples[i], power[i], count);
|
||||||
/* internal loopback: loop back TX audio to RX */
|
/* internal loopback: loop back TX audio to RX */
|
||||||
|
@ -458,7 +465,7 @@ cant_recover:
|
||||||
sender_receive(inst, samples[i], count, rf_level_db[i]);
|
sender_receive(inst, samples[i], count, rf_level_db[i]);
|
||||||
}
|
}
|
||||||
if (inst->loopback == 3)
|
if (inst->loopback == 3)
|
||||||
jitter_save(&inst->dejitter, samples[i], count);
|
jitter_save(&inst->loop_dejitter, samples[i], count, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifdef DEBUG_TIME_CONSUMPTION
|
#ifdef DEBUG_TIME_CONSUMPTION
|
||||||
|
|
|
@ -73,6 +73,7 @@ typedef struct sender {
|
||||||
|
|
||||||
/* audio buffer for audio to send to transmitter (also used as loopback buffer) */
|
/* audio buffer for audio to send to transmitter (also used as loopback buffer) */
|
||||||
jitter_t dejitter;
|
jitter_t dejitter;
|
||||||
|
jitter_t loop_dejitter;
|
||||||
|
|
||||||
/* audio buffer for audio to send to caller (20ms = 160 samples @ 8000Hz) */
|
/* audio buffer for audio to send to caller (20ms = 160 samples @ 8000Hz) */
|
||||||
sample_t rxbuf[160];
|
sample_t rxbuf[160];
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
#include "endpoint.h"
|
#include "endpoint.h"
|
||||||
#include "helper.h"
|
#include "helper.h"
|
||||||
|
|
||||||
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug)
|
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug)
|
||||||
{
|
{
|
||||||
osmo_cc_session_t *session;
|
osmo_cc_session_t *session;
|
||||||
osmo_cc_session_media_t *media;
|
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;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec)
|
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec)
|
||||||
{
|
{
|
||||||
char offer_sdp[65536];
|
char offer_sdp[65536];
|
||||||
const char *accept_sdp;
|
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);
|
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);
|
osmo_cc_session_t *osmo_cc_helper_audio_offer(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, int debug);
|
||||||
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec);
|
const char *osmo_cc_helper_audio_accept(osmo_cc_session_config_t *conf, void *priv, struct osmo_cc_helper_audio_codecs *codecs, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p, int force_our_codec);
|
||||||
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p);
|
int osmo_cc_helper_audio_negotiate(osmo_cc_msg_t *msg, osmo_cc_session_t **session_p, osmo_cc_session_codec_t **codec_p);
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ struct rtp_x_hdr {
|
||||||
uint16_t length;
|
uint16_t length;
|
||||||
} __attribute__((packed));
|
} __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];
|
static uint8_t data[2048];
|
||||||
int len;
|
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;
|
payload_type = rtph->byte1 & 0x7f;
|
||||||
*sequence_p = ntohs(rtph->sequence);
|
*sequence_p = ntohs(rtph->sequence);
|
||||||
*timestamp_p = ntohl(rtph->timestamp);
|
*timestamp_p = ntohl(rtph->timestamp);
|
||||||
|
*ssrc_p = ntohl(rtph->ssrc);
|
||||||
|
|
||||||
if (version != RTP_VERSION) {
|
if (version != RTP_VERSION) {
|
||||||
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP version %d not supported.\n", version);
|
PDEBUG(DCC, DEBUG_NOTICE, "Received RTP version %d not supported.\n", version);
|
||||||
|
@ -172,7 +173,7 @@ int osmo_cc_rtp_open(osmo_cc_session_media_t *media)
|
||||||
int flags;
|
int flags;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
media->rtp_ssrc = rand();
|
media->tx_ssrc = rand();
|
||||||
|
|
||||||
osmo_cc_rtp_close(media);
|
osmo_cc_rtp_close(media);
|
||||||
|
|
||||||
|
@ -335,7 +336,7 @@ void osmo_cc_rtp_send(osmo_cc_session_codec_t *codec, uint8_t *data, int len, in
|
||||||
payload_len = len;
|
payload_len = len;
|
||||||
}
|
}
|
||||||
|
|
||||||
rtp_send(codec->media->rtp_socket, payload, payload_len, codec->payload_type_remote, codec->media->tx_sequence, codec->media->tx_timestamp, codec->media->rtp_ssrc);
|
rtp_send(codec->media->rtp_socket, payload, payload_len, codec->payload_type_remote, codec->media->tx_sequence, codec->media->tx_timestamp, codec->media->tx_ssrc);
|
||||||
codec->media->tx_sequence += inc_sequence;
|
codec->media->tx_sequence += inc_sequence;
|
||||||
codec->media->tx_timestamp += inc_timestamp;
|
codec->media->tx_timestamp += inc_timestamp;
|
||||||
|
|
||||||
|
@ -358,7 +359,7 @@ int osmo_cc_rtp_receive(osmo_cc_session_media_t *media)
|
||||||
if (!media || media->rtp_socket <= 0)
|
if (!media || media->rtp_socket <= 0)
|
||||||
return -EIO;
|
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)
|
if (rc < 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
@ -381,7 +382,7 @@ int osmo_cc_rtp_receive(osmo_cc_session_media_t *media)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (codec->media->receive)
|
if (codec->media->receive)
|
||||||
codec->media->receiver(codec, media->rx_sequence, media->rx_timestamp, data, len);
|
codec->media->receiver(codec, media->rx_sequence, media->rx_timestamp, media->rx_ssrc, data, len);
|
||||||
|
|
||||||
if (codec->decoder)
|
if (codec->decoder)
|
||||||
free(data);
|
free(data);
|
||||||
|
|
|
@ -127,7 +127,7 @@ void osmo_cc_free_session(osmo_cc_session_t *session)
|
||||||
free(session);
|
free(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), int debug)
|
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), int debug)
|
||||||
{
|
{
|
||||||
osmo_cc_session_config_t *conf = session->config;
|
osmo_cc_session_config_t *conf = session->config;
|
||||||
osmo_cc_session_media_t *media, **mediap;
|
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;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len))
|
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len))
|
||||||
{
|
{
|
||||||
osmo_cc_session_config_t *conf = media->session->config;
|
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;
|
osmo_cc_session_connection_data_t connection_data_local, connection_data_remote;
|
||||||
struct osmo_cc_session_codec *codec_list;
|
struct osmo_cc_session_codec *codec_list;
|
||||||
int send, receive;
|
int send, receive;
|
||||||
void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len);
|
void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len);
|
||||||
int rtp_socket;
|
int rtp_socket;
|
||||||
int rtcp_socket;
|
int rtcp_socket;
|
||||||
uint32_t rtp_ssrc;
|
uint32_t tx_ssrc, rx_ssrc;
|
||||||
uint16_t tx_sequence, rx_sequence;
|
uint16_t tx_sequence, rx_sequence;
|
||||||
uint32_t tx_timestamp, rx_timestamp;
|
uint32_t tx_timestamp, rx_timestamp;
|
||||||
int accepted;
|
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);
|
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);
|
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);
|
void osmo_cc_free_session(osmo_cc_session_t *session);
|
||||||
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len), int debug);
|
osmo_cc_session_media_t *osmo_cc_add_media(osmo_cc_session_t *session, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, enum osmo_cc_session_media_type type, uint16_t port, enum osmo_cc_session_media_proto proto, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len), int debug);
|
||||||
void osmo_cc_free_media(osmo_cc_session_media_t *media);
|
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);
|
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);
|
void osmo_cc_free_codec(osmo_cc_session_codec_t *codec);
|
||||||
int osmo_cc_session_check(struct osmo_cc_session *session, int remote);
|
int osmo_cc_session_check(struct osmo_cc_session *session, int remote);
|
||||||
const char *osmo_cc_session_send_offer(osmo_cc_session_t *session);
|
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);
|
osmo_cc_session_t *osmo_cc_session_receive_offer(osmo_cc_session_config_t *conf, void *priv, const char *sdp);
|
||||||
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint8_t *data, int len));
|
void osmo_cc_session_accept_media(osmo_cc_session_media_t *media, enum osmo_cc_session_nettype nettype, enum osmo_cc_session_addrtype addrtype, const char *address, int send, int receive, void (*receiver)(struct osmo_cc_session_codec *codec, uint16_t sequence_number, uint32_t timestamp, uint32_t ssrc, uint8_t *data, int len));
|
||||||
void osmo_cc_session_accept_codec(osmo_cc_session_codec_t *codec, void (*encoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len), void (*decoder)(uint8_t *src_data, int src_len, uint8_t **dst_data, int *dst_len));
|
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);
|
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);
|
int osmo_cc_session_receive_answer(osmo_cc_session_t *session, const char *sdp);
|
||||||
|
|
|
@ -17,4 +17,6 @@ typedef struct samplerate {
|
||||||
|
|
||||||
int init_samplerate(samplerate_t *state, double low_samplerate, double high_samplerate, double filter_cutoff);
|
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_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);
|
||||||
|
|
|
@ -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);
|
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 */
|
/* repeater */
|
||||||
rc = jitter_create(&mpt1327->repeater_dejitter, mpt1327->sender.samplerate / 5);
|
rc = jitter_create(&mpt1327->repeater_dejitter, "repeater", mpt1327->sender.samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
PDEBUG(DDSP, DEBUG_ERROR, "Failed to create and init repeater buffer!\n");
|
PDEBUG(DDSP, DEBUG_ERROR, "Failed to create and init repeater buffer!\n");
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -235,7 +235,7 @@ void sender_receive(sender_t *sender, sample_t *samples, int length, double __at
|
||||||
if (mpt1327->dsp_mode == DSP_MODE_TRAFFIC) {
|
if (mpt1327->dsp_mode == DSP_MODE_TRAFFIC) {
|
||||||
/* if repeater mode, store sample in jitter buffer */
|
/* if repeater mode, store sample in jitter buffer */
|
||||||
if (mpt1327->repeater)
|
if (mpt1327->repeater)
|
||||||
jitter_save(&mpt1327->repeater_dejitter, samples, length);
|
jitter_save(&mpt1327->repeater_dejitter, samples, length, 0, 0, 0, 0);
|
||||||
|
|
||||||
if (mpt1327->unit && mpt1327->unit->callref) {
|
if (mpt1327->unit && mpt1327->unit->callref) {
|
||||||
int count;
|
int count;
|
||||||
|
@ -279,6 +279,7 @@ static int fsk_send_bit(void *inst)
|
||||||
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
||||||
{
|
{
|
||||||
mpt1327_t *mpt1327 = (mpt1327_t *) sender;
|
mpt1327_t *mpt1327 = (mpt1327_t *) sender;
|
||||||
|
int input_num;
|
||||||
|
|
||||||
if (mpt1327->dsp_mode == DSP_MODE_OFF) {
|
if (mpt1327->dsp_mode == DSP_MODE_OFF) {
|
||||||
memset(power, 0, length);
|
memset(power, 0, length);
|
||||||
|
@ -289,7 +290,9 @@ void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length
|
||||||
memset(power, 1, length);
|
memset(power, 1, length);
|
||||||
|
|
||||||
if (mpt1327->dsp_mode == DSP_MODE_TRAFFIC) {
|
if (mpt1327->dsp_mode == DSP_MODE_TRAFFIC) {
|
||||||
jitter_load(&mpt1327->sender.dejitter, samples, length);
|
input_num = samplerate_upsample_input_num(&sender->srstate, length);
|
||||||
|
jitter_load(&sender->dejitter, samples, input_num);
|
||||||
|
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
|
||||||
/* if repeater mode, sum samples from jitter buffer to samples */
|
/* if repeater mode, sum samples from jitter buffer to samples */
|
||||||
if (mpt1327->repeater) {
|
if (mpt1327->repeater) {
|
||||||
sample_t uplink[length];
|
sample_t uplink[length];
|
||||||
|
|
|
@ -1642,7 +1642,7 @@ void call_down_release(int callref, __attribute__((unused)) int cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_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)
|
||||||
{
|
{
|
||||||
mpt1327_unit_t *unit;
|
mpt1327_unit_t *unit;
|
||||||
|
|
||||||
|
@ -1652,11 +1652,8 @@ void call_down_audio(int callref, sample_t *samples, int count)
|
||||||
if (!unit->tc)
|
if (!unit->tc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (unit->tc->state == STATE_BUSY && unit->tc->dsp_mode == DSP_MODE_TRAFFIC) {
|
if (unit->tc->state == STATE_BUSY && unit->tc->dsp_mode == DSP_MODE_TRAFFIC)
|
||||||
sample_t up[(int)((double)count * unit->tc->sender.srstate.factor + 0.5) + 10];
|
jitter_save(&unit->tc->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||||
count = samplerate_upsample(&unit->tc->sender.srstate, samples, count, up);
|
|
||||||
jitter_save(&unit->tc->sender.dejitter, up, count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_info(void)
|
void dump_info(void)
|
||||||
|
|
|
@ -446,7 +446,7 @@ static void dial_tone(nmt_t *nmt, sample_t *samples, int length)
|
||||||
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
||||||
{
|
{
|
||||||
nmt_t *nmt = (nmt_t *) sender;
|
nmt_t *nmt = (nmt_t *) sender;
|
||||||
int count;
|
int count, input_num;
|
||||||
|
|
||||||
memset(power, 1, length);
|
memset(power, 1, length);
|
||||||
|
|
||||||
|
@ -454,7 +454,9 @@ again:
|
||||||
switch (nmt->dsp_mode) {
|
switch (nmt->dsp_mode) {
|
||||||
case DSP_MODE_AUDIO:
|
case DSP_MODE_AUDIO:
|
||||||
case DSP_MODE_DTMF:
|
case DSP_MODE_DTMF:
|
||||||
jitter_load(&nmt->sender.dejitter, samples, length);
|
input_num = samplerate_upsample_input_num(&sender->srstate, length);
|
||||||
|
jitter_load(&sender->dejitter, samples, input_num);
|
||||||
|
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
|
||||||
/* send after dejitter, so audio is flushed */
|
/* send after dejitter, so audio is flushed */
|
||||||
if (nmt->dms.tx_frame_valid) {
|
if (nmt->dms.tx_frame_valid) {
|
||||||
fsk_mod_send(&nmt->fsk_mod, samples, length, 0);
|
fsk_mod_send(&nmt->fsk_mod, samples, length, 0);
|
||||||
|
|
|
@ -1954,7 +1954,7 @@ void call_down_release(int callref, int __attribute__((unused)) cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_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)
|
||||||
{
|
{
|
||||||
transaction_t *trans;
|
transaction_t *trans;
|
||||||
nmt_t *nmt;
|
nmt_t *nmt;
|
||||||
|
@ -1967,11 +1967,9 @@ void call_down_audio(int callref, sample_t *samples, int count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (nmt->dsp_mode == DSP_MODE_AUDIO || nmt->dsp_mode == DSP_MODE_DTMF) {
|
if (nmt->dsp_mode == DSP_MODE_AUDIO || nmt->dsp_mode == DSP_MODE_DTMF) {
|
||||||
sample_t up[(int)((double)count * nmt->sender.srstate.factor + 0.5) + 10];
|
|
||||||
if (nmt->compandor)
|
if (nmt->compandor)
|
||||||
compress_audio(&nmt->cstate, samples, count);
|
compress_audio(&nmt->cstate, samples, count);
|
||||||
count = samplerate_upsample(&nmt->sender.srstate, samples, count, up);
|
jitter_save(&nmt->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||||
jitter_save(&nmt->sender.dejitter, up, count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -565,7 +565,7 @@ void call_down_release(int callref, int cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_audio(int __attribute__((unused)) callref, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
|
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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -335,7 +335,7 @@ static int super_send_bit(void *inst)
|
||||||
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
void sender_send(sender_t *sender, sample_t *samples, uint8_t *power, int length)
|
||||||
{
|
{
|
||||||
r2000_t *r2000 = (r2000_t *) sender;
|
r2000_t *r2000 = (r2000_t *) sender;
|
||||||
int count;
|
int count, input_num;
|
||||||
|
|
||||||
again:
|
again:
|
||||||
switch (r2000->dsp_mode) {
|
switch (r2000->dsp_mode) {
|
||||||
|
@ -346,7 +346,9 @@ again:
|
||||||
case DSP_MODE_AUDIO_TX:
|
case DSP_MODE_AUDIO_TX:
|
||||||
case DSP_MODE_AUDIO_TX_RX:
|
case DSP_MODE_AUDIO_TX_RX:
|
||||||
memset(power, 1, length);
|
memset(power, 1, length);
|
||||||
jitter_load(&r2000->sender.dejitter, samples, length);
|
input_num = samplerate_upsample_input_num(&sender->srstate, length);
|
||||||
|
jitter_load(&sender->dejitter, samples, input_num);
|
||||||
|
samplerate_upsample(&sender->srstate, samples, input_num, samples, length);
|
||||||
iir_process(&r2000->super_tx_hp, samples, length);
|
iir_process(&r2000->super_tx_hp, samples, length);
|
||||||
/* do pre-emphasis */
|
/* do pre-emphasis */
|
||||||
if (r2000->pre_emphasis)
|
if (r2000->pre_emphasis)
|
||||||
|
|
|
@ -1529,7 +1529,7 @@ void call_down_release(int callref, int __attribute__((unused)) cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_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)
|
||||||
{
|
{
|
||||||
sender_t *sender;
|
sender_t *sender;
|
||||||
r2000_t *r2000;
|
r2000_t *r2000;
|
||||||
|
@ -1544,11 +1544,9 @@ void call_down_audio(int callref, sample_t *samples, int count)
|
||||||
|
|
||||||
if (r2000->dsp_mode == DSP_MODE_AUDIO_TX
|
if (r2000->dsp_mode == DSP_MODE_AUDIO_TX
|
||||||
|| r2000->dsp_mode == DSP_MODE_AUDIO_TX_RX) {
|
|| r2000->dsp_mode == DSP_MODE_AUDIO_TX_RX) {
|
||||||
sample_t up[(int)((double)count * r2000->sender.srstate.factor + 0.5) + 10];
|
|
||||||
if (r2000->compandor)
|
if (r2000->compandor)
|
||||||
compress_audio(&r2000->cstate, samples, count);
|
compress_audio(&r2000->cstate, samples, count);
|
||||||
count = samplerate_upsample(&r2000->sender.srstate, samples, count, up);
|
jitter_save(&r2000->sender.dejitter, samples, count, 1, sequence, timestamp, ssrc);
|
||||||
jitter_save(&r2000->sender.dejitter, up, count);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -106,8 +106,8 @@ int radio_init(radio_t *radio, int buffer_size, int samplerate, double frequency
|
||||||
PDEBUG(DRADIO, DEBUG_ERROR, "Failed to open sound device!\n");
|
PDEBUG(DRADIO, DEBUG_ERROR, "Failed to open sound device!\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
jitter_create(&radio->tx_dejitter[0], radio->tx_audio_samplerate / 5);
|
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], radio->tx_audio_samplerate / 5);
|
jitter_create(&radio->tx_dejitter[1], "right", radio->tx_audio_samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
|
||||||
radio->tx_audio_mode = AUDIO_MODE_AUDIODEV;
|
radio->tx_audio_mode = AUDIO_MODE_AUDIODEV;
|
||||||
#else
|
#else
|
||||||
rc = -ENOTSUP;
|
rc = -ENOTSUP;
|
||||||
|
@ -173,8 +173,8 @@ int radio_init(radio_t *radio, int buffer_size, int samplerate, double frequency
|
||||||
PDEBUG(DRADIO, DEBUG_ERROR, "Failed to open sound device!\n");
|
PDEBUG(DRADIO, DEBUG_ERROR, "Failed to open sound device!\n");
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
jitter_create(&radio->rx_dejitter[0], radio->rx_audio_samplerate / 5);
|
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], radio->rx_audio_samplerate / 5);
|
jitter_create(&radio->rx_dejitter[1], "right", radio->rx_audio_samplerate, sizeof(sample_t), 0.050, 0.500, JITTER_FLAG_NONE);
|
||||||
radio->rx_audio_mode |= AUDIO_MODE_AUDIODEV;
|
radio->rx_audio_mode |= AUDIO_MODE_AUDIODEV;
|
||||||
#else
|
#else
|
||||||
rc = -ENOTSUP;
|
rc = -ENOTSUP;
|
||||||
|
@ -494,10 +494,10 @@ int radio_tx(radio_t *radio, float *baseband, int signal_num)
|
||||||
else
|
else
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
jitter_save(&radio->tx_dejitter[0], audio_samples[0], rc);
|
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);
|
jitter_load(&radio->tx_dejitter[0], audio_samples[0], audio_num);
|
||||||
if (radio->tx_audio_channels == 2) {
|
if (radio->tx_audio_channels == 2) {
|
||||||
jitter_save(&radio->tx_dejitter[1], audio_samples[1], rc);
|
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);
|
jitter_load(&radio->tx_dejitter[1], audio_samples[1], audio_num);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -554,9 +554,10 @@ int radio_tx(radio_t *radio, float *baseband, int signal_num)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* upsample */
|
/* upsample */
|
||||||
signal_num = samplerate_upsample(&radio->tx_resampler[0], audio_samples[0], audio_num, signal_samples[0]);
|
signal_num = samplerate_upsample_output_num(&radio->tx_resampler[0], audio_num);
|
||||||
|
samplerate_upsample(&radio->tx_resampler[0], audio_samples[0], audio_num, signal_samples[0], signal_num);
|
||||||
if (radio->stereo)
|
if (radio->stereo)
|
||||||
samplerate_upsample(&radio->tx_resampler[1], audio_samples[1], audio_num, signal_samples[1]);
|
samplerate_upsample(&radio->tx_resampler[1], audio_samples[1], audio_num, signal_samples[1], signal_num);
|
||||||
|
|
||||||
/* prepare baseband */
|
/* prepare baseband */
|
||||||
memset(baseband, 0, sizeof(float) * 2 * signal_num);
|
memset(baseband, 0, sizeof(float) * 2 * signal_num);
|
||||||
|
@ -727,9 +728,9 @@ int radio_rx(radio_t *radio, float *baseband, int signal_num)
|
||||||
wave_write(&radio->wave_rx_rec, samples, audio_num);
|
wave_write(&radio->wave_rx_rec, samples, audio_num);
|
||||||
#ifdef HAVE_ALSA
|
#ifdef HAVE_ALSA
|
||||||
if ((radio->rx_audio_mode & AUDIO_MODE_AUDIODEV)) {
|
if ((radio->rx_audio_mode & AUDIO_MODE_AUDIODEV)) {
|
||||||
jitter_save(&radio->rx_dejitter[0], samples[0], audio_num);
|
jitter_save(&radio->rx_dejitter[0], samples[0], audio_num, 0, 0, 0 ,0);
|
||||||
if (radio->rx_audio_channels == 2)
|
if (radio->rx_audio_channels == 2)
|
||||||
jitter_save(&radio->rx_dejitter[1], samples[1], audio_num);
|
jitter_save(&radio->rx_dejitter[1], samples[1], audio_num, 0, 0, 0 ,0);
|
||||||
audio_num = sound_get_tosend(radio->rx_sound, radio->signal_buffer_size);
|
audio_num = sound_get_tosend(radio->rx_sound, radio->signal_buffer_size);
|
||||||
jitter_load(&radio->rx_dejitter[0], samples[0], audio_num);
|
jitter_load(&radio->rx_dejitter[0], samples[0], audio_num);
|
||||||
if (radio->rx_audio_channels == 2)
|
if (radio->rx_audio_channels == 2)
|
||||||
|
|
|
@ -395,7 +395,7 @@ void call_down_release(int callref, int cause)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Receive audio from call instance. */
|
/* Receive audio from call instance. */
|
||||||
void call_down_audio(int __attribute__((unused)) callref, sample_t __attribute__((unused)) *samples, int __attribute__((unused)) count)
|
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)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue