mirror of
https://github.com/LizardByte/Sunshine.git
synced 2025-01-30 03:32:43 +00:00
1d6ea8c759
Names are more stable than IDs on Windows
1240 lines
45 KiB
HTML
1240 lines
45 KiB
HTML
<div id="app" class="container">
|
|
<h1 class="my-4">Configuration</h1>
|
|
<div class="form" v-if="config">
|
|
<!--Header-->
|
|
<ul class="nav nav-tabs">
|
|
<li class="nav-item" v-for="tab in tabs" :key="tab.id">
|
|
<a
|
|
class="nav-link"
|
|
:class="{'active': tab.id === currentTab}"
|
|
href="#"
|
|
@click="currentTab = tab.id"
|
|
>{{tab.name}}</a
|
|
>
|
|
</li>
|
|
</ul>
|
|
<!--General Tab-->
|
|
<div v-if="currentTab === 'general'" class="config-page">
|
|
<!--Sunshine Name-->
|
|
<div class="mb-3">
|
|
<label for="sunshine_name" class="form-label">Sunshine Name</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="sunshine_name"
|
|
placeholder="Sunshine"
|
|
v-model="config.sunshine_name"
|
|
/>
|
|
<div class="form-text">
|
|
The name displayed by Moonlight. If not specified, the PC's hostname is used
|
|
</div>
|
|
</div>
|
|
<!--Log Level-->
|
|
<div class="mb-3">
|
|
<label for="min_log_level" class="form-label">Log Level</label>
|
|
<select
|
|
id="min_log_level"
|
|
class="form-select"
|
|
v-model="config.min_log_level"
|
|
>
|
|
<option value="0">Verbose</option>
|
|
<option value="1">Debug</option>
|
|
<option value="2">Info</option>
|
|
<option value="3">Warning</option>
|
|
<option value="4">Error</option>
|
|
<option value="5">Fatal</option>
|
|
<option value="6">None</option>
|
|
</select>
|
|
<div class="form-text">
|
|
The minimum log level printed to standard out
|
|
</div>
|
|
</div>
|
|
<!--Log Path-->
|
|
<div class="mb-3">
|
|
<label for="log_path" class="form-label">Logfile Path</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="log_path"
|
|
placeholder="sunshine.log"
|
|
v-model="config.log_path"
|
|
/>
|
|
<div class="form-text">
|
|
The file where the current logs of Sunshine are stored.
|
|
</div>
|
|
</div>
|
|
<!--Origin Web UI Allowed-->
|
|
<div class="mb-3">
|
|
<label for="origin_web_ui_allowed" class="form-label"
|
|
>Origin Web UI Allowed</label
|
|
>
|
|
<select
|
|
id="origin_web_ui_allowed"
|
|
class="form-select"
|
|
v-model="config.origin_web_ui_allowed"
|
|
>
|
|
<option value="pc">Only localhost may access Web UI</option>
|
|
<option value="lan">Only those in LAN may access Web UI</option>
|
|
<option value="wan">Anyone may access Web UI</option>
|
|
</select>
|
|
<div class="form-text">
|
|
The origin of the remote endpoint address that is not denied access to Web UI
|
|
</div>
|
|
</div>
|
|
<!--UPnP-->
|
|
<div class="mb-3">
|
|
<label for="upnp" class="form-label">UPnP</label>
|
|
<select id="upnp" class="form-select" v-model="config.upnp">
|
|
<option value="disabled">Disabled</option>
|
|
<option value="enabled">Enabled</option>
|
|
</select>
|
|
<div class="form-text">Automatically configure port forwarding</div>
|
|
</div>
|
|
<!--Gamepads-->
|
|
<div class="mb-3" v-if="platform === 'windows'">
|
|
<label for="gamepad" class="form-label">Gamepads</label>
|
|
<select id="gamepad" class="form-select" v-model="config.gamepad">
|
|
<option value="ds4">DS4 (PS4)</option>
|
|
<option value="x360">X360 (Xbox 360)</option>
|
|
</select>
|
|
<div class="form-text">Choose which type of gamepad to Emulate on the host</div>
|
|
</div>
|
|
<!--Ping Timeout-->
|
|
<div class="mb-3">
|
|
<label for="ping_timeout" class="form-label">Ping Timeout</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="ping_timeout"
|
|
placeholder="10000"
|
|
v-model="config.ping_timeout"
|
|
/>
|
|
<div class="form-text">
|
|
How long to wait in milliseconds for data from moonlight before shutting down the stream
|
|
</div>
|
|
</div>
|
|
<!--Advertised FPS and Resolutions-->
|
|
<div class="mb-3">
|
|
<label for="ping_timeout" class="form-label"
|
|
>Advertised Resolutions and FPS</label
|
|
>
|
|
<div class="resolutions-container">
|
|
<label>Resolutions</label>
|
|
<div class="resolutions d-flex flex-wrap">
|
|
<div
|
|
class="p-2 ms-item m-2 d-flex justify-content-between"
|
|
v-for="(r,i) in resolutions"
|
|
:key="r"
|
|
>
|
|
<span class="px-2">{{r}}</span>
|
|
<span style="cursor: pointer" @click="resolutions.splice(i,1)"
|
|
>×</span
|
|
>
|
|
</div>
|
|
<form
|
|
@submit.prevent="resolutions.push(resIn);resIn = '';"
|
|
class="d-flex align-items-center"
|
|
>
|
|
<input
|
|
type="text"
|
|
v-model="resIn"
|
|
required
|
|
pattern="[0-9]+x[0-9]+"
|
|
style="
|
|
border-top-right-radius: 0;
|
|
border-bottom-right-radius: 0;
|
|
"
|
|
class="form-control"
|
|
/>
|
|
<button
|
|
style="border-top-left-radius: 0; border-bottom-left-radius: 0"
|
|
class="btn btn-success"
|
|
>
|
|
+
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="fps-container">
|
|
<label>FPS</label>
|
|
<div class="fps d-flex flex-wrap">
|
|
<div
|
|
class="p-2 ms-item m-2 d-flex justify-content-between"
|
|
v-for="(f,i) in fps"
|
|
:key="f"
|
|
>
|
|
<span class="px-2">{{f}}</span>
|
|
<span style="cursor: pointer" @click="fps.splice(i,1)"
|
|
>×</span
|
|
>
|
|
</div>
|
|
<form
|
|
@submit.prevent="fps.push(fpsIn);fpsIn = '';"
|
|
class="d-flex align-items-center"
|
|
>
|
|
<input
|
|
type="text"
|
|
v-model="fpsIn"
|
|
required
|
|
pattern="[0-9]+"
|
|
style="
|
|
width: 6ch;
|
|
border-top-right-radius: 0;
|
|
border-bottom-right-radius: 0;
|
|
"
|
|
class="form-control"
|
|
/>
|
|
<button
|
|
style="border-top-left-radius: 0; border-bottom-left-radius: 0"
|
|
class="btn btn-success"
|
|
>
|
|
+
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
<div class="form-text">
|
|
The display modes advertised by Sunshine<br />
|
|
Some versions of Moonlight, such as Moonlight-nx (Switch), rely on this list to ensure that the requested
|
|
resolutions and fps are supported.<br>
|
|
This setting does <b>not</b> change how the screen stream is sent to Moonlight
|
|
</div>
|
|
</div>
|
|
<!-- Mapping Key AltRight to Key Windows -->
|
|
<div class="mb-3">
|
|
<label for="mapkey" class="form-label"
|
|
>Map Right Alt key to Windows key</label
|
|
>
|
|
<select
|
|
id="mapkey"
|
|
class="form-select"
|
|
v-model="config.key_rightalt_to_key_win"
|
|
>
|
|
<option value="disabled">Disabled</option>
|
|
<option value="enabled">Enabled</option>
|
|
</select>
|
|
<div class="form-text">
|
|
It may be possible that you cannot send the Windows Key from Moonlight directly.<br />
|
|
In those cases it may be useful to make Sunshine think the Right Alt key is the Windows key
|
|
</div>
|
|
</div>
|
|
<!-- Global Prep Commands -->
|
|
<div class="mb-3 d-flex flex-column">
|
|
<label class="form-label">Command Preparations</label>
|
|
<div class="form-text">
|
|
Configure a list of commands to be executed before or after running any application.
|
|
If any of the specified preparation commands fail, the application launch process will be aborted.
|
|
</div>
|
|
<table class="table" v-if="global_prep_cmd.length > 0">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col"><i class="fas fa-play"></i> Do Command</th>
|
|
<th scope="col"><i class="fas fa-undo"></i> Undo Command</th>
|
|
<th scope="col" v-if="platform === 'windows'">
|
|
<i class="fas fa-shield-alt"></i> Run as Admin
|
|
</th>
|
|
<th scope="col"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr v-for="(c, i) in global_prep_cmd">
|
|
<td>
|
|
<input type="text" class="form-control monospace" v-model="c.do" />
|
|
</td>
|
|
<td>
|
|
<input type="text" class="form-control monospace" v-model="c.undo" />
|
|
</td>
|
|
<td v-if="platform === 'windows'">
|
|
<div class="form-check">
|
|
<input
|
|
type="checkbox"
|
|
class="form-check-input"
|
|
:id="'prep-cmd-admin-' + i"
|
|
v-model="c.elevated"
|
|
true-value="true"
|
|
false-value="false"
|
|
/>
|
|
<label :for="'prep-cmd-admin-' + i" class="form-check-label"
|
|
>Elevated</label
|
|
>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<button class="btn btn-danger" @click="$delete(global_prep_cmd, i)">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
<button class="btn btn-success" @click="add_global_prep_cmd">
|
|
<i class="fas fa-plus"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
<button class="mt-2 btn btn-success" style="margin: 0 auto" @click="add_global_prep_cmd">
|
|
+ Add
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<!--Files Tab-->
|
|
<div v-if="currentTab === 'files'" class="config-page">
|
|
<!--Private Key-->
|
|
<div class="mb-3">
|
|
<label for="pkey" class="form-label">Private Key</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="pkey"
|
|
placeholder="/dir/pkey.pem"
|
|
v-model="config.pkey"
|
|
/>
|
|
<div class="form-text">The private key must be 2048 bits</div>
|
|
</div>
|
|
<!--Cert-->
|
|
<div class="mb-3">
|
|
<label for="cert" class="form-label">Cert</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="cert"
|
|
placeholder="/dir/cert.pem"
|
|
v-model="config.cert"
|
|
/>
|
|
<div class="form-text">
|
|
The certificate must be signed with a 2048 bit key
|
|
</div>
|
|
</div>
|
|
|
|
<!--State File-->
|
|
<div class="mb-3">
|
|
<label for="file_state" class="form-label">State File</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="file_state"
|
|
placeholder="sunshine_state.json"
|
|
v-model="config.file_state"
|
|
/>
|
|
<div class="form-text">
|
|
The file where current state of Sunshine is stored
|
|
</div>
|
|
</div>
|
|
<!--Apps File-->
|
|
<div class="mb-3">
|
|
<label for="file_apps" class="form-label">Apps File</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="file_apps"
|
|
placeholder="apps.json"
|
|
v-model="config.file_apps"
|
|
/>
|
|
<div class="form-text">
|
|
The file where current apps of Sunshine are stored
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-if="currentTab === 'input'" class="config-page">
|
|
<!--Back Button Timeout-->
|
|
<div class="mb-3">
|
|
<label for="back_button_timeout" class="form-label"
|
|
>Back Button Timeout</label
|
|
>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="back_button_timeout"
|
|
placeholder="2000"
|
|
v-model="config.back_button_timeout"
|
|
/>
|
|
<div class="form-text">
|
|
The back/select button on the controller.<br />
|
|
On the Shield, the home and power button are not passed to Moonlight.<br />
|
|
If, after the timeout, the back button is still pressed down, Home/Guide button press is emulated.<br />
|
|
If back_button_timeout < 0, then the Home/Guide button will not be emulated<br />
|
|
</div>
|
|
</div>
|
|
<!--Enable Mouse Input-->
|
|
<div class="mb-3">
|
|
<label for="mouse" class="form-label"
|
|
>Enable Mouse Input</label
|
|
>
|
|
<select
|
|
id="mouse"
|
|
class="form-select"
|
|
v-model="config.mouse"
|
|
>
|
|
<option value="disabled">Disabled</option>
|
|
<option value="enabled">Enabled</option>
|
|
</select>
|
|
<div class="form-text">
|
|
Allows guests to control the host system with the mouse
|
|
</div>
|
|
</div>
|
|
<!--Enable Keyboard Input-->
|
|
<div class="mb-3">
|
|
<label for="keyboard" class="form-label"
|
|
>Enable Keyboard Input</label
|
|
>
|
|
<select
|
|
id="keyboard"
|
|
class="form-select"
|
|
v-model="config.keyboard"
|
|
>
|
|
<option value="disabled">Disabled</option>
|
|
<option value="enabled">Enabled</option>
|
|
</select>
|
|
<div class="form-text">
|
|
Allows guests to control the host system with the keyboard
|
|
</div>
|
|
</div>
|
|
<!--Enable Gamepad Input-->
|
|
<div class="mb-3">
|
|
<label for="gamepad" class="form-label"
|
|
>Enable Gamepad Input</label
|
|
>
|
|
<select
|
|
id="gamepad"
|
|
class="form-select"
|
|
v-model="config.controller"
|
|
>
|
|
<option value="disabled">Disabled</option>
|
|
<option value="enabled">Enabled</option>
|
|
</select>
|
|
<div class="form-text">
|
|
Allows guests to control the host system with a gamepad / controller
|
|
</div>
|
|
</div>
|
|
<!-- Key Repeat Delay-->
|
|
<div class="mb-3" v-if="platform === 'windows'">
|
|
<label for="key_repeat_delay" class="form-label"
|
|
>Key Repeat Delay</label
|
|
>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="key_repeat_delay"
|
|
placeholder="500"
|
|
v-model="config.key_repeat_delay"
|
|
/>
|
|
<div class="form-text">
|
|
Control how fast keys will repeat themselves<br />
|
|
The initial delay in milliseconds before repeating keys
|
|
</div>
|
|
</div>
|
|
<!-- Key Repeat Frequency-->
|
|
<div class="mb-3" v-if="platform === 'windows'">
|
|
<label for="key_repeat_frequency" class="form-label"
|
|
>Key Repeat Frequency</label
|
|
>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="key_repeat_frequency"
|
|
placeholder="24.9"
|
|
v-model="config.key_repeat_frequency"
|
|
/>
|
|
<div class="form-text">
|
|
How often keys repeat every second<br />
|
|
This configurable option supports decimals
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--Files Tab-->
|
|
<div v-if="currentTab === 'av'" class="config-page">
|
|
<!--Audio Sink-->
|
|
<div class="mb-3" v-if="platform === 'windows'">
|
|
<label for="audio_sink" class="form-label">Audio Sink</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="audio_sink"
|
|
placeholder="Speakers (High Definition Audio Device)"
|
|
v-model="config.audio_sink"
|
|
/>
|
|
<div class="form-text">
|
|
The name of the audio sink used for audio capture. If not set, the default audio device will be used.<br />
|
|
You can find the name of the audio sink using the following command:<br />
|
|
<pre>tools\audio-info.exe</pre>
|
|
If you have multiple audio devices with identical names, use the Device ID instead.
|
|
</div>
|
|
</div>
|
|
<div class="mb-3" v-if="platform === 'linux'">
|
|
<label for="audio_sink" class="form-label">Audio Sink</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="audio_sink"
|
|
placeholder="alsa_output.pci-0000_09_00.3.analog-stereo"
|
|
v-model="config.audio_sink"
|
|
/>
|
|
<div class="form-text">
|
|
The name of the audio sink used for Audio Loopback<br />
|
|
If you do not specify this variable, pulseaudio will select the default monitor device.<br />
|
|
<br />
|
|
You can find the name of the audio sink using either command:<br />
|
|
<pre>pacmd list-sinks | grep "name:"</pre>
|
|
<pre>pactl info | grep Source</pre>
|
|
<br />
|
|
</div>
|
|
</div>
|
|
<div class="mb-3" v-if="platform === 'macos'">
|
|
<label for="audio_sink" class="form-label">Audio Sink</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="audio_sink"
|
|
placeholder="BlackHole 2ch"
|
|
v-model="config.audio_sink"
|
|
/>
|
|
<div class="form-text">
|
|
The name of the audio sink used for Audio Loopback<br />
|
|
Sunshine can only access microphones on macOS due to system limitations.<br />
|
|
To stream system audio using <a
|
|
href="https://github.com/mattingalls/Soundflower"
|
|
target="_blank">
|
|
Soundflower
|
|
</a> or <a
|
|
href="https://github.com/ExistentialAudio/BlackHole"
|
|
target="_blank">
|
|
BlackHole
|
|
</a>.
|
|
</div>
|
|
</div>
|
|
<!--Virtual Sink-->
|
|
<div class="mb-3" v-if="platform === 'windows'">
|
|
<label for="virtual_sink" class="form-label">Virtual Sink</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="virtual_sink"
|
|
placeholder="Steam Streaming Speakers"
|
|
v-model="config.virtual_sink"
|
|
/>
|
|
<div class="form-text">
|
|
The virtual sink is an audio device that's virtual (like Steam Streaming Speakers). It allows Sunshine to
|
|
stream audio, while muting the host PC speakers.
|
|
</div>
|
|
</div>
|
|
<!--Adapter Name -->
|
|
<div class="mb-3" v-if="platform === 'windows'">
|
|
<label for="adapter_name" class="form-label">Adapter Name</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="adapter_name"
|
|
placeholder="Radeon RX 580 Series"
|
|
v-model="config.adapter_name"
|
|
/>
|
|
<div class="form-text" v-if="platform === 'windows'">
|
|
You can select the GPU you want to stream:<br />
|
|
Note: This GPU must have a display connected and powered on.<br />
|
|
The appropriate values can be found using the following command:<br />
|
|
<pre>tools\dxgi-info.exe</pre>
|
|
</div>
|
|
</div>
|
|
<!--Output Name -->
|
|
<div class="mb-3 config-page" v-if="platform === 'windows'">
|
|
<label for="output_name" class="form-label">Output Name</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="output_name"
|
|
placeholder="\\.\DISPLAY1"
|
|
v-model="config.output_name"
|
|
/>
|
|
<div class="form-text">
|
|
You can select the display you want to stream:<br />
|
|
Note: If you specified a GPU above, this display must be connected to that GPU.<br />
|
|
The appropriate values can be found using the following command:<br />
|
|
tools\dxgi-info.exe<br />
|
|
</div>
|
|
</div>
|
|
<!--DwmFlush-->
|
|
<div class="mb-3" v-if="platform === 'windows'">
|
|
<label for="dwmflush" class="form-label">DwmFlush</label>
|
|
<select id="dwmflush" class="form-select" v-model="config.dwmflush">
|
|
<option value="disabled">Disabled</option>
|
|
<option value="enabled">Enabled</option>
|
|
</select>
|
|
<div class="form-text">
|
|
Improves capture latency/smoothness during mouse movement.<br />
|
|
Disable if you encounter any VSync-related issues.
|
|
</div>
|
|
</div>
|
|
<div class="mb-3 config-page" v-if="platform === 'linux'">
|
|
<label for="output_name" class="form-label">Monitor number</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="output_name"
|
|
placeholder="0"
|
|
v-model="config.output_name"
|
|
/>
|
|
<div class="form-text">
|
|
During Sunshine startup, you should see the list of detected monitors, e.g.:<br />
|
|
<br />
|
|
<pre style="white-space: pre-line;">
|
|
Info: Detecting connected monitors
|
|
Info: Detected monitor 0: DVI-D-0, connected: false
|
|
Info: Detected monitor 1: HDMI-0, connected: true
|
|
Info: Detected monitor 2: DP-0, connected: true
|
|
Info: Detected monitor 3: DP-1, connected: false
|
|
Info: Detected monitor 4: DVI-D-1, connected: false
|
|
</pre>
|
|
You need to use the value before the colon in the output, e.g. <b>1</b>.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div v-if="currentTab === 'advanced'" class="config-page">
|
|
<!--Port family-->
|
|
<div class="mb-3">
|
|
<label for="port" class="form-label">Port</label>
|
|
<input
|
|
type="number"
|
|
min="0"
|
|
max="65529"
|
|
class="form-control"
|
|
id="port"
|
|
placeholder="47989"
|
|
v-model="config.port"
|
|
/>
|
|
<div class="form-text">Set the family of ports used by Sunshine</div>
|
|
</div>
|
|
<!-- Quantization Parameter -->
|
|
<div class="mb-3">
|
|
<label for="qp" class="form-label">Quantization Parameter</label>
|
|
<input
|
|
type="number"
|
|
class="form-control"
|
|
id="qp"
|
|
placeholder="28"
|
|
v-model="config.qp"
|
|
/>
|
|
<div class="form-text">
|
|
Quantization Parameter<br />
|
|
Some devices may not support Constant Bit Rate.<br />
|
|
For those devices, QP is used instead.<br />
|
|
Higher value means more compression, but less quality<br />
|
|
</div>
|
|
</div>
|
|
<!-- Min Threads -->
|
|
<div class="mb-3">
|
|
<label for="min_threads" class="form-label"
|
|
>Minimum Software Encoding Thread Count</label
|
|
>
|
|
<input
|
|
type="number"
|
|
min="1"
|
|
class="form-control"
|
|
id="min_threads"
|
|
placeholder="1"
|
|
v-model="config.min_threads"
|
|
/>
|
|
<div class="form-text">
|
|
Increasing the value slightly reduces encoding efficiency, but the tradeoff is usually<br />
|
|
worth it to gain the use of more CPU cores for encoding. The ideal value is the lowest<br />
|
|
value that can reliably encode at your desired streaming settings on your hardware.
|
|
</div>
|
|
</div>
|
|
<!--HEVC Support -->
|
|
<div class="mb-3">
|
|
<label for="hevc_mode" class="form-label">HEVC Support</label>
|
|
<select id="hevc_mode" class="form-select" v-model="config.hevc_mode">
|
|
<option value="0">
|
|
Sunshine will specify support for HEVC based on encoder
|
|
</option>
|
|
<option value="1">
|
|
Sunshine will not advertise support for HEVC
|
|
</option>
|
|
<option value="2">
|
|
Sunshine will advertise support for HEVC Main profile
|
|
</option>
|
|
<option value="3">
|
|
Sunshine will advertise support for HEVC Main and Main10 (HDR) profiles
|
|
</option>
|
|
</select>
|
|
<div class="form-text">
|
|
Allows the client to request HEVC Main or HEVC Main10 video streams.<br />
|
|
HEVC is more CPU-intensive to encode, so enabling this may reduce performance when using software encoding.
|
|
</div>
|
|
</div>
|
|
<!--Capture-->
|
|
<div class="mb-3" v-if="platform === 'linux'">
|
|
<label for="capture" class="form-label">Force a Specific Capture Method</label>
|
|
<select id="capture" class="form-select" v-model="config.capture">
|
|
<option value="">Autodetect</option>
|
|
<option value="nvfbc">NvFBC</option>
|
|
<option value="wlr">wlroots</option>
|
|
<option value="kms">KMS</option>
|
|
<option value="x11">X11</option>
|
|
</select>
|
|
<div class="form-text">
|
|
Force a specific capture method, otherwise Sunshine will use the first one that works. NvFBC requires patched nvidia drivers.
|
|
</div>
|
|
</div>
|
|
<!--Encoder-->
|
|
<div class="mb-3">
|
|
<label for="encoder" class="form-label">Force a Specific Encoder</label>
|
|
<select id="encoder" class="form-select" v-model="config.encoder">
|
|
<option value="">Autodetect</option>
|
|
<option value="nvenc" v-if="platform === 'windows' || platform === 'linux'">NVIDIA NVENC</option>
|
|
<option value="quicksync" v-if="platform === 'windows'">Intel QuickSync</option>
|
|
<option value="amdvce" v-if="platform === 'windows'">AMD AMF/VCE</option>
|
|
<option value="vaapi" v-if="platform === 'linux'">VA-API</option>
|
|
<option value="videotoolbox" v-if="platform === 'macos'">VideoToolbox</option>
|
|
<option value="software">Software</option>
|
|
</select>
|
|
<div class="form-text">
|
|
Force a specific encoder, otherwise Sunshine will use the first encoder that is available<br />
|
|
Note: If you specify a hardware encoder on Windows, it must match the GPU where the display is connected.
|
|
</div>
|
|
</div>
|
|
<!--FEC Percentage-->
|
|
<div class="mb-3">
|
|
<label for="fec_percentage" class="form-label">FEC Percentage</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="fec_percentage"
|
|
placeholder="20"
|
|
v-model="config.fec_percentage"
|
|
/>
|
|
<div class="form-text">
|
|
Percentage of error correcting packets per data packet in each video frame.<br />
|
|
Higher values can correct for more network packet loss, but at the cost of increasing bandwidth usage.<br />
|
|
The default value of 20 is what GeForce Experience uses.
|
|
</div>
|
|
</div>
|
|
<!--Channels-->
|
|
<div class="mb-3">
|
|
<label for="channels" class="form-label">Channels</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="channels"
|
|
placeholder="1"
|
|
v-model="config.channels"
|
|
/>
|
|
<div class="form-text">
|
|
When multicasting, it could be useful to have different configurations for each connected Client. For example:
|
|
<ul>
|
|
<li>
|
|
Clients connected through WAN and LAN have different bitrate constraints.
|
|
</li>
|
|
<li>
|
|
Decoders may require different settings for color
|
|
</li>
|
|
</ul>
|
|
Unlike simply broadcasting to multiple Client, this will generate distinct video streams.<br />
|
|
Note, CPU usage increases for each distinct video stream generated
|
|
</div>
|
|
</div>
|
|
<!--Credentials File-->
|
|
<div class="mb-3">
|
|
<label for="credentials_file" class="form-label"
|
|
>Web Manager Credentials File</label
|
|
>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="credentials_file"
|
|
placeholder="sunshine_state.json"
|
|
v-model="config.credentials_file"
|
|
/>
|
|
<div class="form-text">
|
|
Store Username/Password separately from Sunshine's state file.
|
|
</div>
|
|
</div>
|
|
<!--Origin PIN Allowed-->
|
|
<div class="mb-3">
|
|
<label for="origin_pin_allowed" class="form-label"
|
|
>Origin PIN Allowed</label
|
|
>
|
|
<select
|
|
id="origin_pin_allowed"
|
|
class="form-select"
|
|
v-model="config.origin_pin_allowed"
|
|
>
|
|
<option value="pc">Only localhost may access /pin</option>
|
|
<option value="lan">Only those in LAN may access /pin</option>
|
|
<option value="wan">Anyone may access /pin</option>
|
|
</select>
|
|
<div class="form-text">
|
|
The origin of the remote endpoint address that is not denied for HTTP method /pin
|
|
</div>
|
|
</div>
|
|
<!--External IP-->
|
|
<div class="mb-3">
|
|
<label for="external_ip" class="form-label">External IP</label>
|
|
<input
|
|
type="text"
|
|
class="form-control"
|
|
id="external_ip"
|
|
placeholder="123.456.789.12"
|
|
v-model="config.external_ip"
|
|
/>
|
|
<div class="form-text">
|
|
If no external IP address is given, Sunshine will automatically detect external IP
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--Software Settings-->
|
|
<div v-if="currentTab === 'sw'" class="config-page">
|
|
<div class="mb-3">
|
|
<label for="sw_preset" class="form-label">SW Presets</label>
|
|
<select id="sw_preset" class="form-select" v-model="config.sw_preset">
|
|
<option value="ultrafast">ultrafast</option>
|
|
<option value="superfast">superfast (default)</option>
|
|
<option value="veryfast">veryfast</option>
|
|
<option value="faster">faster</option>
|
|
<option value="fast">fast</option>
|
|
<option value="medium">medium</option>
|
|
<option value="slow">slow</option>
|
|
<option value="slower">slower</option>
|
|
<option value="veryslow">veryslow</option>
|
|
</select>
|
|
<div class="form-text">
|
|
Optimize the trade-off between encoding speed (encoded frames per second) and compression efficiency (quality per bit in the bitstream). Defaults to superfast.
|
|
</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="sw_tune" class="form-label">SW Tune</label>
|
|
<select id="sw_tune" class="form-select" v-model="config.sw_tune">
|
|
<option value="film">film -- use for high quality movie content; lowers deblocking</option>
|
|
<option value="animation">animation -- good for cartoons; uses higher deblocking and more reference frames</option>
|
|
<option value="grain">grain -- preserves the grain structure in old, grainy film material</option>
|
|
<option value="stillimage">stillimage -- good for slideshow-like content</option>
|
|
<option value="fastdecode">fastdecode -- allows faster decoding by disabling certain filters</option>
|
|
<option value="zerolatency">zerolatency -- good for fast encoding and low-latency streaming (default)</option>
|
|
</select>
|
|
<div class="form-text">
|
|
Tuning options, which are applied after the preset. Defaults to zerolatency.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!--Nvidia Encoder Settings-->
|
|
<div v-if="currentTab === 'nv'" class="config-page">
|
|
<!--NVENC SETTINGS-->
|
|
<div class="mb-3">
|
|
<label for="nv_preset" class="form-label">NVENC Preset</label>
|
|
<select id="nv_preset" class="form-select" v-model="config.nv_preset">
|
|
<option value="p1">p1 -- fastest (lowest quality)</option>
|
|
<option value="p2">p2 -- faster (lower quality)</option>
|
|
<option value="p3">p3 -- fast (low quality)</option>
|
|
<option value="p4">p4 -- medium (default)</option>
|
|
<option value="p5">p5 -- slow (good quality)</option>
|
|
<option value="p6">p6 -- slower (better quality)</option>
|
|
<option value="p7">p7 -- slowest (best quality)</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="nv_tune" class="form-label">NVENC Tune</label>
|
|
<select id="nv_tune" class="form-select" v-model="config.nv_tune">
|
|
<option value="hq">hq -- high quality</option>
|
|
<option value="ll">ll -- low latency</option>
|
|
<option value="ull">ull -- ultra low latency (default)</option>
|
|
<option value="lossless">lossless -- lossless</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="nv_rc" class="form-label">NVENC Rate Control</label>
|
|
<select id="nv_rc" class="form-select" v-model="config.nv_rc">
|
|
<option value="constqp">constqp -- constant qp mode</option>
|
|
<option value="vbr">vbr -- variable bitrate</option>
|
|
<option value="cbr">cbr -- constant bitrate (default)</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="nv_coder" class="form-label">NVENC Coder (H264)</label>
|
|
<select id="nv_coder" class="form-select" v-model="config.nv_coder">
|
|
<option value="auto">auto -- let ffmpeg decide (default)</option>
|
|
<option value="cabac">cabac -- context adaptive binary arithmetic coding - higher quality</option>
|
|
<option value="cavlc">cavlc -- context adaptive variable-length coding - faster decode</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<!--Intel Encoder Settings-->
|
|
<div v-if="currentTab === 'qsv'" class="config-page">
|
|
<div class="mb-3">
|
|
<label for="qsv_preset" class="form-label">QuickSync Preset</label>
|
|
<select id="qsv_preset" class="form-select" v-model="config.qsv_preset">
|
|
<option value="veryfast">fastest (lowest quality)</option>
|
|
<option value="faster">faster (lower quality)</option>
|
|
<option value="fast">fast (low quality)</option>
|
|
<option value="medium">medium (default)</option>
|
|
<option value="slow">slow (good quality)</option>
|
|
<option value="slower">slower (better quality)</option>
|
|
<option value="slowest">slowest (best quality)</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="qsv_coder" class="form-label">QuickSync Coder (H264)</label>
|
|
<select id="qsv_coder" class="form-select" v-model="config.qsv_coder">
|
|
<option value="auto">auto -- let ffmpeg decide (default)</option>
|
|
<option value="cabac">cabac -- context adaptive binary arithmetic coding - higher quality</option>
|
|
<option value="cavlc">cavlc -- context adaptive variable-length coding - faster decode</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<!--AMD Encoder Settings-->
|
|
<div v-if="currentTab === 'amd'" class="config-page">
|
|
<!--Presets-->
|
|
<div class="mb-3">
|
|
<label for="amd_quality" class="form-label">AMF Quality</label>
|
|
<select
|
|
id="amd_quality"
|
|
class="form-select"
|
|
v-model="config.amd_quality">
|
|
<option value="speed">speed -- prefer speed</option>
|
|
<option value="balanced">balanced -- balanced (default)</option>
|
|
<option value="quality">quality -- prefer quality</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="amd_rc" class="form-label">AMF Rate Control</label>
|
|
<select id="amd_rc" class="form-select" v-model="config.amd_rc">
|
|
<option value="cqp">cqp -- constant qp mode</option>
|
|
<option value="vbr_latency">vbr_latency -- latency constrained variable bitrate (default)</option>
|
|
<option value="vbr_peak">vbr_peak -- peak contrained variable bitrate</option>
|
|
<option value="cbr">cbr -- constant bitrate</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="amd_usage" class="form-label">AMF Usage</label>
|
|
<select id="amd_usage" class="form-select" v-model="config.amd_usage">
|
|
<option value="transcoding">transcoding -- transcoding (slowest)</option>
|
|
<option value="webcam">webcam -- webcam (slow)</option>
|
|
<option value="lowlatency">lowlatency - low latency (fast)</option>
|
|
<option value="ultralowlatency">ultralowlatency - ultra low latency (fastest)</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="amd_preanalysis" class="form-label">AMF Preanalysis</label>
|
|
<select id="amd_preanalysis" class="form-select" v-model="config.amd_preanalysis">
|
|
<option value="enabled">enabled</option>
|
|
<option value="disabled">disabled (default)</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="amd_vbaq" class="form-label">AMF Variance Based Adaptive Quantization (VBAQ)</label>
|
|
<select id="amd_vbaq" class="form-select" v-model="config.amd_vbaq">
|
|
<option value="enabled">enabled (default)</option>
|
|
<option value="disabled">disabled</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="amd_coder" class="form-label">AMF Coder (H264)</label>
|
|
<select id="amd_coder" class="form-select" v-model="config.amd_coder">
|
|
<option value="auto">auto -- let ffmpeg decide (default)</option>
|
|
<option value="cabac">cabac -- context adaptive variable-length coding - higher quality</option>
|
|
<option value="cavlc">cavlc -- context adaptive binary arithmetic coding - faster decode</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<!--VA-API Encoder Settings-->
|
|
<div v-if="currentTab === 'va-api'" class="config-page">
|
|
<input
|
|
class="form-control"
|
|
id="adapter_name"
|
|
placeholder="/dev/dri/renderD128"
|
|
v-model="config.adapter_name"
|
|
/>
|
|
</div>
|
|
<!--VideoToolbox Encoder Settings-->
|
|
<div v-if="currentTab === 'vt'" class="config-page">
|
|
<!--Presets-->
|
|
<div class="mb-3">
|
|
<label for="vt_coder" class="form-label">VideoToolbox Coder</label>
|
|
<select id="vt_coder" class="form-select" v-model="config.vt_coder">
|
|
<option value="auto">auto</option>
|
|
<option value="cabac">cabac</option>
|
|
<option value="cavlc">cavlc</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="vt_software" class="form-label">VideoToolbox Software Encoding</label>
|
|
<select id="vt_software" class="form-select" v-model="config.vt_software">
|
|
<option value="auto">auto</option>
|
|
<option value="disabled">disabled</option>
|
|
<option value="allowed">allowed</option>
|
|
<option value="forced">forced</option>
|
|
</select>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="vt_realtime" class="form-label">VideoToolbox Realtime Encoding</label>
|
|
<select id="vt_realtime" class="form-select" v-model="config.vt_realtime">
|
|
<option value="enabled">enabled</option>
|
|
<option value="disabled">disabled</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="alert alert-success my-4" v-if="saved && !restarted">
|
|
<b>Success!</b> Click 'Apply' to restart Sunshine and apply changes. This will terminate any running sessions.
|
|
</div>
|
|
<div class="alert alert-success my-4" v-if="restarted">
|
|
<b>Success!</b> Sunshine is restarting to apply changes.
|
|
</div>
|
|
<div class="mb-3 buttons">
|
|
<button class="btn btn-primary" @click="save">Save</button>
|
|
<button class="btn btn-success" @click="apply" v-if="saved && !restarted">Apply</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// create dictionary for defaultConfig
|
|
const defaultConfig = {
|
|
"amd_coder": "auto",
|
|
"amd_preanalysis": "disabled",
|
|
"amd_quality": "balanced",
|
|
"amd_rc": "vbr_latency",
|
|
"amd_usage": "ultralowlatency",
|
|
"amd_vbaq": "enabled",
|
|
"capture": "",
|
|
"controller": "enabled",
|
|
"dwmflush": "enabled",
|
|
"encoder": "",
|
|
"fps": "[10,30,60,90,120]",
|
|
"gamepad": "x360",
|
|
"hevc_mode": 0,
|
|
"key_rightalt_to_key_win": "disabled",
|
|
"keyboard": "enabled",
|
|
"min_log_level": 2,
|
|
"mouse": "enabled",
|
|
"nv_coder": "auto",
|
|
"nv_preset": "p4",
|
|
"nv_rc": "cbr",
|
|
"nv_tune": "ull",
|
|
"origin_pin_allowed": "pc",
|
|
"origin_web_ui_allowed": "lan",
|
|
"qsv_coder": "auto",
|
|
"qsv_preset": "medium",
|
|
"resolutions": "[352x240,480x360,858x480,1280x720,1920x1080,2560x1080,3440x1440,1920x1200,3840x2160,3840x1600]",
|
|
"sw_preset": "superfast",
|
|
"sw_tune": "zerolatency",
|
|
"upnp": "disabled",
|
|
"vt_coder": "auto",
|
|
"vt_realtime": "enabled",
|
|
"vt_software": "auto",
|
|
"global_prep_cmd": "[]",
|
|
}
|
|
|
|
new Vue({
|
|
el: "#app",
|
|
data() {
|
|
return {
|
|
platform: "",
|
|
saved: false,
|
|
restarted: false,
|
|
config: null,
|
|
fps: [],
|
|
resolutions: [],
|
|
currentTab: "general",
|
|
resIn: "",
|
|
fpsIn: "",
|
|
global_prep_cmd: [],
|
|
tabs: [
|
|
{
|
|
id: "general",
|
|
name: "General",
|
|
},
|
|
{
|
|
id: "files",
|
|
name: "Files",
|
|
},
|
|
{
|
|
id: "input",
|
|
name: "Input",
|
|
},
|
|
{
|
|
id: "av",
|
|
name: "Audio/Video",
|
|
},
|
|
{
|
|
id: "advanced",
|
|
name: "Advanced",
|
|
},
|
|
{
|
|
id: "nv",
|
|
name: "NVIDIA NVENC Encoder",
|
|
},
|
|
{
|
|
id: "qsv",
|
|
name: "Intel QuickSync Encoder",
|
|
},
|
|
{
|
|
id: "amd",
|
|
name: "AMD AMF Encoder",
|
|
},
|
|
{
|
|
id: "va-api",
|
|
name: "VA-API Encoder",
|
|
},
|
|
{
|
|
id: "vt",
|
|
name: "VideoToolbox Encoder",
|
|
},
|
|
{
|
|
id: "sw",
|
|
name: "Software Encoder",
|
|
},
|
|
],
|
|
};
|
|
},
|
|
created() {
|
|
fetch("/api/config")
|
|
.then((r) => r.json())
|
|
.then((r) => {
|
|
this.config = r;
|
|
this.platform = this.config.platform;
|
|
|
|
var app = document.getElementById("app");
|
|
if (this.platform === "windows") {
|
|
this.tabs = this.tabs.filter((el) => {
|
|
return el.id !== "va-api" && el.id !== "vt";
|
|
});
|
|
}
|
|
if (this.platform === "linux") {
|
|
this.tabs = this.tabs.filter((el) => {
|
|
return el.id !== "amd" && el.id !== "qsv" && el.id !== "vt";
|
|
});
|
|
}
|
|
if (this.platform === "macos") {
|
|
this.tabs = this.tabs.filter((el) => {
|
|
return el.id !== "amd" && el.id !== "nv" && el.id !== "qsv" && el.id !== "va-api";
|
|
});
|
|
}
|
|
|
|
// remove values we don't want in the config file
|
|
delete this.config.platform;
|
|
delete this.config.status;
|
|
delete this.config.version;
|
|
//Populate default values if not present in config
|
|
for (let key in defaultConfig) {
|
|
if (this.config[key] === undefined) {
|
|
this.config[key] = defaultConfig[key]
|
|
}
|
|
}
|
|
|
|
this.fps = JSON.parse(this.config.fps);
|
|
//Resolutions should be fixed because are not valid JSON
|
|
let res = this.config.resolutions.substring(
|
|
1,
|
|
this.config.resolutions.length - 1
|
|
);
|
|
let resolutions = [];
|
|
res.split(",").forEach((r) => resolutions.push(r.trim()));
|
|
this.resolutions = resolutions;
|
|
|
|
this.config.global_prep_cmd = this.config.global_prep_cmd || [];
|
|
this.global_prep_cmd = JSON.parse(this.config.global_prep_cmd);
|
|
});
|
|
},
|
|
methods: {
|
|
serialize() {
|
|
let nl = this.config === "windows" ? "\r\n" : "\n";
|
|
this.config.resolutions =
|
|
"[" +
|
|
nl +
|
|
" " +
|
|
this.resolutions.join("," + nl + " ") +
|
|
nl +
|
|
"]";
|
|
// remove quotes from values in fps
|
|
this.config.fps = JSON.stringify(this.fps).replace(/"/g, "");
|
|
this.config.global_prep_cmd = JSON.stringify(this.global_prep_cmd);
|
|
},
|
|
save() {
|
|
this.saved = false;
|
|
this.restarted = false;
|
|
this.serialize();
|
|
|
|
// create a temp copy of this.config to use for the post request
|
|
let config = JSON.parse(JSON.stringify(this.config))
|
|
|
|
// delete default values from this.config
|
|
for (let key in defaultConfig) {
|
|
let delete_value = false
|
|
if (key === "resolutions" || key === "fps") {
|
|
let regex = /([\d]+x[\d]+)/g
|
|
// Use a regular expression to find each value and replace it with a quoted version
|
|
|
|
let config_value = JSON.parse(config[key].replace(regex, '"$1"')).toString()
|
|
let default_value = JSON.parse(defaultConfig[key].replace(regex, '"$1"')).toString()
|
|
|
|
if (config_value === default_value) {
|
|
delete_value = true
|
|
}
|
|
}
|
|
|
|
if (config[key] === defaultConfig[key]) {
|
|
delete_value = true
|
|
}
|
|
|
|
if (delete_value) {
|
|
delete config[key]
|
|
}
|
|
}
|
|
|
|
return fetch("/api/config", {
|
|
method: "POST",
|
|
body: JSON.stringify(config),
|
|
}).then((r) => {
|
|
if (r.status === 200) {
|
|
this.saved = true
|
|
return this.saved
|
|
}
|
|
else {
|
|
return false
|
|
}
|
|
});
|
|
},
|
|
apply() {
|
|
this.saved = this.restarted = false;
|
|
let saved = this.save();
|
|
|
|
saved.then((result) => {
|
|
if (result === true) {
|
|
this.restarted = true;
|
|
setTimeout(() => {
|
|
this.saved = this.restarted = false;
|
|
}, 5000);
|
|
fetch("/api/restart", {
|
|
method: "POST"
|
|
});
|
|
}
|
|
});
|
|
},
|
|
add_global_prep_cmd() {
|
|
let template = {
|
|
do: "",
|
|
undo: "",
|
|
};
|
|
|
|
if(this.platform === 'windows'){
|
|
template = {...template, elevated: false};
|
|
}
|
|
this.global_prep_cmd.push(template);
|
|
},
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.config-page {
|
|
padding: 1em;
|
|
border: 1px solid #dee2e6;
|
|
border-top: none;
|
|
}
|
|
|
|
.buttons {
|
|
padding: 1em 0;
|
|
}
|
|
|
|
.ms-item {
|
|
background-color: #ccc;
|
|
font-size: 12px;
|
|
font-weight: bold;
|
|
}
|
|
</style>
|