Trunk Recorder plugin that captures raw IMBE/AMBE voice codec frames and saves them as .dvcf files and/or publishes them over MQTT.
This is the companion plugin to IMBE-ASR — it provides the raw codec data that IMBE-ASR uses to transcribe P25 radio calls without audio reconstruction.
Standard ASR pipelines for P25 radio work like this:
codec params → reconstruct audio (libimbe) → mel spectrogram → ASR
Every step is lossy. IMBE-ASR skips all of it by reading the codec parameters directly. But to do that, you need the pre-vocoder codec frames — which trunk-recorder normally discards after audio synthesis.
This plugin taps trunk-recorder's voice_codec_data() callback to capture the raw frames before they're consumed by the vocoder.
- File writing — saves
.dvcfsidecar files alongside audio recordings (same base name,.dvcfextension) - MQTT publishing — publishes codec frames as
audio_dvcf_base64in the MQTT call-end message, compatible with tr-engine - Analog-safe — only digital calls (P25, DMR, D-STAR, YSF) produce
.dvcffiles; analog calls are ignored automatically - Stale-call reaper — detects calls that never receive a
call_end(a known trunk-recorder bug) and salvages their data instead of leaking resources - Both features independently toggleable via config
.dvcf = Digital Voice Codec Frames. SymbolStream v2 binary format — see DVCF_SPEC.md for the full specification.
Files are a concatenation of binary messages (8-byte SSSP headers + codec frame payloads) representing a single call. They can be:
- Played back for offline transcription
- Streamed directly to a TCP consumer
- Uploaded to the IMBE-ASR server via HTTP
- Trunk Recorder with
voice_codec_data()plugin API (merged in TrunkRecorder/trunk-recorder#1098) - libmosquitto / Paho MQTT C++ (for MQTT publishing)
- Boost (already required by trunk-recorder)
Builds as a user_plugins drop-in — no fork of trunk-recorder required. Requires trunk-recorder with voice_codec_data() plugin API support (v5.0+).
# 1. Clone trunk-recorder
git clone https://github.com/TrunkRecorder/trunk-recorder.git
cd trunk-recorder
# 2. Drop this plugin into user_plugins/
mkdir -p user_plugins
git clone https://github.com/trunk-reporter/tr-plugin-dvcf user_plugins/mqtt_dvcf
# 3. Build with local plugins enabled
cmake -B build -DUSE_LOCAL_PLUGINS=ON
cmake --build build -j$(nproc)
# 4. Install
sudo cmake --install buildThe compiled libmqtt_dvcf.so will be installed to /usr/local/lib/trunk-recorder/ (or your configured prefix).
- Paho MQTT C++ library (
libpaho-mqttpp3-dev) - Paho MQTT C library (
libpaho-mqtt3as-dev) - Boost (already required by trunk-recorder)
# Ubuntu/Debian
sudo apt-get install libpaho-mqtt3as-dev libpaho-mqttpp3-devAdd to your trunk-recorder config.json:
{
"plugins": [
{
"name": "mqtt_dvcf",
"library": "libmqtt_dvcf",
"write_enabled": true,
"mqtt_enabled": true,
"broker": "tcp://localhost:1883",
"topic": "tr/feeds",
"clientid": "dvcf-plugin",
"username": "",
"password": "",
"qos": 0,
"stale_call_timeout_sec": 300
}
]
}| Option | Default | Description |
|---|---|---|
write_enabled |
true |
Write .dvcf sidecar files to disk |
mqtt_enabled |
false |
Publish codec frames over MQTT |
broker |
tcp://localhost:1883 |
MQTT broker URL |
topic |
trunk-recorder |
MQTT topic prefix (publishes to {topic}/dvcf) |
clientid |
dvcf-handler |
MQTT client ID |
username |
"" |
MQTT username (optional) |
password |
"" |
MQTT password (optional) |
qos |
0 |
MQTT QoS level |
stale_call_timeout_sec |
300 |
Seconds of inactivity before a call without call_end is reaped. Data is salvaged to .stale files when possible. |
When mqtt_enabled: true, the plugin publishes a JSON message on {topic}/dvcf containing the base64-encoded .dvcf content and call metadata:
{
"audio_dvcf_base64": "<base64-encoded .dvcf content>",
"metadata": {
"talkgroup": 9170,
"talkgroup_tag": "Fire Dispatch",
"talkgroup_alpha_tag": "BUTCO FD",
"talkgroup_group": "Fire",
"freq": 855737500,
"start_time": 1711234567,
"stop_time": 1711234590,
"call_length": 23,
"signal": -42.5,
"noise": -110.2,
"freq_error": 12,
"spike_count": 0,
"emergency": false,
"priority": 3,
"phase2_tdma": false,
"tdma_slot": 0,
"short_name": "butco",
"filename": "9170-1711234567_855737500.wav",
"srcList": [{"src": 1234567, "time": 1711234567, "pos": 0.0, "emergency": 0, "signal_system": "", "tag": "Engine 5"}]
}
}tr-engine recognizes the audio_dvcf_base64 field and routes the call to the IMBE-ASR transcription provider.
trunk-recorder + mqtt_dvcf plugin
→ MQTT (audio_dvcf_base64)
→ tr-engine (STT_PROVIDER=imbe)
→ imbe-asr server
→ transcript stored in database
For live streaming (lower latency), see symbolstream which streams frames in real time over TCP/UDP.
- IMBE-ASR — ASR model that reads .dvcf files directly
- tr-engine — backend that ingests MQTT and calls IMBE-ASR
- symbolstream — real-time codec frame streaming plugin