This guide covers cloning the project from GitLab (develop), building and installing the FreeSWITCH module, configuring the relay, starting services, and verifying the setup.
Working layout after setup
|
Item |
Path |
|---|---|
|
Relay app |
|
|
Module |
|
|
Speech libs |
|
|
Config |
|
|
Service |
|
How the pieces fit
|
Part |
Where it runs |
|---|---|
|
FreeSWITCH module ( |
Inside FreeSWITCH. It attaches to call audio. |
|
Microsoft Speech SDK |
Also inside FreeSWITCH (libs under |
|
Relay ( |
Separate systemd service. Starts and stops transcription on bridge or hangup, and posts text to CCM. Light load. |
Transcription starts when the call is bridged, not on early media.
Prerequisites
-
SSH access (root or sudo) on the FreeSWITCH server
-
FreeSWITCH running (
fs_cliworks) -
GitLab access:
https://gitlab.expertflow.com/rtc/media-bug-for-transcription -
Packages:
git,python3,python3-venv,python3-pip,gcc,g++,pkg-config,curl,taryou can use the below command -
apt-get update && apt-get install -y git python3 python3-venv python3-pip gcc g++ pkg-config curl tar -
Now please verify as well
-
dpkg-query -W -f='${Package}: ${Status}\n' git python3 python3-venv python3-pip gcc g++ pkg-config curl tar 2>&1 -
FreeSWITCH headers available on the server
pkg-config --cflags freeswitch
pkg-config --libs freeswitch
You will need for config: Azure Speech key, region (or endpoint), language, ESL password, CCM settings, and tenant domain names.
Step 1 — Clone the repo (develop)
The module source (.c / .cpp) is already in the repo under vendor/drachtio-freeswitch-modules/modules/mod_azure_transcribe/. A normal git clone of develop brings it. There is no separate module download.
The compiled .so and Microsoft Speech SDK binaries are not in Git. You still build those on the FreeSWITCH host (next steps), or copy a matching .so and lib/ from another host with the same FreeSWITCH architecture.
cd /root
git clone https://gitlab.expertflow.com/rtc/media-bug-for-transcription.git transcription_media_bug
cd /root/transcription_media_bug
git checkout develop
git pull origin develop
Confirm source is present:
pwd
ls tmb_app_azure.py scripts systemd relay .env.azure.example
ls vendor/drachtio-freeswitch-modules/modules/mod_azure_transcribe
You should see files such as mod_azure_transcribe.c, azure_transcribe_glue.cpp, and header files. If that folder is missing, you are on the wrong branch or an incomplete clone. Stop and fix before building.
Step 2 — Build and install the FreeSWITCH module
Build on an x86_64 Linux FreeSWITCH host (Speech SDK path lib/x64 below). FreeSWITCH development headers must be installed (pkg-config --cflags freeswitch).
2.1 Download the Microsoft Speech SDK
cd /root/transcription_media_bug
mkdir -p deps
curl -fsSL -L -o /tmp/SpeechSDK-Linux.tar.gz "https://aka.ms/csspeech/linuxbinary"
rm -rf deps/speechsdk
mkdir -p deps/speechsdk
tar -xzf /tmp/SpeechSDK-Linux.tar.gz -C deps/speechsdk --strip-components=1
ls deps/speechsdk/lib/x64 | head
If lib/x64 is missing, list deps/speechsdk/lib/ and use the architecture folder that exists on this host.
2.2 Compile
cd /root/transcription_media_bug
ROOT=/root/transcription_media_bug
SAFE=/var/lib/freeswitch/tmb
SDK_ROOT=$ROOT/deps/speechsdk
SRC=$ROOT/vendor/drachtio-freeswitch-modules/modules/mod_azure_transcribe
APP=$ROOT/build/azure/src
OUT=$ROOT/build/azure/out
OBJ=$ROOT/build/azure/obj
mkdir -p "$OUT" "$OBJ" "$SAFE/lib"
rm -rf "$APP"
mkdir -p "$APP"
cp -a "$SRC"/. "$APP"/
INC_CXX=$(find "$SDK_ROOT" -type d -name cxx_api | head -1)
INC_C=$(find "$SDK_ROOT" -type d -name c_api | head -1)
LIBDIR=$SDK_ROOT/lib/x64
FS_CFLAGS=$(pkg-config --cflags freeswitch)
FS_LIBS=$(pkg-config --libs freeswitch)
test -n "$INC_CXX" && test -n "$INC_C" && test -d "$LIBDIR"
test -f "$APP/mod_azure_transcribe.c"
test -f "$APP/azure_transcribe_glue.cpp"
gcc -fPIC -g -O2 $FS_CFLAGS -I"$APP" \
-c "$APP/mod_azure_transcribe.c" -o "$OBJ/mod_azure_transcribe.o"
g++ -fPIC -g -O2 -std=c++14 $FS_CFLAGS -I"$APP" -I"$INC_CXX" -I"$INC_C" \
-c "$APP/azure_transcribe_glue.cpp" -o "$OBJ/azure_transcribe_glue.o"
g++ -shared -o "$OUT/mod_azure_transcribe.so" \
"$OBJ/mod_azure_transcribe.o" "$OBJ/azure_transcribe_glue.o" \
-Wl,-rpath,"$SAFE/lib" -L"$LIBDIR" -lMicrosoft.CognitiveServices.Speech.core \
$FS_LIBS -lpthread -lcrypto -lssl -lz
2.3 Install module and libs
cp -a "$LIBDIR"/. "$SAFE/lib/"
cp -a "$OUT/mod_azure_transcribe.so" "$SAFE/mod_azure_transcribe.so"
# FreeSWITCH user is often www-data — confirm with: ps aux | grep freeswitch
chown -R www-data:www-data "$SAFE"
chmod 755 "$SAFE/mod_azure_transcribe.so"
Check:
ls /var/lib/freeswitch/tmb/mod_azure_transcribe.so
ls /var/lib/freeswitch/tmb/lib | head
ldd /var/lib/freeswitch/tmb/mod_azure_transcribe.so
You should see mod_azure_transcribe.so, Speech libraries such as libMicrosoft.CognitiveServices.Speech.core.so, and ldd should not show those libs as missing.
Keep Speech libs only under /var/lib/freeswitch/tmb/lib/ (not under /usr/lib).
2.4 Load the module now
Use this server’s ESL password (replace YOUR_ESL_PASSWORD):
ESL_PW='YOUR_ESL_PASSWORD'
fs_cli -p "$ESL_PW" -x "unload mod_azure_transcribe" >/dev/null 2>&1 || true
fs_cli -p "$ESL_PW" -x "load /var/lib/freeswitch/tmb/mod_azure_transcribe.so"
fs_cli -p "$ESL_PW" -x "module_exists mod_azure_transcribe"
Must print true.
2.5 Autoload after FreeSWITCH restarts
Do this after .env.azure has the correct ESL_PASSWORD (Step 4), or export the password first:
cd /root/transcription_media_bug
# if .env.azure is not ready yet:
# export ESL_PASSWORD='YOUR_ESL_PASSWORD'
bash scripts/install_azure_autoload.sh
This installs a FreeSWITCH systemd drop-in so the module loads on every FreeSWITCH start (custom path plus library path for Speech libs).
Step 3 — Python venv
cd /root/transcription_media_bug
apt-get update
apt-get install -y python3-venv python3-pip
python3 -m venv venv
./venv/bin/pip install -U pip
./venv/bin/pip install -r relay/requirements.txt
./venv/bin/pip install aiohttp
./venv/bin/python -m py_compile tmb_app_azure.py
Step 4 — Configure .env.azure
cd /root/transcription_media_bug
cp -n .env.azure.example .env.azure
chmod 600 .env.azure
nano .env.azure
|
Key |
What to set |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FreeSWITCH |
|
|
Optional inbound DID filter inside those tenants. Prefer |
|
|
|
|
|
|
|
|
|
|
|
Start small (for example |
|
|
|
|
|
|
|
|
|
|
|
ESL password for this FreeSWITCH |
|
|
Azure Speech key |
|
|
Example: |
|
|
Matching endpoint if required |
|
|
Example: |
|
|
Example: |
|
|
Fallback when domain is missing |
|
|
|
|
|
Leave blank. Outbound service id comes from the call header |
How control works
-
Tenants →
TMB_DOMAIN_ALLOWLIST -
Call types →
TMB_ENABLE_* -
Optional inbound DIDs →
TMB_ALLOWLIST -
CCM host → FS
domain_name+CCM_ROOT_DOMAIN
Example:
TMB_MODE=active
TMB_DOMAIN_ALLOWLIST=thirdparty
TMB_ALLOWLIST=*
TMB_ENABLE_INBOUND=true
TMB_ENABLE_MANUAL_OUTBOUND=true
TMB_ENABLE_CAMPAIGN=true
CCM_ROOT_DOMAIN=intelicx.ai
CCM_BASE_URL=https://thirdparty.intelicx.ai/ccm
Check without printing secrets:
grep -E '^(TMB_MODE|TMB_DOMAIN_ALLOWLIST|TMB_ALLOWLIST|TMB_ROOT|ESL_HOST|ESL_PORT|AZURE_REGION|AZURE_LANG|CCM_ROOT_DOMAIN|CCM_BASE_URL)=' .env.azure
grep -q '^AZURE_SUBSCRIPTION_KEY=.\+' .env.azure && echo KEY=PRESENT || echo KEY=MISSING
grep -q '^ESL_PASSWORD=.\+' .env.azure && echo ESL_PASSWORD=PRESENT || echo ESL_PASSWORD=MISSING
If you have not run Step 2.5 yet, install module autoload now:
cd /root/transcription_media_bug
bash scripts/install_azure_autoload.sh
Step 5 — Install and start the relay
cd /root/transcription_media_bug
cp systemd/tmb-relay-azure.service /etc/systemd/system/
systemctl daemon-reload
systemctl enable tmb-relay-azure
systemctl restart tmb-relay-azure
systemctl status tmb-relay-azure --no-pager
journalctl -u tmb-relay-azure -n 40 --no-pager
Must be active (running).
Step 6 — Go live and test
-
Set
TMB_MODE=activein.env.azure -
Restart the relay
-
Make a bridged call on an allowed tenant and speak
-
Watch the logs
systemctl restart tmb-relay-azure
journalctl -u tmb-relay-azure -f
You should see BRIDGE, start tx, FINAL, and CCM ok. Text should show on the agent desk on the same conversation as the call (not a second inbound topic).
Outbound CCM fields
|
Field |
Source |
|---|---|
|
Conversation id |
|
|
Service identifier |
|
|
Customer identifier |
Customer number on the call |
Inbound
|
Field |
Source |
|---|---|
|
Conversation id |
Channel / Voice Connector conversation id when present |
|
Service identifier |
Inbound service DN / headers |
|
Customer identifier |
Real caller ANI |
Final health check
cd /root/transcription_media_bug
ls tmb_app_azure.py
ls /var/lib/freeswitch/tmb/mod_azure_transcribe.so
ls /var/lib/freeswitch/tmb/lib | head
systemctl is-active tmb-relay-azure
fs_cli -x "module_exists mod_azure_transcribe"
Expected: app present, module and libs present, relay active, module true.
Useful commands
systemctl status tmb-relay-azure
journalctl -u tmb-relay-azure -f
systemctl restart tmb-relay-azure
touch /root/transcription_media_bug/DISABLE.azure && systemctl restart tmb-relay-azure
rm -f /root/transcription_media_bug/DISABLE.azure && systemctl restart tmb-relay-azure
fs_cli -x "module_exists mod_azure_transcribe"
fs_cli -x "unload mod_azure_transcribe"
fs_cli -x "load /var/lib/freeswitch/tmb/mod_azure_transcribe.so"
After a relay-only git pull: restart tmb-relay-azure.
After a module rebuild: copy .so and libs, then unload and load the module.
Capacity
dual_mono uses two Azure streams per bridged call. Keep TMB_MAX_CONCURRENT in line with Azure quota and server CPU/RAM.
Things to avoid
-
Do not put Speech libs under
/usr/libor/usr/local/lib -
Do not run
make installon FreeSWITCH only for this module -
Do not run Deepgram and Azure relays on the same traffic together
-
Do not commit Azure keys or ESL passwords to Git
-
Do not set
CCM_OUTBOUND_SERVICE_IDENTIFIERto an inbound Branch DN
Checklist
-
Cloned
develop; module source present undervendor/.../mod_azure_transcribe/ -
Speech SDK downloaded; module built (
.sois not in Git) -
Module
.soandlib/under/var/lib/freeswitch/tmb/;lddOK -
module_exists= true; autoload installed with the correct ESL password -
venv ready;
.env.azurefilled (domain allowlist, CCM root, Azure, ESL; outbound SI env blank) -
tmb-relay-azureactive -
Bridged test call shows
FINALand desk text on one conversation