modaudiostream

January 26, 2026 ยท View on GitHub

Production-grade WebSocket audio streaming for FreeSWITCH.

Streams real-time audio between FreeSWITCH and external systems with correct lifecycle management, thread safety and predictable memory usage.

Update (22/2/2025)

:rocket: Introducing Bi-Directional Streaming with Automatic Playback

A new version, mod_audio_stream v1.0.3, has been published, featuring raw binary audio streaming over WebSocket. It can be downloaded from the Releases section and is available as a pre-built Debian 12 package.

The playback feature allows continuous forward streaming while playback runs independently, enabling full-duplex audio between the caller and the WebSocket endpoint.

Key features:

  • Full-duplex audio streaming (caller โ†” WebSocket)
  • Supports both base64-encoded and raw binary audio
  • Playback can be tracked, paused, and resumed dynamically

๐Ÿ”น This release is a commercial product, available for free use (including commercial use) with a limitation of 10 concurrent streaming channels. For users requiring more than 10 channels, or access to the source code, please contact us for licensing options.

Why the Commercial Edition Exists

The community edition of mod_audio_stream provides production-ready, uni-directional WebSocket audio streaming for ASR and real-time audio processing use cases. The commercial edition exists because real-world telephony systems require solving several non-trivial engineering problems that only appear under real concurrency and production load, such as:

  • correct FreeSWITCH session lifecycle management
  • thread-safe audio injection and shutdown
  • safe reconnection and cleanup under load
  • bounded and predictable memory usage
  • correct interaction with record_session / uuid_record

The commercial edition is designed and tested for high-concurrency environments (thousands of simultaneous calls, 5000+), where correctness, stability and resource usage are critical.

About

  • The purpose of mod_audio_stream was to provide a simple, low-dependency yet effective module for streaming audio and receiving responses from a websocket server.
  • Introduced libwsc, our in-house, RFC-6455 compliant websocket client developed specifically for mod_audio_stream.
    • Replaces ixwebsocket, which served us well for the past few years. libwsc is libevent-based, extremely lightweight, and optimized for low-latency audio streaming.
  • This module was inspired by mod_audio_fork.

Installation

Dependencies

It requires libfreeswitch-dev, libssl-dev, zlib1g-dev, libevent-dev and libspeexdsp-dev on Debian/Ubuntu which are regular packages for Freeswitch installation.

Building

After cloning please execute: git submodule init and git submodule update to initialize the submodule.

Custom path

If you built FreeSWITCH from source, eq. install dir is /usr/local/freeswitch, add path to pkgconfig:

export PKG_CONFIG_PATH=/usr/local/freeswitch/lib/pkgconfig

To build the module, from the cloned repository:

mkdir build && cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
make
sudo make install

TLS is OFF by default. To build with TLS support add -DUSE_TLS=ON to cmake line.

DEB Package

To build DEB package after making the module:

cpack -G DEB

Debian package will be placed in root directory _packages folder.

Scripted Build & Installation

sudo apt-get -y install git \
    && cd /usr/src/ \
    && git clone https://github.com/amigniter/mod_audio_stream.git \
    && cd mod_audio_stream \
    && sudo bash ./build-mod-audio-stream.sh

Channel variables

The following channel variables can be used to fine tune websocket connection and also configure mod_audio_stream logging:

VariableDescriptionDefault
STREAM_MESSAGE_DEFLATEtrue or 1, disables per message deflateoff
STREAM_HEART_BEATnumber of seconds, interval to send the heart beatoff
STREAM_SUPPRESS_LOGtrue or 1, suppresses printing to logoff
STREAM_BUFFER_SIZEbuffer duration in milliseconds, divisible by 2020
STREAM_EXTRA_HEADERSJSON object for additional headers in string formatnone
STREAM_NO_RECONNECTtrue or 1, disables automatic websocket reconnectionoff
STREAM_TLS_CA_FILECA cert or bundle, or the special values SYSTEM or NONESYSTEM
STREAM_TLS_KEY_FILEoptional client key for WSS connectionsnone
STREAM_TLS_CERT_FILEoptional client cert for WSS connectionsnone
STREAM_TLS_DISABLE_HOSTNAME_VALIDATIONtrue or 1 disable hostname check in WSS connectionsfalse
  • Per message deflate compression option is enabled by default. It can lead to a very nice bandwidth savings. To disable it set the channel var to true|1.
  • Heart beat, sent every xx seconds when there is no traffic to make sure that load balancers do not kill an idle connection.
  • Suppress parameter is omitted by default(false). All the responses from websocket server will be printed to the log. Not to flood the log you can suppress it by setting the value to true|1. Events are fired still, it only affects printing to the log.
  • Buffer Size actually represents a duration of audio chunk sent to websocket. If you want to send e.g. 100ms audio packets to your ws endpoint you would set this variable to 100. If ommited, default packet size of 20ms will be sent as grabbed from the audio channel (which is default FreeSWITCH frame size)
  • Extra headers should be a JSON object with key-value pairs representing additional HTTP headers. Each key should be a header name, and its corresponding value should be a string.
    {
        "Header1": "Value1",
        "Header2": "Value2",
        "Header3": "Value3"
    }
    
  • Websocket automatic reconnection is on by default. To disable it set this channel variable to true or 1.
    • libwsc does not support automatic reconnection.
  • TLS (for WSS) options can be fine tuned with the STREAM_TLS_* channel variables:
    • STREAM_TLS_CA_FILE the ca certificate (or certificate bundle) file. By default is SYSTEM which means use the system defaults. Can be NONE which result in no peer verification.
    • STREAM_TLS_CERT_FILE optional client tls certificate file sent to the server.
    • STREAM_TLS_KEY_FILE optional client tls key file for the given certificate.
    • STREAM_TLS_DISABLE_HOSTNAME_VALIDATION if true, disables the check of the hostname against the peer server certificate. Defaults to false, which enforces hostname match with the peer certificate.

API

Commands

The freeswitch module exposes the following API commands:

uuid_audio_stream <uuid> start <wss-url> <mix-type> <sampling-rate> <metadata>

Attaches a media bug and starts streaming audio (in L16 format) to the websocket server. FS default is 8k. If sampling-rate is other than 8k it will be resampled.

  • uuid - Freeswitch channel unique id
  • wss-url - websocket url ws:// or wss://
  • mix-type - choice of
    • "mono" - single channel containing caller's audio
    • "mixed" - single channel containing both caller and callee audio
    • "stereo" - two channels with caller audio in one and callee audio in the other.
  • sampling-rate - choice of
    • "8k" = 8000 Hz sample rate will be generated
    • "16k" = 16000 Hz sample rate will be generated
  • metadata - (optional) a valid utf-8 text to send. It will be sent the first before audio streaming starts.
uuid_audio_stream <uuid> send_text <metadata>

Sends a text to the websocket server. Requires a valid utf-8 text.

uuid_audio_stream <uuid> stop <metadata>

Stops audio stream and closes websocket connection. If metadata is provided it will be sent before the connection is closed.

uuid_audio_stream <uuid> pause

Pauses audio stream

uuid_audio_stream <uuid> resume

Resumes audio stream

Events

Module will generate the following event types:

  • mod_audio_stream::json
  • mod_audio_stream::connect
  • mod_audio_stream::disconnect
  • mod_audio_stream::error
  • mod_audio_stream::play

response

Message received from websocket endpoint. Json expected, but it contains whatever the websocket server's response is.

Freeswitch event generated

Name: mod_audio_stream::json Body: WebSocket server response

connect

Successfully connected to websocket server.

Freeswitch event generated

Name: mod_audio_stream::connect Body: JSON

{
	"status": "connected"
}

disconnect

Disconnected from websocket server.

Freeswitch event generated

Name: mod_audio_stream::disconnect Body: JSON

{
	"status": "disconnected",
	"message": {
		"code": 1000,
		"reason": "Normal closure"
	}
}
  • code: <int>
  • reason: <string>

error

There is an error with the connection. Multiple fields will be available on the event to describe the error.

Freeswitch event generated

Name: mod_audio_stream::error Body: JSON

{
	"status": "error",
	"message": {
		"code": 1,
		"error": "String explaining the error"
	}
}
  • code: <int>
  • error: <string>

Possible code values

CodeEnum NameMeaning
1IOI/O error when reading/writing sockets
2INVALID_HEADERServer sent a malformed WebSocket header
3SERVER_MASKEDServer frames were masked (not allowed by spec)
4NOT_SUPPORTEDRequested feature (e.g. extension) not supported
5PING_TIMEOUTNo PONG received within timeout
6CONNECT_FAILEDTCP connection or DNS lookup failed
7TLS_INIT_FAILEDCouldn't initialize SSL/TLS context
8SSL_HANDSHAKE_FAILEDSSL/TLS handshake with server failed
9SSL_ERRORGeneric OpenSSL error (certificate, cipher, etc.)
10TIMEOUTTimeout
11PROTOCOLWebSocket protocol error

play

Name: mod_audio_stream::play Body: JSON

Websocket server may return JSON object containing base64 encoded audio to be played by the user. To use this feature, response must follow the format:

{
  "type": "streamAudio",
  "data": {
    "audioDataType": "raw",
    "sampleRate": 8000,
    "audioData": "base64 encoded audio"
  }
}
  • audioDataType: <raw|wav|mp3|ogg>

Event generated by the module (subclass: mod_audio_stream::play) will be the same as the data element with the file added to it representing filePath:

{
  "audioDataType": "raw",
  "sampleRate": 8000,
  "file": "/path/to/the/file"
}

If printing to the log is not suppressed, response printed to the console will look the same as the event. The original response containing base64 encoded audio is replaced because it can be quite huge.

All the files generated by this feature will reside at the temp directory and will be deleted when the session is closed.