/* Структура для передачи параметров в таймер */
struct reinvite_ctx {
pjsua_call_id call_id;
};
/* Callback, который безопасно выполнится в контексте главного потока PJSIP */
static void async_reinvite_cb(pj_timer_heap_t *timer_heap, struct pj_timer_entry *entry)
{
struct reinvite_ctx *ctx = (struct reinvite_ctx *) entry->user_data;
printf("[MEDIA_ALIGN] Executing delayed pjsua_call_reinvite for Call %d...\n", ctx->call_id);
// Безопасно инициируем пересоздание медиа-стримов
pjsua_call_reinvite(ctx->call_id, PJ_TRUE, NULL);
// Освобождаем память, выделенную под контекст
pj_pool_t *pool = pjsua_get_pool(); // Берём глобальный или любой временный пул
// Так как это куча PJSIP, если выделили через malloc, используйте free().
// Ниже мы выделим через malloc для простоты, чтобы не трогать пулы.
free(ctx);
free(entry);
}
/* Callback called by the library when call's media state has changed */
static void on_call_media_state(pjsua_call_id call_id)
{
pjsua_call_info ci;
pjsua_call_get_info(call_id, &ci);
if (ci.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
#ifdef USE_DYNAMIC_AUDIO_RATE
pjsua_call_media_info *media = &ci.media;
if (media->type == PJMEDIA_TYPE_AUDIO) {
pjmedia_port *media_port = NULL;
media_port = pjsua_var.calls[call_id].media[0].strm.a.media_port;
if (!media_port) {
printf("[MEDIA] Error: Failed to get call media port\n");
return;
}
unsigned negotiated_rate = media_port->info.fmt.det.aud.clock_rate;
unsigned current_bridge_rate = pjsua_var.mconf->clock_rate; // Текущий рейт моста
printf("Detected sound codec rate=%u Hz (Current Bridge=%u Hz)\n", negotiated_rate, current_bridge_rate);
// Проверяем: если рейт кодека отличается от рейта, под который создавались стримы
if (negotiated_rate != current_bridge_rate) {
printf("[MEDIA_ALIGN] Rate mismatch! Scheduling async pjsua_call_reinvite...\n");
// Выделяем память под структуру задачи
struct reinvite_ctx *ctx = malloc(sizeof(struct reinvite_ctx));
pj_timer_entry *timer_entry = malloc(sizeof(pj_timer_entry));
if (ctx && timer_entry) {
ctx->call_id = call_id;
pj_timer_entry_init(timer_entry, 0, ctx, &async_reinvite_cb);
// Задаем минимальную задержку (например, 10 мс), чтобы дать текущему
// обработчику on_call_media_state спокойно завершиться и разблокировать мьютексы
pj_time_val delay = {0, 10};
pjsua_schedule_timer(timer_entry, &delay);
} else {
printf("[MEDIA_ALIGN] Error: OOM allocation for reinvite task failed\n");
}
} else {
pjsua_var.media_cfg.snd_clock_rate = negotiated_rate;
printf("Rates match. Set snd_clock_rate=%u\n", pjsua_var.media_cfg.snd_clock_rate);
}
}
#endif
// When media is active, connect call to sound device.
pjsua_conf_connect(ci.conf_slot, 0);
pjsua_conf_connect(0, ci.conf_slot);
}
}