From 7621beb83c179b8282976318dea4e6f70cb32896 Mon Sep 17 00:00:00 2001 From: Andrea Bondavalli Date: Tue, 16 Feb 2021 20:57:31 +0100 Subject: [PATCH] - added platform compatiblity test used to test issue #17 - removed demo - updated documentation with info about the platform compatiblity test --- README.md | 69 +++++++++++----- demo/.gitignore | 2 - demo/status.json | 28 ------- run_demo.sh | 98 ----------------------- run_test.sh | 157 +++++++++++++++++++++++++++++++++++++ test/.gitignore | 7 ++ test/Makefile | 8 ++ test/check.cc | 85 ++++++++++++++++++++ test/createtest.cc | 70 +++++++++++++++++ {demo => test}/daemon.conf | 12 +-- test/status.json | 28 +++++++ 11 files changed, 411 insertions(+), 153 deletions(-) delete mode 100644 demo/.gitignore delete mode 100644 demo/status.json delete mode 100755 run_demo.sh create mode 100755 run_test.sh create mode 100644 test/.gitignore create mode 100644 test/Makefile create mode 100644 test/check.cc create mode 100644 test/createtest.cc rename {demo => test}/daemon.conf (66%) create mode 100644 test/status.json diff --git a/README.md b/README.md index 0886c5e..bace2d5 100644 --- a/README.md +++ b/README.md @@ -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 ## -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 ## - -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 ## + +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 ## -* 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 diff --git a/demo/.gitignore b/demo/.gitignore deleted file mode 100644 index b26a45e..0000000 --- a/demo/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -sink_test.wav - diff --git a/demo/status.json b/demo/status.json deleted file mode 100644 index 58d0082..0000000 --- a/demo/status.json +++ /dev/null @@ -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 ] - } ] -} diff --git a/run_demo.sh b/run_demo.sh deleted file mode 100755 index 12c99ca..0000000 --- a/run_demo.sh +++ /dev/null @@ -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 ..." - diff --git a/run_test.sh b/run_test.sh new file mode 100755 index 0000000..a138937 --- /dev/null +++ b/run_test.sh @@ -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 ..." diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..427f821 --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,7 @@ +sink_test.raw +*.o +createtest +check +status_.json +daemon_.json + diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 0000000..55ccbe1 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,8 @@ +CXX=g++ +CC=g++ +all: check createtest +createtest: createtest.o +check: check.o +clean: + rm *.o + rm check createtest diff --git a/test/check.cc b/test/check.cc new file mode 100644 index 0000000..c1f47c2 --- /dev/null +++ b/test/check.cc @@ -0,0 +1,85 @@ +// basic file operations +#include +#include + +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; +} diff --git a/test/createtest.cc b/test/createtest.cc new file mode 100644 index 0000000..0ad92b8 --- /dev/null +++ b/test/createtest.cc @@ -0,0 +1,70 @@ +// basic file operations +#include +#include + +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; + +} diff --git a/demo/daemon.conf b/test/daemon.conf similarity index 66% rename from demo/daemon.conf rename to test/daemon.conf index e0756b3..fc4df41 100644 --- a/demo/daemon.conf +++ b/test/daemon.conf @@ -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" } diff --git a/test/status.json b/test/status.json new file mode 100644 index 0000000..00faa6d --- /dev/null +++ b/test/status.json @@ -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 ] + } ] +}