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 Dec 11, 2024
1 parent 1543f58 commit 2215927
Show file tree
Hide file tree
Showing 22 changed files with 1,650 additions and 97 deletions.
204 changes: 204 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,210 @@ 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">
@warning{Windows only!}
Perform additional configuration for the display device:
<ul>
<li>`disabled` - perform no additional configuration (disables all `dd_` configuration options).</li>
<li>`verify_only` - verify that display is active only (required for changing display mode and other options).</li>
<li>`ensure_active` - activate the display if it's currently inactive.</li>
<li>`ensure_primary` - activate the display if it's currently inactive and make it primary.</li>
<li>`ensure_only_display` - activate the display if it's currently inactive and disable all others.</li>
</ul>
</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>
</table>

### dd_resolution_option

<table>
<tr>
<td>Description</td>
<td colspan="2">
@warning{Windows only!}
@note{"Optimize game settings" must be enabled for this option to work.}
Perform additional resolution configuration for the display device:
<ul>
<li>`disabled` - perform no additional configuration.</li>
<li>`automatic` - change resolution to the requested resolution from the client.</li>
<li>`manual` - change resolution to the user specified one (set via `dd_manual_resolution`).</li>
</ul>
</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>
</table>

### dd_manual_resolution

<table>
<tr>
<td>Description</td>
<td colspan="2">
@warning{Windows only!}
@note{`dd_resolution_option` must be set to `manual`}
Specify manual resolution to be used.
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">No value</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">
@warning{Windows only!}
Perform additional refresh rate configuration for the display device:
<ul>
<li>`disabled` - perform no additional configuration.</li>
<li>`automatic` - change refresh rate to the requested FPS value from the client.</li>
<li>`manual` - change refresh rate to the user specified one (set via `dd_manual_refresh_rate`).</li>
</ul>
</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>
</table>

### dd_manual_refresh_rate

<table>
<tr>
<td>Description</td>
<td colspan="2">
@warning{Windows only!}
@note{`dd_refresh_rate_option` must be set to `manual`}
Specify manual refresh rate to be used.
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">No value</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">
@warning{Windows only!}
Perform additional HDR configuration for the display device:
<ul>
<li>`disabled` - perform no additional configuration.</li>
<li>`automatic` - change HDR to the requested state from the client if the display supports it.</li>
</ul>
</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>
</table>

### dd_wa_hdr_toggle

<table>
<tr>
<td>Description</td>
<td colspan="2">
@warning{Windows only!}
@note{This option works independently of `dd_hdr_option`}
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.
</td>
</tr>
<tr>
<td>Default</td>
<td colspan="2">@code{}false@endcode</td>
</tr>
<tr>
<td>Example</td>
<td colspan="2">@code{}
dd_wa_hdr_toggle = true
@endcode</td>
</tr>
</table>

### dd_config_revert_delay

<table>
<tr>
<td>Description</td>
<td colspan="2">
@warning{Windows only!}
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.
</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 2215927

Please sign in to comment.