Загрузка данных
#include <pjsua-lib/pjsua.h>
#include <pjsua-lib/pjsua_internal.h> // Обязательно для доступа к open_snd_dev и create_aud_param
/*
* Функция принудительно закрывает текущий звуковой порт и открывает его заново
* с точными параметрами частоты (clock_rate) и фрейма (samples_per_frame).
*/
static pj_status_t force_reopen_sound_device(unsigned clock_rate, unsigned channel_count, unsigned samples_per_frame)
{
pj_status_t status = PJ_SUCCESS;
printf("[AUDIO_REOPEN] Force closing old sound device...\n");
/* 1. Если звуковой порт уже открыт, корректно закрываем его */
if (pjsua_var.snd_port) {
pjmedia_snd_port_destroy(pjsua_var.snd_port);
pjsua_var.snd_port = NULL;
}
/* 2. Настраиваем параметры для нового звукового порта на основе параметров кодека */
if (pjsua_var.cap_dev != PJSUA_SND_NULL_DEV) {
pjmedia_snd_port_param param;
pjmedia_snd_port_param_default(¶m);
param.ec_options = pjsua_var.media_cfg.ec_options;
/* Заполняем параметры аудио-девайса под нужную частоту дискретизации */
status = create_aud_param(¶m.base,
pjsua_var.cap_dev,
pjsua_var.play_dev,
clock_rate,
channel_count,
samples_per_frame,
16, /* bits_per_sample (стандарт для PCM) */
PJ_FALSE);
if (status != PJ_SUCCESS) {
printf("[AUDIO_REOPEN] Error: create_aud_param failed with status %d\n", status);
return status;
}
param.options = 0;
printf("[AUDIO_REOPEN] Opening sound device: %u Hz, %u channels, SPF: %u\n",
clock_rate, channel_count, samples_per_frame);
/* 3. Вызываем низкоуровневое открытие аудио-устройства */
status = open_snd_dev(¶m);
if (status != PJ_SUCCESS) {
printf("[AUDIO_REOPEN] Error: open_snd_dev failed with status %d\n", status);
return status;
}
} else {
/* Если используется Null-audio бэкенд */
status = pjsua_set_snd_dev(pjsua_var.cap_dev, pjsua_var.play_dev);
}
return status;
}
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.strm.a.media_port;
if (media_port) {
/* Узнаем параметры, которые согласовал кодек (например, Speex 8000 или 16000) */
pjmedia_audio_format_detail *afd;
afd = pjmedia_format_get_audio_format_detail(&media_port->info.fmt, 1);
if (afd) {
unsigned codec_rate = afd->clock_rate;
unsigned codec_channels = afd->channel_count;
unsigned codec_spf = PJMEDIA_AFD_SPF(afd);
/* Получаем текущую конфигурацию Порта 0 (Звуковой карты) из моста */
pjmedia_conf_port_info port0_info;
if (pjmedia_conf_get_port_info(pjsua_var.mconf, 0, &port0_info) == PJ_SUCCESS) {
/* Если частота звуковой карты не совпадает с частотой кодека — переоткрываем аппаратуру */
if (port0_info.clock_rate != codec_rate) {
printf("[MEDIA_ALIGN] Rate mismatch! Port0=%u Hz, Codec=%u Hz. Forcing reopen...\n",
port0_info.clock_rate, codec_rate);
/*
* Также синхронизируем дефолтные переменные pjsua, чтобы мост при
* последующих вызовах connect знал новую целевую частоту.
*/
pjsua_var.media_cfg.snd_clock_rate = codec_rate;
pjsua_var.media_cfg.samples_per_frame = codec_spf;
/* Вызываем принудительный аппаратный перезапуск */
force_reopen_sound_device(codec_rate, codec_channels, codec_spf);
} else {
printf("[MEDIA_ALIGN] Rates already match: %u Hz. No action needed.\n", codec_rate);
}
}
}
}
}
#endif
// Выполняем коммутацию портов в мосту.
// Так как Port 0 только что переоткрылся (или уже соответствовал) нужной частоте кодека,
// внутренний механизм PJSIP свяжет их напрямую 1:1 без создания ресемплеров!
pjsua_conf_connect(ci.conf_slot, 0);
pjsua_conf_connect(0, ci.conf_slot);
}
}