Загрузка данных


#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);
        }
    }
}