Isolate call audio TX tasks by instance binding epoch
This commit is contained in:
1 parent
266406261a
commit
e11eca93bb
2 files changed
+28
-7
No files matched your search
+24
-5
@@ -48,6 +48,7 @@ static int g_prepared_capture = 0;
|
||||
static int g_aec_enabled = 1; /* по умолчанию: настройка aec_enabled */
|
||||
static struct audio_compressor* g_compressor = NULL;
|
||||
static uint64_t g_tx_generation;
|
||||
static uint64_t g_instance_generation; /* Эпоха привязки; смена звонка её не меняет. */
|
||||
static uint64_t g_capture_epoch;
|
||||
static int64_t g_capture_end = INT64_MIN;
|
||||
static int g_codec_delay;
|
||||
@@ -69,6 +70,7 @@ struct call_audio_tx {
|
||||
struct posted_task task;
|
||||
struct UTUN_INSTANCE* inst;
|
||||
uint64_t call_id, generation, created_tb;
|
||||
uint64_t instance_generation;
|
||||
int len;
|
||||
struct call_capture_time timing;
|
||||
uint8_t opus[256];
|
||||
@@ -77,9 +79,20 @@ struct call_audio_tx {
|
||||
static void call_audio_send(void* arg) {
|
||||
struct call_audio_tx* tx = arg;
|
||||
pthread_mutex_lock(&g_mtx);
|
||||
int current = tx->generation == g_tx_generation;
|
||||
int owner = tx->instance_generation == g_instance_generation && g_inst == tx->inst;
|
||||
int current = owner && tx->generation == g_tx_generation;
|
||||
if (current) call_audio_duration_add(&g_post_time, (get_time_tb() - tx->created_tb) * 100);
|
||||
if (g_tx_pending) --g_tx_pending;
|
||||
/* Старый звонок той же привязки всё ещё занимает свой слот очереди. */
|
||||
if (owner) {
|
||||
if (g_tx_pending) --g_tx_pending;
|
||||
else DEBUG_ERROR(DEBUG_CATEGORY_CALL, "%s: TX pending underflow call=%llu instance_epoch=%llu", CALL_AUDIO_ID,
|
||||
(unsigned long long)tx->call_id, (unsigned long long)tx->instance_generation);
|
||||
}
|
||||
if (!current) DEBUG_TRACE(DEBUG_CATEGORY_CALL,
|
||||
"%s: stale TX call=%llu owner=%d instance_epoch=%llu/%llu call_epoch=%llu/%llu pending=%u", CALL_AUDIO_ID,
|
||||
(unsigned long long)tx->call_id, owner, (unsigned long long)tx->instance_generation,
|
||||
(unsigned long long)g_instance_generation, (unsigned long long)tx->generation,
|
||||
(unsigned long long)g_tx_generation, g_tx_pending);
|
||||
int send = current && g_active && !g_ending && g_inst == tx->inst && g_call_id == tx->call_id;
|
||||
if (get_time_tb() - tx->created_tb > 2000) {
|
||||
send = 0;
|
||||
@@ -104,13 +117,16 @@ static int call_audio_decode_cb(void* arg, const uint8_t* enc, int len, int16_t*
|
||||
}
|
||||
|
||||
int call_audio_init(struct UTUN_INSTANCE* inst) {
|
||||
if (!inst) return -1;
|
||||
if (!inst) { DEBUG_ERROR(DEBUG_CATEGORY_CALL, "%s: init missing instance", CALL_AUDIO_ID); return -1; }
|
||||
pthread_mutex_lock(&g_mtx);
|
||||
if (g_inst == inst) { pthread_mutex_unlock(&g_mtx); return 0; }
|
||||
unsigned int previous_pending = g_tx_pending;
|
||||
g_inst = inst; g_tx_pending = 0;
|
||||
++g_instance_generation;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: initialized inst=%p instance_epoch=%llu previous_pending=%u", CALL_AUDIO_ID,
|
||||
(void*)inst, (unsigned long long)g_instance_generation, previous_pending);
|
||||
pthread_mutex_unlock(&g_mtx);
|
||||
call_set_audio_ops(inst, &g_ops);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: initialized", CALL_AUDIO_ID);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -119,8 +135,10 @@ void call_audio_destroy(struct UTUN_INSTANCE* inst) {
|
||||
call_set_audio_ops(inst, NULL);
|
||||
pthread_mutex_lock(&g_mtx);
|
||||
g_inst = NULL;
|
||||
++g_instance_generation;
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: destroyed inst=%p instance_epoch=%llu pending=%u", CALL_AUDIO_ID,
|
||||
(void*)inst, (unsigned long long)g_instance_generation, g_tx_pending);
|
||||
pthread_mutex_unlock(&g_mtx);
|
||||
DEBUG_INFO(DEBUG_CATEGORY_CALL, "%s: destroyed", CALL_AUDIO_ID);
|
||||
}
|
||||
|
||||
/* ── uasync-поток: приём медиа (кладём кодированный кадр в jitter-буфер) ── */
|
||||
@@ -452,6 +470,7 @@ static int call_audio_feed_impl(uint64_t call_id, const int16_t* pcm, int count,
|
||||
}
|
||||
task->inst = inst; task->call_id = call_id;
|
||||
task->generation = g_tx_generation; task->created_tb = get_time_tb();
|
||||
task->instance_generation = g_instance_generation;
|
||||
task->timing = (struct call_capture_time){capture_pts_us, g_capture_epoch, (uint32_t)g_codec_delay};
|
||||
task->len = len;
|
||||
memcpy(task->opus, opus, (size_t)len);
|
||||
|
||||
@@ -32,9 +32,11 @@ extern "C" {
|
||||
#define CALL_AUDIO_FRAME_SAMPLES (CALL_AUDIO_SAMPLE_RATE * CALL_AUDIO_FRAME_MS / 1000) /* 960 */
|
||||
|
||||
/* Зарегистрировать общий для процесса аудио-движок в call.c; повтор с тем же inst идемпотентен.
|
||||
* Известный дефект 26.10.2026: destroy/init на живом uasync со старыми TX-задачами нарушает
|
||||
* учёт лимита8 новых задач. Подробности и план изоляции эпох привязки — doc/call_audit_findings.txt,раздел6. */
|
||||
* init/destroy сериализованы lifecycle ядра; producers остановлены перед освобождением inst/ua.
|
||||
* Лимит8 TX-задач относится к текущей привязке. Старые задачи не отправляют медиа и не меняют
|
||||
* её счётчик/позднюю статистику; uasync освобождает их после callback либо при destroy. 0 = успех, -1 = нет inst. */
|
||||
int call_audio_init(struct UTUN_INSTANCE* inst);
|
||||
/* Снять регистрацию и инвалидировать TX-привязку; codec/jitter освобождаются отдельно через stop/release. */
|
||||
void call_audio_destroy(struct UTUN_INSTANCE* inst);
|
||||
|
||||
/* GUI-поток: создать codec + jitter + tones. Старый API принимает raw PCM и использует core AEC.
|
||||
|
||||
Reference in new issue
Block a user