From 825efb512ab8d3d5a804eb913d5835445a48fe3f Mon Sep 17 00:00:00 2001 From: loki Date: Wed, 19 May 2021 21:44:42 +0200 Subject: [PATCH] Based on client request, mute the host on Linux --- sunshine/audio.cpp | 28 +++++++++++++++------- sunshine/audio.h | 8 ++++++- sunshine/nvhttp.cpp | 57 +++++++++++++++++++++------------------------ sunshine/rtsp.cpp | 7 +++++- sunshine/rtsp.h | 2 ++ sunshine/stream.h | 1 - 6 files changed, 61 insertions(+), 42 deletions(-) diff --git a/sunshine/audio.cpp b/sunshine/audio.cpp index 39158f7b..6c34a0f9 100644 --- a/sunshine/audio.cpp +++ b/sunshine/audio.cpp @@ -18,8 +18,10 @@ using sample_queue_t = std::shared_ptr>> struct audio_ctx_t { // We want to change the sink for the first stream only std::unique_ptr sink_flag; + std::unique_ptr control; + bool restore_sink; platf::sink_t sink; }; @@ -72,7 +74,7 @@ auto control_shared = safe::make_shared(start_audio_control, stop_a void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t config, void *channel_data) { //FIXME: Pick correct opus_stream_config_t based on config.channels - auto stream = &stream_configs[map_stream(config.channels, config.high_quality)]; + auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; opus_t opus { opus_multistream_encoder_create( stream->sampleRate, @@ -102,7 +104,7 @@ void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t confi void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t config, void *channel_data) { //FIXME: Pick correct opus_stream_config_t based on config.channels - auto stream = &stream_configs[map_stream(config.channels, config.high_quality)]; + auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; auto ref = control_shared.ref(); if(!ref) { @@ -116,7 +118,8 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co return; } - std::string *sink = &ref->sink.host; + std::string *sink = + config::audio.sink.empty() ? &ref->sink.host : &config::audio.sink; if(ref->sink.null) { auto &null = *ref->sink.null; switch(stream->channelCount) { @@ -132,12 +135,14 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co } } - // Only the first may change the default sink - if( - !ref->sink_flag->exchange(true, std::memory_order_acquire) && - control->set_sink(*sink)) { + // Only the first to start a session may change the default sink + if(!ref->sink_flag->exchange(true, std::memory_order_acquire)) { + ref->restore_sink = !config.flags[config_t::HOST_AUDIO]; - return; + // If the client requests audio on the host, don't change the default sink + if(!config.flags[config_t::HOST_AUDIO] && control->set_sink(*sink)) { + return; + } } auto samples = std::make_shared(30); @@ -212,11 +217,18 @@ int start_audio_control(audio_ctx_t &ctx) { return -1; } + // The default sink has not been replaced yet. + ctx.restore_sink = false; + ctx.sink = std::move(*sink); return 0; } void stop_audio_control(audio_ctx_t &ctx) { // restore audio-sink if applicable + if(!ctx.restore_sink) { + return; + } + const std::string &sink = config::audio.sink.empty() ? ctx.sink.host : config::audio.sink; if(!sink.empty()) { // Best effort, it's allowed to fail diff --git a/sunshine/audio.h b/sunshine/audio.h index 01f145af..27018423 100644 --- a/sunshine/audio.h +++ b/sunshine/audio.h @@ -24,11 +24,17 @@ struct opus_stream_config_t { extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG]; struct config_t { + enum flags_e : int { + HIGH_QUALITY, + HOST_AUDIO, + MAX_FLAGS + }; + int packetDuration; int channels; int mask; - bool high_quality; + std::bitset flags; }; using packet_t = util::buffer_t; diff --git a/sunshine/nvhttp.cpp b/sunshine/nvhttp.cpp index 8f9068d7..ebf6468a 100644 --- a/sunshine/nvhttp.cpp +++ b/sunshine/nvhttp.cpp @@ -164,6 +164,20 @@ void update_id_client(const std::string &uniqueID, std::string &&cert, op_e op) } } +stream::launch_session_t make_launch_session(bool host_audio, const args_t &args) { + stream::launch_session_t launch_session; + + launch_session.host_audio = host_audio; + launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); + uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); + auto prepend_iv_p = (uint8_t *)&prepend_iv; + + auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); + std::fill(next, std::end(launch_session.iv), 0); + + return launch_session; +} + void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin) { if(sess.async_insert_pin.salt.size() < 32) { tree.put("root.paired", 0); @@ -343,7 +357,6 @@ void pair(std::shared_ptr> &add_cert, std::shared_ auto args = request->parse_query_string(); if(args.find("uniqueid"s) == std::end(args)) { - tree.put("root.resume", 0); tree.put("root..status_code", 400); return; @@ -523,7 +536,6 @@ void applist(resp_https_t response, req_https_t request) { auto args = request->parse_query_string(); if(args.find("uniqueid"s) == std::end(args)) { - tree.put("root.resume", 0); tree.put("root..status_code", 400); return; @@ -554,7 +566,7 @@ void applist(resp_https_t response, req_https_t request) { } } -void launch(resp_https_t response, req_https_t request) { +void launch(bool &host_audio, resp_https_t response, req_https_t request) { print_req(request); pt::ptree tree; @@ -574,9 +586,9 @@ void launch(resp_https_t response, req_https_t request) { auto args = request->parse_query_string(); if( - args.find("uniqueid"s) == std::end(args) || - args.find("rikey"s) == std::end(args) || args.find("rikey"s) == std::end(args) || + args.find("rikeyid"s) == std::end(args) || + args.find("localAudioPlayMode"s) == std::end(args) || args.find("appid"s) == std::end(args)) { tree.put("root.resume", 0); @@ -605,23 +617,14 @@ void launch(resp_https_t response, req_https_t request) { } } - stream::launch_session_t launch_session; - - auto clientID = args.at("uniqueid"s); - launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); - uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); - auto prepend_iv_p = (uint8_t *)&prepend_iv; - - auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); - std::fill(next, std::end(launch_session.iv), 0); - - stream::launch_session_raise(launch_session); + host_audio = util::from_view(args.at("localAudioPlayMode")); + stream::launch_session_raise(make_launch_session(host_audio, args)); tree.put("root..status_code", 200); tree.put("root.gamesession", 1); } -void resume(resp_https_t response, req_https_t request) { +void resume(bool &host_audio, resp_https_t response, req_https_t request) { print_req(request); pt::ptree tree; @@ -649,11 +652,8 @@ void resume(resp_https_t response, req_https_t request) { return; } - stream::launch_session_t launch_session; - auto args = request->parse_query_string(); if( - args.find("uniqueid"s) == std::end(args) || args.find("rikey"s) == std::end(args) || args.find("rikeyid"s) == std::end(args)) { @@ -663,15 +663,7 @@ void resume(resp_https_t response, req_https_t request) { return; } - auto clientID = args.at("uniqueid"s); - launch_session.gcm_key = *util::from_hex(args.at("rikey"s), true); - uint32_t prepend_iv = util::endian::big(util::from_view(args.at("rikeyid"s))); - auto prepend_iv_p = (uint8_t *)&prepend_iv; - - auto next = std::copy(prepend_iv_p, prepend_iv_p + sizeof(prepend_iv), std::begin(launch_session.iv)); - std::fill(next, std::end(launch_session.iv), 0); - - stream::launch_session_raise(launch_session); + stream::launch_session_raise(make_launch_session(host_audio, args)); tree.put("root..status_code", 200); tree.put("root.resume", 1); @@ -844,6 +836,9 @@ void start(std::shared_ptr shutdown_event) { return 1; }); + // /resume doesn't get the parameter "localAudioPlayMode" + // /launch will store it in host_audio + bool host_audio {}; https_server_t https_server { ctx, boost::asio::ssl::verify_peer | boost::asio::ssl::verify_fail_if_no_peer_cert | boost::asio::ssl::verify_client_once }; http_server_t http_server; @@ -853,9 +848,9 @@ void start(std::shared_ptr shutdown_event) { https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair(add_cert, resp, req); }; https_server.resource["^/applist$"]["GET"] = applist; https_server.resource["^/appasset$"]["GET"] = appasset; - https_server.resource["^/launch$"]["GET"] = launch; + https_server.resource["^/launch$"]["GET"] = [&host_audio](auto resp, auto req) { launch(host_audio, resp, req); }; https_server.resource["^/pin/([0-9]+)$"]["GET"] = pin; - https_server.resource["^/resume$"]["GET"] = resume; + https_server.resource["^/resume$"]["GET"] = [&host_audio](auto resp, auto req) { resume(host_audio, resp, req); }; https_server.resource["^/cancel$"]["GET"] = cancel; https_server.config.reuse_address = true; diff --git a/sunshine/rtsp.cpp b/sunshine/rtsp.cpp index 1dae7c94..5b46fa76 100644 --- a/sunshine/rtsp.cpp +++ b/sunshine/rtsp.cpp @@ -69,6 +69,7 @@ public: } void session_raise(launch_session_t launch_session) { + //FIXME: If client abandons us at this stage, _slot_count won't be raised again. --_slot_count; launch_event.raise(launch_session); } @@ -408,12 +409,16 @@ void cmd_announce(rtsp_server_t *server, net::peer_t peer, msg_t &&req) { args.try_emplace("x-nv-aqos.packetDuration"sv, "5"sv); config_t config; + + config.audio.flags[audio::config_t::HOST_AUDIO] = launch_session->host_audio; try { config.audio.channels = util::from_view(args.at("x-nv-audio.surround.numChannels"sv)); config.audio.mask = util::from_view(args.at("x-nv-audio.surround.channelMask"sv)); - config.audio.high_quality = util::from_view(args.at("x-nv-audio.surround.AudioQuality"sv)); config.audio.packetDuration = util::from_view(args.at("x-nv-aqos.packetDuration"sv)); + config.audio.flags[audio::config_t::HIGH_QUALITY] = + util::from_view(args.at("x-nv-audio.surround.AudioQuality"sv)); + config.packetsize = util::from_view(args.at("x-nv-video[0].packetSize"sv)); config.monitor.height = util::from_view(args.at("x-nv-video[0].clientViewportHt"sv)); diff --git a/sunshine/rtsp.h b/sunshine/rtsp.h index cab06fc4..f5e085b3 100644 --- a/sunshine/rtsp.h +++ b/sunshine/rtsp.h @@ -14,6 +14,8 @@ namespace stream { struct launch_session_t { crypto::aes_t gcm_key; crypto::aes_t iv; + + bool host_audio; }; void launch_session_raise(launch_session_t launch_session); diff --git a/sunshine/stream.h b/sunshine/stream.h index 57f69403..9c8dc5ae 100644 --- a/sunshine/stream.h +++ b/sunshine/stream.h @@ -18,7 +18,6 @@ struct config_t { video::config_t monitor; int packetsize; - bool sops; std::optional gcmap; };