G729 Decoding - NormB/sipnab GitHub Wiki

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 also decodes the core layer of G.729.1 streams: see The core layer of G.729.1.

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

Before it does anything, the command prints what it downloads, the sha256 it checks, where it writes the file, and why sipnab does not ship the tables, then asks:

sipnab g729 install builds the G.729 decoder's codebook tables.

Source: https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-G.729-201206-I!!SOFT-ZST-E&type=items. sipnab downloads the ITU-T G.729 (06/2012) Release 3 package (18476968 bytes), checks that its sha256 is 979680ff3b52b13a5701453178efd32b53e340114638fd330fdb6669eec86620, keeps only the numeric codebook values, writes them to /home/you/.config/sipnab/g729-tables.bin, and deletes the download.

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. Everything sipnab ships is MIT OR Apache-2.0. Details: https://github.com/NormB/sipnab/blob/main/crates/g729/PROVENANCE.md

Install the G.729 tables? [y/N]

Answer y or yes (any case) to install. Any other answer installs nothing, prints Not installed: the answer was not yes. and exits 1.

After a yes, the command:

  1. downloads the ITU-T G.729 (06/2012) software package, Release 3, from itu.int (18,476,968 bytes);
  2. checks the package's sha256 against the pinned value 979680ff3b52b13a5701453178efd32b53e340114638fd330fdb6669eec86620;
  3. reads the numeric codebook values out of it in memory, and checks their sha256 against a second pinned value;
  4. writes them to g729-tables.bin, mode 0644, by an atomic rename;
  5. deletes the download;
  6. prints Installed the G.729 tables: with the file's path, and Values sha256: with the hash of the values.

sipnab refuses a package whose sha256 differs: it prints the expected and the actual hash and Nothing was installed., and exits 1. A file that is not the package, given with --from, reads:

Refused fake.zip: its sha256 is 5040121d75ccd1afe14dd9d30f016bb9cce741a3276212993c0b315552145af8, expected 979680ff3b52b13a5701453178efd32b53e340114638fd330fdb6669eec86620 (the ITU-T G.729 (06/2012) Release 3 package). Nothing was installed.

Check that the file is the ITU-T G.729 (06/2012) Release 3 package zip, and download it again if its hash differs.

--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

On a host without the tables it prints:

G.729 tables: not installed
Table file: /home/you/.config/sipnab/g729-tables.bin
G.729 not decoded: codebook tables not installed; run sipnab g729 install

With the tables in place it prints G.729 tables: installed, the file it read, and Values sha256: with the hash 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. It checks that file as a run does: it names each key it does not know on stderr, and refuses a file a run refuses, with the run's message and exit status, without reporting on it. An empty [g729] tables is one such file:

invalid config value: [g729] tables is empty; give a path, or remove the key for the default

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

With --from, the notice the command prints before it asks starts Source: <path>. Nothing is downloaded. and ends The source is not changed. After the install it prints what it read, for example Read the table file <path> (sha256 <hash>), then the path it wrote and Values sha256:.

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.

The core layer of G.729.1

G.729.1 (RFC 4749) is a wideband codec built on G.729. Each 20 ms frame starts with an 8 kbit/s core layer, which holds two G.729 frames. Higher layers add narrowband quality at 12 kbit/s and wideband audio from 14 to 32 kbit/s. sipnab decodes the core layer and nothing above it.

What sipnab does with a G.729.1 stream:

  • It recognizes the stream from the SDP: sipnab names a payload type that a=rtpmap:<pt> G7291/16000 binds G7291, and runs it on the 16 kHz RTP clock, 320 timestamp units per 20 ms frame. sipnab cannot name a G.729.1 stream whose SDP the capture does not hold, and does not decode it.
  • It decodes the first 20 octets of every frame, at every bit rate the call used, as two G.729 frames, into 160 samples at 8 kHz. The audio is narrowband. It is not the audio a G.729.1 decoder plays.
  • A SID (RFC 5459: frame type 14 alone, or 2, 3 or 6 octets after the frames) drives the G.729 Annex B comfort noise from its first 15 bits. sipnab does not read the rest of the SID.
  • A packet of frame type 15 (no data) decodes nothing and is not a loss. sipnab ignores a packet of frame type 12 or 13 (reserved), counts it as a frame that failed to decode, and conceals its 20 ms as a lost packet. It ignores octets that are neither a whole frame nor a SID.
  • sipnab fills lost packets and pauses by the rules in Lost, late and repeated packets, and counts each 20 ms frame as two 10 ms G.729 frames.
  • It uses the same tables, installed the same way. Without them, sipnab leaves a G.729.1 stream out with the same sentence and command as G.729.

Every output that carries the decoded audio says what it is and names the bit rates of the frames decoded: the note inside the WAV file, the export summary, the vCon's capture caveat, the decoded_as field of a live RTP leg object (in the stop message, every frame sent), and the decoded_as field of the audio socket's stream message (the frames sipnab decoded before it announced the stream). For example, a one-way call of 250 packets at 32 kbit/s:

# Run all of these, in order.
sipnab -N -q --no-cli-print -I g7291-call.pcap --retain-audio \
  --export-vcon [email protected] > call.vcon.json
jq -r '.analysis[0].body.capture_completeness.media_note' call.vcon.json
Produced by sipnab 0.5.222 via mechanism sipnab-capture. Audio decoded from
RTP payload this run retained; it is bounded by where the capture point sat
and by what retention kept, and is not a recording made by the endpoints.
INCOMPLETE. — G.729.1: this audio is the G.729.1 core layer (8 kbit/s)
decoded as G.729 at 8 kHz; the stream's frames were sent at 32 kbit/s; the
layers above 8 kbit/s (narrowband enhancement and wideband) are not
decoded, so this is not the audio a G.729.1 decoder produces. — PARTIAL:
media in only ONE direction reached this capture; no stream going the other
way was observed. That may be a one-way call, or a capture point that only
sees one leg -- this file cannot tell you which.

The output is one line, wrapped here to fit the page.

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. Everything sipnab ships is MIT OR Apache-2.0. Details: crates/g729/PROVENANCE.md.

  • No file in the sipnab repository, crate, binary or package holds an ITU-T table value. Each release scans the built binaries and packages for any run of six or more integers that matches the tables.
  • The tables that the Recommendation defines by a formula, sipnab's own code generates.
  • 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.

⚠️ **GitHub.com Fallback** ⚠️