diff --git a/android/Snapcast/src/main/java/de/badaix/snapcast/control/json/Client.java b/android/Snapcast/src/main/java/de/badaix/snapcast/control/json/Client.java index 5ea0662e..293aaa8e 100644 --- a/android/Snapcast/src/main/java/de/badaix/snapcast/control/json/Client.java +++ b/android/Snapcast/src/main/java/de/badaix/snapcast/control/json/Client.java @@ -30,6 +30,7 @@ public class Client implements JsonSerialisable { private ClientConfig config; private Time_t lastSeen; private boolean connected; + private string clientId; private boolean deleted = false; public Client(JSONObject json) { @@ -67,6 +68,7 @@ public class Client implements JsonSerialisable { lastSeen = new Time_t(json.getJSONObject("lastSeen")); connected = json.getBoolean("connected"); + clientId = json.getString("id"); } catch (JSONException e) { e.printStackTrace(); } @@ -81,6 +83,7 @@ public class Client implements JsonSerialisable { json.put("config", config.toJson()); json.put("lastSeen", lastSeen.toJson()); json.put("connected", connected); + json.put("id", clientId); } catch (JSONException e) { e.printStackTrace(); } @@ -118,13 +121,20 @@ public class Client implements JsonSerialisable { public String getVisibleName() { if ((config.getName() != null) && !config.getName().isEmpty()) return config.getName(); - return host.getName(); + String name = host.getName(); + if (config.getInstance() > 1) + name += " #" + config.getInstance(); + return name; } public boolean isConnected() { return connected; } + public string getId() { + return clientId; + } + public boolean isDeleted() { return deleted; } @@ -151,6 +161,7 @@ public class Client implements JsonSerialisable { return false; if (config != null ? !config.equals(that.config) : that.config != null) return false; if (connected != that.connected) return false; + if (clientId != null ? !clientId.equals(that.clientId) : that.clientId != null) return false; return (deleted == that.deleted); } @@ -160,6 +171,7 @@ public class Client implements JsonSerialisable { result = 31 * result + (snapclient != null ? snapclient.hashCode() : 0); result = 31 * result + (config != null ? config.hashCode() : 0); result = 31 * result + (connected ? 1 : 0); + result = 31 * result + (clientId != null ? clientId.hashCode() : 0); result = 31 * result + (deleted ? 1 : 0); return result; } diff --git a/android/Snapcast/src/main/java/de/badaix/snapcast/control/json/ClientConfig.java b/android/Snapcast/src/main/java/de/badaix/snapcast/control/json/ClientConfig.java index 5fa341f3..eeb9d7fb 100644 --- a/android/Snapcast/src/main/java/de/badaix/snapcast/control/json/ClientConfig.java +++ b/android/Snapcast/src/main/java/de/badaix/snapcast/control/json/ClientConfig.java @@ -29,6 +29,7 @@ public class ClientConfig implements JsonSerialisable { Volume volume; int latency = 0; String stream = ""; + int instance = 1; public ClientConfig() { volume = new Volume(); @@ -45,6 +46,7 @@ public class ClientConfig implements JsonSerialisable { volume = new Volume(json.getJSONObject("volume")); latency = json.getInt("latency"); stream = json.getString("stream"); + instance = json.getInt("instance"); } catch (JSONException e) { e.printStackTrace(); } @@ -58,6 +60,7 @@ public class ClientConfig implements JsonSerialisable { json.put("volume", volume.toJson()); json.put("latency", latency); json.put("stream", stream); + json.put("instance", instance); } catch (JSONException e) { e.printStackTrace(); } @@ -96,6 +99,10 @@ public class ClientConfig implements JsonSerialisable { this.stream = stream; } + public int getInstance() { + return instance; + } + @Override public String toString() { return toJson().toString(); @@ -111,6 +118,7 @@ public class ClientConfig implements JsonSerialisable { if (latency != that.latency) return false; if (name != null ? !name.equals(that.name) : that.name != null) return false; if (stream != null ? !stream.equals(that.stream) : that.stream != null) return false; + if (instance != that.instance) return false; return !(volume != null ? !volume.equals(that.volume) : that.volume != null); } @@ -120,6 +128,7 @@ public class ClientConfig implements JsonSerialisable { int result = name != null ? name.hashCode() : 0; result = 31 * result + (volume != null ? volume.hashCode() : 0); result = 31 * result + latency; + result = 31 * result + instance; result = 31 * result + (stream != null ? stream.hashCode() : 0); return result; } diff --git a/client/snapClient.cpp b/client/snapClient.cpp index 003ad94f..0b1e9a29 100644 --- a/client/snapClient.cpp +++ b/client/snapClient.cpp @@ -145,6 +145,12 @@ int main (int argc, char **argv) exit(EXIT_SUCCESS); } + if (instance <= 0) + { + cout << "instance id must be >= 1\n"; + exit(EXIT_FAILURE); + } + std::clog.rdbuf(new Log("snapclient", LOG_DAEMON)); signal(SIGHUP, signal_handler); diff --git a/server/config.cpp b/server/config.cpp index 6b0c7fc8..f83c6424 100644 --- a/server/config.cpp +++ b/server/config.cpp @@ -89,21 +89,21 @@ void Config::save() } -ClientInfoPtr Config::getClientInfo(const std::string& macAddress, bool add) +ClientInfoPtr Config::getClientInfo(const std::string& clientId, bool add) { - if (macAddress.empty()) + if (clientId.empty()) return nullptr; for (auto client: clients) { - if (client->host.mac == macAddress) + if (client->clientId == clientId) return client; } if (!add) return nullptr; - ClientInfoPtr client = make_shared(macAddress); + ClientInfoPtr client = make_shared(clientId); clients.push_back(client); return client; diff --git a/server/config.h b/server/config.h index e25090f8..862a4052 100644 --- a/server/config.h +++ b/server/config.h @@ -34,6 +34,8 @@ T jGet(const json& j, const std::string& what, const T& def) { try { + if (!j.count(what)) + return def; return j[what].get(); } catch(...) @@ -71,7 +73,7 @@ struct Volume struct Host { - Host(const std::string& _macAddress = "") : name(""), mac(_macAddress), os(""), arch(""), ip("") + Host() : name(""), mac(""), os(""), arch(""), ip("") { } @@ -112,7 +114,7 @@ struct Host struct ClientConfig { - ClientConfig() : name(""), volume(100), latency(0), streamId("") + ClientConfig() : name(""), volume(100), latency(0), streamId(""), instance(1) { } @@ -122,8 +124,9 @@ struct ClientConfig volume.fromJson(j["volume"]); latency = jGet(j, "latency", 0); streamId = trim_copy(jGet(j, "stream", "")); + instance = jGet(j, "instance", 1); } - + json toJson() { json j; @@ -131,6 +134,7 @@ struct ClientConfig j["volume"] = volume.toJson(); j["latency"] = latency; j["stream"] = trim_copy(streamId); + j["instance"] = instance; return j; } @@ -138,6 +142,7 @@ struct ClientConfig Volume volume; int32_t latency; std::string streamId; + size_t instance; }; @@ -207,7 +212,7 @@ struct Snapserver : public Snapcast struct ClientInfo { - ClientInfo(const std::string& _macAddress = "") : host(_macAddress), connected(false) + ClientInfo(const std::string& _clientId = "") : clientId(_clientId), connected(false) { lastSeen.tv_sec = 0; lastSeen.tv_usec = 0; @@ -215,34 +220,10 @@ struct ClientInfo void fromJson(const json& j) { - if (j.count("host") && !j["host"].is_string()) - { - host.fromJson(j["host"]); - } - else - { - host.ip = jGet(j, "IP", ""); - host.mac = jGet(j, "MAC", ""); - host.name = jGet(j, "host", ""); - } - - if (j.count("snapclient")) - snapclient.fromJson(j["snapclient"]); - else - snapclient.version = jGet(j, "version", ""); - - if (j.count("config")) - { - config.fromJson(j["config"]); - } - else - { - config.name = trim_copy(jGet(j, "name", "")); - config.volume.fromJson(j["volume"]); - config.latency = jGet(j, "latency", 0); - config.streamId = trim_copy(jGet(j, "stream", "")); - } - + clientId = jGet(j, "id", j["host"]); + host.fromJson(j["host"]); + snapclient.fromJson(j["snapclient"]); + config.fromJson(j["config"]); lastSeen.tv_sec = jGet(j["lastSeen"], "sec", 0); lastSeen.tv_usec = jGet(j["lastSeen"], "usec", 0); connected = jGet(j, "connected", true); @@ -251,6 +232,7 @@ struct ClientInfo json toJson() { json j; + j["id"] = clientId; j["host"] = host.toJson(); j["snapclient"] = snapclient.toJson(); j["config"] = config.toJson(); @@ -260,6 +242,7 @@ struct ClientInfo return j; } + std::string clientId; Host host; Snapclient snapclient; ClientConfig config; diff --git a/server/streamServer.cpp b/server/streamServer.cpp index 66f00b05..92877ad8 100644 --- a/server/streamServer.cpp +++ b/server/streamServer.cpp @@ -79,8 +79,8 @@ void StreamServer::onDisconnect(StreamSession* streamSession) if (session == nullptr) return; - logO << "onDisconnect: " << session->macAddress << "\n"; - ClientInfoPtr clientInfo = Config::instance().getClientInfo(streamSession->macAddress); + logO << "onDisconnect: " << session->clientId << "\n"; + ClientInfoPtr clientInfo = Config::instance().getClientInfo(streamSession->clientId); logD << "sessions: " << sessions_.size() << "\n"; // don't block: remove StreamSession in a thread auto func = [](shared_ptr s)->void{s->stop();}; @@ -246,7 +246,7 @@ void StreamServer::onMessageReceived(StreamSession* connection, const msg::BaseM connection->sendAsync(timeMsg, true); // refresh connection state - ClientInfoPtr client = Config::instance().getClientInfo(connection->macAddress); + ClientInfoPtr client = Config::instance().getClientInfo(connection->clientId); if (client != nullptr) { gettimeofday(&client->lastSeen, NULL); @@ -257,17 +257,17 @@ void StreamServer::onMessageReceived(StreamSession* connection, const msg::BaseM { msg::Hello helloMsg; helloMsg.deserialize(baseMessage, buffer); - connection->macAddress = helloMsg.getMacAddress(); - logO << "Hello from " << connection->macAddress << ", host: " << helloMsg.getHostName() << ", v" << helloMsg.getVersion() + connection->clientId = helloMsg.getClientId(); + logO << "Hello from " << connection->clientId << ", host: " << helloMsg.getHostName() << ", v" << helloMsg.getVersion() << ", ClientName: " << helloMsg.getClientName() << ", OS: " << helloMsg.getOS() << ", Arch: " << helloMsg.getArch() << ", Protocol version: " << helloMsg.getProtocolVersion() << "\n"; - logD << "request kServerSettings: " << connection->macAddress << "\n"; + logD << "request kServerSettings: " << connection->clientId << "\n"; // std::lock_guard mlock(mutex_); - ClientInfoPtr clientInfo = Config::instance().getClientInfo(connection->macAddress, true); + ClientInfoPtr clientInfo = Config::instance().getClientInfo(connection->clientId, true); if (clientInfo == nullptr) { - logE << "could not get client info for MAC: " << connection->macAddress << "\n"; + logE << "could not get client info for client: " << connection->clientId << "\n"; } else { @@ -281,7 +281,8 @@ void StreamServer::onMessageReceived(StreamSession* connection, const msg::BaseM connection->sendAsync(serverSettings); } - ClientInfoPtr client = Config::instance().getClientInfo(connection->macAddress); + ClientInfoPtr client = Config::instance().getClientInfo(connection->clientId); + client->host.mac = helloMsg.getMacAddress(); client->host.ip = connection->getIP(); client->host.name = helloMsg.getHostName(); client->host.os = helloMsg.getOS(); @@ -289,6 +290,7 @@ void StreamServer::onMessageReceived(StreamSession* connection, const msg::BaseM client->snapclient.version = helloMsg.getVersion(); client->snapclient.name = helloMsg.getClientName(); client->snapclient.protocolVersion = helloMsg.getProtocolVersion(); + client->config.instance = helloMsg.getInstance(); client->connected = true; gettimeofday(&client->lastSeen, NULL); @@ -324,14 +326,13 @@ session_ptr StreamServer::getStreamSession(StreamSession* streamSession) const } -session_ptr StreamServer::getStreamSession(const std::string& mac) const +session_ptr StreamServer::getStreamSession(const std::string& clientId) const { // logO << "getStreamSession: " << mac << "\n"; std::lock_guard mlock(sessionsMutex_); for (auto session: sessions_) { -// logO << "getStreamSession, checking: " << session->macAddress << "\n"; - if (session->macAddress == mac) + if (session->clientId == clientId) return session; } return nullptr; diff --git a/server/streamSession.h b/server/streamSession.h index 64428361..d8c14632 100644 --- a/server/streamSession.h +++ b/server/streamSession.h @@ -74,7 +74,7 @@ public: /// Max playout latency. No need to send PCM data that is older than bufferMs void setBufferMs(size_t bufferMs); - std::string macAddress; + std::string clientId; std::string getIP() {