Based on client request, mute the host on Linux

This commit is contained in:
loki
2021-05-19 21:44:42 +02:00
parent cd870bdcdd
commit 825efb512a
6 changed files with 61 additions and 42 deletions
+20 -8
View File
@@ -18,8 +18,10 @@ using sample_queue_t = std::shared_ptr<safe::queue_t<std::vector<std::int16_t>>>
struct audio_ctx_t { struct audio_ctx_t {
// We want to change the sink for the first stream only // We want to change the sink for the first stream only
std::unique_ptr<std::atomic_bool> sink_flag; std::unique_ptr<std::atomic_bool> sink_flag;
std::unique_ptr<platf::audio_control_t> control; std::unique_ptr<platf::audio_control_t> control;
bool restore_sink;
platf::sink_t sink; platf::sink_t sink;
}; };
@@ -72,7 +74,7 @@ auto control_shared = safe::make_shared<audio_ctx_t>(start_audio_control, stop_a
void encodeThread(packet_queue_t packets, sample_queue_t samples, config_t config, void *channel_data) { 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 //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( opus_t opus { opus_multistream_encoder_create(
stream->sampleRate, 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) { 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 //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(); auto ref = control_shared.ref();
if(!ref) { if(!ref) {
@@ -116,7 +118,8 @@ void capture(safe::signal_t *shutdown_event, packet_queue_t packets, config_t co
return; return;
} }
std::string *sink = &ref->sink.host; std::string *sink =
config::audio.sink.empty() ? &ref->sink.host : &config::audio.sink;
if(ref->sink.null) { if(ref->sink.null) {
auto &null = *ref->sink.null; auto &null = *ref->sink.null;
switch(stream->channelCount) { 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 // Only the first to start a session may change the default sink
if( if(!ref->sink_flag->exchange(true, std::memory_order_acquire)) {
!ref->sink_flag->exchange(true, std::memory_order_acquire) && ref->restore_sink = !config.flags[config_t::HOST_AUDIO];
control->set_sink(*sink)) {
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<sample_queue_t::element_type>(30); auto samples = std::make_shared<sample_queue_t::element_type>(30);
@@ -212,11 +217,18 @@ int start_audio_control(audio_ctx_t &ctx) {
return -1; return -1;
} }
// The default sink has not been replaced yet.
ctx.restore_sink = false;
ctx.sink = std::move(*sink); ctx.sink = std::move(*sink);
return 0; return 0;
} }
void stop_audio_control(audio_ctx_t &ctx) { void stop_audio_control(audio_ctx_t &ctx) {
// restore audio-sink if applicable // restore audio-sink if applicable
if(!ctx.restore_sink) {
return;
}
const std::string &sink = config::audio.sink.empty() ? ctx.sink.host : config::audio.sink; const std::string &sink = config::audio.sink.empty() ? ctx.sink.host : config::audio.sink;
if(!sink.empty()) { if(!sink.empty()) {
// Best effort, it's allowed to fail // Best effort, it's allowed to fail
+7 -1
View File
@@ -24,11 +24,17 @@ struct opus_stream_config_t {
extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG]; extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG];
struct config_t { struct config_t {
enum flags_e : int {
HIGH_QUALITY,
HOST_AUDIO,
MAX_FLAGS
};
int packetDuration; int packetDuration;
int channels; int channels;
int mask; int mask;
bool high_quality; std::bitset<MAX_FLAGS> flags;
}; };
using packet_t = util::buffer_t<std::uint8_t>; using packet_t = util::buffer_t<std::uint8_t>;
+26 -31
View File
@@ -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<crypto::aes_t>(args.at("rikey"s), true);
uint32_t prepend_iv = util::endian::big<uint32_t>(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) { void getservercert(pair_session_t &sess, pt::ptree &tree, const std::string &pin) {
if(sess.async_insert_pin.salt.size() < 32) { if(sess.async_insert_pin.salt.size() < 32) {
tree.put("root.paired", 0); tree.put("root.paired", 0);
@@ -343,7 +357,6 @@ void pair(std::shared_ptr<safe::queue_t<crypto::x509_t>> &add_cert, std::shared_
auto args = request->parse_query_string(); auto args = request->parse_query_string();
if(args.find("uniqueid"s) == std::end(args)) { if(args.find("uniqueid"s) == std::end(args)) {
tree.put("root.resume", 0);
tree.put("root.<xmlattr>.status_code", 400); tree.put("root.<xmlattr>.status_code", 400);
return; return;
@@ -523,7 +536,6 @@ void applist(resp_https_t response, req_https_t request) {
auto args = request->parse_query_string(); auto args = request->parse_query_string();
if(args.find("uniqueid"s) == std::end(args)) { if(args.find("uniqueid"s) == std::end(args)) {
tree.put("root.resume", 0);
tree.put("root.<xmlattr>.status_code", 400); tree.put("root.<xmlattr>.status_code", 400);
return; 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<SimpleWeb::HTTPS>(request); print_req<SimpleWeb::HTTPS>(request);
pt::ptree tree; pt::ptree tree;
@@ -574,9 +586,9 @@ void launch(resp_https_t response, req_https_t request) {
auto args = request->parse_query_string(); auto args = request->parse_query_string();
if( if(
args.find("uniqueid"s) == std::end(args) ||
args.find("rikey"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)) { args.find("appid"s) == std::end(args)) {
tree.put("root.resume", 0); 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; host_audio = util::from_view(args.at("localAudioPlayMode"));
stream::launch_session_raise(make_launch_session(host_audio, args));
auto clientID = args.at("uniqueid"s);
launch_session.gcm_key = *util::from_hex<crypto::aes_t>(args.at("rikey"s), true);
uint32_t prepend_iv = util::endian::big<uint32_t>(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);
tree.put("root.<xmlattr>.status_code", 200); tree.put("root.<xmlattr>.status_code", 200);
tree.put("root.gamesession", 1); 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<SimpleWeb::HTTPS>(request); print_req<SimpleWeb::HTTPS>(request);
pt::ptree tree; pt::ptree tree;
@@ -649,11 +652,8 @@ void resume(resp_https_t response, req_https_t request) {
return; return;
} }
stream::launch_session_t launch_session;
auto args = request->parse_query_string(); auto args = request->parse_query_string();
if( 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("rikeyid"s) == std::end(args)) {
@@ -663,15 +663,7 @@ void resume(resp_https_t response, req_https_t request) {
return; return;
} }
auto clientID = args.at("uniqueid"s); stream::launch_session_raise(make_launch_session(host_audio, args));
launch_session.gcm_key = *util::from_hex<crypto::aes_t>(args.at("rikey"s), true);
uint32_t prepend_iv = util::endian::big<uint32_t>(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);
tree.put("root.<xmlattr>.status_code", 200); tree.put("root.<xmlattr>.status_code", 200);
tree.put("root.resume", 1); tree.put("root.resume", 1);
@@ -844,6 +836,9 @@ void start(std::shared_ptr<safe::signal_t> shutdown_event) {
return 1; 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 }; 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; http_server_t http_server;
@@ -853,9 +848,9 @@ void start(std::shared_ptr<safe::signal_t> shutdown_event) {
https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair<SimpleWeb::HTTPS>(add_cert, resp, req); }; https_server.resource["^/pair$"]["GET"] = [&add_cert](auto resp, auto req) { pair<SimpleWeb::HTTPS>(add_cert, resp, req); };
https_server.resource["^/applist$"]["GET"] = applist; https_server.resource["^/applist$"]["GET"] = applist;
https_server.resource["^/appasset$"]["GET"] = appasset; 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<SimpleWeb::HTTPS>; https_server.resource["^/pin/([0-9]+)$"]["GET"] = pin<SimpleWeb::HTTPS>;
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.resource["^/cancel$"]["GET"] = cancel;
https_server.config.reuse_address = true; https_server.config.reuse_address = true;
+6 -1
View File
@@ -69,6 +69,7 @@ public:
} }
void session_raise(launch_session_t launch_session) { 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; --_slot_count;
launch_event.raise(launch_session); 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); args.try_emplace("x-nv-aqos.packetDuration"sv, "5"sv);
config_t config; config_t config;
config.audio.flags[audio::config_t::HOST_AUDIO] = launch_session->host_audio;
try { try {
config.audio.channels = util::from_view(args.at("x-nv-audio.surround.numChannels"sv)); 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.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.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.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)); config.monitor.height = util::from_view(args.at("x-nv-video[0].clientViewportHt"sv));
+2
View File
@@ -14,6 +14,8 @@ namespace stream {
struct launch_session_t { struct launch_session_t {
crypto::aes_t gcm_key; crypto::aes_t gcm_key;
crypto::aes_t iv; crypto::aes_t iv;
bool host_audio;
}; };
void launch_session_raise(launch_session_t launch_session); void launch_session_raise(launch_session_t launch_session);
-1
View File
@@ -18,7 +18,6 @@ struct config_t {
video::config_t monitor; video::config_t monitor;
int packetsize; int packetsize;
bool sops;
std::optional<int> gcmap; std::optional<int> gcmap;
}; };