diff --git a/src/config.cpp b/src/config.cpp index 6892e3029..037decd13 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1209,6 +1209,7 @@ namespace config { string_f(vars, "fallback_mode", video.fallback_mode); bool_f(vars, "isolated_virtual_display_option", video.isolated_virtual_display_option); bool_f(vars, "ignore_encoder_probe_failure", video.ignore_encoder_probe_failure); + bool_f(vars, "enable_uso", video.enable_uso); path_f(vars, "pkey", nvhttp.pkey); path_f(vars, "cert", nvhttp.cert); diff --git a/src/config.h b/src/config.h index a6d7c8338..21e29bb9f 100644 --- a/src/config.h +++ b/src/config.h @@ -149,6 +149,15 @@ namespace config { std::string fallback_mode; bool isolated_virtual_display_option; bool ignore_encoder_probe_failure; + + /** + * @brief Whether to use UDP segmentation offload (USO) when sending batched video packets on Windows. + * + * Some NIC drivers (notably VirtIO) accept the UDP_SEND_MSG_SIZE control message but silently + * drop the datagram instead of segmenting it, which results in no video ever reaching the client. + * Set to false to send video as regular MTU-sized packets instead. + */ + bool enable_uso = true; }; struct audio_t { diff --git a/src/platform/common.h b/src/platform/common.h index 2073d5937..bd00e5079 100644 --- a/src/platform/common.h +++ b/src/platform/common.h @@ -674,6 +674,14 @@ namespace platf { uint16_t target_port; boost::asio::ip::address &source_address; + /** + * @brief Whether to use UDP segmentation offload (USO) for the batch. + * + * Only supported on Windows and only meaningful when more than one message + * block is sent in a single call. + */ + bool enable_uso; + /** * @brief Returns a payload buffer descriptor for the given payload offset. * @param offset The offset in the total payload data (bytes). diff --git a/src/platform/windows/misc.cpp b/src/platform/windows/misc.cpp index ce07c7be9..c89f167e2 100644 --- a/src/platform/windows/misc.cpp +++ b/src/platform/windows/misc.cpp @@ -1491,7 +1491,10 @@ namespace platf { memcpy(WSA_CMSG_DATA(cm), &pktInfo, sizeof(pktInfo)); } - if (send_info.block_count > 1) { + // Only request USO when the user opted in. Some NIC drivers (notably VirtIO) accept the + // UDP_SEND_MSG_SIZE control message but silently drop the datagram instead of segmenting + // it, which breaks video streaming without any error being reported. + if (send_info.enable_uso && send_info.block_count > 1) { cmbuflen += WSA_CMSG_SPACE(sizeof(DWORD)); cm = WSA_CMSG_NXTHDR(&msg, cm); diff --git a/src/stream.cpp b/src/stream.cpp index bceeb93d3..68d9a7904 100644 --- a/src/stream.cpp +++ b/src/stream.cpp @@ -1519,6 +1519,7 @@ namespace stream { peer_address, session->video.peer.port(), session->localAddress, + config::video.enable_uso, }; size_t next_shard_to_send = 0; diff --git a/src_assets/common/assets/web/config.html b/src_assets/common/assets/web/config.html index 1c638b6fa..446119291 100644 --- a/src_assets/common/assets/web/config.html +++ b/src_assets/common/assets/web/config.html @@ -213,6 +213,7 @@

{{ $t('config.configuration') }}

"max_bitrate": 0, "minimum_fps_target": 0, "isolated_virtual_display_option": "disabled", + "enable_uso": "enabled", }, }, {