|
|
|
@ -25,7 +25,7 @@ |
|
|
|
|
|
|
|
|
|
|
|
#define CALL_AUDIO_ID "call_audio" |
|
|
|
#define CALL_AUDIO_ID "call_audio" |
|
|
|
#define CALL_AUDIO_BITRATE 32000 |
|
|
|
#define CALL_AUDIO_BITRATE 32000 |
|
|
|
#define CALL_STALL_TB 5000 /* 500мс без медиа и недобор PCM → глитч-тон */ |
|
|
|
#define CALL_STALL_SAMPLES (CALL_AUDIO_SAMPLE_RATE / 2) /* 500мс без PCM из jitter → глитч-тон */ |
|
|
|
#define CALL_AEC_FILTER_SAMPLES 14400 /* хвост эха 300мс @48кГц */ |
|
|
|
#define CALL_AEC_FILTER_SAMPLES 14400 /* хвост эха 300мс @48кГц */ |
|
|
|
#define CALL_AEC_DELAY_FRAMES 2 /* desktop: минимальная задержка duplex */ |
|
|
|
#define CALL_AEC_DELAY_FRAMES 2 /* desktop: минимальная задержка duplex */ |
|
|
|
|
|
|
|
|
|
|
|
@ -36,7 +36,7 @@ static int g_ending = 0; |
|
|
|
static int g_glitch_started = 0; /* только аудиопоток */ |
|
|
|
static int g_glitch_started = 0; /* только аудиопоток */ |
|
|
|
static int g_ended_started = 0; /* только аудио-поток: тон завершения запущен */ |
|
|
|
static int g_ended_started = 0; /* только аудио-поток: тон завершения запущен */ |
|
|
|
static uint64_t g_call_id = 0; |
|
|
|
static uint64_t g_call_id = 0; |
|
|
|
static uint64_t g_last_media_tb = 0; |
|
|
|
static int g_gap_samples = 0; /* только аудиопоток; насыщение на пороге */ |
|
|
|
static opus_codec_encoder_t* g_encoder = NULL; |
|
|
|
static opus_codec_encoder_t* g_encoder = NULL; |
|
|
|
static opus_codec_decoder_t* g_decoder = NULL; |
|
|
|
static opus_codec_decoder_t* g_decoder = NULL; |
|
|
|
static struct call_jitter* g_vj = NULL; |
|
|
|
static struct call_jitter* g_vj = NULL; |
|
|
|
@ -85,7 +85,6 @@ void call_audio_on_media(struct UTUN_INSTANCE* inst, uint64_t call_id, |
|
|
|
pthread_mutex_lock(&g_mtx); |
|
|
|
pthread_mutex_lock(&g_mtx); |
|
|
|
if (g_active && call_id == g_call_id && g_vj && opus && len > 0) { |
|
|
|
if (g_active && call_id == g_call_id && g_vj && opus && len > 0) { |
|
|
|
call_jitter_push(g_vj, opus, len); |
|
|
|
call_jitter_push(g_vj, opus, len); |
|
|
|
g_last_media_tb = get_time_tb(); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
pthread_mutex_unlock(&g_mtx); |
|
|
|
pthread_mutex_unlock(&g_mtx); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -185,7 +184,7 @@ int call_audio_start(uint64_t call_id) { |
|
|
|
g_ending = 0; |
|
|
|
g_ending = 0; |
|
|
|
g_ended_started = 0; |
|
|
|
g_ended_started = 0; |
|
|
|
g_glitch_started = 0; |
|
|
|
g_glitch_started = 0; |
|
|
|
g_last_media_tb = 0; |
|
|
|
g_gap_samples = 0; |
|
|
|
pthread_mutex_unlock(&g_mtx); |
|
|
|
pthread_mutex_unlock(&g_mtx); |
|
|
|
|
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: started call=0x%016llx aec=%d delay=%d compressor=%s gain=%d", CALL_AUDIO_ID, |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: started call=0x%016llx aec=%d delay=%d compressor=%s gain=%d", CALL_AUDIO_ID, |
|
|
|
@ -220,7 +219,7 @@ void call_audio_release(uint64_t call_id) { |
|
|
|
g_ending = 0; |
|
|
|
g_ending = 0; |
|
|
|
g_ended_started = 0; |
|
|
|
g_ended_started = 0; |
|
|
|
g_call_id = 0; |
|
|
|
g_call_id = 0; |
|
|
|
g_last_media_tb = 0; |
|
|
|
g_gap_samples = 0; |
|
|
|
enc = g_encoder; g_encoder = NULL; |
|
|
|
enc = g_encoder; g_encoder = NULL; |
|
|
|
dec = g_decoder; g_decoder = NULL; |
|
|
|
dec = g_decoder; g_decoder = NULL; |
|
|
|
j = g_vj; g_vj = NULL; |
|
|
|
j = g_vj; g_vj = NULL; |
|
|
|
@ -347,7 +346,6 @@ int call_audio_pull_pcm(uint64_t call_id, int16_t* out, int max_samples) { |
|
|
|
int active = 0, ending = 0; |
|
|
|
int active = 0, ending = 0; |
|
|
|
struct call_jitter* j = NULL; |
|
|
|
struct call_jitter* j = NULL; |
|
|
|
struct call_tones* tones = NULL; |
|
|
|
struct call_tones* tones = NULL; |
|
|
|
uint64_t last_media = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pthread_mutex_lock(&g_mtx); |
|
|
|
pthread_mutex_lock(&g_mtx); |
|
|
|
if (g_active && call_id == g_call_id) { |
|
|
|
if (g_active && call_id == g_call_id) { |
|
|
|
@ -355,7 +353,6 @@ int call_audio_pull_pcm(uint64_t call_id, int16_t* out, int max_samples) { |
|
|
|
ending = g_ending; |
|
|
|
ending = g_ending; |
|
|
|
j = g_vj; |
|
|
|
j = g_vj; |
|
|
|
tones = g_tones; |
|
|
|
tones = g_tones; |
|
|
|
last_media = g_last_media_tb; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
pthread_mutex_unlock(&g_mtx); |
|
|
|
pthread_mutex_unlock(&g_mtx); |
|
|
|
|
|
|
|
|
|
|
|
@ -375,21 +372,36 @@ int call_audio_pull_pcm(uint64_t call_id, int16_t* out, int max_samples) { |
|
|
|
n = call_tones_render(tones, out, max_samples); |
|
|
|
n = call_tones_render(tones, out, max_samples); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
n = call_jitter_pull(j, out, max_samples); |
|
|
|
n = call_jitter_pull(j, out, max_samples); |
|
|
|
uint64_t now = get_time_tb(); |
|
|
|
int previous_gap = g_gap_samples; |
|
|
|
int stalled = n < max_samples && last_media != 0 && now - last_media >= CALL_STALL_TB; |
|
|
|
if (n > 0) g_gap_samples = 0; |
|
|
|
if (stalled != g_glitch_started) { |
|
|
|
int missing = max_samples - n; |
|
|
|
g_glitch_started = stalled; |
|
|
|
int quiet = CALL_STALL_SAMPLES - g_gap_samples; |
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: gap tone %s call=%016llx media_gap=%llums pcm=%d/%d", |
|
|
|
if (quiet > missing) quiet = missing; |
|
|
|
CALL_AUDIO_ID, stalled ? "started" : "stopped", (unsigned long long)call_id, |
|
|
|
int tone_samples = missing - quiet; |
|
|
|
(unsigned long long)((now - last_media) / 10), n, max_samples); |
|
|
|
if (n > 0 && previous_gap > 0) |
|
|
|
} |
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "%s: PCM resumed call=%016llx gap=%dms pcm=%d/%d", CALL_AUDIO_ID, |
|
|
|
if (stalled) { |
|
|
|
(unsigned long long)call_id, previous_gap * 1000 / CALL_AUDIO_SAMPLE_RATE, n, max_samples); |
|
|
|
call_tones_glitch_start(tones); |
|
|
|
if (missing > 0 && g_gap_samples == 0) |
|
|
|
n += call_tones_render(tones, out + n, max_samples - n); |
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_CALL, "%s: PCM gap started call=%016llx pcm=%d/%d", CALL_AUDIO_ID, |
|
|
|
} else { |
|
|
|
(unsigned long long)call_id, n, max_samples); |
|
|
|
|
|
|
|
g_gap_samples += quiet; |
|
|
|
|
|
|
|
/* Новый PCM прерывает предыдущий цикл тона, даже если хвост блока снова пуст. */ |
|
|
|
|
|
|
|
if (g_glitch_started && (n > 0 || tone_samples == 0)) { |
|
|
|
|
|
|
|
g_glitch_started = 0; |
|
|
|
call_tones_glitch_stop(tones); |
|
|
|
call_tones_glitch_stop(tones); |
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: gap tone stopped call=%016llx pcm=%d/%d", CALL_AUDIO_ID, |
|
|
|
|
|
|
|
(unsigned long long)call_id, n, max_samples); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
memset(out + n, 0, (size_t)quiet * sizeof(*out)); |
|
|
|
|
|
|
|
if (tone_samples > 0) { |
|
|
|
|
|
|
|
if (!g_glitch_started) { |
|
|
|
|
|
|
|
g_glitch_started = 1; |
|
|
|
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: gap tone started call=%016llx pcm_gap=500ms pcm=%d/%d", CALL_AUDIO_ID, |
|
|
|
|
|
|
|
(unsigned long long)call_id, n, max_samples); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
call_tones_glitch_start(tones); |
|
|
|
|
|
|
|
call_tones_render(tones, out + n + quiet, tone_samples); |
|
|
|
} |
|
|
|
} |
|
|
|
for (int i = n; i < max_samples; i++) out[i] = 0; |
|
|
|
|
|
|
|
n = max_samples; |
|
|
|
n = max_samples; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|