fix: Fix good chunk of packet loss and frame drop issues

- Also added new latency control parameter, not super visible differences, but it's cool :)
This commit is contained in:
DatCaptainHorse
2025-12-04 02:21:46 +02:00
parent 549a98bc48
commit b743dab332
24 changed files with 813 additions and 804 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -28,7 +28,7 @@ prost = "0.14"
prost-types = "0.14"
parking_lot = "0.12"
byteorder = "1.5"
libp2p = { version = "0.56", features = ["identify", "dns", "tcp", "noise", "ping", "tokio", "serde", "yamux", "macros", "websocket", "autonat"] }
libp2p = { version = "0.56", features = ["identify", "dns", "tcp", "noise", "ping", "tokio", "serde", "yamux", "macros", "autonat", "quic"] }
libp2p-identify = "0.47"
libp2p-ping = "0.47"
libp2p-autonat = { version = "0.15", features = ["v2"] }
@@ -37,7 +37,7 @@ libp2p-yamux = "0.47"
libp2p-noise = "0.46"
libp2p-dns = { version = "0.44", features = ["tokio"] }
libp2p-tcp = { version = "0.44", features = ["tokio"] }
libp2p-websocket = "0.45"
libp2p-quic = { version = "0.13", features = ["tokio"] }
dashmap = "6.1"
anyhow = "1.0"
unsigned-varint = "0.8"

View File

@@ -122,6 +122,14 @@ impl Args {
.value_parser(value_parser!(encoding_args::RateControlMethod))
.default_value("cbr"),
)
.arg(
Arg::new("video-latency-control")
.long("video-latency-control")
.env("VIDEO_LATENCY_CONTROL")
.help("Video latency control")
.value_parser(value_parser!(encoding_args::LatencyControl))
.default_value("lowest-latency"),
)
.arg(
Arg::new("video-cqp")
.long("video-cqp")
@@ -165,6 +173,14 @@ impl Args {
)
.default_value("8"),
)
.arg(
Arg::new("keyframe-dist-secs")
.long("keyframe-dist-secs")
.env("KEYFRAME_DIST_SECS")
.help("Distance between keyframes in seconds")
.value_parser(value_parser!(u32).range(1..))
.default_value("1"),
)
.arg(
Arg::new("audio-capture-method")
.long("audio-capture-method")
@@ -195,6 +211,14 @@ impl Args {
.value_parser(value_parser!(encoding_args::RateControlMethod))
.default_value("cbr"),
)
.arg(
Arg::new("audio-latency-control")
.long("audio-latency-control")
.env("AUDIO_LATENCY_CONTROL")
.help("Audio latency control")
.value_parser(value_parser!(encoding_args::LatencyControl))
.default_value("lowest-latency"),
)
.arg(
Arg::new("audio-bitrate")
.long("audio-bitrate")

View File

@@ -60,6 +60,12 @@ pub enum RateControl {
CBR(RateControlCBR),
}
#[derive(Debug, PartialEq, Eq, Clone, ValueEnum)]
pub enum LatencyControl {
LowestLatency,
HighestQuality,
}
pub struct EncodingOptionsBase {
/// Codec (e.g. "h264", "opus" etc.)
pub codec: Codec,
@@ -67,6 +73,8 @@ pub struct EncodingOptionsBase {
pub encoder: Option<String>,
/// Rate control method (e.g. "cqp", "vbr", "cbr")
pub rate_control: RateControl,
/// Latency control option, what to tweak settings towards (latency or quality)
pub latency_control: LatencyControl,
}
impl EncodingOptionsBase {
pub fn debug_print(&self) {
@@ -87,6 +95,14 @@ impl EncodingOptionsBase {
tracing::info!("-> Target Bitrate: {}", cbr.target_bitrate);
}
}
match &self.latency_control {
LatencyControl::LowestLatency => {
tracing::info!("> Latency Control: Priorizing lowest latency");
}
LatencyControl::HighestQuality => {
tracing::info!("> Latency Control: Priorizing quality at the cost of latency");
}
}
}
}
@@ -94,6 +110,7 @@ pub struct VideoEncodingOptions {
pub base: EncodingOptionsBase,
pub encoder_type: EncoderType,
pub bit_depth: u32,
pub keyframe_dist_secs: u32,
}
impl VideoEncodingOptions {
pub fn from_matches(matches: &clap::ArgMatches) -> Self {
@@ -125,6 +142,10 @@ impl VideoEncodingOptions {
max_bitrate: matches.get_one::<u32>("video-bitrate-max").unwrap().clone(),
}),
},
latency_control: matches
.get_one::<LatencyControl>("video-latency-control")
.unwrap_or(&LatencyControl::LowestLatency)
.clone(),
},
encoder_type: matches
.get_one::<EncoderType>("video-encoder-type")
@@ -134,6 +155,10 @@ impl VideoEncodingOptions {
.get_one::<u32>("video-bit-depth")
.copied()
.unwrap_or(8),
keyframe_dist_secs: matches
.get_one::<u32>("keyframe-dist-secs")
.copied()
.unwrap_or(1),
}
}
@@ -142,6 +167,7 @@ impl VideoEncodingOptions {
self.base.debug_print();
tracing::info!("> Encoder Type: {}", self.encoder_type.as_str());
tracing::info!("> Bit Depth: {}", self.bit_depth);
tracing::info!("> Keyframe Distance Seconds: {}", self.keyframe_dist_secs);
}
}
impl Deref for VideoEncodingOptions {
@@ -208,6 +234,10 @@ impl AudioEncodingOptions {
}),
wot => panic!("Invalid rate control method for audio: {}", wot.as_str()),
},
latency_control: matches
.get_one::<LatencyControl>("audio-latency-control")
.unwrap_or(&LatencyControl::LowestLatency)
.clone(),
},
capture_method: matches
.get_one::<AudioCaptureMethod>("audio-capture-method")

View File

@@ -74,7 +74,6 @@ pub enum EncoderAPI {
QSV,
VAAPI,
NVENC,
AMF,
SOFTWARE,
UNKNOWN,
}
@@ -85,7 +84,6 @@ impl EncoderAPI {
Self::QSV => "Intel QuickSync Video",
Self::VAAPI => "Video Acceleration API",
Self::NVENC => "NVIDIA NVENC",
Self::AMF => "AMD Media Framework",
Self::SOFTWARE => "Software",
Self::UNKNOWN => "Unknown",
}
@@ -167,8 +165,6 @@ fn get_encoder_api(encoder: &str, encoder_type: &EncoderType) -> EncoderAPI {
EncoderAPI::VAAPI
} else if encoder.starts_with("nv") {
EncoderAPI::NVENC
} else if encoder.starts_with("amf") {
EncoderAPI::AMF
} else {
EncoderAPI::UNKNOWN
}
@@ -275,9 +271,9 @@ pub fn encoder_low_latency_params(
encoder: &VideoEncoderInfo,
_rate_control: &RateControl,
framerate: u32,
keyframe_dist_secs: u32,
) -> VideoEncoderInfo {
// 1 second keyframe interval for fast recovery, is this too taxing?
let mut encoder_optz = encoder_gop_params(encoder, framerate);
let mut encoder_optz = encoder_gop_params(encoder, framerate * keyframe_dist_secs);
match encoder_optz.encoder_api {
EncoderAPI::QSV => {
@@ -293,16 +289,6 @@ pub fn encoder_low_latency_params(
encoder_optz.set_parameter("tune", "ultra-low-latency");
encoder_optz.set_parameter("zerolatency", "true");
}
EncoderAPI::AMF => {
encoder_optz.set_parameter("preset", "speed");
let usage = match encoder_optz.codec {
VideoCodec::H264 | VideoCodec::H265 => "ultra-low-latency",
VideoCodec::AV1 => "low-latency",
};
if !usage.is_empty() {
encoder_optz.set_parameter("usage", usage);
}
}
EncoderAPI::SOFTWARE => match encoder_optz.name.as_str() {
"openh264enc" => {
encoder_optz.set_parameter("complexity", "low");
@@ -330,6 +316,56 @@ pub fn encoder_low_latency_params(
encoder_optz
}
pub fn encoder_high_quality_params(
encoder: &VideoEncoderInfo,
_rate_control: &RateControl,
framerate: u32,
keyframe_dist_secs: u32,
) -> VideoEncoderInfo {
let mut encoder_optz = encoder_gop_params(encoder, framerate * keyframe_dist_secs);
match encoder_optz.encoder_api {
EncoderAPI::QSV => {
encoder_optz.set_parameter("low-latency", "false");
encoder_optz.set_parameter("target-usage", "1");
}
EncoderAPI::VAAPI => {
encoder_optz.set_parameter("target-usage", "1");
}
EncoderAPI::NVENC => {
encoder_optz.set_parameter("multi-pass", "two-pass");
encoder_optz.set_parameter("preset", "p7");
encoder_optz.set_parameter("tune", "high-quality");
encoder_optz.set_parameter("zerolatency", "false");
encoder_optz.set_parameter("spatial-aq", "true");
encoder_optz.set_parameter("rc-lookahead", "3");
}
EncoderAPI::SOFTWARE => match encoder_optz.name.as_str() {
"openh264enc" => {
encoder_optz.set_parameter("complexity", "high");
encoder_optz.set_parameter("usage-type", "screen");
}
"x264enc" => {
encoder_optz.set_parameter("rc-lookahead", "3");
encoder_optz.set_parameter("speed-preset", "medium");
}
"svtav1enc" => {
encoder_optz.set_parameter("preset", "8");
encoder_optz.set_parameter("parameters-string", "lookahead=3");
}
"av1enc" => {
encoder_optz.set_parameter("usage-profile", "realtime");
encoder_optz.set_parameter("cpu-used", "8");
encoder_optz.set_parameter("lag-in-frames", "3");
}
_ => {}
},
_ => {}
}
encoder_optz
}
pub fn get_compatible_encoders(gpus: &Vec<GPUInfo>) -> Vec<VideoEncoderInfo> {
let mut encoders = Vec::new();
let registry = gstreamer::Registry::get();
@@ -427,16 +463,6 @@ pub fn get_compatible_encoders(gpus: &Vec<GPUInfo>) -> Vec<VideoEncoderInfo> {
None
}
}
EncoderAPI::AMF if element.has_property("device") => {
let device_id = match element.property_value("device").get::<u32>() {
Ok(v) => Some(v as usize),
Err(_) => None,
};
device_id.and_then(|id| {
get_gpus_by_vendor(&gpus, GPUVendor::AMD).get(id).cloned()
})
}
_ => None,
}
})
@@ -549,7 +575,6 @@ pub fn get_best_compatible_encoder(
score += match encoder.encoder_api {
EncoderAPI::NVENC => 3,
EncoderAPI::QSV => 3,
EncoderAPI::AMF => 3,
EncoderAPI::VAAPI => 2,
EncoderAPI::SOFTWARE => 1,
EncoderAPI::UNKNOWN => 0,

View File

@@ -8,6 +8,7 @@ mod p2p;
mod proto;
use crate::args::encoding_args;
use crate::args::encoding_args::LatencyControl;
use crate::enc_helper::{EncoderAPI, EncoderType};
use crate::gpu::{GPUInfo, GPUVendor};
use crate::input::controller::ControllerManager;
@@ -130,11 +131,20 @@ fn handle_encoder_video_settings(
args: &args::Args,
video_encoder: &enc_helper::VideoEncoderInfo,
) -> enc_helper::VideoEncoderInfo {
let mut optimized_encoder = enc_helper::encoder_low_latency_params(
&video_encoder,
&args.encoding.video.rate_control,
args.app.framerate,
);
let mut optimized_encoder = match args.encoding.video.latency_control {
LatencyControl::LowestLatency => enc_helper::encoder_low_latency_params(
&video_encoder,
&args.encoding.video.rate_control,
args.app.framerate,
args.encoding.video.keyframe_dist_secs,
),
LatencyControl::HighestQuality => enc_helper::encoder_high_quality_params(
&video_encoder,
&args.encoding.video.rate_control,
args.app.framerate,
args.encoding.video.keyframe_dist_secs,
),
};
// Handle rate-control method
match &args.encoding.video.rate_control {
encoding_args::RateControl::CQP(cqp) => {
@@ -429,39 +439,34 @@ async fn main() -> Result<(), Box<dyn Error>> {
webrtcsink.set_property("do-retransmission", false);
/* Queues */
let video_queue = gstreamer::ElementFactory::make("queue")
// Sink queues
let video_sink_queue = gstreamer::ElementFactory::make("queue").build()?;
let audio_sink_queue = gstreamer::ElementFactory::make("queue").build()?;
// Source queues
let video_source_queue = gstreamer::ElementFactory::make("queue")
.property("max-size-buffers", 2u32)
.property("max-size-time", 0u64)
.property("max-size-bytes", 0u32)
.build()?;
let audio_queue = gstreamer::ElementFactory::make("queue")
let audio_source_queue = gstreamer::ElementFactory::make("queue")
.property("max-size-buffers", 2u32)
.property("max-size-time", 0u64)
.property("max-size-bytes", 0u32)
.build()?;
/* Clock Sync */
let video_clocksync = gstreamer::ElementFactory::make("clocksync")
.property("sync-to-first", true)
.build()?;
let audio_clocksync = gstreamer::ElementFactory::make("clocksync")
.property("sync-to-first", true)
.build()?;
// Add elements to the pipeline
pipeline.add_many(&[
webrtcsink.upcast_ref(),
&video_sink_queue,
&audio_sink_queue,
&video_encoder,
&caps_filter,
&video_queue,
&video_clocksync,
&video_source_queue,
&video_source,
&audio_encoder,
&audio_capsfilter,
&audio_queue,
&audio_clocksync,
&audio_source_queue,
&audio_rate,
&audio_converter,
&audio_source,
@@ -493,16 +498,24 @@ async fn main() -> Result<(), Box<dyn Error>> {
&audio_converter,
&audio_rate,
&audio_capsfilter,
&audio_queue,
&audio_clocksync,
&audio_source_queue,
&audio_encoder,
])?;
// Link audio parser to audio encoder if present, otherwise just webrtcsink
if let Some(parser) = &audio_parser {
gstreamer::Element::link_many(&[&audio_encoder, parser, webrtcsink.upcast_ref()])?;
gstreamer::Element::link_many(&[
&audio_encoder,
parser,
&audio_sink_queue,
webrtcsink.upcast_ref(),
])?;
} else {
gstreamer::Element::link_many(&[&audio_encoder, webrtcsink.upcast_ref()])?;
gstreamer::Element::link_many(&[
&audio_encoder,
&audio_sink_queue,
webrtcsink.upcast_ref(),
])?;
}
// With zero-copy..
@@ -512,26 +525,20 @@ async fn main() -> Result<(), Box<dyn Error>> {
gstreamer::Element::link_many(&[
&video_source,
&caps_filter,
&video_queue,
&video_clocksync,
&video_source_queue,
&vapostproc,
&va_caps_filter,
&video_encoder,
])?;
} else if video_encoder_info.encoder_api == EncoderAPI::NVENC {
// NVENC pipeline
gstreamer::Element::link_many(&[
&video_source,
&caps_filter,
&video_encoder,
])?;
gstreamer::Element::link_many(&[&video_source, &caps_filter, &video_encoder])?;
}
} else {
gstreamer::Element::link_many(&[
&video_source,
&caps_filter,
&video_queue,
&video_clocksync,
&video_source_queue,
&video_converter.unwrap(),
&video_encoder,
])?;
@@ -539,21 +546,24 @@ async fn main() -> Result<(), Box<dyn Error>> {
// Link video parser if present with webrtcsink, otherwise just link webrtc sink
if let Some(parser) = &video_parser {
gstreamer::Element::link_many(&[&video_encoder, parser, webrtcsink.upcast_ref()])?;
gstreamer::Element::link_many(&[
&video_encoder,
parser,
&video_sink_queue,
webrtcsink.upcast_ref(),
])?;
} else {
gstreamer::Element::link_many(&[&video_encoder, webrtcsink.upcast_ref()])?;
gstreamer::Element::link_many(&[
&video_encoder,
&video_sink_queue,
webrtcsink.upcast_ref(),
])?;
}
// Make sure QOS is disabled to avoid latency
video_encoder.set_property("qos", true);
video_source.set_property("do-timestamp", &false);
audio_source.set_property("do-timestamp", &false);
// Optimize latency of pipeline
video_source
.sync_state_with_parent()
.expect("failed to sync with parent");
video_source.set_property("do-timestamp", &true);
audio_source.set_property("do-timestamp", &true);
pipeline.set_property("latency", &0u64);
pipeline.set_property("async-handling", true);
pipeline.set_property("message-forward", true);

View File

@@ -55,9 +55,8 @@ impl NestriP2P {
noise::Config::new,
yamux::Config::default,
)?
.with_quic()
.with_dns()?
.with_websocket(noise::Config::new, yamux::Config::default)
.await?
.with_behaviour(|key| NestriBehaviour::new(key.public()))?
.build(),
));

View File

@@ -1,14 +1,12 @@
// @generated
// This file is @generated by prost-build.
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoTimestampEntry {
#[prost(string, tag="1")]
pub stage: ::prost::alloc::string::String,
#[prost(message, optional, tag="2")]
pub time: ::core::option::Option<::prost_types::Timestamp>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProtoLatencyTracker {
#[prost(string, tag="1")]
@@ -19,8 +17,7 @@ pub struct ProtoLatencyTracker {
// Mouse messages
/// MouseMove message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoMouseMove {
#[prost(int32, tag="1")]
pub x: i32,
@@ -28,8 +25,7 @@ pub struct ProtoMouseMove {
pub y: i32,
}
/// MouseMoveAbs message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoMouseMoveAbs {
#[prost(int32, tag="1")]
pub x: i32,
@@ -37,8 +33,7 @@ pub struct ProtoMouseMoveAbs {
pub y: i32,
}
/// MouseWheel message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoMouseWheel {
#[prost(int32, tag="1")]
pub x: i32,
@@ -46,15 +41,13 @@ pub struct ProtoMouseWheel {
pub y: i32,
}
/// MouseKeyDown message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoMouseKeyDown {
#[prost(int32, tag="1")]
pub key: i32,
}
/// MouseKeyUp message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoMouseKeyUp {
#[prost(int32, tag="1")]
pub key: i32,
@@ -62,24 +55,26 @@ pub struct ProtoMouseKeyUp {
// Keyboard messages
/// KeyDown message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoKeyDown {
#[prost(int32, tag="1")]
pub key: i32,
}
/// KeyUp message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, Copy, PartialEq, ::prost::Message)]
#[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoKeyUp {
#[prost(int32, tag="1")]
pub key: i32,
}
// Clipboard message
// message ProtoClipboard {
// string content = 1; // Clipboard content
// }
// Controller messages
/// ControllerAttach message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoControllerAttach {
/// One of the following enums: "ps", "xbox" or "switch"
#[prost(string, tag="1")]
@@ -92,8 +87,7 @@ pub struct ProtoControllerAttach {
pub session_id: ::prost::alloc::string::String,
}
/// ControllerDetach message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoControllerDetach {
/// Session specific slot number (0-3)
#[prost(int32, tag="1")]
@@ -103,8 +97,7 @@ pub struct ProtoControllerDetach {
pub session_id: ::prost::alloc::string::String,
}
/// ControllerRumble message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoControllerRumble {
/// Session specific slot number (0-3)
#[prost(int32, tag="1")]
@@ -123,7 +116,6 @@ pub struct ProtoControllerRumble {
pub duration: i32,
}
/// ControllerStateBatch - single message containing full or partial controller state
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProtoControllerStateBatch {
/// Session specific slot number (0-3)
@@ -188,8 +180,8 @@ pub mod proto_controller_state_batch {
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
UpdateType::FullState => "FULL_STATE",
UpdateType::Delta => "DELTA",
Self::FullState => "FULL_STATE",
Self::Delta => "DELTA",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
@@ -204,8 +196,7 @@ pub mod proto_controller_state_batch {
}
// WebRTC + signaling
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct RtcIceCandidateInit {
#[prost(string, tag="1")]
pub candidate: ::prost::alloc::string::String,
@@ -216,8 +207,7 @@ pub struct RtcIceCandidateInit {
#[prost(string, optional, tag="4")]
pub username_fragment: ::core::option::Option<::prost::alloc::string::String>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct RtcSessionDescriptionInit {
#[prost(string, tag="1")]
pub sdp: ::prost::alloc::string::String,
@@ -225,29 +215,25 @@ pub struct RtcSessionDescriptionInit {
pub r#type: ::prost::alloc::string::String,
}
/// ProtoICE message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoIce {
#[prost(message, optional, tag="1")]
pub candidate: ::core::option::Option<RtcIceCandidateInit>,
}
/// ProtoSDP message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoSdp {
#[prost(message, optional, tag="1")]
pub sdp: ::core::option::Option<RtcSessionDescriptionInit>,
}
/// ProtoRaw message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoRaw {
#[prost(string, tag="1")]
pub data: ::prost::alloc::string::String,
}
/// ProtoClientRequestRoomStream message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoClientRequestRoomStream {
#[prost(string, tag="1")]
pub room_name: ::prost::alloc::string::String,
@@ -255,8 +241,7 @@ pub struct ProtoClientRequestRoomStream {
pub session_id: ::prost::alloc::string::String,
}
/// ProtoClientDisconnected message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoClientDisconnected {
#[prost(string, tag="1")]
pub session_id: ::prost::alloc::string::String,
@@ -264,13 +249,11 @@ pub struct ProtoClientDisconnected {
pub controller_slots: ::prost::alloc::vec::Vec<i32>,
}
/// ProtoServerPushStream message
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]
pub struct ProtoServerPushStream {
#[prost(string, tag="1")]
pub room_name: ::prost::alloc::string::String,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProtoMessageBase {
#[prost(string, tag="1")]
@@ -278,7 +261,6 @@ pub struct ProtoMessageBase {
#[prost(message, optional, tag="2")]
pub latency: ::core::option::Option<ProtoLatencyTracker>,
}
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProtoMessage {
#[prost(message, optional, tag="1")]
@@ -288,8 +270,7 @@ pub struct ProtoMessage {
}
/// Nested message and enum types in `ProtoMessage`.
pub mod proto_message {
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Payload {
/// Input types
#[prost(message, tag="2")]
@@ -304,6 +285,7 @@ pub mod proto_message {
MouseKeyUp(super::ProtoMouseKeyUp),
#[prost(message, tag="7")]
KeyDown(super::ProtoKeyDown),
/// ProtoClipboard clipboard = 9;
#[prost(message, tag="8")]
KeyUp(super::ProtoKeyUp),
/// Controller input types