Загрузка данных
#include <pjsua-lib/pjsua.h>
#include <zephyr_i2s_dev.h>
// Глобальные трекеры состояния
static unsigned current_bridge_rate = 16000; // Начальная дефолтная частота системы
static pj_bool_t is_ringing = PJ_FALSE; // Флаг активного рингтона
/* Вспомогательный метод для безопасного запуска рингера */
void app_start_ringer(const char *wav_file_path)
{
pj_status_t status;
// Рингер всегда инициализируется на текущей системной частоте
printf("Starting ringer at %u Hz for file: %s\n", current_bridge_rate, wav_file_path);
// Если рингер уже играет, на всякий случай тушим
if (is_ringing) {
pjsua_ring_stop();
is_ringing = PJ_FALSE;
}
// Включаем рингер встроенными средствами PJSUA
// (Или вашей кастомной функцией воспроизведения wav)
status = pjsua_ring_start(pj_str((char*)wav_file_path), PJ_TRUE);
if (status == PJ_SUCCESS) {
is_ringing = PJ_TRUE;
} else {
printf("Error: Failed to start ringer: %d\n", status);
}
}
/* Вспомогательный метод для безопасной остановки рингера */
void app_stop_ringer(void)
{
if (is_ringing) {
printf("Stopping ringer explicitly before media switch...\n");
pjsua_ring_stop();
is_ringing = PJ_FALSE;
}
}
/* КЛЮЧЕВОЙ КОЛЛБЕК: Вызывается PJSIP при согласовании SDP (изменении медиа) */
static void on_call_media_state(pjsua_call_id call_id)
{
pjsua_call_info call_info;
pjsua_call_get_info(call_id, &call_info);
// Обрабатываем только переход медиа в активную фазу разговора
if (call_info.media_status == PJSUA_CALL_MEDIA_ACTIVE) {
pjsua_call_media_info *media = &call_info.media;
if (media->type == PJMEDIA_TYPE_AUDIO) {
// 1. Извлекаем реальную частоту кодека, согласованную для данного звонка
unsigned negotiated_codec_rate = media->stream.audio->remote_codec_param->setting.info.clock_rate;
printf("\n============ MEDIA CHANNELS ACTIVE ============\n");
printf("Negotiated Codec Sample Rate: %u Hz\n", negotiated_codec_rate);
printf("Current Active Bridge Rate : %u Hz\n", current_bridge_rate);
printf("===============================================\n");
// 2. ОБЯЗАТЕЛЬНО останавливаем рингер ДО манипуляций с железом
app_stop_ringer();
// 3. Проверяем, совпадает ли частота кодека с текущей частотой моста и I2S
if (negotiated_codec_rate != current_bridge_rate) {
printf("Rate mismatch detected! Initiating complete media sub-system teardown...\n");
// Шаг А: Принудительно гасим низкоуровневые DMA-потоки Zephyr
zephyr_i2s_stop(1); // Остановка RX
zephyr_i2s_stop(0); // Остановка TX
k_msleep(10); // Даем DMA аппаратно завершить транзакции
// Шаг Б: Полностью уничтожаем медиа-мост PJSIP (освобождаем старые буферы)
printf("Destroying pjsua media bridge...\n");
pjsua_media_destroy();
// Шаг В: Перепрограммируем тактовое дерево i.MX RT1024, кодек и размеры сдвигов буферов
printf("Reconfiguring MCUX SAI and Audio Codec hardware clocks...\n");
int ret = zephyr_i2s_set_rate(negotiated_codec_rate);
if (ret < 0) {
printf("CRITICAL ERROR: Hardware failed to transition to %u Hz! Retrying init at old rate...\n", negotiated_codec_rate);
// Аварийный откат, если железо не смогло встать на выбранную частоту
zephyr_i2s_set_rate(current_bridge_rate);
} else {
// Если железо успешно перестроилось, обновляем наш глобальный трекер частоты
current_bridge_rate = negotiated_codec_rate;
}
// Шаг Г: Конфигурируем и поднимаем НОВЫЙ медиа-мост PJSIP на "родной" частоте
printf("Re-initializing pjsua media with native clock_rate=%u\n", current_bridge_rate);
pjsua_media_config media_cfg;
pjsua_media_config_default(&media_cfg);
// Мост и звуковая карта теперь жестко завязаны на частоту кодека 1:1
media_cfg.clock_rate = current_bridge_rate;
media_cfg.snd_clock_rate = current_bridge_rate;
pj_status_t status = pjsua_media_init(&media_cfg);
if (status != PJ_SUCCESS) {
printf("CRITICAL ERROR: pjsua_media_init failed: %d\n", status);
return;
}
// Перезапрашиваем call_info, так как внутренние медиа-порты PJSIP пересоздались
pjsua_call_get_info(call_id, &call_info);
}
// 4. Подключаем звонок к конференц-мосту (теперь буферы и частоты совпадают байт-в-байт)
pjsua_conf_port_id conf_slot = pjsua_call_get_conf_port_id(call_id);
pjsua_conf_connect(conf_slot, 0); // Направляем поток из сети в динамик (0)
pjsua_conf_connect(0, conf_slot); // Направляем поток с микрофона (0) в сеть
printf("SUCCESS: Call media bridged natively at %u Hz without any software resampling!\n\n", current_bridge_rate);
}
}
// Если звонок завершился (DISCONNECTED), возвращаем частоту по умолчанию для рингера будущих звонков
else if (call_info.state == PJSUA_CALL_STATE_DISCONNECTED) {
printf("Call disconnected. Returning hardware to idle rate (16000 Hz) for next ringer...\n");
app_stop_ringer();
if (current_bridge_rate != 16000) {
zephyr_i2s_stop(1);
zephyr_i2s_stop(0);
pjsua_media_destroy();
zephyr_i2s_set_rate(16000);
current_bridge_rate = 16000;
pjsua_media_config media_cfg;
pjsua_media_config_default(&media_cfg);
media_cfg.clock_rate = 16000;
media_cfg.snd_clock_rate = 16000;
pjsua_media_init(&media_cfg);
}
}
}