diff --git a/docs/configuration.md b/docs/configuration.md index f853e418..2b282d1c 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -186,32 +186,6 @@ editing the `conf` file in a text editor. Use the examples as reference. -### channels - - - - - - - - - - - - - - -
Description - Sunshine can support multiple clients streaming simultaneously, - at the cost of higher CPU and GPU usage. - @note{All connected clients share control of the same streaming session.} - @warning{Some hardware encoders may have limitations that reduce performance with multiple streams.} -
Default@code{} - 1 - @endcode
Example@code{} - channels = 1 - @endcode
- ### global_prep_cmd diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index da91630d..133a063b 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -165,6 +165,14 @@ high as long as the encoder is used. ### Gamescope compatibility Some users have reported stuttering issues when streaming games running within Gamescope. +### Occasional flickering in KDE + +The `blur` plugin causes flickering during streaming for some people. Disable it from the +KDE System Settings or from the commandline: +```bash +qdbus org.kde.KWin /Effects unloadEffect blur +``` + ## macOS ### Dynamic session lookup failed diff --git a/package.json b/package.json index 78d6994c..0b29b400 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,9 @@ "vue-i18n": "9.14.0" }, "devDependencies": { - "@vitejs/plugin-vue": "4.6.2", - "serve": "14.2.3", - "vite": "4.5.2", - "vite-plugin-ejs": "1.6.4" + "@vitejs/plugin-vue": "5.1.4", + "serve": "14.2.4", + "vite": "5.3.6", + "vite-plugin-ejs": "1.7.0" } } diff --git a/src/config.cpp b/src/config.cpp index fa32fb26..d9f59b12 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -403,7 +403,6 @@ namespace config { APPS_JSON_PATH, 20, // fecPercentage - 1, // channels ENCRYPTION_MODE_NEVER, // lan_encryption_mode ENCRYPTION_MODE_OPPORTUNISTIC, // wan_encryption_mode @@ -1096,8 +1095,6 @@ namespace config { stream.ping_timeout = std::chrono::milliseconds(to); } - int_between_f(vars, "channels", stream.channels, { 1, std::numeric_limits::max() }); - int_between_f(vars, "lan_encryption_mode", stream.lan_encryption_mode, { 0, 2 }); int_between_f(vars, "wan_encryption_mode", stream.wan_encryption_mode, { 0, 2 }); diff --git a/src/config.h b/src/config.h index 5a3ab9f1..74c89c21 100644 --- a/src/config.h +++ b/src/config.h @@ -101,9 +101,6 @@ namespace config { int fec_percentage; - // max unique instances of video and audio streams - int channels; - // Video encryption settings for LAN and WAN streams int lan_encryption_mode; int wan_encryption_mode; diff --git a/src/network.cpp b/src/network.cpp index 6012e32e..dcfdb0e9 100644 --- a/src/network.cpp +++ b/src/network.cpp @@ -163,7 +163,7 @@ namespace net { } host_t - host_create(af_e af, ENetAddress &addr, std::size_t peers, std::uint16_t port) { + host_create(af_e af, ENetAddress &addr, std::uint16_t port) { static std::once_flag enet_init_flag; std::call_once(enet_init_flag, []() { enet_initialize(); @@ -173,7 +173,8 @@ namespace net { enet_address_set_host(&addr, any_addr.data()); enet_address_set_port(&addr, port); - auto host = host_t { enet_host_create(af == IPV4 ? AF_INET : AF_INET6, &addr, peers, 0, 0, 0) }; + // Maximum of 128 clients, which should be enough for anyone + auto host = host_t { enet_host_create(af == IPV4 ? AF_INET : AF_INET6, &addr, 128, 0, 0, 0) }; // Enable opportunistic QoS tagging (automatically disables if the network appears to drop tagged packets) enet_socket_set_option(host->socket, ENET_SOCKOPT_QOS, 1); diff --git a/src/network.h b/src/network.h index dbae81b9..98de905b 100644 --- a/src/network.h +++ b/src/network.h @@ -53,7 +53,7 @@ namespace net { from_address(const std::string_view &view); host_t - host_create(af_e af, ENetAddress &addr, std::size_t peers, std::uint16_t port); + host_create(af_e af, ENetAddress &addr, std::uint16_t port); /** * @brief Get the address family enum value from a string. diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 7782d81c..5c21bc60 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -1004,14 +1004,6 @@ namespace nvhttp { return; } - if (rtsp_stream::session_count() == config::stream.channels) { - tree.put("root.resume", 0); - tree.put("root..status_code", 503); - tree.put("root..status_message", "The host's concurrent stream limit has been reached. Stop an existing stream or increase the 'Channels' value in the Sunshine Web UI."); - - return; - } - auto args = request->parse_query_string(); if ( args.find("rikey"s) == std::end(args) || @@ -1112,16 +1104,6 @@ namespace nvhttp { return; } - // It is possible that due a race condition that this if-statement gives a false negative, - // that is automatically resolved in rtsp_server_t - if (rtsp_stream::session_count() == config::stream.channels) { - tree.put("root.resume", 0); - tree.put("root..status_code", 503); - tree.put("root..status_message", "The host's concurrent stream limit has been reached. Stop an existing stream or increase the 'Channels' value in the Sunshine Web UI."); - - return; - } - auto current_appid = proc::proc.running(); if (current_appid == 0) { tree.put("root.resume", 0); @@ -1213,19 +1195,11 @@ namespace nvhttp { return; } - // It is possible that due a race condition that this if-statement gives a false positive, - // the client should try again - if (rtsp_stream::session_count() != 0) { - tree.put("root.resume", 0); - tree.put("root..status_code", 503); - tree.put("root..status_message", "All sessions must be disconnected before quitting"); - - return; - } - tree.put("root.cancel", 1); tree.put("root..status_code", 200); + rtsp_stream::terminate_sessions(); + if (proc::proc.running() > 0) { proc::proc.terminate(); } diff --git a/src/rtsp.cpp b/src/rtsp.cpp index c6e1ccfd..f985f234 100644 --- a/src/rtsp.cpp +++ b/src/rtsp.cpp @@ -26,6 +26,7 @@ extern "C" { #include "sync.h" #include "video.h" +#include #include namespace asio = boost::asio; @@ -417,13 +418,6 @@ namespace rtsp_stream { int bind(net::af_e af, std::uint16_t port, boost::system::error_code &ec) { - { - auto lg = _session_slots.lock(); - - _session_slots->resize(config::stream.channels); - _slot_count = config::stream.channels; - } - acceptor.open(af == net::IPV4 ? tcp::v4() : tcp::v6(), ec); if (ec) { return -1; @@ -529,7 +523,6 @@ namespace rtsp_stream { } raised_timeout = now + config::stream.ping_timeout; - --_slot_count; launch_event.raise(std::move(launch_session)); } @@ -552,9 +545,14 @@ namespace rtsp_stream { } } + /** + * @brief Get the number of active sessions. + * @return Count of active sessions. + */ int - session_count() const { - return config::stream.channels - _slot_count; + session_count() { + auto lg = _session_slots.lock(); + return _session_slots->size(); } safe::event_t> launch_event; @@ -573,20 +571,21 @@ namespace rtsp_stream { auto discarded = launch_event.pop(0s); if (discarded) { BOOST_LOG(debug) << "Event timeout: "sv << discarded->unique_id; - ++_slot_count; } } auto lg = _session_slots.lock(); - for (auto &slot : *_session_slots) { - if (slot && (all || stream::session::state(*slot) == stream::session::state_e::STOPPING)) { - stream::session::stop(*slot); - stream::session::join(*slot); + for (auto i = _session_slots->begin(); i != _session_slots->end();) { + auto &slot = *(*i); + if (all || stream::session::state(slot) == stream::session::state_e::STOPPING) { + stream::session::stop(slot); + stream::session::join(slot); - slot.reset(); - - ++_slot_count; + i = _session_slots->erase(i); + } + else { + i++; } } @@ -595,27 +594,25 @@ namespace rtsp_stream { } } + /** + * @brief Removes the provided session from the set of sessions. + * @param session The session to remove. + */ void - clear(std::shared_ptr *session_p) { + remove(const std::shared_ptr &session) { auto lg = _session_slots.lock(); - - session_p->reset(); - - ++_slot_count; + _session_slots->erase(session); } - std::shared_ptr * - accept(std::shared_ptr &session) { + /** + * @brief Inserts the provided session into the set of sessions. + * @param session The session to insert. + */ + void + insert(const std::shared_ptr &session) { auto lg = _session_slots.lock(); - - for (auto &slot : *_session_slots) { - if (!slot) { - slot = session; - return &slot; - } - } - - return nullptr; + _session_slots->emplace(session); + BOOST_LOG(info) << "New streaming session started [active sessions: "sv << _session_slots->size() << ']'; } std::shared_ptr @@ -646,10 +643,9 @@ namespace rtsp_stream { private: std::unordered_map _map_cmd_cb; - sync_util::sync_t>> _session_slots; + sync_util::sync_t>> _session_slots; std::chrono::steady_clock::time_point raised_timeout; - int _slot_count; boost::asio::io_service ios; tcp::acceptor acceptor { ios }; @@ -687,6 +683,11 @@ namespace rtsp_stream { return server.get_all_session_uuids(); } + void + terminate_sessions() { + server.clear(true); + } + int send(tcp::socket &sock, const std::string_view &sv) { std::size_t bytes_send = 0; @@ -1145,19 +1146,12 @@ namespace rtsp_stream { } auto stream_session = stream::session::alloc(config, session); - - auto slot = server->accept(stream_session); - if (!slot) { - BOOST_LOG(info) << "Ran out of slots for client from ["sv << ']'; - - respond(sock, session, &option, 503, "Service Unavailable", req->sequenceNumber, {}); - return; - } + server->insert(stream_session); if (stream::session::start(*stream_session, sock.remote_endpoint().address().to_string())) { BOOST_LOG(error) << "Failed to start a streaming session"sv; - server->clear(slot); + server->remove(stream_session); respond(sock, session, &option, 500, "Internal Server Error", req->sequenceNumber, {}); return; } diff --git a/src/rtsp.h b/src/rtsp.h index 0792756c..e1111796 100644 --- a/src/rtsp.h +++ b/src/rtsp.h @@ -63,6 +63,10 @@ namespace rtsp_stream { void launch_session_clear(uint32_t launch_session_id); + /** + * @brief Get the number of active sessions. + * @return Count of active sessions. + */ int session_count(); @@ -72,6 +76,12 @@ namespace rtsp_stream { std::list get_all_session_uuids(); + /** + * @brief Terminates all running streaming sessions. + */ + void + terminate_sessions(); + void rtpThread(); diff --git a/src/stream.cpp b/src/stream.cpp index 2526e5c3..0a2b6066 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -259,7 +259,7 @@ namespace stream { public: int bind(net::af_e address_family, std::uint16_t port) { - _host = net::host_create(address_family, _addr, config::stream.channels, port); + _host = net::host_create(address_family, _addr, port); return !(bool) _host; } diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index e53d56b6..073297dd 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -219,7 +219,6 @@ id: "advanced", name: "Advanced", options: { - "channels": 1, "fec_percentage": 20, "qp": 28, "min_threads": 2, diff --git a/src_assets/common/assets/web/configs/tabs/General.vue b/src_assets/common/assets/web/configs/tabs/General.vue index 28a2ad9b..692b6816 100644 --- a/src_assets/common/assets/web/configs/tabs/General.vue +++ b/src_assets/common/assets/web/configs/tabs/General.vue @@ -89,16 +89,6 @@ onMounted(() => {
{{ $t('config.log_level_desc') }}
- -
- - -
- {{ $t('config.channels_desc_1') }}
- {{ $t('_common.note') }} {{ $t('config.channels_desc_2') }} -
-
-