mirror of
https://github.com/nestriness/nestri.git
synced 2025-12-11 00:05:36 +02:00
feat(runner): Rust updates and improvements (#196)
## Description - Updates to latest Rust 2024 🎉 - Make DataChannel messages ordered on nestri-server side - Bugfixes and code improvements + formatting ## Type of Change - [x] Bug fix (non-breaking change) - [x] New feature (non-breaking change) ## Checklist - [x] I have updated relevant documentation - [x] My code follows the project's coding style - [x] My changes generate no new warnings/errors --------- Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com> Co-authored-by: Wanjohi <elviswanjohi47@gmail.com>
This commit is contained in:
committed by
GitHub
parent
ea96fed4f6
commit
b18b08b822
2
.github/workflows/runner.yml
vendored
2
.github/workflows/runner.yml
vendored
@@ -7,6 +7,7 @@ on:
|
||||
paths:
|
||||
- "containers/runner.Containerfile"
|
||||
- "packages/scripts/**"
|
||||
- "packages/server/**"
|
||||
- ".github/workflows/runner.yml"
|
||||
schedule:
|
||||
- cron: 7 0 * * 1,3,6 # Regularly to keep that build cache warm
|
||||
@@ -16,6 +17,7 @@ on:
|
||||
- "containers/runner.Containerfile"
|
||||
- ".github/workflows/runner.yml"
|
||||
- "packages/scripts/**"
|
||||
- "packages/server/**"
|
||||
tags:
|
||||
- v*.*.*
|
||||
release:
|
||||
|
||||
1049
packages/server/Cargo.lock
generated
1049
packages/server/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "nestri-server"
|
||||
version = "0.1.0-alpha.2"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[[bin]]
|
||||
name = "nestri-server"
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct RateControlCQP {
|
||||
/// Constant Quantization Parameter (CQP) quality level
|
||||
pub quality: u32,
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct RateControlVBR {
|
||||
/// Target bitrate in kbps
|
||||
pub target_bitrate: i32,
|
||||
/// Maximum bitrate in kbps
|
||||
pub max_bitrate: i32,
|
||||
}
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct RateControlCBR {
|
||||
/// Target bitrate in kbps
|
||||
pub target_bitrate: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum RateControl {
|
||||
/// Constant Quantization Parameter
|
||||
CQP(RateControlCQP),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use crate::gpu::{self, get_gpu_by_card_path, get_gpus_by_vendor, GPUInfo};
|
||||
use crate::args::encoding_args::RateControl;
|
||||
use crate::gpu::{self, GPUInfo, get_gpu_by_card_path, get_gpus_by_vendor};
|
||||
use gst::prelude::*;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Clone)]
|
||||
@@ -245,7 +246,10 @@ pub fn encoder_gop_params(encoder: &VideoEncoderInfo, gop_size: u32) -> VideoEnc
|
||||
})
|
||||
}
|
||||
|
||||
pub fn encoder_low_latency_params(encoder: &VideoEncoderInfo) -> VideoEncoderInfo {
|
||||
pub fn encoder_low_latency_params(
|
||||
encoder: &VideoEncoderInfo,
|
||||
rate_control: &RateControl,
|
||||
) -> VideoEncoderInfo {
|
||||
let mut encoder_optz = encoder_gop_params(encoder, 30);
|
||||
|
||||
match encoder_optz.encoder_api {
|
||||
@@ -283,13 +287,8 @@ pub fn encoder_low_latency_params(encoder: &VideoEncoderInfo) -> VideoEncoderInf
|
||||
encoder_optz.set_parameter("tune", "zerolatency");
|
||||
}
|
||||
"svtav1enc" => {
|
||||
encoder_optz.set_parameter("preset", "12");
|
||||
let suffix = if encoder_optz.get_parameters_string().contains("cbr") {
|
||||
":pred-struct=1"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
encoder_optz.set_parameter("parameters-string", &format!("lookahead=0{}", suffix));
|
||||
encoder_optz.set_parameter("preset", "11");
|
||||
encoder_optz.set_parameter("parameters-string", "lookahead=0");
|
||||
}
|
||||
"av1enc" => {
|
||||
encoder_optz.set_parameter("usage-profile", "realtime");
|
||||
|
||||
@@ -4,13 +4,13 @@ mod gpu;
|
||||
mod latency;
|
||||
mod messages;
|
||||
mod nestrisink;
|
||||
mod websocket;
|
||||
mod proto;
|
||||
mod websocket;
|
||||
|
||||
use crate::args::encoding_args;
|
||||
use crate::gpu::GPUVendor;
|
||||
use crate::nestrisink::NestriSignaller;
|
||||
use crate::websocket::NestriWebSocket;
|
||||
use crate::gpu::GPUVendor;
|
||||
use futures_util::StreamExt;
|
||||
use gst::prelude::*;
|
||||
use gstrswebrtc::signaller::Signallable;
|
||||
@@ -55,12 +55,19 @@ fn handle_gpus(args: &args::Args) -> Option<gpu::GPUInfo> {
|
||||
gpu = filtered_gpus.get(args.device.gpu_index as usize).cloned();
|
||||
} else {
|
||||
// get first GPU
|
||||
gpu = filtered_gpus.into_iter().find(|g| *g.vendor() != GPUVendor::UNKNOWN);
|
||||
gpu = filtered_gpus
|
||||
.into_iter()
|
||||
.find(|g| *g.vendor() != GPUVendor::UNKNOWN);
|
||||
}
|
||||
}
|
||||
if gpu.is_none() {
|
||||
println!("No GPU found with the specified parameters: vendor='{}', name='{}', index='{}', card_path='{}'",
|
||||
args.device.gpu_vendor, args.device.gpu_name, args.device.gpu_index, args.device.gpu_card_path);
|
||||
println!(
|
||||
"No GPU found with the specified parameters: vendor='{}', name='{}', index='{}', card_path='{}'",
|
||||
args.device.gpu_vendor,
|
||||
args.device.gpu_name,
|
||||
args.device.gpu_index,
|
||||
args.device.gpu_card_path
|
||||
);
|
||||
return None;
|
||||
}
|
||||
let gpu = gpu.unwrap();
|
||||
@@ -83,7 +90,11 @@ fn handle_encoder_video(args: &args::Args) -> Option<enc_helper::VideoEncoderInf
|
||||
encoder.codec.to_str(),
|
||||
encoder.encoder_api.to_str(),
|
||||
encoder.encoder_type.to_str(),
|
||||
if let Some(gpu) = &encoder.gpu_info { gpu.device_name() } else { "CPU" },
|
||||
if let Some(gpu) = &encoder.gpu_info {
|
||||
gpu.device_name()
|
||||
} else {
|
||||
"CPU"
|
||||
},
|
||||
);
|
||||
}
|
||||
// Pick most suitable video encoder based on given arguments
|
||||
@@ -99,8 +110,12 @@ fn handle_encoder_video(args: &args::Args) -> Option<enc_helper::VideoEncoderInf
|
||||
);
|
||||
}
|
||||
if video_encoder.is_none() {
|
||||
println!("No video encoder found with the specified parameters: name='{}', vcodec='{}', type='{}'",
|
||||
args.encoding.video.encoder, args.encoding.video.codec, args.encoding.video.encoder_type);
|
||||
println!(
|
||||
"No video encoder found with the specified parameters: name='{}', vcodec='{}', type='{}'",
|
||||
args.encoding.video.encoder,
|
||||
args.encoding.video.codec,
|
||||
args.encoding.video.encoder_type
|
||||
);
|
||||
return None;
|
||||
}
|
||||
let video_encoder = video_encoder.unwrap();
|
||||
@@ -113,7 +128,8 @@ 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);
|
||||
let mut optimized_encoder =
|
||||
enc_helper::encoder_low_latency_params(&video_encoder, &args.encoding.video.rate_control);
|
||||
// Handle rate-control method
|
||||
match &args.encoding.video.rate_control {
|
||||
encoding_args::RateControl::CQP(cqp) => {
|
||||
@@ -240,14 +256,14 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
&match &args.encoding.audio.rate_control {
|
||||
encoding_args::RateControl::CBR(cbr) => cbr.target_bitrate * 1000i32,
|
||||
encoding_args::RateControl::VBR(vbr) => vbr.target_bitrate * 1000i32,
|
||||
_ => 128i32,
|
||||
_ => 128000i32,
|
||||
},
|
||||
);
|
||||
|
||||
/* Video */
|
||||
// Video Source Element
|
||||
let video_source = gst::ElementFactory::make("waylanddisplaysrc").build()?;
|
||||
video_source.set_property("render-node", &gpu.render_path());
|
||||
video_source.set_property_from_str("render-node", gpu.render_path());
|
||||
|
||||
// Caps Filter Element (resolution, fps)
|
||||
let caps_filter = gst::ElementFactory::make("capsfilter").build()?;
|
||||
@@ -289,6 +305,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let webrtcsink = BaseWebRTCSink::with_signaller(Signallable::from(signaller.clone()));
|
||||
webrtcsink.set_property_from_str("stun-server", "stun://stun.l.google.com:19302");
|
||||
webrtcsink.set_property_from_str("congestion-control", "disabled");
|
||||
webrtcsink.set_property("do-retransmission", false);
|
||||
|
||||
// Add elements to the pipeline
|
||||
pipeline.add_many(&[
|
||||
@@ -343,7 +360,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
|
||||
// Optimize latency of pipeline
|
||||
video_source.sync_state_with_parent().expect("failed to sync with parent");
|
||||
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);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::messages::{
|
||||
decode_message_as, encode_message, AnswerType, JoinerType, MessageAnswer, MessageBase,
|
||||
MessageICE, MessageJoin, MessageSDP,
|
||||
AnswerType, JoinerType, MessageAnswer, MessageBase, MessageICE, MessageJoin, MessageSDP,
|
||||
decode_message_as, encode_message,
|
||||
};
|
||||
use crate::proto::proto::proto_input::InputType::{
|
||||
KeyDown, KeyUp, MouseKeyDown, MouseKeyUp, MouseMove, MouseMoveAbs, MouseWheel,
|
||||
@@ -10,7 +10,7 @@ use crate::websocket::NestriWebSocket;
|
||||
use glib::subclass::prelude::*;
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
use gst_webrtc::{gst_sdp, WebRTCSDPType, WebRTCSessionDescription};
|
||||
use gst_webrtc::{WebRTCSDPType, WebRTCSessionDescription, gst_sdp};
|
||||
use gstrswebrtc::signaller::{Signallable, SignallableImpl};
|
||||
use prost::Message;
|
||||
use std::collections::HashSet;
|
||||
@@ -144,8 +144,9 @@ impl Signaller {
|
||||
&[
|
||||
&"nestri-data-channel",
|
||||
&gst::Structure::builder("config")
|
||||
.field("ordered", &false)
|
||||
.field("ordered", &true)
|
||||
.field("max-retransmits", &0u32)
|
||||
.field("priority", "high")
|
||||
.build(),
|
||||
],
|
||||
),
|
||||
@@ -337,12 +338,14 @@ impl ObjectSubclass for Signaller {
|
||||
impl ObjectImpl for Signaller {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPS: LazyLock<Vec<glib::ParamSpec>> = LazyLock::new(|| {
|
||||
vec![glib::ParamSpecBoolean::builder("manual-sdp-munging")
|
||||
.nick("Manual SDP munging")
|
||||
.blurb("Whether the signaller manages SDP munging itself")
|
||||
.default_value(false)
|
||||
.read_only()
|
||||
.build()]
|
||||
vec![
|
||||
glib::ParamSpecBoolean::builder("manual-sdp-munging")
|
||||
.nick("Manual SDP munging")
|
||||
.blurb("Whether the signaller manages SDP munging itself")
|
||||
.default_value(false)
|
||||
.read_only()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPS.as_ref()
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::sync::Arc;
|
||||
use crate::websocket::NestriWebSocket;
|
||||
use gst::glib;
|
||||
use gst::subclass::prelude::*;
|
||||
use gstrswebrtc::signaller::Signallable;
|
||||
use crate::websocket::NestriWebSocket;
|
||||
use std::sync::Arc;
|
||||
|
||||
mod imp;
|
||||
|
||||
@@ -22,4 +22,4 @@ impl Default for NestriSignaller {
|
||||
fn default() -> Self {
|
||||
panic!("Cannot create NestriSignaller without NestriWebSocket");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
pub mod proto;
|
||||
pub mod proto;
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoTimestampEntry {
|
||||
#[prost(string, tag="1")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub stage: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag="2")]
|
||||
#[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")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub sequence_id: ::prost::alloc::string::String,
|
||||
#[prost(message, repeated, tag="2")]
|
||||
#[prost(message, repeated, tag = "2")]
|
||||
pub timestamps: ::prost::alloc::vec::Vec<ProtoTimestampEntry>,
|
||||
}
|
||||
/// MouseMove message
|
||||
@@ -21,11 +21,11 @@ pub struct ProtoLatencyTracker {
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoMouseMove {
|
||||
/// Fixed value "MouseMove"
|
||||
#[prost(string, tag="1")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub r#type: ::prost::alloc::string::String,
|
||||
#[prost(int32, tag="2")]
|
||||
#[prost(int32, tag = "2")]
|
||||
pub x: i32,
|
||||
#[prost(int32, tag="3")]
|
||||
#[prost(int32, tag = "3")]
|
||||
pub y: i32,
|
||||
}
|
||||
/// MouseMoveAbs message
|
||||
@@ -33,11 +33,11 @@ pub struct ProtoMouseMove {
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoMouseMoveAbs {
|
||||
/// Fixed value "MouseMoveAbs"
|
||||
#[prost(string, tag="1")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub r#type: ::prost::alloc::string::String,
|
||||
#[prost(int32, tag="2")]
|
||||
#[prost(int32, tag = "2")]
|
||||
pub x: i32,
|
||||
#[prost(int32, tag="3")]
|
||||
#[prost(int32, tag = "3")]
|
||||
pub y: i32,
|
||||
}
|
||||
/// MouseWheel message
|
||||
@@ -45,11 +45,11 @@ pub struct ProtoMouseMoveAbs {
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoMouseWheel {
|
||||
/// Fixed value "MouseWheel"
|
||||
#[prost(string, tag="1")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub r#type: ::prost::alloc::string::String,
|
||||
#[prost(int32, tag="2")]
|
||||
#[prost(int32, tag = "2")]
|
||||
pub x: i32,
|
||||
#[prost(int32, tag="3")]
|
||||
#[prost(int32, tag = "3")]
|
||||
pub y: i32,
|
||||
}
|
||||
/// MouseKeyDown message
|
||||
@@ -57,9 +57,9 @@ pub struct ProtoMouseWheel {
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoMouseKeyDown {
|
||||
/// Fixed value "MouseKeyDown"
|
||||
#[prost(string, tag="1")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub r#type: ::prost::alloc::string::String,
|
||||
#[prost(int32, tag="2")]
|
||||
#[prost(int32, tag = "2")]
|
||||
pub key: i32,
|
||||
}
|
||||
/// MouseKeyUp message
|
||||
@@ -67,9 +67,9 @@ pub struct ProtoMouseKeyDown {
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoMouseKeyUp {
|
||||
/// Fixed value "MouseKeyUp"
|
||||
#[prost(string, tag="1")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub r#type: ::prost::alloc::string::String,
|
||||
#[prost(int32, tag="2")]
|
||||
#[prost(int32, tag = "2")]
|
||||
pub key: i32,
|
||||
}
|
||||
/// KeyDown message
|
||||
@@ -77,9 +77,9 @@ pub struct ProtoMouseKeyUp {
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoKeyDown {
|
||||
/// Fixed value "KeyDown"
|
||||
#[prost(string, tag="1")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub r#type: ::prost::alloc::string::String,
|
||||
#[prost(int32, tag="2")]
|
||||
#[prost(int32, tag = "2")]
|
||||
pub key: i32,
|
||||
}
|
||||
/// KeyUp message
|
||||
@@ -87,53 +87,53 @@ pub struct ProtoKeyDown {
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoKeyUp {
|
||||
/// Fixed value "KeyUp"
|
||||
#[prost(string, tag="1")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub r#type: ::prost::alloc::string::String,
|
||||
#[prost(int32, tag="2")]
|
||||
#[prost(int32, tag = "2")]
|
||||
pub key: i32,
|
||||
}
|
||||
/// Union of all Input types
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoInput {
|
||||
#[prost(oneof="proto_input::InputType", tags="1, 2, 3, 4, 5, 6, 7")]
|
||||
#[prost(oneof = "proto_input::InputType", tags = "1, 2, 3, 4, 5, 6, 7")]
|
||||
pub input_type: ::core::option::Option<proto_input::InputType>,
|
||||
}
|
||||
/// Nested message and enum types in `ProtoInput`.
|
||||
pub mod proto_input {
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
#[derive(Clone, PartialEq, ::prost::Oneof)]
|
||||
pub enum InputType {
|
||||
#[prost(message, tag="1")]
|
||||
#[prost(message, tag = "1")]
|
||||
MouseMove(super::ProtoMouseMove),
|
||||
#[prost(message, tag="2")]
|
||||
#[prost(message, tag = "2")]
|
||||
MouseMoveAbs(super::ProtoMouseMoveAbs),
|
||||
#[prost(message, tag="3")]
|
||||
#[prost(message, tag = "3")]
|
||||
MouseWheel(super::ProtoMouseWheel),
|
||||
#[prost(message, tag="4")]
|
||||
#[prost(message, tag = "4")]
|
||||
MouseKeyDown(super::ProtoMouseKeyDown),
|
||||
#[prost(message, tag="5")]
|
||||
#[prost(message, tag = "5")]
|
||||
MouseKeyUp(super::ProtoMouseKeyUp),
|
||||
#[prost(message, tag="6")]
|
||||
#[prost(message, tag = "6")]
|
||||
KeyDown(super::ProtoKeyDown),
|
||||
#[prost(message, tag="7")]
|
||||
#[prost(message, tag = "7")]
|
||||
KeyUp(super::ProtoKeyUp),
|
||||
}
|
||||
}
|
||||
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||
#[derive(Clone, PartialEq, ::prost::Message)]
|
||||
pub struct ProtoMessageBase {
|
||||
#[prost(string, tag="1")]
|
||||
#[prost(string, tag = "1")]
|
||||
pub payload_type: ::prost::alloc::string::String,
|
||||
#[prost(message, optional, tag="2")]
|
||||
#[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 ProtoMessageInput {
|
||||
#[prost(message, optional, tag="1")]
|
||||
#[prost(message, optional, tag = "1")]
|
||||
pub message_base: ::core::option::Option<ProtoMessageBase>,
|
||||
#[prost(message, optional, tag="2")]
|
||||
#[prost(message, optional, tag = "2")]
|
||||
pub data: ::core::option::Option<ProtoInput>,
|
||||
}
|
||||
// @@protoc_insertion_point(module)
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use crate::messages::{decode_message, encode_message, MessageBase, MessageLog};
|
||||
use crate::messages::{MessageBase, MessageLog, decode_message, encode_message};
|
||||
use futures_util::StreamExt;
|
||||
use futures_util::sink::SinkExt;
|
||||
use futures_util::stream::{SplitSink, SplitStream};
|
||||
use futures_util::StreamExt;
|
||||
use log::{Level, Log, Metadata, Record};
|
||||
use std::collections::HashMap;
|
||||
use std::error::Error;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
use tokio::net::TcpStream;
|
||||
use tokio::sync::{mpsc, Mutex, Notify};
|
||||
use tokio::sync::{Mutex, Notify, mpsc};
|
||||
use tokio::time::sleep;
|
||||
use tokio_tungstenite::tungstenite::{Message, Utf8Bytes};
|
||||
use tokio_tungstenite::{connect_async, MaybeTlsStream, WebSocketStream};
|
||||
use tokio_tungstenite::{MaybeTlsStream, WebSocketStream, connect_async};
|
||||
|
||||
type Callback = Box<dyn Fn(String) + Send + Sync>;
|
||||
type WSRead = SplitStream<WebSocketStream<MaybeTlsStream<TcpStream>>>;
|
||||
@@ -95,7 +95,9 @@ impl NestriWebSocket {
|
||||
while let Some(message_result) = ws_read.next().await {
|
||||
match message_result {
|
||||
Ok(message) => {
|
||||
let data = message.into_text().expect("failed to turn message into text");
|
||||
let data = message
|
||||
.into_text()
|
||||
.expect("failed to turn message into text");
|
||||
let base_message = match decode_message(data.to_string()) {
|
||||
Ok(base_message) => base_message,
|
||||
Err(e) => {
|
||||
|
||||
Reference in New Issue
Block a user