pub mod app; use std::cell::RefCell; use std::pin::pin; use std::rc::Rc; use std::time::Duration; use app::STATE; use asynchronous_codec::Decoder; use asynchronous_codec::Encoder; use futures::AsyncRead; use futures::AsyncWrite; use futures::Sink; use futures::SinkExt; use futures::Stream; use futures::StreamExt; use mumble_protocol::control::ControlCodec; use mumble_protocol::control::ControlPacket; use mumble_protocol::control::{msgs, ClientControlCodec}; use mumble_protocol::Clientbound; use mumble_protocol::Serverbound; use wasm_bindgen::prelude::*; use wasm_bindgen_futures::JsFuture; use web_sys::console; use web_sys::js_sys::Uint8Array; use web_sys::AudioData; use web_sys::AudioContext; use web_sys::AudioContextOptions; use web_sys::MediaStream; use web_sys::AudioDecoder; use web_sys::AudioDecoderConfig; use web_sys::AudioDecoderInit; use web_sys::CodecState; use web_sys::EncodedAudioChunk; use web_sys::EncodedAudioChunkInit; use web_sys::EncodedAudioChunkType; use web_sys::MediaStreamTrackGenerator; use web_sys::MediaStreamTrackGeneratorInit; use web_sys::WebTransport; use web_sys::WebTransportOptions; use wasm_bindgen_futures::spawn_local as spawn; //#[wasm_bindgen] //extern "C" { //} fn configure_audio_context( audio_stream_generator: &MediaStreamTrackGenerator, ) -> AudioContext { let js_tracks = web_sys::js_sys::Array::new(); js_tracks.push(audio_stream_generator); let media_stream = MediaStream::new_with_tracks(&js_tracks).unwrap(); let mut audio_context_options = AudioContextOptions::new(); audio_context_options.sample_rate(48000 as f32); let audio_context = AudioContext::new_with_context_options(&audio_context_options).unwrap(); let gain_node = audio_context.create_gain().unwrap(); gain_node.set_channel_count(1); let source = audio_context .create_media_stream_source(&media_stream) .unwrap(); let _ = source.connect_with_audio_node(&gain_node).unwrap(); let _ = gain_node .connect_with_audio_node(&audio_context.destination()) .unwrap(); audio_context } pub async fn network_entrypoint() { async_std::task::sleep(Duration::from_millis(3000)).await; console::log_1(&"Rust via WASM!".into()); let server_hash = vec![ 14, 162, 111, 176, 34, 113, 218, 69, 177, 18, 13, 180, 232, 204, 49, 65, 161, 195, 36, 238, 23, 95, 174, 190, 24, 216, 105, 89, 236, 147, 206, 139, ]; let hash = web_sys::js_sys::Uint8Array::from(server_hash.as_slice()); let object = web_sys::js_sys::Object::new(); web_sys::js_sys::Reflect::set( &object, &JsValue::from_str("algorithm"), &JsValue::from_str("sha-256"), ) .unwrap(); //web_sys::js_sys::Reflect::set(&object, &JsValue::from_str("value"), &hash).unwrap(); web_sys::js_sys::Reflect::set(&object, &"value".into(), &hash).unwrap(); let array = web_sys::js_sys::Array::new(); array.push(&object); console::log_1(&object.clone().into()); console::log_1(&"Created option object!".into()); let mut options = WebTransportOptions::new(); options.server_certificate_hashes(&array); console::log_1(&"Created WebTransportOptions!".into()); *STATE.status.write() = "Connecting to WebTransport...".into(); let transport = match WebTransport::new_with_options( "https://localhost:4433/?hostname=ohea.xyz&port=64738&username=test", &options, ) { Ok(x) => x, Err(e) => { console::log_1(&e.into()); panic!(); } }; console::log_1(&"Created WebTransport connection object.".into()); console::log_1(&transport.clone().into()); if let Err(e) = wasm_bindgen_futures::JsFuture::from(transport.ready()).await { console::log_1(&e.into()); panic!(); } console::log_1(&"Transport is ready.".into()); *STATE.status.write() = "Creating stream...".into(); let stream: web_sys::WebTransportBidirectionalStream = match wasm_bindgen_futures::JsFuture::from(transport.create_bidirectional_stream()).await { Ok(x) => x.into(), Err(e) => { console::log_1(&e.into()); panic!(); } }; let wasm_stream_readable = wasm_streams::ReadableStream::from_raw(stream.readable().into()); let wasm_stream_writable = wasm_streams::WritableStream::from_raw(stream.writable().into()); let read_codec = ClientControlCodec::new(); let write_codec = ClientControlCodec::new(); let mut reader = asynchronous_codec::FramedRead::new(wasm_stream_readable.into_async_read(), read_codec); let mut writer = asynchronous_codec::FramedWrite::new(wasm_stream_writable.into_async_write(), write_codec); let (send_chan, writer_recv_chan) = async_std::channel::unbounded(); spawn(async move { while let Ok(msg) = writer_recv_chan.recv().await { if let Err(e) = writer.send(msg).await { console::log_1(&e.to_string().into()); break; } } }); // Get version packet let version = reader.next().await.unwrap().unwrap(); console::log_1(&"Got version packet".into()); console::log_1(&format!("{:#?}", version).into()); // Send version packet let mut msg = msgs::Version::new(); msg.set_version(0x000010204); msg.set_release(format!("{} {}", "mumbleweb2", "6.9.0")); //msg.set_os("Chrome".to_string()); send_chan.send(msg.into()).await.unwrap(); console::log_1(&"Sent version packet".into()); // Send authenticate packet let mut msg = msgs::Authenticate::new(); msg.set_username("mumbleweb2".to_string()); msg.set_opus(true); send_chan.send(msg.into()).await.unwrap(); console::log_1(&"Sent authenticate packet".into()); { let send_chan = send_chan.clone(); spawn(async move { loop { console::log_1(&"Sending ping".into()); if let Err(e) = send_chan.send(msgs::Ping::new().into()).await { console::log_1(&e.to_string().into()); break; } async_std::task::sleep(Duration::from_millis(3000)).await; } }); } let error = Closure::wrap(Box::new(move |e: JsValue| { console::log_1(&e); }) as Box); let audio_stream_generator = MediaStreamTrackGenerator::new(&MediaStreamTrackGeneratorInit::new("audio")).unwrap(); // The audio context is used to reproduce audio. let _audio_context = configure_audio_context(&audio_stream_generator); let output = Closure::wrap(Box::new(move |audio_data: AudioData| { console::log_1(&"Got audio".into()); console::log_1(&audio_data.clone().into()); let writable = audio_stream_generator.writable(); if writable.locked() { return; } if let Err(e) = writable.get_writer().map(|writer| { wasm_bindgen_futures::spawn_local(async move { if let Err(e) = JsFuture::from(writer.ready()).await { console::log_1(&format!("write chunk error {:?}", e).into()); } if let Err(e) = JsFuture::from(writer.write_with_chunk(&audio_data)).await { console::log_1(&format!("write chunk error {:?}", e).into()); }; writer.release_lock(); }); }) { console::log_1(&e); } }) as Box); let audio_decoder = AudioDecoder::new(&AudioDecoderInit::new( error.as_ref().unchecked_ref(), output.as_ref().unchecked_ref(), )).unwrap(); console::log_1(&"Created Audio Decoder".into()); console::log_1(&audio_decoder); audio_decoder.configure(&AudioDecoderConfig::new( "opus", 1, 48000, )); loop { match reader.next().await { Some(Ok(msg)) => { match msg { ControlPacket::UDPTunnel(u) => { match *u.clone() { mumble_protocol::voice::VoicePacket::Audio { _dst, target, session_id, seq_num, payload, position_info, } => { console::log_1(&"Voice packet found".into()); if let mumble_protocol::voice::VoicePacketPayload::Opus( audio_payload, end_bit, ) = payload { let js_audio_payload = Uint8Array::from(audio_payload.as_ref()); audio_decoder.decode(&EncodedAudioChunk::new( &EncodedAudioChunkInit::new( &js_audio_payload.into(), 0.0, EncodedAudioChunkType::Key, ), ).unwrap()); console::log_1(&"Oueued audio chunk for decoding".into()); //let mut encoded_audio_chunk_init = // web_sys::EncodedAudioChunkInit::new( // &js_audio_payload.into(), // 0.0, // web_sys::EncodedAudioChunkType::Delta, // ); ////encoded_audio_chunk_init.duration(1.0); //let encoded_audio_chunk = // web_sys::EncodedAudioChunk::new(&encoded_audio_chunk_init) // .unwrap(); //console::log_1(&encoded_audio_chunk); } } _ => { unreachable!("TCP tunnels UDP packets should not contain pings"); // I think? } } console::log_1(&"Got UDP tunnel".into()); console::log_1(&format!("{:#?}", u).into()); } _ => { console::log_1(&format!("{:#?}", msg).into()); } } } None => { break; } Some(Err(e)) => { console::log_1(&e.to_string().into()); break; } } } //async fn handle_send( // mut writer: Rc>>>, // send_queue: Queue, //) { // loop { // let msg = send_queue.get(); // client.send(msg).await.unwrap(); // } //} //async fn handle_ping( //) { // //pin!(client); // loop { // let ping = msgs::Ping::new(); // client.borrow_mut().send(ping.into()).await.unwrap(); // console::log_1(&"Sent ping packet".into()); // // async_std::task::sleep(Duration::from_millis(3000)).await; // } //} //let queue_write, queue_read = Queue::new(); //spawn(handle_send(writer, queue_write)); //spawn(handle_ping(queue_read)); //loop { // let msg = reader.next().await.unwrap().unwrap(); // console::log_1(&format!("{:#?}", msg).into()); //} // let result: Option<(Version, u32)> = loop { // match client.next().await { // None => break None, // Some(packet) => { // let packet = packet.unwrap(); // } // } // }; //let client = ClientControlCodec::new().framed(stream); //let client = ClientControlCodec::new().framed(stream); //let reader = stream.readable(); //let writer = stream.writable(); //console::log_1(&stream.into()); //*STATE.status.write() = "Ready!".into(); //return stream.into(); }