Skip to content

Commit

Permalink
feat: Configure display device based on user config
Browse files Browse the repository at this point in the history
  • Loading branch information
FrogTheFrog committed Jan 5, 2025
1 parent 2e5c291 commit b575552
Show file tree
Hide file tree
Showing 22 changed files with 1,656 additions and 98 deletions.
241 changes: 241 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,247 @@ editing the `conf` file in a text editor. Use the examples as reference.
</tr>
</table>

### dd_configuration_option

<table>
<tr>
<td>Description</td>
<td colspan="2">
Perform mandatory verification and additional configuration for the display device.
@note{Applies to Windows only.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}verify_only@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
dd_configuration_option = ensure_only_display
@endcode</td>
</tr>
<tr>
<td rowspan="5">Choices</td>
<td>disabled</td>
<td>Perform no additional configuration (disables all `dd_` configuration options).</td>
</tr>
<tr>
<td>verify_only</td>
<td>Verify that display is active only (this is a mandatory step without any extra steps to verify display state).</td>
</tr>
<tr>
<td>ensure_active</td>
<td>Activate the display if it's currently inactive.</td>
</tr>
<tr>
<td>ensure_primary</td>
<td>Activate the display if it's currently inactive and make it primary.</td>
</tr>
<tr>
<td>ensure_only_display</td>
<td>Activate the display if it's currently inactive and disable all others.</td>
</tr>
</table>

### dd_resolution_option

<table>
<tr>
<td>Description</td>
<td colspan="2">
Perform additional resolution configuration for the display device.
@note{"Optimize game settings" must be enabled in Moonlight for this option to work.}
@note{Applies to Windows only.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}automatic@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
dd_resolution_option = manual
@endcode</td>
</tr>
<tr>
<td rowspan="3">Choices</td>
<td>disabled</td>
<td>Perform no additional configuration.</td>
</tr>
<tr>
<td>automatic</td>
<td>Change resolution to the requested resolution from the client.</td>
</tr>
<tr>
<td>manual</td>
<td>Change resolution to the user specified one (set via [dd_manual_resolution](#dd_manual_resolution)).</td>
</tr>
</table>

### dd_manual_resolution

<table>
<tr>
<td>Description</td>
<td colspan="2">
Specify manual resolution to be used.
@note{[dd_resolution_option](#dd_resolution_option) must be set to `manual`}
@note{Applies to Windows only.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">n/a</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
dd_manual_resolution = 1920x1080
@endcode</td>
</tr>
</table>

### dd_refresh_rate_option

<table>
<tr>
<td>Description</td>
<td colspan="2">
Perform additional refresh rate configuration for the display device.
@note{Applies to Windows only.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}automatic@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
dd_refresh_rate_option = manual
@endcode</td>
</tr>
<tr>
<td rowspan="3">Choices</td>
<td>disabled</td>
<td>Perform no additional configuration.</td>
</tr>
<tr>
<td>automatic</td>
<td>Change refresh rate to the requested FPS value from the client.</td>
</tr>
<tr>
<td>manual</td>
<td>Change refresh rate to the user specified one (set via [dd_manual_refresh_rate](#dd_manual_refresh_rate)).</td>
</tr>
</table>

### dd_manual_refresh_rate

<table>
<tr>
<td>Description</td>
<td colspan="2">
Specify manual refresh rate to be used.
@note{[dd_refresh_rate_option](#dd_refresh_rate_option) must be set to `manual`}
@note{Applies to Windows only.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">n/a</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
dd_manual_resolution = 120
dd_manual_resolution = 59.95
@endcode</td>
</tr>
</table>

### dd_hdr_option

<table>
<tr>
<td>Description</td>
<td colspan="2">
Perform additional HDR configuration for the display device.
@note{Applies to Windows only.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}automatic@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
dd_hdr_option = disabled
@endcode</td>
</tr>
<tr>
<td rowspan="2">Choices</td>
<td>disabled</td>
<td>Perform no additional configuration.</td>
</tr>
<tr>
<td>automatic</td>
<td>Change HDR to the requested state from the client if the display supports it.</td>
</tr>
</table>

### dd_wa_hdr_toggle

<table>
<tr>
<td>Description</td>
<td colspan="2">
When using virtual display device as for streaming, it might display incorrect (high-contrast) color.
With this option enabled, Sunshine will try to mitigate this issue.
@note{This option works independently of [dd_hdr_option](#dd_hdr_option)}
@note{Applies to Windows only.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}
disabled
@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
dd_wa_hdr_toggle = enabled
@endcode</td>
</tr>
</table>

### dd_config_revert_delay

<table>
<tr>
<td>Description</td>
<td colspan="2">
Additional delay in milliseconds to wait before reverting configuration when the app has been closed or the last session terminated.
Main purpose is to provide a smoother transition when quickly switching between apps.
@note{Applies to Windows only.}
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}3000@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
dd_config_revert_delay = 1500
@endcode</td>
</tr>
</table>

### min_fps_factor

<table>
Expand Down
34 changes: 21 additions & 13 deletions src/audio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,6 @@ namespace audio {
using opus_t = util::safe_ptr<OpusMSEncoder, opus_multistream_encoder_destroy>;
using sample_queue_t = std::shared_ptr<safe::queue_t<std::vector<float>>>;

struct audio_ctx_t {
// We want to change the sink for the first stream only
std::unique_ptr<std::atomic_bool> sink_flag;

std::unique_ptr<platf::audio_control_t> control;

bool restore_sink;
platf::sink_t sink;
};

static int
start_audio_control(audio_ctx_t &ctx);
static void
Expand Down Expand Up @@ -95,8 +85,6 @@ namespace audio {
},
};

auto control_shared = safe::make_shared<audio_ctx_t>(start_audio_control, stop_audio_control);

void
encodeThread(sample_queue_t samples, config_t config, void *channel_data) {
auto packets = mail::man->queue<packet_t>(mail::audio_packets);
Expand Down Expand Up @@ -149,7 +137,7 @@ namespace audio {
apply_surround_params(stream, config.customStreamParams);
}

auto ref = control_shared.ref();
auto ref = get_audio_ctx_ref();
if (!ref) {
return;
}
Expand Down Expand Up @@ -255,6 +243,26 @@ namespace audio {
}
}

audio_ctx_ref_t
get_audio_ctx_ref() {
static auto control_shared { safe::make_shared<audio_ctx_t>(start_audio_control, stop_audio_control) };
return control_shared.ref();
}

bool
is_audio_ctx_sink_available(const audio_ctx_t &ctx) {
if (!ctx.control) {
return false;
}

const std::string &sink = ctx.sink.host.empty() ? config::audio.sink : ctx.sink.host;
if (sink.empty()) {
return false;
}

return ctx.control->is_sink_available(sink);
}

int
map_stream(int channels, bool quality) {
int shift = quality ? 1 : 0;
Expand Down
44 changes: 44 additions & 0 deletions src/audio.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
*/
#pragma once

// local includes
#include "platform/common.h"
#include "thread_safe.h"
#include "utility.h"

Expand Down Expand Up @@ -55,8 +57,50 @@ namespace audio {
std::bitset<MAX_FLAGS> flags;
};

struct audio_ctx_t {
// We want to change the sink for the first stream only
std::unique_ptr<std::atomic_bool> sink_flag;

std::unique_ptr<platf::audio_control_t> control;

bool restore_sink;
platf::sink_t sink;
};

using buffer_t = util::buffer_t<std::uint8_t>;
using packet_t = std::pair<void *, buffer_t>;
using audio_ctx_ref_t = safe::shared_t<audio_ctx_t>::ptr_t;

void
capture(safe::mail_t mail, config_t config, void *channel_data);

/**
* @brief Get the reference to the audio context.
* @returns A shared pointer reference to audio context.
* @note Aside from the configuration purposes, it can be used to extend the
* audio sink lifetime to capture sink earlier and restore it later.
*
* @examples
* audio_ctx_ref_t audio = get_audio_ctx_ref()
* @examples_end
*/
audio_ctx_ref_t
get_audio_ctx_ref();

/**
* @brief Check if the audio sink held by audio context is available.
* @returns True if available (and can probably be restored), false otherwise.
* @note Useful for delaying the release of audio context shared pointer (which
* tries to restore original sink).
*
* @examples
* audio_ctx_ref_t audio = get_audio_ctx_ref()
* if (audio.get()) {
* return is_audio_ctx_sink_available(*audio.get());
* }
* return false;
* @examples_end
*/
bool
is_audio_ctx_sink_available(const audio_ctx_t &ctx);
} // namespace audio
Loading

0 comments on commit b575552

Please sign in to comment.