Docs Decode G.729 audio

Decode G.729 audio

Install the G.729 codebook tables sipnab does not ship, on a host with internet access or without it, check the installed tables, and what a G.729 stream looks like in an export when they are missing.

On this page

sipnab decodes G.729 audio (Annex A, with Annex B comfort noise) into the same outputs as G.711, G.722 and Opus:

  • WAV exports;
  • the recording in a vCon;
  • the live audio route;
  • the dead-air and clipping measurement.

It needs one file per host first: the decoder’s codebook tables. sipnab builds that file from the ITU-T G.729 software package on your host. This page says how to install it, and why sipnab does not ship it.

Install the tables

Run this once on each host that decodes G.729:

sipnab g729 install

The command:

  1. prints where the tables come from, the package’s sha256 and its copyright statements, then asks Install the G.729 tables? [y/N];
  2. downloads the ITU-T G.729 (06/2012) software package, Release 3, from itu.int (about 18 MB);
  3. checks the package’s sha256 against the pinned value 979680ff3b52b13a5701453178efd32b53e340114638fd330fdb6669eec86620;
  4. reads the numeric codebook values out of it in memory, and checks their sha256 against a second pinned value;
  5. writes them to g729-tables.bin, mode 0644, by an atomic rename;
  6. deletes the download.

--yes answers the question, for scripts and Dockerfiles:

sipnab g729 install --yes

The file goes in the directory of the user config file: $XDG_CONFIG_HOME/sipnab/g729-tables.bin, else ~/.config/sipnab/g729-tables.bin. To put it elsewhere, give the path with --g729-tables, and give the same path to the runs that decode, or set it once in the config file:

[g729]
tables = "/srv/sipnab/g729-tables.bin"

A service account usually has no home directory, so a server install names the path:

sudo sipnab g729 install --yes --g729-tables /srv/sipnab/g729-tables.bin

Check the installed tables

sipnab g729 status

It prints G.729 tables: installed or not installed, the file it read, and the sha256 of the values. It exits 0 when the file holds the tables and 1 when it does not. --g729-tables, -f and --no-config work as they do for a run, so the command reports on the file a run with the same options reads.

Hosts without internet access

--from installs from a copy and opens no connection. It takes any of:

  • the package zip, downloaded elsewhere. The same sha256 check applies:
    sipnab g729 install --from 'T-REC-G.729-201206-I!!SOFT-ZST-E.zip'
    
  • a directory extracted from the package;
  • the g729-tables.bin of another host. sipnab checks its values against the same pinned hash:
    sipnab g729 install --yes --from /mnt/usb/g729-tables.bin
    

Copying g729-tables.bin to the path a run reads works as well. --from adds the check.

Every release binary that decodes G.729, the static musl tarballs and the -noaudio packages included, can download the package. --from is the offline method in each of them.

When the tables are missing

sipnab leaves a G.729 stream out of the audio, never puts silence in its place, and says why and what to run:

G.729 not decoded: codebook tables not installed; run sipnab g729 install

A WAV export, the REST and MCP audio exports and a vCon’s capture caveat all carry that sentence. sipnab exports the audio of the call’s other streams as usual.

The command line, the REST API, MCP and vCon export never download the tables on their own: a capture tool on a server does not open a network connection nobody asked for. The terminal UI asks once, the first time it shows a G.729 stream:

This call uses G.729. Download the decoder tables from itu.int now? [y/N]

With [g729] auto_install = true, the terminal UI downloads without asking. Nothing else reads that key.

A run reads tables installed while it is going when it next decodes a G.729 stream. sipnab keeps G.729 payload whenever the build has the g729 feature, so --retain-audio audio held before the install decodes too.

Lost, late and repeated packets

sipnab places each G.729 packet by its RTP timestamp and fills time in which no packet arrived with the decoder’s own output:

  • A missing RTP sequence number marks a packet the sender sent and the capture did not see. sipnab hands the decoder its frames as lost frames, and the decoder conceals them.
  • In the rest of the time the sender sent nothing (Annex B discontinuous transmission). sipnab hands the decoder those frames as frames the sender did not transmit, and the decoder carries the comfort noise on.
  • sipnab sizes a lost packet by the stream’s usual speech packet: the most frequent number of 10 ms frames among the speech packets received.
  • After speech, if the lost packets at that size fill the gap or more, sipnab decodes the whole gap as lost frames. If they fill less of it, a pause began inside the gap. sipnab then decodes the lost frames first, counts the last lost packet as the one-frame SID that starts a pause, and decodes the rest of the gap as pause.
  • After a SID frame, sipnab decodes the pause first and the lost frames last, right before the packet that resumed.
  • With no sequence number missing, sipnab decodes the whole gap as pause.

When a loss sits next to a pause, the timestamps and sequence numbers can fit two layouts of the same call, and sipnab decodes the layout the rules above give. Two examples, each four or three frames long:

  • In a stream of two-frame speech packets, the capture misses both the SID before a pause and the first speech packet after it, in a four-frame gap. Two lost packets of two frames fill the gap, so sipnab decodes four lost frames. The call had one lost frame, two frames of pause, and one lost frame.
  • In a stream of three-frame speech packets, the capture misses the SID after speech, in a three-frame gap. One lost packet of three frames fills the gap, so sipnab decodes three lost frames. The call had one lost frame and two frames of pause.

In both, two lost speech packets (the first example) or one lost three-frame speech packet (the second) fit the same timestamps and sequence numbers, and for those calls the frames sipnab decodes are the call’s. Where the call had the other layout, the decoded audio differs only in the frames of that gap.

An export decodes packets that arrived out of order in timestamp order, and decodes a packet that repeats a timestamp once. The live audio route sends each packet as it reads it, so it skips a packet that arrives after it has sent later audio.

Why sipnab does not ship the tables

The trained codebook values of G.729 are published in the reference C code of the ITU-T G.729 software package, and that code’s notice keeps its authors’ rights. So sipnab reads them from your own copy of the package instead of shipping them. sipnab’s own code is MIT OR Apache-2.0. sipnab’s code generates the tables that the Recommendation defines by a formula. See crates/g729/PROVENANCE.md for the verbatim notices, the holders, the package hash and the clean-room record.

  • No file in the sipnab repository, crate, binary or package holds an ITU-T table value. A test scans every file in the repository for any run of six or more integers that matches the tables, and the release workflow runs the same scan over the built binaries and packages.
  • sipnab g729 install takes the values from the ITU-T package on your host. sipnab never hosts or mirrors that package.

Provenance

The decoder is the sipnab-g729 crate in this repository, written in Rust from the text of ITU-T G.729 and its Annexes A and B by a clean-room process: the author of the Rust never read the ITU-T C code. It decodes all 16 ITU-T decoder test vectors bit-exactly. crates/g729/PROVENANCE.md records who read what, the hashes of every file involved, and the package’s copyright statements in full.