configuration is stored in file snapserver.conf

This commit is contained in:
badaix 2019-10-06 13:28:22 +02:00
parent fa508eafba
commit 72bd17cb20
11 changed files with 266 additions and 89 deletions

View file

@ -4,7 +4,7 @@
<title>Snapcast Interface</title> <title>Snapcast Interface</title>
Taken from <a href="https://github.com/derglaus/snapcast-websockets-ui">derglaus/snapcast-websockets-ui</a> for testing purposes Taken from <a href="https://github.com/derglaus/snapcast-websockets-ui">derglaus/snapcast-websockets-ui</a> for testing purposes
<script> <script>
var connection = new WebSocket('ws://127.0.0.1:8080/jsonrpc'); var connection = new WebSocket('ws://127.0.0.1:1780/jsonrpc');
var server; var server;
connection.onmessage = function (e) { connection.onmessage = function (e) {

View file

@ -32,8 +32,9 @@ using namespace std;
using json = nlohmann::json; using json = nlohmann::json;
ControlServer::ControlServer(boost::asio::io_context* io_context, size_t port, ControlMessageReceiver* controlMessageReceiver) ControlServer::ControlServer(boost::asio::io_context* io_context, const ServerSettings::TcpSettings& tcp_settings,
: io_context_(io_context), port_(port), controlMessageReceiver_(controlMessageReceiver) const ServerSettings::HttpSettings& http_settings, ControlMessageReceiver* controlMessageReceiver)
: io_context_(io_context), controlMessageReceiver_(controlMessageReceiver), tcp_settings_(tcp_settings), http_settings_(http_settings)
{ {
} }
@ -95,7 +96,7 @@ void ControlServer::startAccept()
auto accept_handler_http = [this](error_code ec, tcp::socket socket) { auto accept_handler_http = [this](error_code ec, tcp::socket socket) {
if (!ec) if (!ec)
handleAccept<ControlSessionHttp>(std::move(socket)); handleAccept<ControlSessionHttp>(std::move(socket), http_settings_);
else else
LOG(ERROR) << "Error while accepting socket connection: " << ec.message() << "\n"; LOG(ERROR) << "Error while accepting socket connection: " << ec.message() << "\n";
}; };
@ -114,8 +115,8 @@ void ControlServer::startAccept()
} }
template <typename SessionType> template <typename SessionType, typename... Args>
void ControlServer::handleAccept(tcp::socket socket) void ControlServer::handleAccept(tcp::socket socket, Args&&... args)
{ {
try try
{ {
@ -126,7 +127,7 @@ void ControlServer::handleAccept(tcp::socket socket)
setsockopt(socket.native_handle(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); setsockopt(socket.native_handle(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
// socket->set_option(boost::asio::ip::tcp::no_delay(false)); // socket->set_option(boost::asio::ip::tcp::no_delay(false));
SLOG(NOTICE) << "ControlServer::NewConnection: " << socket.remote_endpoint().address().to_string() << endl; SLOG(NOTICE) << "ControlServer::NewConnection: " << socket.remote_endpoint().address().to_string() << endl;
shared_ptr<SessionType> session = make_shared<SessionType>(this, std::move(socket)); shared_ptr<SessionType> session = make_shared<SessionType>(this, std::move(socket), std::forward<Args>(args)...);
{ {
std::lock_guard<std::recursive_mutex> mlock(session_mutex_); std::lock_guard<std::recursive_mutex> mlock(session_mutex_);
session->start(); session->start();
@ -179,10 +180,10 @@ std::pair<acceptor_ptr, acceptor_ptr> ControlServer::createAcceptors(size_t port
void ControlServer::start() void ControlServer::start()
{ {
// TODO: should be possible to be disabled if (tcp_settings_.enabled)
acceptor_tcp_ = createAcceptors(port_); acceptor_tcp_ = createAcceptors(tcp_settings_.port);
// TODO: make port configurable, should be possible to be disabled if (http_settings_.enabled)
acceptor_http_ = createAcceptors(8080); acceptor_http_ = createAcceptors(http_settings_.port);
startAccept(); startAccept();
} }

View file

@ -33,6 +33,7 @@
#include "message/codecHeader.h" #include "message/codecHeader.h"
#include "message/message.h" #include "message/message.h"
#include "message/serverSettings.h" #include "message/serverSettings.h"
#include "server_settings.hpp"
using boost::asio::ip::tcp; using boost::asio::ip::tcp;
using acceptor_ptr = std::unique_ptr<tcp::acceptor>; using acceptor_ptr = std::unique_ptr<tcp::acceptor>;
@ -44,7 +45,8 @@ using acceptor_ptr = std::unique_ptr<tcp::acceptor>;
class ControlServer : public ControlMessageReceiver class ControlServer : public ControlMessageReceiver
{ {
public: public:
ControlServer(boost::asio::io_context* io_context, size_t port, ControlMessageReceiver* controlMessageReceiver = nullptr); ControlServer(boost::asio::io_context* io_context, const ServerSettings::TcpSettings& tcp_settings, const ServerSettings::HttpSettings& http_settings,
ControlMessageReceiver* controlMessageReceiver = nullptr);
virtual ~ControlServer(); virtual ~ControlServer();
void start(); void start();
@ -60,9 +62,8 @@ private:
void startAccept(); void startAccept();
std::pair<acceptor_ptr, acceptor_ptr> createAcceptors(size_t port); std::pair<acceptor_ptr, acceptor_ptr> createAcceptors(size_t port);
template <typename SessionType> template <typename SessionType, typename... Args>
void handleAccept(tcp::socket socket); void handleAccept(tcp::socket socket, Args&&... args);
// void handleAcceptWs(tcp::socket socket);
void cleanup(); void cleanup();
mutable std::recursive_mutex session_mutex_; mutable std::recursive_mutex session_mutex_;
@ -72,7 +73,8 @@ private:
std::pair<acceptor_ptr, acceptor_ptr> acceptor_http_; std::pair<acceptor_ptr, acceptor_ptr> acceptor_http_;
boost::asio::io_context* io_context_; boost::asio::io_context* io_context_;
size_t port_; ServerSettings::TcpSettings tcp_settings_;
ServerSettings::HttpSettings http_settings_;
ControlMessageReceiver* controlMessageReceiver_; ControlMessageReceiver* controlMessageReceiver_;
}; };

View file

@ -21,6 +21,7 @@
#include "common/queue.h" #include "common/queue.h"
#include "message/message.h" #include "message/message.h"
#include "server_settings.hpp"
#include <atomic> #include <atomic>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include <condition_variable> #include <condition_variable>
@ -30,7 +31,6 @@
#include <string> #include <string>
#include <thread> #include <thread>
using boost::asio::ip::tcp; using boost::asio::ip::tcp;

View file

@ -98,7 +98,8 @@ std::string path_cat(boost::beast::string_view base, boost::beast::string_view p
} }
} // namespace } // namespace
ControlSessionHttp::ControlSessionHttp(ControlMessageReceiver* receiver, tcp::socket&& socket) : ControlSession(receiver), socket_(std::move(socket)) ControlSessionHttp::ControlSessionHttp(ControlMessageReceiver* receiver, tcp::socket&& socket, const ServerSettings::HttpSettings& settings)
: ControlSession(receiver), socket_(std::move(socket)), settings_(settings)
{ {
LOG(DEBUG) << "ControlSessionHttp\n"; LOG(DEBUG) << "ControlSessionHttp\n";
} }
@ -183,10 +184,11 @@ void ControlSessionHttp::handle_request(http::request<Body, http::basic_fields<A
if (req.target().empty() || req.target()[0] != '/' || req.target().find("..") != beast::string_view::npos) if (req.target().empty() || req.target()[0] != '/' || req.target().find("..") != beast::string_view::npos)
return send(bad_request("Illegal request-target")); return send(bad_request("Illegal request-target"));
// TODO: configurable, enable/disable if (settings_.doc_root.empty())
std::string doc_root = "../control"; return send(not_found(req.target()));
// Build the path to the requested file // Build the path to the requested file
std::string path = path_cat(doc_root, req.target()); std::string path = path_cat(settings_.doc_root, req.target());
if (req.target().back() == '/') if (req.target().back() == '/')
path.append("index.html"); path.append("index.html");

View file

@ -39,7 +39,7 @@ class ControlSessionHttp : public ControlSession, public std::enable_shared_from
{ {
public: public:
/// ctor. Received message from the client are passed to MessageReceiver /// ctor. Received message from the client are passed to MessageReceiver
ControlSessionHttp(ControlMessageReceiver* receiver, tcp::socket&& socket); ControlSessionHttp(ControlMessageReceiver* receiver, tcp::socket&& socket, const ServerSettings::HttpSettings& settings);
~ControlSessionHttp() override; ~ControlSessionHttp() override;
void start() override; void start() override;
void stop() override; void stop() override;
@ -71,6 +71,7 @@ protected:
protected: protected:
tcp::socket socket_; tcp::socket socket_;
beast::flat_buffer buffer_; beast::flat_buffer buffer_;
ServerSettings::HttpSettings settings_;
}; };

View file

@ -0,0 +1,63 @@
/***
This file is part of snapcast
Copyright (C) 2014-2019 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
the Free Software Foundation, either version 3 of the License, or
(at your option) 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 <http://www.gnu.org/licenses/>.
***/
#ifndef SERVER_SETTINGS_HPP
#define SERVER_SETTINGS_HPP
#include <string>
#include <vector>
struct ServerSettings
{
struct HttpSettings
{
bool enabled{true};
size_t port{1780};
std::string doc_root{""};
};
struct TcpSettings
{
bool enabled{true};
size_t port{1705};
};
struct StreamSettings
{
size_t port{1704};
std::vector<std::string> pcmStreams;
std::string codec{"flac"};
int32_t bufferMs{1000};
std::string sampleFormat{"48000:16:2"};
size_t streamReadMs{20};
bool sendAudioToMutedClients{false};
};
struct LoggingSettings
{
bool debug{false};
std::string debug_logfile{""};
};
HttpSettings http;
TcpSettings tcp;
StreamSettings stream;
LoggingSettings logging;
};
#endif

View file

@ -20,7 +20,7 @@
#include <memory> #include <memory>
#include <sys/resource.h> #include <sys/resource.h>
#include "popl.hpp" #include "../externals/popl/include/popl.hpp"
#ifdef HAS_DAEMON #ifdef HAS_DAEMON
#include "common/daemon.h" #include "common/daemon.h"
#endif #endif
@ -31,6 +31,7 @@
#include "common/utils/string_utils.h" #include "common/utils/string_utils.h"
#include "encoder/encoderFactory.h" #include "encoder/encoderFactory.h"
#include "message/message.h" #include "message/message.h"
#include "server_settings.hpp"
#include "streamServer.h" #include "streamServer.h"
#if defined(HAS_AVAHI) || defined(HAS_BONJOUR) #if defined(HAS_AVAHI) || defined(HAS_BONJOUR)
#include "publishZeroConf/publishmDNS.h" #include "publishZeroConf/publishmDNS.h"
@ -55,35 +56,57 @@ int main(int argc, char* argv[])
int exitcode = EXIT_SUCCESS; int exitcode = EXIT_SUCCESS;
try try
{ {
StreamServerSettings settings; ServerSettings settings;
std::string pcmStream = "pipe:///tmp/snapfifo?name=default"; std::string pcmStream = "pipe:///tmp/snapfifo?name=default";
std::string config_file = "/etc/snapserver.conf";
OptionParser op("Allowed options"); OptionParser op("Allowed options");
auto helpSwitch = op.add<Switch>("h", "help", "Produce help message"); auto helpSwitch = op.add<Switch>("h", "help", "Produce help message");
auto groffSwitch = op.add<Switch, Attribute::hidden>("", "groff", "produce groff message"); auto groffSwitch = op.add<Switch, Attribute::hidden>("", "groff", "produce groff message");
auto debugOption = op.add<Implicit<string>, Attribute::hidden>("", "debug", "enable debug logging", "");
auto versionSwitch = op.add<Switch>("v", "version", "Show version number"); auto versionSwitch = op.add<Switch>("v", "version", "Show version number");
op.add<Value<size_t>>("p", "port", "Server port", settings.port, &settings.port);
op.add<Value<size_t>>("", "controlPort", "Remote control port", settings.controlPort, &settings.controlPort);
auto streamValue = op.add<Value<string>>(
"s", "stream", "URI of the PCM input stream.\nFormat: TYPE://host/path?name=NAME\n[&codec=CODEC]\n[&sampleformat=SAMPLEFORMAT]", pcmStream,
&pcmStream);
op.add<Value<string>>("", "sampleformat", "Default sample format", settings.sampleFormat, &settings.sampleFormat);
op.add<Value<string>>("c", "codec", "Default transport codec\n(flac|ogg|pcm)[:options]\nType codec:? to get codec specific options", settings.codec,
&settings.codec);
op.add<Value<size_t>>("", "streamBuffer", "Default stream read buffer [ms]", settings.streamReadMs, &settings.streamReadMs);
op.add<Value<int>>("b", "buffer", "Buffer [ms]", settings.bufferMs, &settings.bufferMs);
op.add<Switch>("", "sendToMuted", "Send audio to muted clients", &settings.sendAudioToMutedClients);
#ifdef HAS_DAEMON #ifdef HAS_DAEMON
int processPriority(0); int processPriority(0);
auto daemonOption = op.add<Implicit<int>>("d", "daemon", "Daemonize\noptional process priority [-20..19]", 0, &processPriority); auto daemonOption = op.add<Implicit<int>>("d", "daemon", "Daemonize\noptional process priority [-20..19]", 0, &processPriority);
auto userValue = op.add<Value<string>>("", "user", "the user[:group] to run snapserver as when daemonized", ""); auto userValue = op.add<Value<string>>("", "user", "the user[:group] to run snapserver as when daemonized", "");
#endif #endif
op.add<Value<string>>("c", "config", "path to the configuration file", config_file, &config_file);
// debug settings
OptionParser conf("");
conf.add<Switch>("", "logging.debug", "enable debug logging", &settings.logging.debug);
conf.add<Value<string>>("", "logging.debug_logfile", "log file name for the debug logs (debug must be enabled)", settings.logging.debug_logfile,
&settings.logging.debug_logfile);
// stream settings
conf.add<Value<size_t>>("p", "stream.port", "Server port", settings.stream.port, &settings.stream.port);
conf.add<Value<size_t>>("", "stream.controlPort", "Remote control port", settings.tcp.port, &settings.tcp.port);
auto streamValue = conf.add<Value<string>>(
"s", "stream.stream", "URI of the PCM input stream.\nFormat: TYPE://host/path?name=NAME\n[&codec=CODEC]\n[&sampleformat=SAMPLEFORMAT]", pcmStream,
&pcmStream);
conf.add<Value<string>>("", "stream.sampleformat", "Default sample format", settings.stream.sampleFormat, &settings.stream.sampleFormat);
conf.add<Value<string>>("c", "stream.codec", "Default transport codec\n(flac|ogg|pcm)[:options]\nType codec:? to get codec specific options",
settings.stream.codec, &settings.stream.codec);
conf.add<Value<size_t>>("", "stream.streamBuffer", "Default stream read buffer [ms]", settings.stream.streamReadMs, &settings.stream.streamReadMs);
conf.add<Value<int>>("b", "stream.buffer", "Buffer [ms]", settings.stream.bufferMs, &settings.stream.bufferMs);
conf.add<Switch>("", "stream.sendToMuted", "Send audio to muted clients", &settings.stream.sendAudioToMutedClients);
// HTTP RPC settings
conf.add<Switch>("", "http.enabled", "enable HTTP Json RPC (HTTP POST and websockets)", &settings.http.enabled);
conf.add<Value<size_t>>("", "http.port", "which port the server should listen to", settings.http.port, &settings.http.port);
conf.add<Value<string>>("", "http.doc_root", "serve a website from the doc_root location", settings.http.doc_root, &settings.http.doc_root);
// TCP RPC settings
conf.add<Switch>("", "tcp.enabled", "enable TCP Json RPC)", &settings.tcp.enabled);
conf.add<Value<size_t>>("", "tcp.port", "which port the server should listen to", settings.tcp.port, &settings.tcp.port);
// TODO: Should be possible to override settings on command line
try try
{ {
op.parse(argc, argv); op.parse(argc, argv);
conf.parse(config_file);
} }
catch (const std::invalid_argument& e) catch (const std::invalid_argument& e)
{ {
@ -116,34 +139,25 @@ int main(int argc, char* argv[])
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
if (!streamValue->is_set()) if (settings.stream.codec.find(":?") != string::npos)
settings.pcmStreams.push_back(streamValue->value());
for (size_t n = 0; n < streamValue->count(); ++n)
{
cout << streamValue->value(n) << "\n";
settings.pcmStreams.push_back(streamValue->value(n));
}
if (settings.codec.find(":?") != string::npos)
{ {
EncoderFactory encoderFactory; EncoderFactory encoderFactory;
std::unique_ptr<Encoder> encoder(encoderFactory.createEncoder(settings.codec)); std::unique_ptr<Encoder> encoder(encoderFactory.createEncoder(settings.stream.codec));
if (encoder) if (encoder)
{ {
cout << "Options for codec \"" << encoder->name() << "\":\n" cout << "Options for codec \"" << encoder->name() << "\":\n"
<< " " << encoder->getAvailableOptions() << "\n" << " " << encoder->getAvailableOptions() << "\n"
<< " Default: \"" << encoder->getDefaultOptions() << "\"\n"; << " Default: \"" << encoder->getDefaultOptions() << "\"\n";
} }
return 1; exit(EXIT_SUCCESS);
} }
AixLog::Log::init<AixLog::SinkNative>("snapserver", AixLog::Severity::trace, AixLog::Type::special); AixLog::Log::init<AixLog::SinkNative>("snapserver", AixLog::Severity::trace, AixLog::Type::special);
if (debugOption->is_set()) if (settings.logging.debug)
{ {
AixLog::Log::instance().add_logsink<AixLog::SinkCout>(AixLog::Severity::trace, AixLog::Type::all, "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)"); AixLog::Log::instance().add_logsink<AixLog::SinkCout>(AixLog::Severity::trace, AixLog::Type::all, "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)");
if (!debugOption->value().empty()) if (!settings.logging.debug_logfile.empty())
AixLog::Log::instance().add_logsink<AixLog::SinkFile>(AixLog::Severity::trace, AixLog::Type::all, debugOption->value(), AixLog::Log::instance().add_logsink<AixLog::SinkFile>(AixLog::Severity::trace, AixLog::Type::all, settings.logging.debug_logfile,
"%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)"); "%Y-%m-%d %H-%M-%S.#ms [#severity] (#tag_func)");
} }
else else
@ -151,6 +165,14 @@ int main(int argc, char* argv[])
AixLog::Log::instance().add_logsink<AixLog::SinkCout>(AixLog::Severity::info, AixLog::Type::all, "%Y-%m-%d %H-%M-%S [#severity]"); AixLog::Log::instance().add_logsink<AixLog::SinkCout>(AixLog::Severity::info, AixLog::Type::all, "%Y-%m-%d %H-%M-%S [#severity]");
} }
if (!streamValue->is_set())
settings.stream.pcmStreams.push_back(streamValue->value());
for (size_t n = 0; n < streamValue->count(); ++n)
{
LOG(INFO) << "Adding stream: " << streamValue->value(n) << "\n";
settings.stream.pcmStreams.push_back(streamValue->value(n));
}
signal(SIGHUP, signal_handler); signal(SIGHUP, signal_handler);
signal(SIGTERM, signal_handler); signal(SIGTERM, signal_handler);
@ -194,12 +216,26 @@ int main(int argc, char* argv[])
#if defined(HAS_AVAHI) || defined(HAS_BONJOUR) #if defined(HAS_AVAHI) || defined(HAS_BONJOUR)
PublishZeroConf publishZeroConfg("Snapcast"); PublishZeroConf publishZeroConfg("Snapcast");
publishZeroConfg.publish({mDNSService("_snapcast._tcp", settings.port), mDNSService("_snapcast-jsonrpc._tcp", settings.controlPort), vector<mDNSService> dns_services;
mDNSService("_snapcastjsonrpc._tcp", settings.controlPort)}); dns_services.emplace_back("_snapcast._tcp", settings.stream.port);
dns_services.emplace_back("_snapcast-stream._tcp", settings.stream.port);
if (settings.tcp.enabled)
{
dns_services.emplace_back("_snapcast-jsonrpc._tcp", settings.tcp.port);
dns_services.emplace_back("_snapcast-tcp._tcp", settings.tcp.port);
}
if (settings.http.enabled)
{
dns_services.emplace_back("_snapcast-http._tcp", settings.http.port);
}
publishZeroConfg.publish(dns_services);
#endif #endif
if (settings.bufferMs < 400) if (settings.stream.bufferMs < 400)
settings.bufferMs = 400; {
LOG(WARNING) << "Buffer is less than 400ms, changing to 400ms\n";
settings.stream.bufferMs = 400;
}
boost::asio::io_context io_context; boost::asio::io_context io_context;
std::unique_ptr<StreamServer> streamServer(new StreamServer(&io_context, settings)); std::unique_ptr<StreamServer> streamServer(new StreamServer(&io_context, settings));

87
server/snapserver.conf Normal file
View file

@ -0,0 +1,87 @@
##################################
### ###
### Snapserver config file ###
### ###
##################################
# default values are commented
# to change them, just uncomment
# HTTP RPC ####################################################################
#
[http]
# enable HTTP Json RPC (HTTP POST and websockets)
enabled = true
# address to listen on
# TODO: not implemented yet
bind_to_address = 127.0.0.1
# which port the server should listen to
port = 1780
# serve a website from the doc_root location
doc_root = /home/johannes/Develop/snapcast/control
#
###############################################################################
# TCP RPC #####################################################################
#
[tcp]
# enable TCP Json RPC
enabled = true_
# address to listen on
# TODO: not implemented yet
bind_to_address = 127.0.0.1
# which port the server should listen to
port = 1705
#
###############################################################################
# Stream settings #############################################################
#
[stream]
# address to listen on
# TODO: not implemented yet
#bind_to_address = 0.0.0.0
# which port the server should listen to
#port = 1704
# stream URI of the PCM input stream, can be configured multiple times
# Format: TYPE://host/path?name=NAME[&codec=CODEC][&sampleformat=SAMPLEFORMAT]
stream = pipe:///tmp/snapfifo?name=default
stream = pipe:///tmp/snapfifo2?name=default2
# Default sample format
#sampleformat = 48000:16:2
# Default transport codec
# (flac|ogg|pcm)[:options]
# Type codec:? to get codec specific options
#codec = flac
# Default stream read buffer [ms]
#streamBuffer = 20
# Buffer [ms]
#buffer = 1000
# Send audio to muted clients
#sendToMuted = false
#
###############################################################################
# Logging options #############################################################
#
[logging]
# enable debug logging
#debug = false
# log file name for the debug logs (debug must be enabled)
#debug_logfile =
#
###############################################################################

View file

@ -29,8 +29,8 @@ using namespace std;
using json = nlohmann::json; using json = nlohmann::json;
StreamServer::StreamServer(boost::asio::io_context* io_context, const StreamServerSettings& streamServerSettings) StreamServer::StreamServer(boost::asio::io_context* io_context, const ServerSettings& serverSettings)
: io_context_(io_context), acceptor_v4_(nullptr), acceptor_v6_(nullptr), settings_(streamServerSettings) : io_context_(io_context), acceptor_v4_(nullptr), acceptor_v6_(nullptr), settings_(serverSettings)
{ {
} }
@ -81,7 +81,7 @@ void StreamServer::onChunkRead(const PcmStream* pcmStream, msg::PcmChunk* chunk,
std::lock_guard<std::recursive_mutex> mlock(sessionsMutex_); std::lock_guard<std::recursive_mutex> mlock(sessionsMutex_);
for (auto s : sessions_) for (auto s : sessions_)
{ {
if (!settings_.sendAudioToMutedClients) if (!settings_.stream.sendAudioToMutedClients)
{ {
GroupPtr group = Config::instance().getGroupFromClient(s->clientId); GroupPtr group = Config::instance().getGroupFromClient(s->clientId);
if (group) if (group)
@ -195,9 +195,9 @@ void StreamServer::ProcessRequest(const jsonrpcpp::request_ptr request, jsonrpcp
int latency = request->params().get("latency"); int latency = request->params().get("latency");
if (latency < -10000) if (latency < -10000)
latency = -10000; latency = -10000;
else if (latency > settings_.bufferMs) else if (latency > settings_.stream.bufferMs)
latency = settings_.bufferMs; latency = settings_.stream.bufferMs;
clientInfo->config.latency = latency; //, -10000, settings_.bufferMs); clientInfo->config.latency = latency; //, -10000, settings_.stream.bufferMs);
result["latency"] = clientInfo->config.latency; result["latency"] = clientInfo->config.latency;
notification.reset( notification.reset(
new jsonrpcpp::Notification("Client.OnLatencyChanged", jsonrpcpp::Parameter("id", clientInfo->id, "latency", clientInfo->config.latency))); new jsonrpcpp::Notification("Client.OnLatencyChanged", jsonrpcpp::Parameter("id", clientInfo->id, "latency", clientInfo->config.latency)));
@ -223,7 +223,7 @@ void StreamServer::ProcessRequest(const jsonrpcpp::request_ptr request, jsonrpcp
if (session != nullptr) if (session != nullptr)
{ {
auto serverSettings = make_shared<msg::ServerSettings>(); auto serverSettings = make_shared<msg::ServerSettings>();
serverSettings->setBufferMs(settings_.bufferMs); serverSettings->setBufferMs(settings_.stream.bufferMs);
serverSettings->setVolume(clientInfo->config.volume.percent); serverSettings->setVolume(clientInfo->config.volume.percent);
GroupPtr group = Config::instance().getGroupFromClient(clientInfo); GroupPtr group = Config::instance().getGroupFromClient(clientInfo);
serverSettings->setMuted(clientInfo->config.volume.muted || group->muted); serverSettings->setMuted(clientInfo->config.volume.muted || group->muted);
@ -274,7 +274,7 @@ void StreamServer::ProcessRequest(const jsonrpcpp::request_ptr request, jsonrpcp
if (session != nullptr) if (session != nullptr)
{ {
auto serverSettings = make_shared<msg::ServerSettings>(); auto serverSettings = make_shared<msg::ServerSettings>();
serverSettings->setBufferMs(settings_.bufferMs); serverSettings->setBufferMs(settings_.stream.bufferMs);
serverSettings->setVolume(client->config.volume.percent); serverSettings->setVolume(client->config.volume.percent);
GroupPtr group = Config::instance().getGroupFromClient(client); GroupPtr group = Config::instance().getGroupFromClient(client);
serverSettings->setMuted(client->config.volume.muted || group->muted); serverSettings->setMuted(client->config.volume.muted || group->muted);
@ -667,7 +667,7 @@ void StreamServer::onMessageReceived(StreamSession* streamSession, const msg::Ba
serverSettings->setVolume(client->config.volume.percent); serverSettings->setVolume(client->config.volume.percent);
serverSettings->setMuted(client->config.volume.muted || group->muted); serverSettings->setMuted(client->config.volume.muted || group->muted);
serverSettings->setLatency(client->config.latency); serverSettings->setLatency(client->config.latency);
serverSettings->setBufferMs(settings_.bufferMs); serverSettings->setBufferMs(settings_.stream.bufferMs);
serverSettings->refersTo = helloMsg.id; serverSettings->refersTo = helloMsg.id;
streamSession->sendAsync(serverSettings); streamSession->sendAsync(serverSettings);
@ -793,7 +793,7 @@ void StreamServer::handleAccept(socket_ptr socket)
SLOG(NOTICE) << "StreamServer::NewConnection: " << socket->remote_endpoint().address().to_string() << endl; SLOG(NOTICE) << "StreamServer::NewConnection: " << socket->remote_endpoint().address().to_string() << endl;
shared_ptr<StreamSession> session = make_shared<StreamSession>(this, socket); shared_ptr<StreamSession> session = make_shared<StreamSession>(this, socket);
session->setBufferMs(settings_.bufferMs); session->setBufferMs(settings_.stream.bufferMs);
session->start(); session->start();
std::lock_guard<std::recursive_mutex> mlock(sessionsMutex_); std::lock_guard<std::recursive_mutex> mlock(sessionsMutex_);
@ -811,12 +811,12 @@ void StreamServer::start()
{ {
try try
{ {
controlServer_.reset(new ControlServer(io_context_, settings_.controlPort, this)); controlServer_.reset(new ControlServer(io_context_, settings_.tcp, settings_.http, this));
controlServer_->start(); controlServer_->start();
streamManager_.reset(new StreamManager(this, settings_.sampleFormat, settings_.codec, settings_.streamReadMs)); streamManager_.reset(new StreamManager(this, settings_.stream.sampleFormat, settings_.stream.codec, settings_.stream.streamReadMs));
// throw SnapException("xxx"); // throw SnapException("xxx");
for (const auto& streamUri : settings_.pcmStreams) for (const auto& streamUri : settings_.stream.pcmStreams)
{ {
PcmStreamPtr stream = streamManager_->addStream(streamUri); PcmStreamPtr stream = streamManager_->addStream(streamUri);
if (stream) if (stream)
@ -825,7 +825,7 @@ void StreamServer::start()
streamManager_->start(); streamManager_->start();
bool is_v6_only(true); bool is_v6_only(true);
tcp::endpoint endpoint_v6(tcp::v6(), settings_.port); tcp::endpoint endpoint_v6(tcp::v6(), settings_.stream.port);
try try
{ {
acceptor_v6_ = make_shared<tcp::acceptor>(*io_context_, endpoint_v6); acceptor_v6_ = make_shared<tcp::acceptor>(*io_context_, endpoint_v6);
@ -843,7 +843,7 @@ void StreamServer::start()
if (!acceptor_v6_ || is_v6_only) if (!acceptor_v6_ || is_v6_only)
{ {
tcp::endpoint endpoint_v4(tcp::v4(), settings_.port); tcp::endpoint endpoint_v4(tcp::v4(), settings_.stream.port);
try try
{ {
acceptor_v4_ = make_shared<tcp::acceptor>(*io_context_, endpoint_v4); acceptor_v4_ = make_shared<tcp::acceptor>(*io_context_, endpoint_v4);

View file

@ -34,6 +34,7 @@
#include "message/codecHeader.h" #include "message/codecHeader.h"
#include "message/message.h" #include "message/message.h"
#include "message/serverSettings.h" #include "message/serverSettings.h"
#include "server_settings.hpp"
#include "streamSession.h" #include "streamSession.h"
#include "streamreader/streamManager.h" #include "streamreader/streamManager.h"
@ -42,22 +43,6 @@ using boost::asio::ip::tcp;
typedef std::shared_ptr<tcp::socket> socket_ptr; typedef std::shared_ptr<tcp::socket> socket_ptr;
typedef std::shared_ptr<StreamSession> session_ptr; typedef std::shared_ptr<StreamSession> session_ptr;
struct StreamServerSettings
{
StreamServerSettings()
: port(1704), controlPort(1705), codec("flac"), bufferMs(1000), sampleFormat("48000:16:2"), streamReadMs(20), sendAudioToMutedClients(false)
{
}
size_t port;
size_t controlPort;
std::vector<std::string> pcmStreams;
std::string codec;
int32_t bufferMs;
std::string sampleFormat;
size_t streamReadMs;
bool sendAudioToMutedClients;
};
/// Forwars PCM data to the connected clients /// Forwars PCM data to the connected clients
/** /**
@ -69,7 +54,7 @@ struct StreamServerSettings
class StreamServer : public MessageReceiver, ControlMessageReceiver, PcmListener class StreamServer : public MessageReceiver, ControlMessageReceiver, PcmListener
{ {
public: public:
StreamServer(boost::asio::io_context* io_context, const StreamServerSettings& streamServerSettings); StreamServer(boost::asio::io_context* io_context, const ServerSettings& serverSettings);
virtual ~StreamServer(); virtual ~StreamServer();
void start(); void start();
@ -103,7 +88,7 @@ private:
std::shared_ptr<tcp::acceptor> acceptor_v4_; std::shared_ptr<tcp::acceptor> acceptor_v4_;
std::shared_ptr<tcp::acceptor> acceptor_v6_; std::shared_ptr<tcp::acceptor> acceptor_v6_;
StreamServerSettings settings_; ServerSettings settings_;
Queue<std::shared_ptr<msg::BaseMessage>> messages_; Queue<std::shared_ptr<msg::BaseMessage>> messages_;
std::unique_ptr<ControlServer> controlServer_; std::unique_ptr<ControlServer> controlServer_;
std::unique_ptr<StreamManager> streamManager_; std::unique_ptr<StreamManager> streamManager_;