From 6283677d3dff278797618c5d444afd44876f1a45 Mon Sep 17 00:00:00 2001 From: ABeltramo Date: Sat, 17 Aug 2024 17:11:38 +0100 Subject: [PATCH] fix: surround - properly split channels, fixed opus params and Moonlight settings --- src/core/src/core/audio.hpp | 29 ++++++++++- .../src/platforms/linux/pulseaudio/pulse.cpp | 52 ++++++++++++++++++- src/moonlight-server/rest/endpoints.hpp | 6 +-- src/moonlight-server/rtsp/commands.hpp | 31 +++++++---- .../state/data-structures.hpp | 24 +-------- .../streaming/data-structures.hpp | 2 +- src/moonlight-server/wolf.cpp | 42 ++++++++------- 7 files changed, 125 insertions(+), 61 deletions(-) diff --git a/src/core/src/core/audio.hpp b/src/core/src/core/audio.hpp index d15687b2..ba5fafa8 100644 --- a/src/core/src/core/audio.hpp +++ b/src/core/src/core/audio.hpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace wolf::core::audio { @@ -15,10 +16,34 @@ typedef struct Server Server; std::shared_ptr connect(std::string_view server = {}); +constexpr auto SAMPLE_RATE = 48000; + +struct AudioMode { + + enum Speakers { + FRONT_LEFT, + FRONT_RIGHT, + FRONT_CENTER, + LOW_FREQUENCY, + BACK_LEFT, + BACK_RIGHT, + SIDE_LEFT, + SIDE_RIGHT, + MAX_SPEAKERS, + }; + + int channels{}; + int streams{}; + int coupled_streams{}; + std::vector speakers; + int bitrate{}; + int sample_rate = SAMPLE_RATE; +}; + + struct AudioDevice { std::string_view sink_name; - int n_channels; - int bitrate = 48000; + AudioMode mode; }; struct VSink { diff --git a/src/core/src/platforms/linux/pulseaudio/pulse.cpp b/src/core/src/platforms/linux/pulseaudio/pulse.cpp index 376c4ade..cfff4dce 100644 --- a/src/core/src/platforms/linux/pulseaudio/pulse.cpp +++ b/src/core/src/platforms/linux/pulseaudio/pulse.cpp @@ -3,6 +3,51 @@ #include #include +namespace fmt { +template <> class formatter { +public: + template constexpr auto parse(ParseContext &ctx) { + return ctx.begin(); + } + + template + auto format(const wolf::core::audio::AudioMode::Speakers &speaker, FormatContext &ctx) const { + std::string speaker_name; + // Mapping taken from + // https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/Modules/#module-null-sink + switch (speaker) { + case wolf::core::audio::AudioMode::FRONT_LEFT: + speaker_name = "front-left"; + break; + case wolf::core::audio::AudioMode::FRONT_RIGHT: + speaker_name = "front-right"; + break; + case wolf::core::audio::AudioMode::FRONT_CENTER: + speaker_name = "front-center"; + break; + case wolf::core::audio::AudioMode::LOW_FREQUENCY: + speaker_name = "lfe"; + break; + case wolf::core::audio::AudioMode::BACK_LEFT: + speaker_name = "rear-left"; + break; + case wolf::core::audio::AudioMode::BACK_RIGHT: + speaker_name = "rear-right"; + break; + case wolf::core::audio::AudioMode::SIDE_LEFT: + speaker_name = "side-left"; + break; + case wolf::core::audio::AudioMode::SIDE_RIGHT: + speaker_name = "side-right"; + break; + case wolf::core::audio::AudioMode::MAX_SPEAKERS: + break; + } + return fmt::format_to(ctx.out(), "{}", speaker_name); + } +}; +} // namespace fmt + namespace wolf::core::audio { struct Server { @@ -88,8 +133,11 @@ std::shared_ptr create_virtual_sink(const std::shared_ptr &server queue_op(server, [server, vsink]() { auto device = vsink->device; - auto channel_spec = - fmt::format("rate={} sink_name={} channels={}", device.bitrate, device.sink_name, device.n_channels); + auto channel_spec = fmt::format("rate={} sink_name={} channels={} channel_map={}", + device.mode.sample_rate, + device.sink_name, + device.mode.channels, + fmt::join(device.mode.speakers, ",")); auto operation = pa_context_load_module( server->ctx, "module-null-sink", diff --git a/src/moonlight-server/rest/endpoints.hpp b/src/moonlight-server/rest/endpoints.hpp index 117ca7ad..62385da5 100644 --- a/src/moonlight-server/rest/endpoints.hpp +++ b/src/moonlight-server/rest/endpoints.hpp @@ -247,10 +247,10 @@ create_run_session(const std::shared_ptrconfig->support_hevc, state->config->support_av1}; - // forcing stereo, TODO: what should we select here? + auto surround_info = std::stoi(get_header(headers, "surroundAudioInfo").value_or("196610")); int channelCount = surround_info & (65535); - state::AudioMode audio_mode; + audio::AudioMode audio_mode; switch (channelCount) { case 2: // stereo audio_mode = state->host->audio_modes[0]; @@ -267,8 +267,6 @@ create_run_session(const std::shared_ptr 2) { // 5.1 and 7.1 + std::rotate(mapping_p.begin() + 3, mapping_p.begin() + 4, mapping_p.end()); + } + std::string audio_speakers = mapping_p // | views::transform([](auto speaker) { return (char)(speaker + '0'); }) // - | to; // + | to; + auto surround_params = fmt::format("fmtp:97 surround-params={}{}{}{}", + session.audio_mode.channels, + session.audio_mode.streams, + session.audio_mode.coupled_streams, + audio_speakers); - payloads.push_back({"a", - fmt::format("fmtp:97 surround-params={}{}{}{}", - session.audio_mode.channels, - session.audio_mode.streams, - session.audio_mode.coupled_streams, - audio_speakers)}); + payloads.push_back({"a", surround_params}); + logs::log(logs::debug, "[RTSP] Sending audio surround params: {}", surround_params); payloads.push_back( {"a", fmt::format("x-ss-general.featureFlags: {}", FS_PEN_TOUCH_EVENTS | FS_CONTROLLER_TOUCH_EVENTS)}); @@ -146,7 +156,7 @@ announce(const RTSP_PACKET &req, gst_pipeline = session.app->h264_gst_pipeline; } - auto audio_channels = args["x-nv-audio.surround.numChannels"].value_or(2); + auto audio_channels = args["x-nv-audio.surround.numChannels"].value_or(session.audio_mode.channels); auto fec_percentage = 20; // TODO: setting? long bitrate = args["x-nv-vqos[0].bw.maximumBitrateKbps"].value_or(15500); @@ -209,7 +219,8 @@ announce(const RTSP_PACKET &req, .client_ip = session.ip, .packet_duration = args["x-nv-aqos.packetDuration"].value_or(5), - .channels = audio_channels}; + .channels = audio_channels, + .bitrate = session.audio_mode.bitrate}; event_bus->fire_event(immer::box(audio)); return ok_msg(req.seq_number); diff --git a/src/moonlight-server/state/data-structures.hpp b/src/moonlight-server/state/data-structures.hpp index 55b000fc..c4ccc441 100644 --- a/src/moonlight-server/state/data-structures.hpp +++ b/src/moonlight-server/state/data-structures.hpp @@ -135,32 +135,12 @@ struct Config { immer::vector apps; }; -struct AudioMode { - - enum Speakers { - FRONT_LEFT, - FRONT_RIGHT, - FRONT_CENTER, - LOW_FREQUENCY, - BACK_LEFT, - BACK_RIGHT, - SIDE_LEFT, - SIDE_RIGHT, - MAX_SPEAKERS, - }; - - int channels{}; - int streams{}; - int coupled_streams{}; - immer::array speakers; -}; - /** * Host information like network, certificates and displays */ struct Host { immer::array display_modes; - immer::array audio_modes; + immer::array audio_modes; const X509 *server_cert; const EVP_PKEY *server_pkey; @@ -196,7 +176,7 @@ using JoypadList = immer::map event_bus; std::shared_ptr app; diff --git a/src/moonlight-server/streaming/data-structures.hpp b/src/moonlight-server/streaming/data-structures.hpp index 04d5e181..cc8a7eed 100644 --- a/src/moonlight-server/streaming/data-structures.hpp +++ b/src/moonlight-server/streaming/data-structures.hpp @@ -64,7 +64,7 @@ struct AudioSession { int packet_duration; int channels; - int bitrate = 48000; + int bitrate; }; /** diff --git a/src/moonlight-server/wolf.cpp b/src/moonlight-server/wolf.cpp index ad19fe30..6b25be11 100644 --- a/src/moonlight-server/wolf.cpp +++ b/src/moonlight-server/wolf.cpp @@ -45,34 +45,37 @@ immer::array getDisplayModes() { /** * @brief Get the Audio Modes */ -immer::array getAudioModes() { +immer::array getAudioModes() { return {// Stereo {.channels = 2, .streams = 1, .coupled_streams = 1, - .speakers = {state::AudioMode::FRONT_LEFT, state::AudioMode::FRONT_RIGHT}}, + .speakers = {audio::AudioMode::FRONT_LEFT, audio::AudioMode::FRONT_RIGHT}, + .bitrate = 96000}, // 5.1 {.channels = 6, .streams = 4, .coupled_streams = 2, - .speakers = {state::AudioMode::FRONT_LEFT, - state::AudioMode::FRONT_RIGHT, - state::AudioMode::FRONT_CENTER, - state::AudioMode::LOW_FREQUENCY, - state::AudioMode::BACK_LEFT, - state::AudioMode::BACK_RIGHT}}, + .speakers = {audio::AudioMode::FRONT_LEFT, + audio::AudioMode::FRONT_RIGHT, + audio::AudioMode::FRONT_CENTER, + audio::AudioMode::LOW_FREQUENCY, + audio::AudioMode::BACK_LEFT, + audio::AudioMode::BACK_RIGHT}, + .bitrate = 256000}, // 7.1 {.channels = 8, .streams = 5, .coupled_streams = 3, - .speakers = {state::AudioMode::FRONT_LEFT, - state::AudioMode::FRONT_RIGHT, - state::AudioMode::FRONT_CENTER, - state::AudioMode::LOW_FREQUENCY, - state::AudioMode::BACK_LEFT, - state::AudioMode::BACK_RIGHT, - state::AudioMode::SIDE_LEFT, - state::AudioMode::SIDE_RIGHT}}}; + .speakers = {audio::AudioMode::FRONT_LEFT, + audio::AudioMode::FRONT_RIGHT, + audio::AudioMode::FRONT_CENTER, + audio::AudioMode::LOW_FREQUENCY, + audio::AudioMode::BACK_LEFT, + audio::AudioMode::BACK_RIGHT, + audio::AudioMode::SIDE_LEFT, + audio::AudioMode::SIDE_RIGHT}, + .bitrate = 450000}}; } state::Host get_host_config(std::string_view pkey_filename, std::string_view cert_filename) { @@ -228,10 +231,9 @@ auto setup_sessions_handlers(const immer::box &app_state, auto pulse_sink_name = fmt::format("virtual_sink_{}", session->session_id); std::shared_ptr v_device; if (audio_server && audio_server->server) { - v_device = audio::create_virtual_sink(audio_server->server, - audio::AudioDevice{.sink_name = pulse_sink_name, - .n_channels = session->audio_mode.channels, - .bitrate = 48000}); // TODO: + v_device = audio::create_virtual_sink( + audio_server->server, + audio::AudioDevice{.sink_name = pulse_sink_name, .mode = session->audio_mode}); } /* Setup devices paths */