mirror of
https://github.com/LizardByte/Sunshine.git
synced 2026-08-07 10:20:46 +00:00
feat(rtsp): add option to limit packetsize for clients that cannot configure it (#5153)
Signed-off-by: Georgi Valkov <gvalkov@gmail.com>
This commit is contained in:
@@ -1753,6 +1753,67 @@ editing the `conf` file in a text editor. Use the examples as reference.
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### packetsize
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>Description</td>
|
||||
<td colspan="2">
|
||||
Limit the packetsize to avoid fragmentation on a low MTU link.
|
||||
@note{This helps avoid packet loss and micro-stutter on a layer 2 VPN with
|
||||
clients that cannot configure this value, e.g. Moonlight for Android/iOS.
|
||||
}
|
||||
@tip{To discover the optimal value:
|
||||
<ul>
|
||||
<li>Send ping to the server with don't fragment flag (DF)</li>
|
||||
<li>Find the size of the largest replay, and subtract 16</li>
|
||||
<li>Monitor the traffic to ensure no fragmentation</li>
|
||||
</ul>
|
||||
If using a VPN tunnel:
|
||||
<ul>
|
||||
<li>Set MTU on the TUN/TAP interface, and</li>
|
||||
<li>Ensure no fragmentation both inside and outside the tunnel</li>
|
||||
<li>Max UDP size = MTU size - 28</li>
|
||||
<li>`packetsize` = max UDP size - 16</li>
|
||||
<li>Monitor the traffic to ensure no fragmentation</li>
|
||||
</ul>
|
||||
Sample calculation for OpenVPN layer 2, using IPv4:
|
||||
<ul>
|
||||
<li>1428 bytes for max ICMP/UDP size outside the tunnel</li>
|
||||
<li>Subtract the OpenVPN overhead: 24 bytes (may vary)</li>
|
||||
<li>1404 bytes for Ethernet inside the tunnel</li>
|
||||
<li>Subtract the Ethernet header: 14 bytes</li>
|
||||
<li>1390 bytes for MTU inside the tunnel</li>
|
||||
<li>Subtract the IPv4 header: 20 bytes</li>
|
||||
<li>Subtract the UDP header: 8 bytes</li>
|
||||
<li>1362 bytes for UDP payload</li>
|
||||
<li>Subtract: 16 bytes</li>
|
||||
<li>1346 bytes for `packetsize`</li>
|
||||
</ul>
|
||||
}
|
||||
@warning{Reduce the bitrate when using low values.
|
||||
Values larger than 1456 require jumbo frames.
|
||||
}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Default</td>
|
||||
<td colspan="2">@code{}
|
||||
0
|
||||
@endcode</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Range</td>
|
||||
<td colspan="2">0, 200-65535</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example</td>
|
||||
<td colspan="2">@code{}
|
||||
packetsize = 1346
|
||||
@endcode</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Config Files
|
||||
|
||||
### file_apps
|
||||
|
||||
@@ -544,6 +544,7 @@ namespace config {
|
||||
|
||||
ENCRYPTION_MODE_NEVER, // lan_encryption_mode
|
||||
ENCRYPTION_MODE_OPPORTUNISTIC, // wan_encryption_mode
|
||||
0, // packetsize
|
||||
};
|
||||
|
||||
nvhttp_t nvhttp {
|
||||
@@ -1252,6 +1253,7 @@ namespace config {
|
||||
|
||||
int_between_f(vars, "lan_encryption_mode", stream.lan_encryption_mode, {0, 2});
|
||||
int_between_f(vars, "wan_encryption_mode", stream.wan_encryption_mode, {0, 2});
|
||||
int_between_f(vars, "packetsize", stream.packetsize, {0, PACKETSIZE_MAX});
|
||||
|
||||
path_f(vars, "file_apps", stream.file_apps);
|
||||
#ifndef __ANDROID__
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
#include "nvenc/nvenc_config.h"
|
||||
|
||||
namespace config {
|
||||
// Valid range for the packetsize limit
|
||||
constexpr int PACKETSIZE_MIN = 200;
|
||||
constexpr int PACKETSIZE_MAX = 65535;
|
||||
constexpr int PACKETSIZE_SMALL = 500;
|
||||
constexpr int PACKETSIZE_LARGE = 1456;
|
||||
|
||||
// track modified config options
|
||||
inline std::unordered_map<std::string, std::string> modified_config_settings;
|
||||
|
||||
@@ -177,6 +183,9 @@ namespace config {
|
||||
// Video encryption settings for LAN and WAN streams
|
||||
int lan_encryption_mode;
|
||||
int wan_encryption_mode;
|
||||
|
||||
// Limit the packetsize to avoid fragmentation on a low MTU link
|
||||
int packetsize;
|
||||
};
|
||||
|
||||
struct nvhttp_t {
|
||||
|
||||
@@ -1004,6 +1004,23 @@ namespace rtsp_stream {
|
||||
config.encryptionFlagsEnabled |= SS_ENC_AUDIO;
|
||||
}
|
||||
|
||||
// Limit the packetsize to avoid fragmentation with clients that cannot configure this value
|
||||
if (config::stream.packetsize && config::stream.packetsize < config.packetsize) {
|
||||
if (config::stream.packetsize < config::PACKETSIZE_MIN || config::stream.packetsize > config::PACKETSIZE_MAX) {
|
||||
BOOST_LOG(warning) << "packetsize range: ["sv << config::PACKETSIZE_MIN << "-"sv << config::PACKETSIZE_MAX
|
||||
<< "] invalid value: "sv << config::stream.packetsize;
|
||||
} else {
|
||||
if (config::stream.packetsize < config::PACKETSIZE_SMALL) {
|
||||
BOOST_LOG(info) << "packetsize is small < "sv << config::PACKETSIZE_SMALL << " bytes, reduce bitrate if the stream breaks"sv;
|
||||
} else if (config::stream.packetsize > config::PACKETSIZE_LARGE) {
|
||||
BOOST_LOG(info) << "packetsize is large > "sv << config::PACKETSIZE_LARGE << " bytes, jumbo frames may be used"sv;
|
||||
}
|
||||
|
||||
BOOST_LOG(info) << "packetsize limit: "sv << config.packetsize << " -> "sv << config::stream.packetsize << " bytes"sv;
|
||||
config.packetsize = config::stream.packetsize;
|
||||
}
|
||||
}
|
||||
|
||||
config.monitor.height = (int) util::from_view(args.at("x-nv-video[0].clientViewportHt"sv));
|
||||
config.monitor.width = (int) util::from_view(args.at("x-nv-video[0].clientViewportWd"sv));
|
||||
config.monitor.framerate = (int) util::from_view(args.at("x-nv-video[0].maxFPS"sv));
|
||||
|
||||
@@ -253,6 +253,7 @@
|
||||
"lan_encryption_mode": 0,
|
||||
"wan_encryption_mode": 1,
|
||||
"ping_timeout": 10000,
|
||||
"packetsize": 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -173,6 +173,13 @@ const effectivePort = computed(() => +config.value?.port ?? defaultMoonlightPort
|
||||
<div class="form-text">{{ $t('config.ping_timeout_desc') }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Packet Size Limit -->
|
||||
<div class="mb-3">
|
||||
<label for="packetsize" class="form-label">{{ $t('config.packetsize') }}</label>
|
||||
<input type="number" min="0" max="65535" class="form-control" id="packetsize" placeholder="0" v-model="config.packetsize" />
|
||||
<div class="form-text">{{ $t('config.packetsize_desc') }}</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -319,6 +319,8 @@
|
||||
"output_name": "Display Id",
|
||||
"output_name_desc_unix": "During Sunshine startup, you should see the list of detected displays. Note: You need to use the id value inside the parenthesis. Below is an example; the actual output can be found in the Troubleshooting tab.",
|
||||
"output_name_desc_windows": "Manually specify a display device id to use for capture. If unset, the primary display is captured. Note: If you specified a GPU above, this display must be connected to that GPU. During Sunshine startup, you should see the list of detected displays. Below is an example; the actual output can be found in the Troubleshooting tab.",
|
||||
"packetsize": "Packet Size Limit",
|
||||
"packetsize_desc": "Limit the packet size to avoid fragmentation on a low MTU link. This helps reduce packet loss and micro-stuttering, while streaming over a layer 2 VPN to clients that cannot configure this value, e.g. Moonlight for Android/iOS. Reduce the bitrate when using low values. Values larger than 1456 require jumbo frames. Range: 0, 200-65536. A value of 0 will disable the limit.",
|
||||
"ping_timeout": "Ping Timeout",
|
||||
"ping_timeout_desc": "How long to wait in milliseconds for data from moonlight before shutting down the stream",
|
||||
"pkey": "Private Key",
|
||||
|
||||
Reference in New Issue
Block a user