- added platform compatiblity test used to test issue #17

- removed demo
- updated documentation with info about the platform compatiblity test
This commit is contained in:
Andrea Bondavalli 2021-02-16 20:57:31 +01:00
parent 723b410704
commit 7621beb83c
11 changed files with 411 additions and 153 deletions

View File

@ -83,13 +83,13 @@ The [patches](3rdparty/patches) subdirectory contains patches applied to the ALS
See [ALSA RAVENNA/AES67 Driver README](https://bitbucket.org/MergingTechnologies/ravenna-alsa-lkm/src/master/README.md) for additional information about the Merging Technologies module and for proper Linux Kernel configuration and tuning.
### [demo](demo) directory ###
### [test](test) directory ###
This directory contains a the daemon configuration and status files used to run a short demo on the network loopback device. The [demo](#demo) is described below.
This directory contains the files used to run the daemon platform compatibility test on the network loopback device. The [test](#test) is described below.
## Prerequisite ##
<a name="prerequisite"></a>
The daemon and the demo have been tested with **Ubuntu 18.04** distro on **ARMv7** and with **Ubuntu 18.04, 19.10 and 20.04** distros on **x86** using:
The daemon and the test have been tested with **Ubuntu 18.04** distro on **ARMv7** and with **Ubuntu 18.04, 19.10 and 20.04** distros on **x86** using:
* Linux kernel version >= 4.10.x
* GCC version >= 7.x / clang >= 6.x (C++17 support required)
@ -99,10 +99,14 @@ The daemon and the demo have been tested with **Ubuntu 18.04** distro on **ARMv7
* boost libraries version >= 1.65
* Avahi service discovery (if enabled) >= 0.7
The BeagleBone® Black board with ARM Cortex-A8 32-Bit processor was used for testing on ARMv7.
The following ARM platform have been used for testing:
The BeagleBone® Black board with ARM Cortex-A8 32-Bit processor.
See [Ubuntu 18.04 on BeagleBone® Black](https://elinux.org/BeagleBoardUbuntu) for additional information about how to setup Ubuntu on this board.
The [ubuntu-packages.sh](ubuntu-packages.sh) script can be used to install all the packages required to compile and run the AES67 daemon, the daemon tests and the [demo](#demo).
The NanoPi NEO2 with Allwinner H5, Quad-core 64-bit high-performance Cortex A53 processor.
See [Armbian NanoPi NEO2 ](https://www.armbian.com/nanopi-neo-2/) for additional information about how to setup Ubuntu on this board.
The [ubuntu-packages.sh](ubuntu-packages.sh) script can be used to install all the packages required to compile and run the AES67 daemon, and the [platform compatibility test](#test).
**_Important_** _PulseAudio_ must be disabled or uninstalled for the daemon to work properly, see [PulseAudio and scripts notes](#notes).
## How to build ##
@ -130,29 +134,54 @@ make sure that no instances of the aes67-daemon are running, enter the [tests](d
**_NOTE:_** when running regression tests make sure that no other Ravenna mDNS sources are advertised on the network because this will affect the results. Regression tests run on loopback interface but Avahi ignores the interface parameter set and will forward to the daemon the sources found on all network interfaces.
## Run the demo ##
<a name="demo"></a>
To run a simple demo use the [run\_demo.sh](run_demo.sh) script. See [script notes](#notes).
The demo configures and uses a loopback of 8 channels with AM824 codec (S32 format) at 48Khz and saves the output to a wav file.
## Run the platform compatibility test ##
<a name="test"></a>
Before attempting to use the AES67 daemon on a specific host or board it's highly recommended to run the platform test.
These tests can be executed using the [run\_test.sh](run_test.sh) script. See [script notes](#notes).
The demo performs the following operations:
The script allows a user to test a specific configuration and it can be used to ensure that the daemon will be able to operate smoothly with such config on the selected platform.
* setup system parameters
* stop PulseAudio (if installed). This uses and keeps busy the ALSA playback and capture devices causing instability problems. See [PulseAudio](#notes).
Usage run_test.sh sample_format sample_rate channels duration
sample_format can be one of S16_LE, S24_3LE, S32_LE
sample_rate can be one of 44100, 48000, 96000
channels can be one of 1, 2, 4
duration is in the range 1 to 10 minutes
For example to test the typical AES67 configuration run:
./run_test.sh S24_3LE 48000 2 5
The test performs the following operations:
* check that all the required executables are available
* stop the running daemon instances and remove the ALSA RAVENNA/AES67 module
* compile the test tools under the test folder
* validatesthe input parametersi, prepare the raw input file to be played *./test/test.raw* and the configuration files under the test folder
* stop PulseAudio (if installed). This opens and keeps busy the ALSA playback and capture devices causing problems. See [PulseAudio](#notes).
* install the ALSA RAVENNA/AES67 module
* start the ptp4l as master clock on the network loopback device
* start the AES67 daemon and creates a source and a sink according to the status file in the demo directory
* open a browser on the daemon PTP status page
* start the AES67 daemon and create a source and a sink according to the status file created in the test directory
* wait for the Ravenna driver PTP slave to synchronize
* start recording on the configured ALSA sink for 60 seconds to the wave file in *./demo/sink_test.wav*
* start playing a test sound on the configured ALSA source
* wait for the recording to complete and terminate ptp4l and the AES67 daemon
* start recording on the configured ALSA sink for the specified period tof time to the raw file *./test/sink_test.raw*
* start playing the test file created *./test/test.raw* on the configured ALSA source
* wait for the recording and the playback to complete
* check that the recorded file contains the expected audio samples sequence
* terminate ptp4l and the AES67 daemon
* print the test result that can be either *OK" or *failed at {location}*
If the test result is OK it means that the selected configuration can possibly run smoothly on your platform.
If the test reports a failure you may try to stop all the possible additional loads running on the host and repeat it.
If after this the test fails systematically it means you cannot achieve a good reliability with the specified configuration.
In this case you may try to configure a difference dirver timer basic tick period in the daemon configuration file (parameter *tic\_frame\_size\_at\_1fs* in *test/daemon.conf*).
By default this parameter is set to 48 (1ms latency) and the valid range is from 48 to 480 with steps of 48. Higher values of this parameter lead to higher packets processing latency and this breaks the compatibility with certain devices.
## Notes ##
<a name="notes"></a>
* All the scripts in this repository are provided as a reference to help setting up the system and run a simple demo.
They have been tested on **Ubuntu 18.04 19.10 20.04** distros.
* All the scripts in this repository are provided as a reference to help setting up the system and run the platform compatibility test.
They have been tested on **Ubuntu 18.04 19.10 20.04** distros.
* **PulseAudio** can create instability problems.
Before running the daemon verify that PulseAudio is not running with:
@ -162,7 +191,7 @@ Before running the daemon verify that PulseAudio is not running with:
daemon/scripts/disable_pulseaudio.sh
If after this the process is still alive considering one of these two solutions and reboot the system afterwards:
If after this the process is still alive consider one of these two solutions and reboot the system afterwards:
* Uninstall it completely with:
sudo apt-get remove pulseaudio

2
demo/.gitignore vendored
View File

@ -1,2 +0,0 @@
sink_test.wav

View File

@ -1,28 +0,0 @@
{
"sources": [
{
"id": 0,
"enabled": true,
"name": "ALSA Source 0",
"io": "Audio Device",
"max_samples_per_packet": 6,
"codec": "AM824",
"ttl": 15,
"payload_type": 98,
"dscp": 34,
"refclk_ptp_traceable": false,
"map": [ 0, 1, 2, 3, 4, 5, 6, 7 ]
} ],
"sinks": [
{
"id": 0,
"name": "ALSA Sink 0",
"io": "Audio Device",
"use_sdp": true,
"source": "http://127.0.0.1:8080/api/source/sdp/0",
"sdp": "v=0\no=- 0 0 IN IP4 127.0.0.1\ns=ALSA Source 0\nc=IN IP4 239.1.0.1/15\nt=0 0\na=clock-domain:PTPv2 0\nm=audio 5004 RTP/AVP 98\nc=IN IP4 239.1.0.1/15\na=rtpmap:98 AM824/48000/8\na=sync-time:0\na=framecount:48\na=ptime:0.125\na=mediaclk:direct=0\na=ts-refclk:ptp=IEEE1588-2008:00-00-00-FF-FE-00-00-00:0\na=recvonly\n",
"delay": 576,
"ignore_refclk_gmid": true,
"map": [ 0, 1, 2, 3, 4, 5, 6, 7 ]
} ]
}

View File

@ -1,98 +0,0 @@
#!/bin/bash
#
# Tested on Ubuntu 18.04
#
function cleanup {
#kill and wait for previous daemon instances to exit
sudo killall -q ptp4l
killall -q aes67-daemon
while killall -0 aes67-daemon 2>/dev/null ; do
sleep 1
done
}
if ! [ -x "$(command -v ptp4l)" ]; then
echo 'Error: ptp4l is not installed.' >&2
exit 1
fi
if ! [ -x "$(command -v arecord)" ]; then
echo 'Error: arecord is not installed.' >&2
exit 1
fi
if ! [ -x "$(command -v speaker-test)" ]; then
echo 'Error: speaker-test is not installed.' >&2
exit 1
fi
if ! [ -x "$(command -v ./daemon/aes67-daemon)" ]; then
echo 'Error: aes67-daemon is not compiled.' >&2
exit 1
fi
if ! [ -r "3rdparty/ravenna-alsa-lkm/driver/MergingRavennaALSA.ko" ]; then
echo 'Error: MergingRavennaALSA.ko module is not compiled.' >&2
exit 1
fi
trap cleanup EXIT
#configure system parms
sudo sysctl -w net/ipv4/igmp_max_memberships=66
if [ -x /usr/bin/pulseaudio ]; then
#stop pulseaudio, this seems to open/close ALSA continuosly
echo autospawn = no > $HOME/.config/pulse/client.conf
pulseaudio --kill >/dev/null 2>&1
rm $HOME/.config/pulse/client.conf
#disable pulseaudio
systemctl --user stop pulseaudio.socket > /dev/null 2>&1
systemctl --user stop pulseaudio.sservice > /dev/null 2>&1
fi
#install kernel module
sudo insmod 3rdparty/ravenna-alsa-lkm/driver/MergingRavennaALSA.ko
cleanup
if [ -f ./demo/sink_test.wav ] ; then
rm -f sink_test.wav
fi
echo "Starting PTP master ..."
sudo ptp4l -i lo -l7 -E -S &
#echo "Starting AES67 daemon ..."
./daemon/aes67-daemon -c ./demo/daemon.conf &
#open browser on configuration page
if [ -x "$(command -v xdg-open)" ]; then
xdg-open "http://127.0.0.1:8080/PTP"
fi
echo "Waiting for PTP slave to sync ..."
sleep 30
#starting recording on sink
echo "Starting to record 60 secs from sink ..."
arecord -D plughw:RAVENNA -f S32_LE -d 60 -r 48000 -c 8 -t wav /tmp/sink_test.wav > /dev/null 2>&1 &
sleep 10
#starting playback on source
echo "Starting to playback test on source ..."
speaker-test -F S32_LE -D plughw:RAVENNA -r 48000 -c 8 -t sine > /dev/null 2>&1 &
while killall -0 arecord 2>/dev/null ; do
sleep 1
done
killall speaker-test
if [ -f /tmp/sink_test.wav ] ; then
mv /tmp/sink_test.wav demo/
echo "Recording to file \"demo/sink_test.wav\" successfull"
else
echo "Recording failed"
fi
echo "Terminating processes ..."

157
run_test.sh Executable file
View File

@ -0,0 +1,157 @@
#!/bin/bash
#
# Tested on Ubuntu 18.04
#
function cleanup {
#kill and wait for previous daemon instances to exit
sudo killall -q ptp4l
killall -q aes67-daemon
while killall -0 aes67-daemon 2>/dev/null ; do
sleep 1
done
}
if ! [ -x "$(command -v /usr/sbin/ptp4l)" ]; then
echo 'Error: ptp4l is not installed.' >&2
exit 1
fi
if ! [ -x "$(command -v arecord)" ]; then
echo 'Error: arecord is not installed.' >&2
exit 1
fi
if ! [ -x "$(command -v aplay)" ]; then
echo 'Error: aplay is not installed.' >&2
exit 1
fi
if ! [ -x "$(command -v ./daemon/aes67-daemon)" ]; then
echo 'Error: aes67-daemon is not compiled.' >&2
exit 1
fi
if ! [ -r "3rdparty/ravenna-alsa-lkm/driver/MergingRavennaALSA.ko" ]; then
echo 'Error: MergingRavennaALSA.ko module is not compiled.' >&2
exit 1
fi
cd test
echo 'Compiling tools ...' >&2
make
echo 'Creating test file ...' >&2
if ! ./createtest $1 $2 $3 $4 ; then
echo 'Usage run_test.sh sample_format sample_rate channels duration' >&2
echo ' sample_format can be one of S16_LE, S24_3LE, S32_LE' >&2
echo ' sample_rate can be one of 44100, 48000, 96000' >&2
echo ' channels can be one of 1, 2, 4' >&2
echo ' duration is in the range 1 to 10 minutes' >&2
exit 1
else
echo 'test file created' >&2
fi
cd ..
SAMPLE_FORMAT=$1
SAMPLE_RATE=$2
CHANNELS=$3
DURATION=$4
SEC=$((DURATION*60))
if [ $SAMPLE_FORMAT == "S16_LE" ]; then
CODEC="L16"
elif [ $SAMPLE_FORMAT == "S24_3LE" ]; then
CODEC="L24"
elif [ $SAMPLE_FORMAT == "S32_LE" ] ; then
CODEC="AM824"
fi
if [ $SAMPLE_RATE == "44100" ]; then
PTIME="1.08843537415"
elif [ $SAMPLE_RATE == "48000" ]; then
PTIME="1"
elif [ $SAMPLE_RATE == "96000" ]; then
PTIME="0.5"
fi
MAP="[ "
for (( ch=0; ch<$CHANNELS; ch++ ))
do
MAP+=$ch
if (( ch != ($CHANNELS - 1) )); then
MAP+=","
fi
done
MAP+=" ]"
echo 'Creating configuration files ..' >&2
sed 's/48000/'"$SAMPLE_RATE"'/g;s/status.json/status_.json/g;' test/daemon.conf > test/daemon_.conf
sed 's/\/2/\/'"$CHANNELS"'/g;s/48000/'"$SAMPLE_RATE"'/g;s/L24/'"$CODEC"'/g;s/ptime:1/ptime:'"$PTIME"'/;s/\[ 0, 1 \]/'"$MAP"'/g' test/status.json > test/status_.json
trap cleanup EXIT
#configure system parms
sudo sysctl -w net/ipv4/igmp_max_memberships=66
if [ -x /usr/bin/pulseaudio ]; then
#stop pulseaudio, this seems to open/close ALSA continuosly
echo autospawn = no > $HOME/.config/pulse/client.conf
pulseaudio --kill >/dev/null 2>&1
rm $HOME/.config/pulse/client.conf
#disable pulseaudio
systemctl --user stop pulseaudio.socket > /dev/null 2>&1
systemctl --user stop pulseaudio.sservice > /dev/null 2>&1
fi
#uninstall kernel module
if sudo lsmod | grep MergingRavennaALSA > /dev/null 2>&1 ; then
sudo rmmod MergingRavennaALSA
fi
#install kernel module
sudo insmod 3rdparty/ravenna-alsa-lkm/driver/MergingRavennaALSA.ko
cleanup
if [ -f ./test/sink_test.raw ] ; then
rm -f ./test/sink_test.raw
fi
echo "Starting PTP master ..."
sudo /usr/sbin/ptp4l -i lo -l7 -E -S &
echo "Starting AES67 daemon ..."
./daemon/aes67-daemon -c ./test/daemon_.conf &
echo "Waiting for PTP slave to sync ..."
sleep 30
#starting record on sink
echo "Starting to record $SEC sec from sink ..."
arecord -M -d $SEC -D plughw:RAVENNA -f $SAMPLE_FORMAT -r $SAMPLE_RATE -c $CHANNELS -t raw /tmp/sink_test.raw &
sleep 10
#starting playback on source
echo "Starting to playback test on source ..."
aplay -M -D plughw:RAVENNA -f $SAMPLE_FORMAT -r $SAMPLE_RATE -c $CHANNELS ./test/test.raw > /dev/null 2>&1 &
while killall -0 arecord 2>/dev/null ; do
sleep 1
done
while killall -0 aplay 2>/dev/null ; do
sleep 1
done
if [ -f /tmp/sink_test.raw ] ; then
mv /tmp/sink_test.raw test/
echo "Recording to file \"test/sink_test.raw\" successfull"
else
echo "Recording failed"
fi
echo "Test result:"
cd test
./check $SAMPLE_FORMAT $CHANNELS
cd ..
echo "Terminating processes ..."

7
test/.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
sink_test.raw
*.o
createtest
check
status_.json
daemon_.json

8
test/Makefile Normal file
View File

@ -0,0 +1,8 @@
CXX=g++
CC=g++
all: check createtest
createtest: createtest.o
check: check.o
clean:
rm *.o
rm check createtest

85
test/check.cc Normal file
View File

@ -0,0 +1,85 @@
// basic file operations
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char* argv[])
{
if (argc < 3) {
cerr << "Usage: " << argv[0] << " sample_format channels" << endl;
exit(1);
}
int len(0);
string format(argv[1]);
if (format == "S16_LE")
len = 2;
else if (format == "S24_3LE")
len = 3;
else if (format == "S32_LE")
len = 4;
else {
cerr << "Unsupported format " << format << endl;
exit(1);
}
int channels = atoi(argv[2]);
if (channels != 1 && channels != 2 && channels != 4 && channels != 8) {
cerr << "Unsupported channels " << channels << endl;
exit(1);
}
fstream fl;
fl.open("./sink_test.raw", ios::in|ios::binary);
if (!fl.is_open()) {
cout << "cannot open file " << endl;
exit(1);
}
auto begin = fl.tellp();
fl.seekp(0, ios::end);
auto end = fl.tellp();
fl.seekp(0, ios::beg);
auto prev = fl.tellp();
// skip inital silence
while((prev = fl.tellp()) != end && fl.get() == 0);
uint8_t curr = 0, byte = 0;
bool first = true;
fl.seekp(prev);
while(fl.tellp() != end) {
int ch(channels);
while (ch--) {
if (!first) {
byte = fl.get();
if (byte != (uint8_t)curr) goto end;
}
if (len > 1) {
byte = fl.get();
if (byte != (uint8_t)(curr+1)) goto end;
if (len > 2) {
byte = fl.get();
if (byte != (uint8_t)(curr+2)) goto end;
if (len > 3) {
byte = fl.get();
if (byte != (uint8_t)(curr+3)) goto end;
}
}
}
first = false;
}
curr += len;
}
end:
//cout << "expected " << (int)curr << " byte " << (int)byte << endl;
int rc = 0;
if (fl.tellp() != end)
{
cout << "error at position: " << fl.tellp() << endl;
rc = 1;
}
else cout << "ok" << endl;
fl.close();
return rc;
}

70
test/createtest.cc Normal file
View File

@ -0,0 +1,70 @@
// basic file operations
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char* argv[])
{
if (argc < 5) {
cerr << "Usage " << argv[0] << " sample_format sample_rate channels duration" << endl;
exit(1);
}
int len(0);
string format(argv[1]);
if (format == "S16_LE")
len = 2;
else if (format == "S24_3LE")
len = 3;
else if (format == "S32_LE")
len = 4;
else {
cerr << "Unsupported format " << format << endl;
exit(1);
}
int rate(atoi(argv[2]));
if (rate != 44100 && rate != 48000 && rate != 96000) {
cerr << "Unsupported rate " << rate << endl;
exit(1);
}
int channels = atoi(argv[3]);
if (channels != 1 && channels != 2 && channels != 4 && channels != 8) {
cerr << "Unsupported channels " << channels << endl;
exit(1);
}
int duration = atoi(argv[4]);
if (duration > 10 || duration < 1) {
cerr << "Unsupported duration " << duration << " minutes" << endl;
exit(1);
}
int secs(duration * 60);
unsigned char byte(0);
fstream myfile;
myfile.open("test.raw", ios::out|ios::binary);
while(secs--) {
int samples(rate);
while (samples--) {
int ch(channels);
while (ch--) {
myfile.put(byte);
if (len > 1) {
myfile.put(byte+1);
if (len > 2) {
myfile.put(byte+2);
if (len > 3)
myfile.put(byte+3);
}
}
}
byte+=len;
}
}
myfile.close();
return 0;
}

View File

@ -4,19 +4,21 @@
"http_base_dir": "./webui/build",
"log_severity": 3,
"playout_delay": 0,
"tic_frame_size_at_1fs": 192,
"tic_frame_size_at_1fs": 48,
"max_tic_frame_size": 1024,
"sample_rate": 48000,
"rtp_mcast_base": "239.1.0.1",
"rtp_port": 5004,
"ptp_domain": 0,
"ptp_dscp": 48,
"sap_mcast_addr": "224.2.127.254",
"sap_interval": 30,
"syslog_proto": "none",
"syslog_server": "255.255.255.254:1234",
"status_file": "./demo/status.json",
"mdns_enabled": false,
"status_file": "./test/status.json",
"interface_name": "lo",
"mac_addr": "01:00:5e:01:00:01",
"ip_addr": "127.0.0.1"
"mdns_enabled": false,
"mac_addr": "00:00:00:00:00:00",
"ip_addr": "127.0.0.1",
"node_id": "AES67 daemon 007f0100"
}

28
test/status.json Normal file
View File

@ -0,0 +1,28 @@
{
"sources": [
{
"id": 0,
"enabled": true,
"name": "ALSA Source 0",
"io": "Audio Device",
"max_samples_per_packet": 48,
"codec": "L24",
"ttl": 15,
"payload_type": 98,
"dscp": 34,
"refclk_ptp_traceable": false,
"map": [ 0, 1 ]
} ],
"sinks": [
{
"id": 0,
"name": "ALSA Sink 0",
"io": "Audio Device",
"use_sdp": true,
"source": "http://127.0.0.1:8080/api/source/sdp/0",
"sdp": "v=0\no=- 657664 657666 IN IP4 127.0.0.1\ns=AES67 daemon 000a0900 ALSA Source 0\nc=IN IP4 239.1.0.1/15\nt=0 0\na=clock-domain:PTPv2 0\nm=audio 5004 RTP/AVP 98\nc=IN IP4 239.1.0.1/15\na=rtpmap:98 L24/48000/2\na=sync-time:0\na=framecount:48\na=ptime:1\na=mediaclk:direct=0\na=ts-refclk:ptp=IEEE1588-2008:00-1D-C1-FF-FE-50-36-33:0\na=recvonly\n",
"delay": 960,
"ignore_refclk_gmid": true,
"map": [ 0, 1 ]
} ]
}