这篇记录 AlmaLinux 9 上编译安装 FreeSWITCH 1.10.12 的脚本。

和 Debian 12 版本相比,这个脚本更像线上安装脚本:它会检查系统版本、启用 CRB/EPEL、
固定依赖源码版本、编译 mod_audio_fork,最后还会验证关键二进制和模块是否存在。

环境与目标

验证环境:

1
Linux localhost.localdomain 5.14.0-687.5.3.el9_8.x86_64 x86_64 GNU/Linux

安装目标:

1
2
3
4
FS_VERSION="v1.10.12"
INSTALL_PATH="/usr/local/freeswitch"
SRC_ROOT="/usr/src"
DEPS_ROOT="/usr/src/freeswitch-deps"

最终安装:

  1. FreeSWITCH 1.10.12
  2. libks
  3. sofia-sip
  4. spandsp
  5. signalwire-c
  6. mod_audio_fork
  7. mod_sofiamod_spandspmod_shoutmod_mariadb

安装脚本

新建 freeswitch_install.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#!/bin/bash
set -euo pipefail

FS_VERSION="v1.10.12"
INSTALL_PATH="/usr/local/freeswitch"
SRC_ROOT="/usr/src"
DEPS_ROOT="/usr/src/freeswitch-deps"
JOBS="${JOBS:-$(nproc)}"
PKG_CONFIG_PATH_VALUE="/usr/lib64/pkgconfig:/usr/lib/pkgconfig:/usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig"

log() { echo ">>> $*"; }
fail() { echo "ERROR: $*" >&2; exit 1; }

require_root() {
if [ "$(id -u)" -ne 0 ]; then
fail "请以 root 用户执行该脚本"
fi
}

ensure_almalinux9() {
if [ ! -r /etc/os-release ]; then
fail "无法读取 /etc/os-release"
fi

. /etc/os-release

if [ "${ID:-}" != "almalinux" ] || [[ "${VERSION_ID:-}" != 9* ]]; then
fail "该脚本仅支持 AlmaLinux 9,当前系统: ${PRETTY_NAME:-unknown}"
fi
}

install_packages() {
log "启用 AlmaLinux CRB 与 EPEL 仓库"
dnf -y install dnf-plugins-core epel-release
dnf config-manager --set-enabled crb
dnf -y makecache

log "安装 FreeSWITCH 与 mod_audio_fork 构建依赖"
dnf -y install \
autoconf automake bison boost-devel bzip2 cmake curl expat-devel \
flac-devel gcc gcc-c++ gdbm-devel git json-c-devel libatomic \
lame-devel ldns-devel libcurl-devel libdb-devel libedit-devel \
libjpeg-turbo-devel libogg-devel libshout-devel libsndfile-devel \
libtiff-devel libtool libuuid-devel libvorbis-devel libwebsockets-devel \
lua-devel make mariadb-connector-c-devel mpg123-devel nasm ncurses-devel \
openssl-devel opus-devel patch pcre-devel pcre2-devel perl \
pkgconf-pkg-config postgresql-devel python3-devel speex-devel \
speexdsp-devel sqlite-devel tar unixODBC-devel wget which yasm zlib-devel
}

clone_deps() {
log "准备依赖源码目录: ${DEPS_ROOT}"
rm -rf "${DEPS_ROOT}"
mkdir -p "${DEPS_ROOT}"
cd "${DEPS_ROOT}"

git clone --depth 1 --branch v2.0.10 https://github.com/signalwire/libks.git
git clone --depth 1 --branch v1.13.17 https://github.com/freeswitch/sofia-sip.git
git clone --depth 1 --branch fs https://github.com/freeswitch/spandsp.git
git clone --depth 1 --branch v2.0.5 https://github.com/signalwire/signalwire-c.git
git clone --depth 1 https://github.com/Wangijun/mod_audio_fork.git
}

build_libks() {
log "编译安装 libks"
cd "${DEPS_ROOT}/libks"
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_INSTALL_LIBDIR=lib64 \
-DWITH_LIBBACKTRACE=0
cmake --build build --parallel "${JOBS}"
touch build/copyright
cmake --install build
ldconfig
}

build_sofia() {
log "编译安装 Sofia-SIP"
cd "${DEPS_ROOT}/sofia-sip"
./bootstrap.sh
./configure --prefix=/usr --libdir=/usr/lib64
make -j"${JOBS}"
make install
ldconfig
}

build_spandsp() {
log "编译安装 SpanDSP"
cd "${DEPS_ROOT}/spandsp"
./bootstrap.sh
./configure --prefix=/usr --libdir=/usr/lib64
make -j"${JOBS}"
make install
ldconfig
}

build_signalwire_c() {
log "编译安装 signalwire-c"
cd "${DEPS_ROOT}/signalwire-c"
export PKG_CONFIG_PATH="${PKG_CONFIG_PATH_VALUE}"
cmake -S . -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/usr \
-DCMAKE_INSTALL_LIBDIR=lib64
cmake --build build --parallel "${JOBS}"
touch build/copyright
cmake --install build
ldconfig
}

clone_freeswitch() {
log "下载 FreeSWITCH ${FS_VERSION}"
cd "${SRC_ROOT}"
rm -rf freeswitch
git clone --depth 1 --branch "${FS_VERSION}" https://github.com/signalwire/freeswitch.git
}

enable_module_line() {
local module_line="$1"
local file="modules.conf"

if grep -qx "${module_line}" "${file}"; then
return
fi

if grep -qx "#${module_line}" "${file}"; then
sed -i "s|^#${module_line}$|${module_line}|" "${file}"
return
fi

echo "${module_line}" >> "${file}"
}

build_freeswitch() {
log "配置并编译 FreeSWITCH"
cd "${SRC_ROOT}/freeswitch"
./bootstrap.sh -j

sed -i '/^applications\/mod_av$/d' modules.conf
sed -i '1i#applications/mod_av' modules.conf

enable_module_line "endpoints/mod_sofia"
enable_module_line "applications/mod_spandsp"
enable_module_line "formats/mod_shout"
enable_module_line "databases/mod_mariadb"

export PKG_CONFIG_PATH="${PKG_CONFIG_PATH_VALUE}"
./configure --prefix="${INSTALL_PATH}" --enable-core-pgsql-support
make -j"${JOBS}"
make install
make cd-sounds-install
make cd-moh-install
}

install_user_and_links() {
log "配置用户、权限和命令链接"
ln -sf "${INSTALL_PATH}/bin/fs_cli" /usr/bin/fs_cli
ln -sf "${INSTALL_PATH}/bin/freeswitch" /usr/bin/freeswitch

if ! getent group freeswitch >/dev/null; then
groupadd freeswitch
fi

if ! getent passwd freeswitch >/dev/null; then
useradd -r -g freeswitch -d "${INSTALL_PATH}" -s /sbin/nologin freeswitch
fi

chown -R freeswitch:freeswitch "${INSTALL_PATH}"
chmod -R ug+rw "${INSTALL_PATH}"
echo "${INSTALL_PATH}/lib" > /etc/ld.so.conf.d/freeswitch.conf
ldconfig
}

build_mod_audio_fork() {
log "编译安装 mod_audio_fork"
cd "${DEPS_ROOT}/mod_audio_fork"
chmod +x build.sh

FREESWITCH_INCLUDE_DIR="${INSTALL_PATH}/include/freeswitch" \
FREESWITCH_LIBRARY="${INSTALL_PATH}/lib/libfreeswitch.so" \
FREESWITCH_MOD_DIR="${INSTALL_PATH}/lib/freeswitch/mod" \
BUILD_TYPE=Release \
./build.sh build

FREESWITCH_INCLUDE_DIR="${INSTALL_PATH}/include/freeswitch" \
FREESWITCH_LIBRARY="${INSTALL_PATH}/lib/libfreeswitch.so" \
FREESWITCH_MOD_DIR="${INSTALL_PATH}/lib/freeswitch/mod" \
BUILD_TYPE=Release \
./build.sh install

chown freeswitch:freeswitch "${INSTALL_PATH}/lib/freeswitch/mod/mod_audio_fork.so"
chmod 755 "${INSTALL_PATH}/lib/freeswitch/mod/mod_audio_fork.so"
}

enable_runtime_modules() {
local modules_xml="${INSTALL_PATH}/etc/freeswitch/autoload_configs/modules.conf.xml"

if [ ! -f "${modules_xml}" ]; then
log "未找到 ${modules_xml},跳过运行时模块配置"
return
fi

for module in mod_shout mod_mariadb mod_audio_fork; do
if grep -Eq "^[[:space:]]*<load module=\"${module}\"/>" "${modules_xml}"; then
continue
fi

if grep -Eq "^[[:space:]]*<!--[[:space:]]*<load module=\"${module}\"/>[[:space:]]*-->" "${modules_xml}"; then
sed -i "s|^[[:space:]]*<!--[[:space:]]*<load module=\"${module}\"/>[[:space:]]*-->.*| <load module=\"${module}\"/>|" "${modules_xml}"
else
sed -i "/<\/modules>/i\ <load module=\"${module}\"/>" "${modules_xml}"
fi
done

chown freeswitch:freeswitch "${modules_xml}"
}

verify_install() {
log "安装结果验证"
test -x "${INSTALL_PATH}/bin/freeswitch"
test -x "${INSTALL_PATH}/bin/fs_cli"
test -f "${INSTALL_PATH}/lib/freeswitch/mod/mod_sofia.so"
test -f "${INSTALL_PATH}/lib/freeswitch/mod/mod_spandsp.so"
test -f "${INSTALL_PATH}/lib/freeswitch/mod/mod_shout.so"
test -f "${INSTALL_PATH}/lib/freeswitch/mod/mod_mariadb.so"
test -f "${INSTALL_PATH}/lib/freeswitch/mod/mod_audio_fork.so"
"${INSTALL_PATH}/bin/freeswitch" -version

if ldd "${INSTALL_PATH}/lib/freeswitch/mod/mod_audio_fork.so" | grep -i 'not found'; then
fail "mod_audio_fork has unresolved shared libraries"
fi
}

main() {
require_root
ensure_almalinux9

install_packages
clone_deps
build_libks
build_sofia
build_spandsp
build_signalwire_c
clone_freeswitch
build_freeswitch
install_user_and_links
build_mod_audio_fork
enable_runtime_modules
verify_install

echo "==============================================================="
echo "FreeSWITCH ${FS_VERSION} 与 mod_audio_fork 安装成功"
echo "可用命令: freeswitch -ncwait, fs_cli"
echo "==============================================================="
}

main "$@"

执行:

1
2
chmod +x freeswitch_install.sh
./freeswitch_install.sh

关键设计

1. 只允许 AlmaLinux 9

脚本开头做了系统校验:

1
2
3
4
. /etc/os-release
if [ "${ID:-}" != "almalinux" ] || [[ "${VERSION_ID:-}" != 9* ]]; then
fail "该脚本仅支持 AlmaLinux 9"
fi

这个限制是有必要的。RHEL 系发行版之间包名、仓库、库目录都有差异。比如 AlmaLinux 9
需要启用 CRB,库目录也通常是 /usr/lib64

2. 固定源码依赖版本

脚本固定了这几个版本:

1
2
3
4
5
libks       v2.0.10
sofia-sip v1.13.17
spandsp fs 分支
signalwire-c v2.0.5
FreeSWITCH v1.10.12

FreeSWITCH 源码编译时,依赖版本漂移很容易造成 configure 失败、模块缺失或者运行时
动态库找不到。安装脚本要稳定,最好不要每次拉默认分支。

3. 明确 lib64

AlmaLinux 9 下,CMake 和 autotools 安装依赖时都显式指定 lib64

1
2
-DCMAKE_INSTALL_LIBDIR=lib64
./configure --prefix=/usr --libdir=/usr/lib64

同时设置:

1
PKG_CONFIG_PATH=/usr/lib64/pkgconfig:/usr/lib/pkgconfig:/usr/local/lib64/pkgconfig:/usr/local/lib/pkgconfig

否则 configure 阶段经常会出现库已经安装了,但是 pkg-config 找不到的情况。

4. 编译但禁用 mod_av

脚本里主动把 applications/mod_av 注释掉:

1
2
sed -i '/^applications\/mod_av$/d' modules.conf
sed -i '1i#applications/mod_av' modules.conf

mod_av 依赖 ffmpeg 相关库,在部分 EL9 环境里容易拉出额外依赖问题。如果当前业务主要是
SIP、RTP、ASR/TTS 音频桥接,不一定需要它。

5. 一起安装 mod_audio_fork

mod_audio_fork 是把 FreeSWITCH 通话音频通过 WebSocket 推给 ASR 服务的关键模块。
脚本把它作为安装流程的一部分:

1
2
3
4
5
FREESWITCH_INCLUDE_DIR="/usr/local/freeswitch/include/freeswitch" \
FREESWITCH_LIBRARY="/usr/local/freeswitch/lib/libfreeswitch.so" \
FREESWITCH_MOD_DIR="/usr/local/freeswitch/lib/freeswitch/mod" \
BUILD_TYPE=Release \
./build.sh build

然后安装到 FreeSWITCH 的模块目录:

1
2
3
./build.sh install
chown freeswitch:freeswitch /usr/local/freeswitch/lib/freeswitch/mod/mod_audio_fork.so
chmod 755 /usr/local/freeswitch/lib/freeswitch/mod/mod_audio_fork.so

脚本还会在 autoload_configs/modules.conf.xml 中确保以下模块加载:

1
2
3
<load module="mod_shout"/>
<load module="mod_mariadb"/>
<load module="mod_audio_fork"/>

启动前配置

配置目录:

1
/usr/local/freeswitch/etc/freeswitch

修改 vars.xml

1
2
3
4
<X-PRE-PROCESS cmd="set" data="default_password=请改成强密码"/>
<X-PRE-PROCESS cmd="set" data="bind_server_ip=$${domain}"/>
<X-PRE-PROCESS cmd="set" data="external_rtp_ip=$${domain}"/>
<X-PRE-PROCESS cmd="set" data="external_sip_ip=$${domain}"/>

如果使用 PostgreSQL,可以在 autoload_configs/switch.conf.xml 里设置:

1
2
<param name="core-db-dsn"
value="pgsql://hostaddr=数据库IP dbname=freeswitch user=用户名 password=密码 options='-c statement_timeout=3000' connect_timeout=5 keepalives=1 keepalives_idle=30 keepalives_interval=5 keepalives_count=3" />

sip_profiles/internal.xmlexternal.xml 中设置:

1
2
<param name="odbc-dsn"
value="pgsql://hostaddr=数据库IP dbname=freeswitch user=用户名 password=密码 options='-c statement_timeout=3000' connect_timeout=5 keepalives=1 keepalives_idle=30 keepalives_interval=5 keepalives_count=3" />

验证

脚本最后已经做了一轮基础验证:

1
2
3
4
5
6
7
8
9
test -x /usr/local/freeswitch/bin/freeswitch
test -x /usr/local/freeswitch/bin/fs_cli
test -f /usr/local/freeswitch/lib/freeswitch/mod/mod_sofia.so
test -f /usr/local/freeswitch/lib/freeswitch/mod/mod_spandsp.so
test -f /usr/local/freeswitch/lib/freeswitch/mod/mod_shout.so
test -f /usr/local/freeswitch/lib/freeswitch/mod/mod_mariadb.so
test -f /usr/local/freeswitch/lib/freeswitch/mod/mod_audio_fork.so
/usr/local/freeswitch/bin/freeswitch -version
ldd /usr/local/freeswitch/lib/freeswitch/mod/mod_audio_fork.so

安装后启动:

1
2
freeswitch -ncwait
fs_cli

fs_cli 中检查:

1
2
3
4
5
version
status
sofia status
module_exists mod_audio_fork
module_exists mod_spandsp

线上注意事项

  1. mod_audio_fork 依赖的共享库必须全部能被 ldd 找到,否则 FreeSWITCH 启动时会加载失败。
  2. mod_audio_fork 只是媒体桥接模块,ASR WebSocket 服务的地址、子协议、鉴权仍需要在业务侧和
    dialplan 中配置好。
  3. ESL 8021、SIP 5060、RTP 端口不要无脑开放公网。SIP 可以按线路来源白名单开放,ESL 应只允许
    内网业务服务器访问。
  4. modules.conf 后要重新编译;改 modules.conf.xml 后通常 reloadxml 或重启模块即可。
  5. 修改 profile、ACL、NAT IP 后,建议执行:
1
2
3
fs_cli -x reloadxml
fs_cli -x "sofia profile internal restart"
fs_cli -x "sofia profile external restart"

重启 profile 会影响当前 SIP 注册和通话,线上要安排窗口。