diff --git a/.github/workflows/daemon-tests.yml b/.github/workflows/daemon-tests.yml new file mode 100644 index 0000000..1972a9f --- /dev/null +++ b/.github/workflows/daemon-tests.yml @@ -0,0 +1,19 @@ +name: Daemon regression Tests + +on: [push] + +jobs: + daemon_tests_job: + runs-on: ubuntu-latest + name: A job to run AES67 daemon regression tests + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Build daemon tests docker image + run: docker build --progress=plain -f ./Dockerfile.daemon_tests -t aes67-daemon-tests . + + - name: Run daemon regression tests + run: docker run aes67-daemon-tests diff --git a/Dockerfile.daemon_tests b/Dockerfile.daemon_tests new file mode 100644 index 0000000..4173851 --- /dev/null +++ b/Dockerfile.daemon_tests @@ -0,0 +1,11 @@ +FROM ubuntu +RUN echo 'APT::Install-Suggests "0";' >> /etc/apt/apt.conf.d/00-docker +RUN echo 'APT::Install-Recommends "0";' >> /etc/apt/apt.conf.d/00-docker +RUN DEBIAN_FRONTEND=noninteractive \ + apt-get update && \ + apt-get install -qq -f -y build-essential clang git cmake libboost-all-dev valgrind linux-sound-base alsa-base alsa-utils libasound2-dev libavahi-client-dev \ + && rm -rf /var/lib/apt/lists/* +COPY . . +RUN ./buildfake.sh +WORKDIR ./daemon/tests +CMD ./daemon-test -p diff --git a/build.sh b/build.sh index 3f12b0f..2c38df0 100755 --- a/build.sh +++ b/build.sh @@ -43,7 +43,7 @@ cd .. cd daemon echo "Building aes67-daemon ..." -cmake -DCPP_HTTPLIB_DIR="$TOPDIR"/3rdparty/cpp-httplib -DRAVENNA_ALSA_LKM_DIR="$TOPDIR"/3rdparty/ravenna-alsa-lkm -DENABLE_TESTS=ON -DWITH_AVAHI=ON . +cmake -DCPP_HTTPLIB_DIR="$TOPDIR"/3rdparty/cpp-httplib -DRAVENNA_ALSA_LKM_DIR="$TOPDIR"/3rdparty/ravenna-alsa-lkm -DENABLE_TESTS=ON -DWITH_AVAHI=ON -DFAKE_DRIVER=OFF . make cd .. diff --git a/buildfake.sh b/buildfake.sh new file mode 100755 index 0000000..3d6b550 --- /dev/null +++ b/buildfake.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# +# Tested on Ubuntu 18.04 +# + +#we need clang when compiling on ARMv7 +export CC=/usr/bin/clang +export CXX=/usr/bin/clang++ + +TOPDIR=$(pwd) + +git config --global http.sslverify false + +cd 3rdparty +if [ ! -d ravenna-alsa-lkm ]; then + git clone --single-branch --branch aes67-daemon https://github.com/bondagit/ravenna-alsa-lkm.git +fi + +if [ ! -d cpp-httplib ]; then + git clone https://github.com/yhirose/cpp-httplib.git + cd cpp-httplib + git checkout 42f9f9107f87ad2ee04be117dbbadd621c449552 + cd .. +fi +cd .. + +cd daemon +echo "Building aes67-daemon ..." +cmake -DCPP_HTTPLIB_DIR="$TOPDIR"/3rdparty/cpp-httplib -DRAVENNA_ALSA_LKM_DIR="$TOPDIR"/3rdparty/ravenna-alsa-lkm -DENABLE_TESTS=ON -DWITH_AVAHI=OFF -DFAKE_DRIVER=ON . +make +cd .. + diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt index 27d74e9..2390458 100644 --- a/daemon/CMakeLists.txt +++ b/daemon/CMakeLists.txt @@ -8,16 +8,17 @@ if( ENABLE_TESTS ) endif() option(WITH_AVAHI "Include mDNS support via Avahi" OFF) +option(FAKE_DRIVER "Use fake driver instead of RAVENNA" OFF) set(CMAKE_CXX_STANDARD 17) # ravena lkm _should_ be provided by the CLI. Nonetheless, we should be able # to find it in system dirs too... -if ( NOT RAVENNNA_ALSA_LKM_DIR ) +if (NOT RAVENNNA_ALSA_LKM_DIR) find_path(RAVENNA_ALSA_LKM_DIR "common/MergingRAVENNACommon.h" REQUIRED) endif() # use sysroot cpp-http lib unless one was explicitly provided in cmdline -if ( NOT CPP_HTTPLIB_DIR ) +if (NOT CPP_HTTPLIB_DIR) find_path( CPP_HTTPLIB_DIR "httplib.h" REQUIRED) endif() @@ -31,13 +32,21 @@ find_package(Boost COMPONENTS system thread log program_options REQUIRED) include_directories(aes67-daemon ${RAVENNA_ALSA_LKM_DIR}/common ${RAVENNA_ALSA_LKM_DIR}/driver ${CPP_HTTPLIB_DIR} ${Boost_INCLUDE_DIR}) add_definitions( -DBOOST_LOG_DYN_LINK -DBOOST_LOG_USE_NATIVE_SYSLOG ) add_compile_options( -Wall ) -add_executable(aes67-daemon error_code.cpp json.cpp main.cpp driver_handler.cpp driver_manager.cpp session_manager.cpp http_server.cpp config.cpp interface.cpp log.cpp sap.cpp browser.cpp rtsp_client.cpp mdns_client.cpp mdns_server.cpp rtsp_server.cpp utils.cpp) +set(SOURCES error_code.cpp json.cpp main.cpp session_manager.cpp http_server.cpp config.cpp interface.cpp log.cpp sap.cpp browser.cpp rtsp_client.cpp mdns_client.cpp mdns_server.cpp rtsp_server.cpp utils.cpp) -if( ENABLE_TESTS ) +if(FAKE_DRIVER) + MESSAGE(STATUS "FAKE_DRIVER") + add_definitions(-D_USE_FAKE_DRIVER_) + list(APPEND SOURCES fake_driver_manager.cpp) +else() + list(APPEND SOURCES driver_handler.cpp driver_manager.cpp) +endif() +add_executable(aes67-daemon ${SOURCES}) + +if(ENABLE_TESTS) add_subdirectory(tests) endif() - target_link_libraries(aes67-daemon ${Boost_LIBRARIES}) if(WITH_AVAHI) MESSAGE(STATUS "WITH_AVAHI") diff --git a/daemon/driver_interface.hpp b/daemon/driver_interface.hpp new file mode 100644 index 0000000..43702fc --- /dev/null +++ b/daemon/driver_interface.hpp @@ -0,0 +1,29 @@ +// +// driver_interface.hpp +// +// Copyright (c) 2019 2022 Andrea Bondavalli. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef _DRIVER_INTERFACE_HPP_ +#define _DRIVER_INTERFACE_HPP_ + +#ifdef _USE_FAKE_DRIVER_ +#include "fake_driver_manager.hpp" +#else +#include "driver_manager.hpp" +#endif + +#endif diff --git a/daemon/fake_driver_manager.cpp b/daemon/fake_driver_manager.cpp new file mode 100644 index 0000000..40983e0 --- /dev/null +++ b/daemon/fake_driver_manager.cpp @@ -0,0 +1,185 @@ +// +// fake_driver_manager.cpp +// +// Copyright (c) 2019 2022 Andrea Bondavalli. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "log.hpp" +#include "fake_driver_manager.hpp" + +static const std::vector ptp_status_str = {"unlocked", "locking", + "locked"}; + +std::shared_ptr DriverManager::create() { + // no need to be thread-safe here + static std::weak_ptr instance; + if (auto ptr = instance.lock()) { + return ptr; + } + auto ptr = std::shared_ptr(new DriverManager()); + instance = ptr; + return ptr; +} + +bool DriverManager::init(const Config& config) { + sample_rate_ = config.get_sample_rate(); + + TPTPConfig ptp_config; + ptp_config.ui8Domain = config.get_ptp_domain(); + ptp_config.ui8DSCP = config.get_ptp_dscp(); + + if (hello()) + return false; + + bool res(false); + if (config.get_driver_restart()) { + res = start() || reset() || + set_interface_name(config.get_interface_name()) || + set_ptp_config(ptp_config) || + set_tic_frame_size_at_1fs(config.get_tic_frame_size_at_1fs()) || + set_playout_delay(config.get_playout_delay()) || + set_max_tic_frame_size(config.get_max_tic_frame_size()); + } + + return !res; +} + +bool DriverManager::terminate(const Config& config) { + if (config.get_driver_restart()) { + stop(); + } + bye(); + return true; +} + +std::error_code DriverManager::hello() { + return std::error_code{}; +} + +std::error_code DriverManager::bye() { + return std::error_code{}; +} + +std::error_code DriverManager::start() { + return std::error_code{}; +} + +std::error_code DriverManager::stop() { + return std::error_code{}; +} + +std::error_code DriverManager::reset() { + return std::error_code{}; +} + +std::error_code DriverManager::set_ptp_config(const TPTPConfig& config) { + BOOST_LOG_TRIVIAL(info) << "fake_driver_manager:: setting PTP Domain " + << (int)config.ui8Domain << " DSCP " + << (int)config.ui8DSCP; + ptp_config_ = config; + return std::error_code{}; +} + +std::error_code DriverManager::get_ptp_config(TPTPConfig& config) { + config = ptp_config_; + BOOST_LOG_TRIVIAL(debug) << "fake_driver_manager:: PTP Domain " + << (int)config.ui8Domain << " DSCP " + << (int)config.ui8DSCP; + return std::error_code{}; +} + +std::error_code DriverManager::get_ptp_status(TPTPStatus& status) { + status.nPTPLockStatus = PTPLS_UNLOCKED; + status.ui64GMID = 0xABABABABABABABAB; + status.i32Jitter = 0; + BOOST_LOG_TRIVIAL(debug) << "fake_driver_manager:: PTP Status " + << ptp_status_str[status.nPTPLockStatus] << " GMID " + << status.ui64GMID << " Jitter " << status.i32Jitter; + return std::error_code{}; +} + +std::error_code DriverManager::set_interface_name(const std::string& ifname) { + return std::error_code{}; +} + +std::error_code DriverManager::add_rtp_stream( + const TRTP_stream_info& stream_info, + uint64_t& stream_handle) { + stream_handle = ++g_handle; + handles_.insert(stream_handle); + BOOST_LOG_TRIVIAL(info) << "fake_driver_manager:: add RTP stream success handle " + << stream_handle; + return std::error_code{}; +} + +std::error_code DriverManager::get_rtp_stream_status( + uint64_t stream_handle, + TRTP_stream_status& stream_status) { + stream_status.u.flags = 0x0; + stream_status.sink_min_time = 0; + if (handles_.find(stream_handle) == handles_.end()) { + return DriverErrc::invalid_value; + } + return std::error_code{}; +} + +std::error_code DriverManager::remove_rtp_stream(uint64_t stream_handle) { + if (handles_.find(stream_handle) == handles_.end()) { + return DriverErrc::invalid_value; + } + handles_.erase(stream_handle); + return std::error_code{}; +} + +std::error_code DriverManager::ping() { + return std::error_code{}; +} + +std::error_code DriverManager::set_sample_rate(uint32_t sample_rate) { + sample_rate_ = sample_rate; + return std::error_code{}; +} + +std::error_code DriverManager::set_tic_frame_size_at_1fs(uint64_t frame_size) { + frame_size_ = frame_size; + return std::error_code{}; +} + +std::error_code DriverManager::set_max_tic_frame_size(uint64_t frame_size) { + max_frame_size_ = frame_size; + return std::error_code{}; +} + +std::error_code DriverManager::set_playout_delay(int32_t delay) { + delay_ = delay; + return std::error_code{}; +} + +std::error_code DriverManager::get_sample_rate(uint32_t& sample_rate) { + sample_rate = sample_rate_; + BOOST_LOG_TRIVIAL(info) << "fake_driver_manager:: sample rate " << sample_rate; + return std::error_code{}; +} + +std::error_code DriverManager::get_number_of_inputs(int32_t& inputs) { + inputs = 0; + return std::error_code{}; +} + +std::error_code DriverManager::get_number_of_outputs(int32_t& outputs) { + outputs = 0; + return std::error_code{}; +} diff --git a/daemon/fake_driver_manager.hpp b/daemon/fake_driver_manager.hpp new file mode 100644 index 0000000..f6c6708 --- /dev/null +++ b/daemon/fake_driver_manager.hpp @@ -0,0 +1,84 @@ +// +// fake_driver_manager.hpp +// +// Copyright (c) 2019 2022 Andrea Bondavalli. All rights reserved. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef _FAKE_DRIVER_MANAGER_HPP_ +#define _FAKE_DRIVER_MANAGER_HPP_ + +#include + +#include "error_code.hpp" +#include "RTP_stream_info.h" +#include "audio_streamer_clock_PTP_defs.h" + +class DriverManager { + public: + static std::shared_ptr create(); + + // driver interface + bool init(const Config& config); + bool terminate(const Config& config); + + std::error_code ping(); // unused, return error + std::error_code set_ptp_config(const TPTPConfig& config); + std::error_code get_ptp_config(TPTPConfig& config); + std::error_code get_ptp_status(TPTPStatus& status); + std::error_code set_interface_name(const std::string& ifname); + std::error_code add_rtp_stream(const TRTP_stream_info& stream_info, + uint64_t& stream_handle); + std::error_code get_rtp_stream_status(uint64_t stream_handle, + TRTP_stream_status& stream_status); + std::error_code remove_rtp_stream(uint64_t stream_handle); + std::error_code get_sample_rate(uint32_t& sample_rate); + std::error_code set_sample_rate(uint32_t sample_rate); + std::error_code set_tic_frame_size_at_1fs(uint64_t frame_size); + std::error_code set_max_tic_frame_size(uint64_t frame_size); + std::error_code set_playout_delay(int32_t delay); + std::error_code get_number_of_inputs(int32_t& inputs); + std::error_code get_number_of_outputs(int32_t& outputs); + + int32_t get_current_output_volume() { return output_volume_; }; + int32_t get_current_output_switch() { return output_switch_; }; + uint32_t get_current_sample_rate() { return sample_rate_; }; + + protected: + // singleton, use create to build + DriverManager(){}; + + // these are used in init/terminate + std::error_code hello(); + std::error_code start(); + std::error_code stop(); + std::error_code reset(); + std::error_code bye(); + + std::error_code retcode_; + + int32_t output_volume_{-20}; + int32_t output_switch_{0}; + uint32_t sample_rate_{0}; + uint64_t frame_size_{0}; + uint64_t max_frame_size_{0}; + uint32_t delay_{0}; + TPTPConfig ptp_config_; + + std::set handles_; + inline static uint16_t g_handle{0}; +}; + +#endif diff --git a/daemon/main.cpp b/daemon/main.cpp index 76b0986..e65f7ba 100644 --- a/daemon/main.cpp +++ b/daemon/main.cpp @@ -23,7 +23,7 @@ #include "browser.hpp" #include "config.hpp" -#include "driver_manager.hpp" +#include "driver_interface.hpp" #include "http_server.hpp" #include "interface.hpp" #include "log.hpp" @@ -35,7 +35,7 @@ namespace po = boost::program_options; namespace postyle = boost::program_options::command_line_style; namespace logging = boost::log; -static std::string version("bondagit-1.5.2"); +static std::string version("bondagit-1.5.3"); static std::atomic terminate = false; void termination_handler(int signum) { diff --git a/daemon/session_manager.cpp b/daemon/session_manager.cpp index 6d44cb7..770251e 100644 --- a/daemon/session_manager.cpp +++ b/daemon/session_manager.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include "json.hpp" #include "log.hpp" diff --git a/daemon/session_manager.hpp b/daemon/session_manager.hpp index b580b14..3fcc16f 100644 --- a/daemon/session_manager.hpp +++ b/daemon/session_manager.hpp @@ -27,7 +27,7 @@ #include #include "config.hpp" -#include "driver_manager.hpp" +#include "driver_interface.hpp" #include "igmp.hpp" #include "sap.hpp" diff --git a/daemon/tests/daemon_test.cpp b/daemon/tests/daemon_test.cpp index 3e8cabe..bb7501f 100644 --- a/daemon/tests/daemon_test.cpp +++ b/daemon/tests/daemon_test.cpp @@ -612,7 +612,7 @@ BOOST_AUTO_TEST_CASE(sink_check_status) { auto is_sink_some_muted = pt.get("sink_flags.some_muted"); auto is_sink_all_muted = pt.get("sink_flags.all_muted"); // BOOST_REQUIRE_MESSAGE(is_sink_muted, "sink is muted"); - BOOST_REQUIRE_MESSAGE(!is_sink_all_muted, "all sinks are mutes"); + BOOST_REQUIRE_MESSAGE(!is_sink_all_muted, "all sinks are muted"); BOOST_REQUIRE_MESSAGE(!is_sink_some_muted, "some sinks are muted"); BOOST_REQUIRE_MESSAGE(cli.remove_sink(0), "removed sink 0"); }