diff --git a/src/audio.cpp b/src/audio.cpp index 1995e380ea7..b79d64259ce 100644 --- a/src/audio.cpp +++ b/src/audio.cpp @@ -34,6 +34,8 @@ namespace audio { start_audio_control(audio_ctx_t &ctx); static void stop_audio_control(audio_ctx_t &); + static void + apply_surround_params(opus_stream_config_t &stream, const stream_params_t ¶ms); int map_stream(int channels, bool quality); @@ -98,24 +100,27 @@ namespace audio { void encodeThread(sample_queue_t samples, config_t config, void *channel_data) { auto packets = mail::man->queue(mail::audio_packets); - auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; + auto stream = stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; + if (config.flags[config_t::CUSTOM_SURROUND_PARAMS]) { + apply_surround_params(stream, config.customStreamParams); + } // Encoding takes place on this thread platf::adjust_thread_priority(platf::thread_priority_e::high); opus_t opus { opus_multistream_encoder_create( - stream->sampleRate, - stream->channelCount, - stream->streams, - stream->coupledStreams, - stream->mapping, + stream.sampleRate, + stream.channelCount, + stream.streams, + stream.coupledStreams, + stream.mapping, OPUS_APPLICATION_RESTRICTED_LOWDELAY, nullptr) }; - opus_multistream_encoder_ctl(opus.get(), OPUS_SET_BITRATE(stream->bitrate)); + opus_multistream_encoder_ctl(opus.get(), OPUS_SET_BITRATE(stream.bitrate)); opus_multistream_encoder_ctl(opus.get(), OPUS_SET_VBR(0)); - auto frame_size = config.packetDuration * stream->sampleRate / 1000; + auto frame_size = config.packetDuration * stream.sampleRate / 1000; while (auto sample = samples->pop()) { buffer_t packet { 1400 }; @@ -135,7 +140,10 @@ namespace audio { void capture(safe::mail_t mail, config_t config, void *channel_data) { auto shutdown_event = mail->event(mail::shutdown); - auto stream = &stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; + auto stream = stream_configs[map_stream(config.channels, config.flags[config_t::HIGH_QUALITY])]; + if (config.flags[config_t::CUSTOM_SURROUND_PARAMS]) { + apply_surround_params(stream, config.customStreamParams); + } auto ref = control_shared.ref(); if (!ref) { @@ -167,7 +175,7 @@ namespace audio { // Prefer the virtual sink if host playback is disabled or there's no other sink if (ref->sink.null && (!config.flags[config_t::HOST_AUDIO] || sink->empty())) { auto &null = *ref->sink.null; - switch (stream->channelCount) { + switch (stream.channelCount) { case 2: sink = &null.stereo; break; @@ -191,8 +199,8 @@ namespace audio { } } - auto frame_size = config.packetDuration * stream->sampleRate / 1000; - auto mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size); + auto frame_size = config.packetDuration * stream.sampleRate / 1000; + auto mic = control->microphone(stream.mapping, stream.channelCount, stream.sampleRate, frame_size); if (!mic) { return; } @@ -213,7 +221,7 @@ namespace audio { shutdown_event->view(); }); - int samples_per_frame = frame_size * stream->channelCount; + int samples_per_frame = frame_size * stream.channelCount; while (!shutdown_event->peek()) { std::vector sample_buffer; @@ -229,7 +237,7 @@ namespace audio { BOOST_LOG(info) << "Reinitializing audio capture"sv; mic.reset(); do { - mic = control->microphone(stream->mapping, stream->channelCount, stream->sampleRate, frame_size); + mic = control->microphone(stream.mapping, stream.channelCount, stream.sampleRate, frame_size); if (!mic) { BOOST_LOG(warning) << "Couldn't re-initialize audio input"sv; } @@ -299,4 +307,12 @@ namespace audio { ctx.control->set_sink(sink); } } + + void + apply_surround_params(opus_stream_config_t &stream, const stream_params_t ¶ms) { + stream.channelCount = params.channelCount; + stream.streams = params.streams; + stream.coupledStreams = params.coupledStreams; + stream.mapping = params.mapping; + } } // namespace audio diff --git a/src/audio.h b/src/audio.h index fe22c94611d..cbe4ae98b1c 100644 --- a/src/audio.h +++ b/src/audio.h @@ -26,12 +26,20 @@ namespace audio { int bitrate; }; + struct stream_params_t { + int channelCount; + int streams; + int coupledStreams; + std::uint8_t mapping[8]; + }; + extern opus_stream_config_t stream_configs[MAX_STREAM_CONFIG]; struct config_t { enum flags_e : int { HIGH_QUALITY, HOST_AUDIO, + CUSTOM_SURROUND_PARAMS, MAX_FLAGS }; @@ -39,6 +47,8 @@ namespace audio { int channels; int mask; + stream_params_t customStreamParams; + std::bitset flags; }; diff --git a/src/nvhttp.cpp b/src/nvhttp.cpp index 695820f4b3a..670bc3f2e48 100644 --- a/src/nvhttp.cpp +++ b/src/nvhttp.cpp @@ -296,6 +296,7 @@ namespace nvhttp { launch_session->appid = util::from_view(get_arg(args, "appid", "unknown")); launch_session->enable_sops = util::from_view(get_arg(args, "sops", "0")); launch_session->surround_info = util::from_view(get_arg(args, "surroundAudioInfo", "196610")); + launch_session->surround_params = (get_arg(args, "surroundParams", "")); launch_session->gcmap = util::from_view(get_arg(args, "gcmap", "0")); launch_session->enable_hdr = util::from_view(get_arg(args, "hdrMode", "0")); diff --git a/src/process.cpp b/src/process.cpp index 89dc4dc5ae5..32af14ebd9d 100644 --- a/src/process.cpp +++ b/src/process.cpp @@ -185,6 +185,7 @@ namespace proc { _env["SUNSHINE_CLIENT_AUDIO_CONFIGURATION"] = "7.1"; break; } + _env["SUNSHINE_CLIENT_AUDIO_SURROUND_PARAMS"] = launch_session->surround_params; if (!_app.output.empty() && _app.output != "null"sv) { #ifdef _WIN32 diff --git a/src/rtsp.cpp b/src/rtsp.cpp index 0180fbee37a..32fa03df353 100644 --- a/src/rtsp.cpp +++ b/src/rtsp.cpp @@ -818,6 +818,12 @@ namespace rtsp_stream { ss << "a=rtpmap:98 AV1/90000"sv << std::endl; } + if (!session.surround_params.empty()) { + // If we have our own surround parameters, advertise them twice first + ss << "a=fmtp:97 surround-params="sv << session.surround_params << std::endl; + ss << "a=fmtp:97 surround-params="sv << session.surround_params << std::endl; + } + for (int x = 0; x < audio::MAX_STREAM_CONFIG; ++x) { auto &stream_config = audio::stream_configs[x]; std::uint8_t mapping[platf::speaker::MAX_SPEAKERS]; @@ -1031,6 +1037,28 @@ namespace rtsp_stream { config.audio.flags[audio::config_t::HIGH_QUALITY] = (content.find("0.0.0.0"sv) == std::string::npos); } } + } else if (session.surround_params.length() > 3) { + // Channels + std::uint8_t c = session.surround_params[0] - '0'; + // Streams + std::uint8_t n = session.surround_params[1] - '0'; + // Coupled streams + std::uint8_t m = session.surround_params[2] - '0'; + auto valid = false; + if ((c == 6 || c == 8) && c == config.audio.channels && n + m == c && session.surround_params.length() == c + 3) { + config.audio.customStreamParams.channelCount = c; + config.audio.customStreamParams.streams = n; + config.audio.customStreamParams.coupledStreams = m; + valid = true; + for (std::uint8_t i = 0; i < c; i++) { + config.audio.customStreamParams.mapping[i] = session.surround_params[i + 3] - '0'; + if (config.audio.customStreamParams.mapping[i] >= c) { + valid = false; + break; + } + } + } + config.audio.flags[audio::config_t::CUSTOM_SURROUND_PARAMS] = valid; } // If the client sent a configured bitrate, we will choose the actual bitrate ourselves diff --git a/src/rtsp.h b/src/rtsp.h index 20bb8453998..16dba1e0592 100644 --- a/src/rtsp.h +++ b/src/rtsp.h @@ -29,6 +29,7 @@ namespace rtsp_stream { int gcmap; int appid; int surround_info; + std::string surround_params; bool enable_hdr; bool enable_sops;