diff --git a/common/utils/string_utils.hpp b/common/utils/string_utils.hpp index b35e7e36..5ab6ad50 100644 --- a/common/utils/string_utils.hpp +++ b/common/utils/string_utils.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -16,8 +16,8 @@ along with this program. If not, see . ***/ -#ifndef STRING_UTILS_H -#define STRING_UTILS_H +#ifndef STRING_UTILS_HPP +#define STRING_UTILS_HPP #include #include @@ -172,6 +172,20 @@ static std::map split_pairs(const std::string& s, char } +static inline std::string& tolower(std::string& s) +{ + std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return static_cast(std::tolower(c)); }); + return s; +} + + +static inline std::string tolower_copy(const std::string& s) +{ + std::string str(s); + return tolower(str); +} + + } // namespace string } // namespace utils diff --git a/server/server.cpp b/server/server.cpp index 3fc65c25..6e47e185 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -693,9 +693,8 @@ void Server::start() { controlServer_ = std::make_unique(io_context_, settings_.tcp, settings_.http, this); streamServer_ = std::make_unique(io_context_, settings_, this); - streamManager_ = - std::make_unique(this, io_context_, settings_.stream.sampleFormat, settings_.stream.codec, settings_.stream.streamChunkMs); - // throw SnapException("xxx"); + streamManager_ = std::make_unique(this, io_context_, settings_); + // Add normal sources first for (const auto& sourceUri : settings_.stream.sources) { diff --git a/server/streamreader/airplay_stream.cpp b/server/streamreader/airplay_stream.cpp index f1edac62..100faddb 100644 --- a/server/streamreader/airplay_stream.cpp +++ b/server/streamreader/airplay_stream.cpp @@ -47,8 +47,8 @@ string hex2str(const string& input) * Without HAS_EXPAT defined no parsing will occur. */ -AirplayStream::AirplayStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri) - : ProcessStream(pcmListener, ioc, uri), port_(5000), pipe_open_timer_(ioc) +AirplayStream::AirplayStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri) + : ProcessStream(pcmListener, ioc, server_settings, uri), port_(5000), pipe_open_timer_(ioc) { logStderr_ = true; diff --git a/server/streamreader/airplay_stream.hpp b/server/streamreader/airplay_stream.hpp index 08758876..51fa4d38 100644 --- a/server/streamreader/airplay_stream.hpp +++ b/server/streamreader/airplay_stream.hpp @@ -59,7 +59,7 @@ class AirplayStream : public ProcessStream { public: /// ctor. Encoded PCM data is passed to the PipeListener - AirplayStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri); + AirplayStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); ~AirplayStream() override; protected: diff --git a/server/streamreader/alsa_stream.cpp b/server/streamreader/alsa_stream.cpp index 6a4867bd..47a0e542 100644 --- a/server/streamreader/alsa_stream.cpp +++ b/server/streamreader/alsa_stream.cpp @@ -65,8 +65,8 @@ void wait(boost::asio::steady_timer& timer, const std::chrono::duration::wait(Timer& timer, const std::chrono::duration -AsioStream::AsioStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri) - : PcmStream(pcmListener, ioc, uri), read_timer_(ioc), state_timer_(ioc) +AsioStream::AsioStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri) + : PcmStream(pcmListener, ioc, server_settings, uri), read_timer_(ioc), state_timer_(ioc) { chunk_ = std::make_unique(sampleFormat_, chunk_ms_); LOG(DEBUG, "AsioStream") << "Chunk duration: " << chunk_->durationMs() << " ms, frames: " << chunk_->getFrameCount() << ", size: " << chunk_->payloadSize diff --git a/server/streamreader/file_stream.cpp b/server/streamreader/file_stream.cpp index c82cf5c1..a2f55c0e 100644 --- a/server/streamreader/file_stream.cpp +++ b/server/streamreader/file_stream.cpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -34,7 +34,8 @@ namespace streamreader static constexpr auto LOG_TAG = "FileStream"; -FileStream::FileStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri) : PosixStream(pcmListener, ioc, uri) +FileStream::FileStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri) + : PosixStream(pcmListener, ioc, server_settings, uri) { struct stat buffer; if (stat(uri_.path.c_str(), &buffer) != 0) diff --git a/server/streamreader/file_stream.hpp b/server/streamreader/file_stream.hpp index 1a7ffbe2..d2e1398c 100644 --- a/server/streamreader/file_stream.hpp +++ b/server/streamreader/file_stream.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -34,7 +34,7 @@ class FileStream : public PosixStream { public: /// ctor. Encoded PCM data is passed to the PipeListener - FileStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri); + FileStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); protected: void do_connect() override; diff --git a/server/streamreader/librespot_stream.cpp b/server/streamreader/librespot_stream.cpp index 91c18cef..ba3dc53e 100644 --- a/server/streamreader/librespot_stream.cpp +++ b/server/streamreader/librespot_stream.cpp @@ -32,7 +32,8 @@ namespace streamreader static constexpr auto LOG_TAG = "LibrespotStream"; -LibrespotStream::LibrespotStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri) : ProcessStream(pcmListener, ioc, uri) +LibrespotStream::LibrespotStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri) + : ProcessStream(pcmListener, ioc, server_settings, uri) { wd_timeout_sec_ = cpt::stoul(uri_.getQuery("wd_timeout", "7800")); ///< 130min diff --git a/server/streamreader/librespot_stream.hpp b/server/streamreader/librespot_stream.hpp index ad17c9c3..5f467547 100644 --- a/server/streamreader/librespot_stream.hpp +++ b/server/streamreader/librespot_stream.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -37,7 +37,7 @@ class LibrespotStream : public ProcessStream { public: /// ctor. Encoded PCM data is passed to the PipeListener - LibrespotStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri); + LibrespotStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); protected: bool killall_; diff --git a/server/streamreader/meta_stream.cpp b/server/streamreader/meta_stream.cpp index f14aee5a..2b7e6613 100644 --- a/server/streamreader/meta_stream.cpp +++ b/server/streamreader/meta_stream.cpp @@ -32,8 +32,9 @@ static constexpr auto LOG_TAG = "MetaStream"; // static constexpr auto kResyncTolerance = 50ms; -MetaStream::MetaStream(PcmListener* pcmListener, const std::vector>& streams, boost::asio::io_context& ioc, const StreamUri& uri) - : PcmStream(pcmListener, ioc, uri), first_read_(true) +MetaStream::MetaStream(PcmListener* pcmListener, const std::vector>& streams, boost::asio::io_context& ioc, + const ServerSettings& server_settings, const StreamUri& uri) + : PcmStream(pcmListener, ioc, server_settings, uri), first_read_(true) { auto path_components = utils::string::split(uri.path, '/'); for (const auto& component : path_components) diff --git a/server/streamreader/meta_stream.hpp b/server/streamreader/meta_stream.hpp index 8000b49f..4a165f04 100644 --- a/server/streamreader/meta_stream.hpp +++ b/server/streamreader/meta_stream.hpp @@ -37,7 +37,8 @@ class MetaStream : public PcmStream, public PcmListener { public: /// ctor. Encoded PCM data is passed to the PcmListener - MetaStream(PcmListener* pcmListener, const std::vector>& streams, boost::asio::io_context& ioc, const StreamUri& uri); + MetaStream(PcmListener* pcmListener, const std::vector>& streams, boost::asio::io_context& ioc, + const ServerSettings& server_settings, const StreamUri& uri); virtual ~MetaStream(); void start() override; diff --git a/server/streamreader/pcm_stream.cpp b/server/streamreader/pcm_stream.cpp index 15a942ae..e47ebfae 100644 --- a/server/streamreader/pcm_stream.cpp +++ b/server/streamreader/pcm_stream.cpp @@ -23,6 +23,7 @@ #include "common/aixlog.hpp" #include "common/snap_exception.hpp" #include "common/str_compat.hpp" +#include "common/utils/string_utils.hpp" #include "encoder/encoder_factory.hpp" #include "pcm_stream.hpp" @@ -33,10 +34,118 @@ namespace streamreader { static constexpr auto LOG_TAG = "PcmStream"; +static constexpr auto SCRIPT_LOG_TAG = "Script"; -PcmStream::PcmStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri) - : active_(false), pcmListeners_{pcmListener}, uri_(uri), chunk_ms_(20), state_(ReaderState::kIdle), ioc_(ioc) +CtrlScript::CtrlScript(boost::asio::io_context& ioc, const std::string& script) : ioc_(ioc), script_(script) +{ +} + + +CtrlScript::~CtrlScript() +{ + stop(); +} + + +void CtrlScript::start(const std::string& stream_id, const ServerSettings& server_setttings) +{ + pipe_stderr_ = bp::pipe(); + pipe_stdout_ = bp::pipe(); + stringstream params; + params << " \"--stream=" + stream_id + "\""; + if (server_setttings.http.enabled) + params << " --snapcast-port=" << server_setttings.http.port; + process_ = bp::child(script_ + params.str(), bp::std_out > pipe_stdout_, bp::std_err > pipe_stderr_); + stream_stdout_ = make_unique(ioc_, pipe_stdout_.native_source()); + stream_stderr_ = make_unique(ioc_, pipe_stderr_.native_source()); + stderrReadLine(); + stdoutReadLine(); +} + + +void CtrlScript::logScript(const std::string& source, std::string line) +{ + if (line.empty()) + return; + + std::ignore = source; + if (line.back() == '\r') + line.resize(line.size() - 1); + auto tmp = utils::string::tolower_copy(line); + AixLog::Severity severity = AixLog::Severity::info; + if (tmp.find(" trace") != string::npos) + severity = AixLog::Severity::trace; + else if (tmp.find(" debug") != string::npos) + severity = AixLog::Severity::debug; + else if (tmp.find(" info") != string::npos) + severity = AixLog::Severity::info; + else if (tmp.find(" warning") != string::npos) + severity = AixLog::Severity::warning; + else if (tmp.find(" error") != string::npos) + severity = AixLog::Severity::error; + else if ((tmp.find(" fatal") != string::npos) || (tmp.find(" critical") != string::npos)) + severity = AixLog::Severity::fatal; + LOG(severity, SCRIPT_LOG_TAG) << line << "\n"; +} + + +void CtrlScript::stderrReadLine() +{ + const std::string delimiter = "\n"; + boost::asio::async_read_until( + *stream_stderr_, streambuf_stderr_, delimiter, + [this, delimiter](const std::error_code& ec, std::size_t bytes_transferred) + { + if (ec) + { + LOG(ERROR, LOG_TAG) << "Error while reading from stderr: " << ec.message() << "\n"; + return; + } + + // Extract up to the first delimiter. + std::string line{buffers_begin(streambuf_stderr_.data()), buffers_begin(streambuf_stderr_.data()) + bytes_transferred - delimiter.length()}; + logScript("stderr", std::move(line)); + streambuf_stderr_.consume(bytes_transferred); + stderrReadLine(); + }); +} + + +void CtrlScript::stdoutReadLine() +{ + const std::string delimiter = "\n"; + boost::asio::async_read_until( + *stream_stdout_, streambuf_stdout_, delimiter, + [this, delimiter](const std::error_code& ec, std::size_t bytes_transferred) + { + if (ec) + { + LOG(ERROR, LOG_TAG) << "Error while reading from stdout: " << ec.message() << "\n"; + return; + } + + // Extract up to the first delimiter. + std::string line{buffers_begin(streambuf_stdout_.data()), buffers_begin(streambuf_stdout_.data()) + bytes_transferred - delimiter.length()}; + logScript("stdout", std::move(line)); + streambuf_stdout_.consume(bytes_transferred); + stdoutReadLine(); + }); +} + + + +void CtrlScript::stop() +{ + if (process_.running()) + { + ::kill(-process_.native_handle(), SIGINT); + } +} + + +PcmStream::PcmStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri) + : active_(false), pcmListeners_{pcmListener}, uri_(uri), chunk_ms_(20), state_(ReaderState::kIdle), ioc_(ioc), server_settings_(server_settings) { encoder::EncoderFactory encoderFactory; if (uri_.query.find(kUriCodec) == uri_.query.end()) @@ -52,6 +161,9 @@ PcmStream::PcmStream(PcmListener* pcmListener, boost::asio::io_context& ioc, con sampleFormat_ = SampleFormat(uri_.query[kUriSampleFormat]); LOG(INFO, LOG_TAG) << "PcmStream: " << name_ << ", sampleFormat: " << sampleFormat_.toString() << "\n"; + if (uri_.query.find(kControlScript) != uri_.query.end()) + ctrl_script_ = std::make_unique(ioc, uri_.query[kControlScript]); + if (uri_.query.find(kUriChunkMs) != uri_.query.end()) chunk_ms_ = cpt::stoul(uri_.query[kUriChunkMs]); @@ -108,6 +220,9 @@ void PcmStream::start() encoder_->init([this](const encoder::Encoder& encoder, std::shared_ptr chunk, double duration) { chunkEncoded(encoder, chunk, duration); }, sampleFormat_); active_ = true; + + if (ctrl_script_) + ctrl_script_->start(getId(), server_settings_); } @@ -184,18 +299,10 @@ void PcmStream::resync(const std::chrono::nanoseconds& duration) json PcmStream::toJson() const { - string state("unknown"); - if (state_ == ReaderState::kIdle) - state = "idle"; - else if (state_ == ReaderState::kPlaying) - state = "playing"; - else if (state_ == ReaderState::kDisabled) - state = "disabled"; - json j = { {"uri", uri_.toJson()}, {"id", getId()}, - {"status", state}, + {"status", to_string(state_)}, }; if (meta_) diff --git a/server/streamreader/pcm_stream.hpp b/server/streamreader/pcm_stream.hpp index 9efb1d0c..2b6047ae 100644 --- a/server/streamreader/pcm_stream.hpp +++ b/server/streamreader/pcm_stream.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -19,18 +19,32 @@ #ifndef PCM_STREAM_HPP #define PCM_STREAM_HPP +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-result" +#pragma GCC diagnostic ignored "-Wunused-parameter" +#pragma GCC diagnostic ignored "-Wmissing-braces" +#include +#pragma GCC diagnostic pop +#include +#include +#include +#include +#include + +#include +#include +#include + #include "common/json.hpp" #include "common/sample_format.hpp" #include "encoder/encoder.hpp" #include "message/codec_header.hpp" #include "message/stream_tags.hpp" +#include "server_settings.hpp" #include "stream_uri.hpp" -#include -#include -#include -#include -#include -#include + + +namespace bp = boost::process; namespace streamreader @@ -46,30 +60,37 @@ enum class ReaderState kDisabled = 3 }; -static std::ostream& operator<<(std::ostream& os, const ReaderState& reader_state) + +static std::string to_string(const ReaderState& reader_state) { switch (reader_state) { case ReaderState::kIdle: - os << "idle"; - break; + return "idle"; case ReaderState::kPlaying: - os << "playing"; - break; + return "playing"; case ReaderState::kDisabled: - os << "disabled"; - break; + return "disabled"; case ReaderState::kUnknown: default: - os << "unknown"; + return "unknown"; } +} + + +static std::ostream& operator<<(std::ostream& os, const ReaderState& reader_state) +{ + os << to_string(reader_state); return os; } + + static constexpr auto kUriCodec = "codec"; static constexpr auto kUriName = "name"; static constexpr auto kUriSampleFormat = "sampleformat"; static constexpr auto kUriChunkMs = "chunk_ms"; +static constexpr auto kControlScript = "controlscript"; /// Callback interface for users of PcmStream @@ -87,6 +108,33 @@ public: }; +class CtrlScript +{ +public: + CtrlScript(boost::asio::io_context& ioc, const std::string& script); + virtual ~CtrlScript(); + + void start(const std::string& stream_id, const ServerSettings& server_setttings); + void stop(); + +private: + void stderrReadLine(); + void stdoutReadLine(); + void logScript(const std::string& source, std::string line); + + bp::child process_; + bp::pipe pipe_stdout_; + bp::pipe pipe_stderr_; + std::unique_ptr stream_stdout_; + std::unique_ptr stream_stderr_; + boost::asio::streambuf streambuf_stdout_; + boost::asio::streambuf streambuf_stderr_; + + boost::asio::io_context& ioc_; + std::string script_; +}; + + /// Reads and decodes PCM data /** * Reads PCM and passes the data to an encoder. @@ -97,7 +145,7 @@ class PcmStream { public: /// ctor. Encoded PCM data is passed to the PcmListener - PcmStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri); + PcmStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); virtual ~PcmStream(); virtual void start(); @@ -137,6 +185,8 @@ protected: ReaderState state_; std::shared_ptr meta_; boost::asio::io_context& ioc_; + ServerSettings server_settings_; + std::unique_ptr ctrl_script_; }; } // namespace streamreader diff --git a/server/streamreader/pipe_stream.cpp b/server/streamreader/pipe_stream.cpp index 0b890e79..d8995ef3 100644 --- a/server/streamreader/pipe_stream.cpp +++ b/server/streamreader/pipe_stream.cpp @@ -36,7 +36,8 @@ namespace streamreader static constexpr auto LOG_TAG = "PipeStream"; -PipeStream::PipeStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri) : PosixStream(pcmListener, ioc, uri) +PipeStream::PipeStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri) + : PosixStream(pcmListener, ioc, server_settings, uri) { umask(0); string mode = uri_.getQuery("mode", "create"); diff --git a/server/streamreader/pipe_stream.hpp b/server/streamreader/pipe_stream.hpp index 4c4b590b..3b72a5a9 100644 --- a/server/streamreader/pipe_stream.hpp +++ b/server/streamreader/pipe_stream.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -37,7 +37,7 @@ class PipeStream : public PosixStream { public: /// ctor. Encoded PCM data is passed to the PipeListener - PipeStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri); + PipeStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); protected: void do_connect() override; diff --git a/server/streamreader/posix_stream.cpp b/server/streamreader/posix_stream.cpp index df375e89..99deea90 100644 --- a/server/streamreader/posix_stream.cpp +++ b/server/streamreader/posix_stream.cpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -37,7 +37,8 @@ namespace streamreader static constexpr auto LOG_TAG = "PosixStream"; static constexpr auto kResyncTolerance = 50ms; -PosixStream::PosixStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri) : AsioStream(pcmListener, ioc, uri) +PosixStream::PosixStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri) + : AsioStream(pcmListener, ioc, server_settings, uri) { if (uri_.query.find("dryout_ms") != uri_.query.end()) dryout_ms_ = cpt::stoul(uri_.query["dryout_ms"]); diff --git a/server/streamreader/posix_stream.hpp b/server/streamreader/posix_stream.hpp index 8acf609f..9ef43b44 100644 --- a/server/streamreader/posix_stream.hpp +++ b/server/streamreader/posix_stream.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -37,7 +37,7 @@ class PosixStream : public AsioStream { public: /// ctor. Encoded PCM data is passed to the PipeListener - PosixStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri); + PosixStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); protected: void connect() override; diff --git a/server/streamreader/process_stream.cpp b/server/streamreader/process_stream.cpp index c513bc35..0b051a51 100644 --- a/server/streamreader/process_stream.cpp +++ b/server/streamreader/process_stream.cpp @@ -35,7 +35,8 @@ namespace streamreader static constexpr auto LOG_TAG = "ProcessStream"; -ProcessStream::ProcessStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri) : PosixStream(pcmListener, ioc, uri) +ProcessStream::ProcessStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri) + : PosixStream(pcmListener, ioc, server_settings, uri) { params_ = uri_.getQuery("params"); wd_timeout_sec_ = cpt::stoul(uri_.getQuery("wd_timeout", "0")); diff --git a/server/streamreader/process_stream.hpp b/server/streamreader/process_stream.hpp index ce38ad11..4839351a 100644 --- a/server/streamreader/process_stream.hpp +++ b/server/streamreader/process_stream.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -19,12 +19,6 @@ #ifndef PROCESS_STREAM_HPP #define PROCESS_STREAM_HPP -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-result" -#pragma GCC diagnostic ignored "-Wunused-parameter" -#pragma GCC diagnostic ignored "-Wmissing-braces" -#include -#pragma GCC diagnostic pop #include #include #include @@ -49,7 +43,7 @@ class ProcessStream : public PosixStream, public WatchdogListener { public: /// ctor. Encoded PCM data is passed to the PipeListener - ProcessStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri); + ProcessStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); ~ProcessStream() override = default; protected: diff --git a/server/streamreader/stream_manager.cpp b/server/streamreader/stream_manager.cpp index c1772907..0849a9a9 100644 --- a/server/streamreader/stream_manager.cpp +++ b/server/streamreader/stream_manager.cpp @@ -38,9 +38,9 @@ using namespace std; namespace streamreader { -StreamManager::StreamManager(PcmListener* pcmListener, boost::asio::io_context& ioc, const std::string& defaultSampleFormat, const std::string& defaultCodec, - size_t defaultChunkBufferMs) - : pcmListener_(pcmListener), sampleFormat_(defaultSampleFormat), codec_(defaultCodec), chunkBufferMs_(defaultChunkBufferMs), ioc_(ioc) +StreamManager::StreamManager(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& settings) + // const std::string& defaultSampleFormat, const std::string& defaultCodec, size_t defaultChunkBufferMs) + : pcmListener_(pcmListener), settings_(settings), ioc_(ioc) { } @@ -55,13 +55,13 @@ PcmStreamPtr StreamManager::addStream(const std::string& uri) PcmStreamPtr StreamManager::addStream(StreamUri& streamUri) { if (streamUri.query.find(kUriSampleFormat) == streamUri.query.end()) - streamUri.query[kUriSampleFormat] = sampleFormat_; + streamUri.query[kUriSampleFormat] = settings_.stream.sampleFormat; if (streamUri.query.find(kUriCodec) == streamUri.query.end()) - streamUri.query[kUriCodec] = codec_; + streamUri.query[kUriCodec] = settings_.stream.codec; if (streamUri.query.find(kUriChunkMs) == streamUri.query.end()) - streamUri.query[kUriChunkMs] = cpt::to_string(chunkBufferMs_); + streamUri.query[kUriChunkMs] = cpt::to_string(settings_.stream.streamChunkMs); // LOG(DEBUG) << "\nURI: " << streamUri.uri << "\nscheme: " << streamUri.scheme << "\nhost: " // << streamUri.host << "\npath: " << streamUri.path << "\nfragment: " << streamUri.fragment << "\n"; @@ -72,20 +72,20 @@ PcmStreamPtr StreamManager::addStream(StreamUri& streamUri) if (streamUri.scheme == "pipe") { - stream = make_shared(pcmListener_, ioc_, streamUri); + stream = make_shared(pcmListener_, ioc_, settings_, streamUri); } else if (streamUri.scheme == "file") { - stream = make_shared(pcmListener_, ioc_, streamUri); + stream = make_shared(pcmListener_, ioc_, settings_, streamUri); } else if (streamUri.scheme == "process") { - stream = make_shared(pcmListener_, ioc_, streamUri); + stream = make_shared(pcmListener_, ioc_, settings_, streamUri); } #ifdef HAS_ALSA else if (streamUri.scheme == "alsa") { - stream = make_shared(pcmListener_, ioc_, streamUri); + stream = make_shared(pcmListener_, ioc_, settings_, streamUri); } #endif else if ((streamUri.scheme == "spotify") || (streamUri.scheme == "librespot")) @@ -94,7 +94,7 @@ PcmStreamPtr StreamManager::addStream(StreamUri& streamUri) // that all constructors of all parent classes also use the overwritten sample // format. streamUri.query[kUriSampleFormat] = "44100:16:2"; - stream = make_shared(pcmListener_, ioc_, streamUri); + stream = make_shared(pcmListener_, ioc_, settings_, streamUri); } else if (streamUri.scheme == "airplay") { @@ -102,15 +102,15 @@ PcmStreamPtr StreamManager::addStream(StreamUri& streamUri) // that all constructors of all parent classes also use the overwritten sample // format. streamUri.query[kUriSampleFormat] = "44100:16:2"; - stream = make_shared(pcmListener_, ioc_, streamUri); + stream = make_shared(pcmListener_, ioc_, settings_, streamUri); } else if (streamUri.scheme == "tcp") { - stream = make_shared(pcmListener_, ioc_, streamUri); + stream = make_shared(pcmListener_, ioc_, settings_, streamUri); } else if (streamUri.scheme == "meta") { - stream = make_shared(pcmListener_, streams_, ioc_, streamUri); + stream = make_shared(pcmListener_, streams_, ioc_, settings_, streamUri); } else { diff --git a/server/streamreader/stream_manager.hpp b/server/streamreader/stream_manager.hpp index c767092c..3de01482 100644 --- a/server/streamreader/stream_manager.hpp +++ b/server/streamreader/stream_manager.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -20,6 +20,8 @@ #define STREAM_MANAGER_HPP #include "pcm_stream.hpp" +#include "server_settings.hpp" + #include #include #include @@ -33,8 +35,7 @@ using PcmStreamPtr = std::shared_ptr; class StreamManager { public: - StreamManager(PcmListener* pcmListener, boost::asio::io_context& ioc, const std::string& defaultSampleFormat, const std::string& defaultCodec, - size_t defaultChunkBufferMs = 20); + StreamManager(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& settings); PcmStreamPtr addStream(const std::string& uri); PcmStreamPtr addStream(StreamUri& streamUri); @@ -49,9 +50,7 @@ public: private: std::vector streams_; PcmListener* pcmListener_; - std::string sampleFormat_; - std::string codec_; - size_t chunkBufferMs_; + ServerSettings settings_; boost::asio::io_context& ioc_; }; diff --git a/server/streamreader/tcp_stream.cpp b/server/streamreader/tcp_stream.cpp index f17a0cc0..844282c5 100644 --- a/server/streamreader/tcp_stream.cpp +++ b/server/streamreader/tcp_stream.cpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -37,8 +37,8 @@ namespace streamreader static constexpr auto LOG_TAG = "TcpStream"; -TcpStream::TcpStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri) - : AsioStream(pcmListener, ioc, uri), reconnect_timer_(ioc) +TcpStream::TcpStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri) + : AsioStream(pcmListener, ioc, server_settings, uri), reconnect_timer_(ioc) { host_ = uri_.host; auto host_port = utils::string::split(host_, ':'); diff --git a/server/streamreader/tcp_stream.hpp b/server/streamreader/tcp_stream.hpp index b5a599ea..8039b746 100644 --- a/server/streamreader/tcp_stream.hpp +++ b/server/streamreader/tcp_stream.hpp @@ -1,6 +1,6 @@ /*** This file is part of snapcast - Copyright (C) 2014-2020 Johannes Pohl + Copyright (C) 2014-2021 Johannes Pohl 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 @@ -36,7 +36,7 @@ class TcpStream : public AsioStream { public: /// ctor. Encoded PCM data is passed to the PipeListener - TcpStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const StreamUri& uri); + TcpStream(PcmListener* pcmListener, boost::asio::io_context& ioc, const ServerSettings& server_settings, const StreamUri& uri); protected: void do_connect() override;