feat: streamer: Disconnect from server

This commit is contained in:
Ahmet Kaan GÜMÜŞ 2024-04-27 19:48:13 +03:00
parent 21d8781188
commit 6eb3e9b419
6 changed files with 117 additions and 55 deletions

View file

@ -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)
} }
} }

View file

@ -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;

View file

@ -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()
} }

View file

@ -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);

View file

@ -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");
}

View file

@ -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(),
} }
} }