The TSN documentation project for linux gives a general guideline how to proceed. Here are some concrete steps.

ALSA AAF plugin

In most distributions, the AAF ALSA plugin required for AVB is not installed and not even available as a distribution package.

We can acquire the sources from the alsa-plugins git repository using the command git clone https://github.com/alsa-project/alsa-plugins.

We also need libavtp, which we can get through git clone https://github.com/AVnu/libavtp. For building libavtp, we need additional packages, installable with sudo apt install meson.

Let'd change to the libavtp folder. Configure the build directory using meson build, then run the building process with ninja -C build. Finally, install libavtp.so to /usr/local/lib by issuing sudo ninja -C build install. Run sudo ldconfig to update the shared linker cache.

Now, let's change to the alsa-plugins folder and initialize building with ./gitcompile. Let's explicitly run ./configure --prefix=/usr/local --enable-aaf in order to be able to install the plugins alongside system packages. Check, if the console output states AAF plugin: yes – otherwise, a prerequisite is missing. make will build the plugins.

The command sudo make install will install the shared plugin libraries to the folder /usr/local/lib/alsa-lib. We should also run libtool --finish /usr/local/lib/alsa-lib.

Streaming

We need to configure the AAF plugin in `/etc/asound.conf' on talker and listener machines alike:

pcm.aaf0_raw {
  type aaf

  # ifname: Network interface used to transmit/receive AVTP packets.
  # VLAN interface
  ifname eth0.5

  # addr: Stream destination MAC address
  addr 01:AA:AA:AA:AA:AA

  # prio: Priority used by the plugin to transmit AVTP traffic. This
  # option is relevant only when operating in playback mode.
  prio 2

  # streamid: Stream ID associated with the AAF stream transmitted or
  # received by the plugin.
  streamid AA:BB:CC:DD:EE:FF:000B

  # mtt: Maximum Transit Time (in microseconds) as defined in AVTP spec
  # section 4.3.3. This option is relevant only when operating in
  # playback mode.
  mtt 2000

  # time_uncertainty: Maximum Time Uncertainty (in microseconds) as
  # defined by AVTP spec section 4.3.3. This option is relevant only when
  # operating in playback mode.
  time_uncertainty 125

  # frames_per_pdu: Number of audio frames transmitted in one AVTPDU.
  frames_per_pdu 12

  # ptime_tolerance: Presentation time tolerance in microseconds.
  # AVTPDUs with presentation time off by +- ptime_tolerance are not
  # considered invalid. This option is relevant only when operating in
  # capture mode.
  ptime_tolerance 100
}

# Additional plug to convert sample type (to S32_BE), if needed
pcm.aaf0 {
  type plug
  slave { pcm aaf0_raw }
}

On the talker system, we can now start a speaker test with sudo ALSA_PLUGIN_DIR=/usr/local/lib/alsa-lib speaker-test -p 25000 -F S32_BE -c 2 -r 48000 -D aaf0.

The play buffer has two components: - period (parameter -p) to be defined in µs. The case of 12 frames per network packet at 48 kHz sample rate amounts to a minimum period time of 12/48000 s = 250 µs. - nperiods (parameter -P), by default 4

On the listener system, we can run a recording with sudo ALSA_PLUGIN_DIR=/usr/local/lib/alsa-lib arecord -t raw -f S32_BE -c 2 -r 48000 -D aaf0 | ffmpeg -f s32be -ar 48000 -ac 2 -i - -c:a pcm_s32le -f wav -y /tmp/rec.wav.

Jack routing on the listener side

Routing AAF input to a jack daemon needs a workaround, as the AAF plugin is not a proper ALSA hardware device, but only a virtual PCM.

We need to have an ALSA jack plugin configured in /etc/asound.conf:

pcm.rawjack {
  type jack
  name "jack-alsa"
  playback_ports {
    0 system:playback_1
    1 system:playback_2
  }
  capture_ports {
    0 system:capture_1
    1 system:capture_2
  }
}

pcm.jack {
  type plug
  slave { pcm "rawjack" }
}

The pcm.jack plugin definition is necessary in order to convert incoming samples to the jack-native FLOAT_LE format, if needed.

For the following, we need to be root, with export ALSA_PLUGIN_DIR=/usr/local/lib/alsa-lib set in the environment.

First, we start a jack server in the background using the dummy interface: JACK_NO_AUDIO_RESERVATION=1 jackd --realtime -d dummy -P 2 -C 2 -r 48000 --period=12 &. The setting --period=12 for minimal latency might be too low for stable and efficient operation.

Then, we can route the AAF input to the ALSA jack plugin: arecord -t raw -f S32_BE -c 2 -r 48000 --period-size=12 -D aaf0 | aplay -D pcm.jack -f S32_BE -c 2 -r 48000 --period-size=12 - &. The --period-size=12 setting used here are most optimistic and correspond to the 12-frame AVB PDU size. These would give minimal latency.

With jack_lsp -c, we can verify that alsa-jack:out_00* is connected to system:playback_*.

We can send the jack signals to an ALSA device hw:1 and synchronize play speed in the course with zita-j2a -j alsa-out -d hw:1 -r 48000 -c 2 &. We connect the signals using jack_connect alsa-jack:out_000 alsa-out:playback_1 and jack_connect alsa-jack:out_001 alsa-out:playback_2.

Jack routing on the talker side

First, the jack server must be started similar to the listener side (see above).

Audio signals from the jack infrastructure can be routed to the AAF output like this: arecord -D pcm.jack -f FLOAT_LE --period-size=12 -c 2 -r 48000 | aplay -t raw -f FLOAT_LE -c 2 -r 48000 --period-size=12 --buffer-size=24 -M -D aaf0 -

Packet inspection

On both the talker and listener sides, inspection of the AVB network traffic is possible with tshark -f "ether proto 0x22f0" -i eth0.5 -c 1 -V which picks out one packet from the VLAN type interface and pretty-prints it.