feat: ✨ streamer: Disconnect from server
This commit is contained in:
parent
21d8781188
commit
6eb3e9b419
6 changed files with 117 additions and 55 deletions
|
@ -1,4 +1,7 @@
|
||||||
use iced::{widget::{button, column, Column}, Command};
|
use iced::{
|
||||||
|
widget::{button, column, Column},
|
||||||
|
Command,
|
||||||
|
};
|
||||||
use tokio::sync::broadcast::{channel, Sender};
|
use tokio::sync::broadcast::{channel, Sender};
|
||||||
|
|
||||||
use crate::{recording, streaming, utils::get_config, Config, BUFFER_LENGTH};
|
use crate::{recording, streaming, utils::get_config, Config, BUFFER_LENGTH};
|
||||||
|
@ -6,13 +9,16 @@ use crate::{recording, streaming, utils::get_config, Config, BUFFER_LENGTH};
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
StartStreaming,
|
StartStreaming,
|
||||||
|
StopStreaming,
|
||||||
ConfigLoad(Config),
|
ConfigLoad(Config),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Streamer {
|
pub struct Streamer {
|
||||||
config: Option<Config>,
|
config: Option<Config>,
|
||||||
sound_stream_producer:Sender<f32>,
|
sound_stream_producer: Sender<f32>,
|
||||||
|
stop_connection_producer: Sender<bool>,
|
||||||
|
stop_recording_producer: Sender<bool>,
|
||||||
}
|
}
|
||||||
impl Default for Streamer {
|
impl Default for Streamer {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -25,13 +31,28 @@ impl Streamer {
|
||||||
Self {
|
Self {
|
||||||
config: None,
|
config: None,
|
||||||
sound_stream_producer: channel(BUFFER_LENGTH).0,
|
sound_stream_producer: channel(BUFFER_LENGTH).0,
|
||||||
|
stop_connection_producer: channel(BUFFER_LENGTH).0,
|
||||||
|
stop_recording_producer: channel(BUFFER_LENGTH).0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn update(&mut self, message:Message) {
|
pub fn update(&mut self, message: Message) {
|
||||||
match message {
|
match message {
|
||||||
Message::StartStreaming => {
|
Message::StartStreaming => {
|
||||||
tokio::spawn(streaming::connect(self.sound_stream_producer.subscribe(), self.config.clone().unwrap()));
|
println!("Start Stream");
|
||||||
tokio::spawn(recording::record(self.sound_stream_producer.clone()));
|
tokio::spawn(streaming::connect(
|
||||||
|
self.sound_stream_producer.subscribe(),
|
||||||
|
self.config.clone().unwrap(),
|
||||||
|
self.stop_connection_producer.subscribe(),
|
||||||
|
));
|
||||||
|
tokio::spawn(recording::record(
|
||||||
|
self.sound_stream_producer.clone(),
|
||||||
|
self.stop_recording_producer.subscribe(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Message::StopStreaming => {
|
||||||
|
println!("Stop Stream");
|
||||||
|
self.stop_connection_producer.send(true).unwrap();
|
||||||
|
self.stop_recording_producer.send(true).unwrap();
|
||||||
}
|
}
|
||||||
Message::ConfigLoad(config) => {
|
Message::ConfigLoad(config) => {
|
||||||
self.config = Some(config);
|
self.config = Some(config);
|
||||||
|
@ -40,13 +61,11 @@ impl Streamer {
|
||||||
}
|
}
|
||||||
pub fn view(&self) -> Column<Message> {
|
pub fn view(&self) -> Column<Message> {
|
||||||
column![
|
column![
|
||||||
button("Start Streaming").on_press(Message::StartStreaming)
|
button("Start Streaming").on_press(Message::StartStreaming),
|
||||||
|
button("Stop Streaming").on_press(Message::StopStreaming),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
pub fn load_config() -> Command<Message> {
|
pub fn load_config() -> Command<Message> {
|
||||||
Command::perform(get_config(), Message::ConfigLoad)
|
Command::perform(get_config(), Message::ConfigLoad)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
pub mod gui;
|
||||||
pub mod recording;
|
pub mod recording;
|
||||||
pub mod streaming;
|
pub mod streaming;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod gui;
|
|
||||||
|
|
||||||
pub const BUFFER_LENGTH: usize = 1000000;
|
pub const BUFFER_LENGTH: usize = 1000000;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use streamer::gui::Streamer;
|
use streamer::gui::Streamer;
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> iced::Result{
|
async fn main() -> iced::Result {
|
||||||
println!("Hello, world!");
|
println!("Hello, world!");
|
||||||
iced::program("Streamer GUI", Streamer::update, Streamer::view)
|
iced::program("Streamer GUI", Streamer::update, Streamer::view)
|
||||||
.load(Streamer::load_config)
|
.load(Streamer::load_config)
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
use tokio::sync::broadcast::Sender;
|
use tokio::sync::broadcast::{Receiver, Sender};
|
||||||
|
|
||||||
pub async fn record(sound_stream_producer: Sender<f32>) {
|
pub async fn record(
|
||||||
|
sound_stream_producer: Sender<f32>,
|
||||||
|
mut stop_recording_consumer: Receiver<bool>,
|
||||||
|
) {
|
||||||
let host = cpal::default_host();
|
let host = cpal::default_host();
|
||||||
let input_device = host.default_input_device().unwrap();
|
let input_device = host.default_input_device().unwrap();
|
||||||
|
|
||||||
|
@ -22,8 +25,11 @@ pub async fn record(sound_stream_producer: Sender<f32>) {
|
||||||
|
|
||||||
input_stream.play().unwrap();
|
input_stream.play().unwrap();
|
||||||
println!("Recording Started");
|
println!("Recording Started");
|
||||||
std::thread::sleep(std::time::Duration::from_secs(1000000000));
|
while let Err(_) = stop_recording_consumer.try_recv() {
|
||||||
println!("DONE I HOPE");
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||||
|
}
|
||||||
|
input_stream.pause().unwrap();
|
||||||
|
println!("Recording Stopped");
|
||||||
}
|
}
|
||||||
fn err_fn(err: cpal::StreamError) {
|
fn err_fn(err: cpal::StreamError) {
|
||||||
eprintln!("Something Happened: {}", err);
|
eprintln!("Something Happened: {}", err);
|
||||||
|
|
|
@ -3,56 +3,77 @@ use std::{io::Write, sync::Arc, time::Duration};
|
||||||
use brotli::CompressorWriter;
|
use brotli::CompressorWriter;
|
||||||
use futures_util::SinkExt;
|
use futures_util::SinkExt;
|
||||||
use ringbuf::HeapRb;
|
use ringbuf::HeapRb;
|
||||||
use tokio::sync::broadcast::{channel, Receiver, Sender};
|
use tokio::{
|
||||||
|
sync::broadcast::{channel, Receiver, Sender},
|
||||||
|
task::JoinHandle,
|
||||||
|
};
|
||||||
use tokio_tungstenite::tungstenite::Message;
|
use tokio_tungstenite::tungstenite::Message;
|
||||||
|
|
||||||
use crate::{Config, BUFFER_LENGTH};
|
use crate::{Config, BUFFER_LENGTH};
|
||||||
const MAX_TOLERATED_MESSAGE_COUNT: usize = 10;
|
const MAX_TOLERATED_MESSAGE_COUNT: usize = 10;
|
||||||
|
|
||||||
pub async fn connect(sound_stream_consumer: Receiver<f32>, streamer_config:Config) {
|
pub async fn connect(
|
||||||
let connect_addr =
|
sound_stream_consumer: Receiver<f32>,
|
||||||
match streamer_config.tls {
|
streamer_config: Config,
|
||||||
|
mut stop_connection_consumer: Receiver<bool>,
|
||||||
|
) {
|
||||||
|
let connect_addr = match streamer_config.tls {
|
||||||
true => format!("wss://{}", streamer_config.address),
|
true => format!("wss://{}", streamer_config.address),
|
||||||
false => format!("ws://{}", streamer_config.address),
|
false => format!("ws://{}", streamer_config.address),
|
||||||
};
|
};
|
||||||
|
|
||||||
let ws_stream;
|
if let Err(_) = stop_connection_consumer.try_recv() {
|
||||||
|
let ws_stream;
|
||||||
|
match streamer_config.tls {
|
||||||
|
true => {
|
||||||
|
let tls_client_config = rustls_platform_verifier::tls_config();
|
||||||
|
let tls_connector =
|
||||||
|
tokio_tungstenite::Connector::Rustls(Arc::new(tls_client_config));
|
||||||
|
|
||||||
match streamer_config.tls {
|
match tokio_tungstenite::connect_async_tls_with_config(
|
||||||
true => {
|
connect_addr.clone(),
|
||||||
let tls_client_config = rustls_platform_verifier::tls_config();
|
None,
|
||||||
let tls_connector = tokio_tungstenite::Connector::Rustls(Arc::new(tls_client_config));
|
false,
|
||||||
|
Some(tls_connector),
|
||||||
match tokio_tungstenite::connect_async_tls_with_config(
|
)
|
||||||
connect_addr.clone(),
|
.await
|
||||||
None,
|
{
|
||||||
false,
|
Ok(wss_stream_connected) => ws_stream = wss_stream_connected.0,
|
||||||
Some(tls_connector),
|
Err(_) => {
|
||||||
)
|
return;
|
||||||
.await
|
}
|
||||||
{
|
}
|
||||||
Ok(wss_stream_connected) => ws_stream = wss_stream_connected.0,
|
}
|
||||||
Err(_) => {
|
false => match tokio_tungstenite::connect_async(connect_addr.clone()).await {
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
false => {
|
|
||||||
match tokio_tungstenite::connect_async(connect_addr.clone()).await {
|
|
||||||
Ok(ws_stream_connected) => ws_stream = ws_stream_connected.0,
|
Ok(ws_stream_connected) => ws_stream = ws_stream_connected.0,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return;
|
return;
|
||||||
},
|
}
|
||||||
}
|
},
|
||||||
},
|
}
|
||||||
|
let (message_producer, message_consumer) = channel(BUFFER_LENGTH);
|
||||||
|
println!("Connected to: {}", connect_addr);
|
||||||
|
let message_organizer_task = tokio::spawn(message_organizer(
|
||||||
|
message_producer,
|
||||||
|
sound_stream_consumer,
|
||||||
|
streamer_config.quality,
|
||||||
|
streamer_config.latency,
|
||||||
|
));
|
||||||
|
let stream_task = tokio::spawn(stream(ws_stream, message_consumer));
|
||||||
|
tokio::spawn(status_checker(
|
||||||
|
message_organizer_task,
|
||||||
|
stream_task,
|
||||||
|
stop_connection_consumer,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
let (message_producer, message_consumer) = channel(BUFFER_LENGTH);
|
|
||||||
println!("Connected to: {}", connect_addr);
|
|
||||||
tokio::spawn(message_organizer(message_producer, sound_stream_consumer, streamer_config.quality, streamer_config.latency));
|
|
||||||
tokio::spawn(stream(ws_stream, message_consumer));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn message_organizer(message_producer: Sender<Message>, mut consumer: Receiver<f32>, quality: u8, latency:u16) {
|
async fn message_organizer(
|
||||||
|
message_producer: Sender<Message>,
|
||||||
|
mut consumer: Receiver<f32>,
|
||||||
|
quality: u8,
|
||||||
|
latency: u16,
|
||||||
|
) {
|
||||||
loop {
|
loop {
|
||||||
let mut messages: Vec<u8> = Vec::new();
|
let mut messages: Vec<u8> = Vec::new();
|
||||||
let mut iteration = consumer.len();
|
let mut iteration = consumer.len();
|
||||||
|
@ -110,7 +131,7 @@ async fn message_organizer(message_producer: Sender<Message>, mut consumer: Rece
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn stream <T: futures_util::Sink<Message> + std::marker::Unpin>(
|
async fn stream<T: futures_util::Sink<Message> + std::marker::Unpin>(
|
||||||
mut ws_stream: T,
|
mut ws_stream: T,
|
||||||
mut message_consumer: Receiver<Message>,
|
mut message_consumer: Receiver<Message>,
|
||||||
) {
|
) {
|
||||||
|
@ -142,3 +163,16 @@ async fn stream <T: futures_util::Sink<Message> + std::marker::Unpin>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn status_checker(
|
||||||
|
message_organizer_task: JoinHandle<()>,
|
||||||
|
stream_task: JoinHandle<()>,
|
||||||
|
mut stop_connection_consumer: Receiver<bool>,
|
||||||
|
) {
|
||||||
|
while let Err(_) = stop_connection_consumer.try_recv() {
|
||||||
|
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||||
|
}
|
||||||
|
stream_task.abort();
|
||||||
|
message_organizer_task.abort();
|
||||||
|
println!("Cleaning Done: Streamer Disconnected");
|
||||||
|
}
|
||||||
|
|
|
@ -5,9 +5,12 @@ use crate::Config;
|
||||||
pub async fn get_config() -> Config {
|
pub async fn get_config() -> Config {
|
||||||
let mut config_file = File::open("configs/streamer_configs.txt").await.unwrap();
|
let mut config_file = File::open("configs/streamer_configs.txt").await.unwrap();
|
||||||
let mut configs_unparsed = String::new();
|
let mut configs_unparsed = String::new();
|
||||||
config_file.read_to_string(&mut configs_unparsed).await.unwrap();
|
config_file
|
||||||
|
.read_to_string(&mut configs_unparsed)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let configs_parsed:Vec<&str> = configs_unparsed.split_terminator("\n").collect();
|
let configs_parsed: Vec<&str> = configs_unparsed.split_terminator("\n").collect();
|
||||||
let mut configs_cleaned: Vec<&str> = vec![];
|
let mut configs_cleaned: Vec<&str> = vec![];
|
||||||
|
|
||||||
for config in configs_parsed {
|
for config in configs_parsed {
|
||||||
|
@ -20,4 +23,4 @@ pub async fn get_config() -> Config {
|
||||||
latency: configs_cleaned[2].parse().unwrap(),
|
latency: configs_cleaned[2].parse().unwrap(),
|
||||||
tls: configs_cleaned[3].parse().unwrap(),
|
tls: configs_cleaned[3].parse().unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue