darn node modueles
This commit is contained in:
parent
fcd782140e
commit
f00e981a3c
14 changed files with 104 additions and 228 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,3 +2,4 @@ target
|
|||
settings.toml
|
||||
.direnv/*
|
||||
logs/*
|
||||
ui/static/node_modules/*
|
||||
|
|
5
Cargo.lock
generated
5
Cargo.lock
generated
|
@ -2046,9 +2046,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
|
@ -4265,6 +4265,7 @@ dependencies = [
|
|||
"gilrs",
|
||||
"gstreamer",
|
||||
"gstreamer-app",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
|
|
@ -37,5 +37,6 @@ tracing-appender = "0.2.3"
|
|||
snafu = "0.8.2"
|
||||
console-subscriber = "0.3.0"
|
||||
tauri = { version = "1.6.1", features = [] }
|
||||
lazy_static = "1.5.0"
|
||||
|
||||
|
||||
|
|
|
@ -84,6 +84,7 @@ Some utility commands:
|
|||
cargo-tauri
|
||||
cargo-edit
|
||||
bacon
|
||||
typescript # .js language server
|
||||
] ++ tauri_packages;
|
||||
inputsFrom = with self.packages.${system}; [ joystick-controller-client ];
|
||||
shellHook =
|
||||
|
|
|
@ -58,6 +58,7 @@ pub enum ApplicationEvent {
|
|||
SocketMessage(Message),
|
||||
MoveEvent(MoveEvent, ConnectionType),
|
||||
TrackerUpdate(TrackerUpdate),
|
||||
WebRTCMessage(String),
|
||||
Close,
|
||||
}
|
||||
|
||||
|
@ -131,6 +132,9 @@ pub async fn start_coordinator(
|
|||
}
|
||||
}
|
||||
}
|
||||
ApplicationEvent::WebRTCMessage(msg) => {
|
||||
info!("Pew pew! WebRTCMessage! {msg}");
|
||||
}
|
||||
ApplicationEvent::TrackerUpdate(update) => match update {
|
||||
TrackerUpdate::Clear => {
|
||||
state.tracker_state.clear();
|
||||
|
|
40
src/main.rs
40
src/main.rs
|
@ -1,9 +1,12 @@
|
|||
// Prevents additional console window on Windows in release, DO NOT REMOVE!!
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use async_channel::Sender;
|
||||
use tokio::{runtime, sync:: RwLock};
|
||||
use tracing::{self, info};
|
||||
use tracing::{self, info, error};
|
||||
use tauri::Manager;
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
use tracing_subscriber;
|
||||
|
@ -19,6 +22,10 @@ mod tauri_functions;
|
|||
|
||||
use coordinator::{start_coordinator, ApplicationEvent};
|
||||
|
||||
lazy_static! {
|
||||
static ref TO_MEC_REF: Mutex<Option<Sender<ApplicationEvent>>> = Mutex::new(None);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
|
@ -43,6 +50,7 @@ fn main() {
|
|||
console_subscriber::init();
|
||||
}
|
||||
|
||||
let (to_mec, mec) = async_channel::bounded::<ApplicationEvent>(10);
|
||||
|
||||
info!("Logging intialized");
|
||||
|
||||
|
@ -53,8 +61,6 @@ fn main() {
|
|||
let rt = runtime::Runtime::new().expect("Could not start tokio runtime");
|
||||
let handle = rt.handle().clone();
|
||||
|
||||
let (to_mec, mec) = async_channel::bounded::<ApplicationEvent>(10);
|
||||
|
||||
let _coordinator = rt.handle().spawn(start_coordinator(
|
||||
mec,
|
||||
to_mec.clone(),
|
||||
|
@ -62,9 +68,35 @@ fn main() {
|
|||
config
|
||||
));
|
||||
|
||||
*TO_MEC_REF.lock().unwrap() = Some(to_mec.clone());
|
||||
|
||||
tauri::Builder::default()
|
||||
.manage(tauri_functions::TauriState { to_mec: to_mec.clone() })
|
||||
.invoke_handler(tauri::generate_handler![tauri_functions::connect_to_camera])
|
||||
.setup(|app| {
|
||||
let _id = app.listen_global("webrtc-event", |event| {
|
||||
match event.payload() {
|
||||
Some(payload) => {
|
||||
if let Ok(e) = TO_MEC_REF.lock() {
|
||||
match e.as_ref() {
|
||||
Some(to_mec) => {
|
||||
if let Err(e) = to_mec.send_blocking(ApplicationEvent::WebRTCMessage(payload.to_string())) {
|
||||
error!("Could not send to mec! {e}");
|
||||
}
|
||||
},
|
||||
None => {
|
||||
error!("TO_MEC_REF was none!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
info!("There was an empty payload!");
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
|
|
|
@ -12,6 +12,5 @@ pub struct TauriState {
|
|||
|
||||
#[tauri::command]
|
||||
pub fn connect_to_camera(state: State<'_, TauriState>) {
|
||||
|
||||
let _ = state.to_mec.send_blocking(ApplicationEvent::CameraConnectionPress);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
<link href="/static/main.css" rel="stylesheet"/>
|
||||
<script src="/static/feather.min.js"></script>
|
||||
<script src="/static/index.js"></script>
|
||||
<script src="/static/gstwebrtc-api-2-0-0.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
|
@ -1,206 +0,0 @@
|
|||
function initCapture(api) {
|
||||
const captureSection = document.getElementById("capture");
|
||||
const clientIdElement = captureSection.querySelector(".client-id");
|
||||
const videoElement = captureSection.getElementsByTagName("video")[0];
|
||||
|
||||
const listener = {
|
||||
connected: function(clientId) { clientIdElement.textContent = clientId; },
|
||||
disconnected: function() { clientIdElement.textContent = "none"; }
|
||||
};
|
||||
api.registerConnectionListener(listener);
|
||||
|
||||
document.getElementById("capture-button").addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (captureSection._producerSession) {
|
||||
captureSection._producerSession.close();
|
||||
} else if (!captureSection.classList.contains("starting")) {
|
||||
captureSection.classList.add("starting");
|
||||
|
||||
const constraints = {
|
||||
video: { width: 1280, height: 720 }
|
||||
};
|
||||
navigator.mediaDevices.getUserMedia(constraints).then((stream) => {
|
||||
const session = api.createProducerSession(stream);
|
||||
if (session) {
|
||||
captureSection._producerSession = session;
|
||||
|
||||
session.addEventListener("error", (event) => {
|
||||
if (captureSection._producerSession === session) {
|
||||
console.error(event.message, event.error);
|
||||
}
|
||||
});
|
||||
|
||||
session.addEventListener("closed", () => {
|
||||
if (captureSection._producerSession === session) {
|
||||
videoElement.pause();
|
||||
videoElement.srcObject = null;
|
||||
captureSection.classList.remove("has-session", "starting");
|
||||
delete captureSection._producerSession;
|
||||
}
|
||||
});
|
||||
|
||||
session.addEventListener("stateChanged", (event) => {
|
||||
if ((captureSection._producerSession === session) &&
|
||||
(event.target.state === GstWebRTCAPI.SessionState.streaming)) {
|
||||
videoElement.srcObject = stream;
|
||||
videoElement.play().catch(() => {});
|
||||
captureSection.classList.remove("starting");
|
||||
}
|
||||
});
|
||||
|
||||
session.addEventListener("clientConsumerAdded", (event) => {
|
||||
if (captureSection._producerSession === session) {
|
||||
console.info(`client consumer added: ${event.detail.peerId}`);
|
||||
}
|
||||
});
|
||||
|
||||
session.addEventListener("clientConsumerRemoved", (event) => {
|
||||
if (captureSection._producerSession === session) {
|
||||
console.info(`client consumer removed: ${event.detail.peerId}`);
|
||||
}
|
||||
});
|
||||
|
||||
captureSection.classList.add("has-session");
|
||||
session.start();
|
||||
} else {
|
||||
for (const track of stream.getTracks()) {
|
||||
track.stop();
|
||||
}
|
||||
|
||||
captureSection.classList.remove("starting");
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error("cannot have access to webcam and microphone", error);
|
||||
captureSection.classList.remove("starting");
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initRemoteStreams(api) {
|
||||
const remoteStreamsElement = document.getElementById("remote-streams");
|
||||
|
||||
const listener = {
|
||||
producerAdded: function(producer) {
|
||||
const producerId = producer.id
|
||||
if (!document.getElementById(producerId)) {
|
||||
remoteStreamsElement.insertAdjacentHTML("beforeend",
|
||||
`<li id="${producerId}">
|
||||
<div class="button">${producer.meta.name || producerId}</div>
|
||||
<div class="video">
|
||||
<div class="spinner">
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</div>
|
||||
<span class="remote-control">©</span>
|
||||
<video></video>
|
||||
<div class="fullscreen"><span title="Toggle fullscreen">▢</span></div>
|
||||
</div>
|
||||
</li>`);
|
||||
|
||||
const entryElement = document.getElementById(producerId);
|
||||
const videoElement = entryElement.getElementsByTagName("video")[0];
|
||||
|
||||
videoElement.addEventListener("playing", () => {
|
||||
if (entryElement.classList.contains("has-session")) {
|
||||
entryElement.classList.add("streaming");
|
||||
}
|
||||
});
|
||||
|
||||
entryElement.addEventListener("click", (event) => {
|
||||
event.preventDefault();
|
||||
if (!event.target.classList.contains("button")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (entryElement._consumerSession) {
|
||||
entryElement._consumerSession.close();
|
||||
} else {
|
||||
const session = api.createConsumerSession(producerId);
|
||||
if (session) {
|
||||
entryElement._consumerSession = session;
|
||||
|
||||
session.addEventListener("error", (event) => {
|
||||
if (entryElement._consumerSession === session) {
|
||||
console.error(event.message, event.error);
|
||||
}
|
||||
});
|
||||
|
||||
session.addEventListener("closed", () => {
|
||||
if (entryElement._consumerSession === session) {
|
||||
videoElement.pause();
|
||||
videoElement.srcObject = null;
|
||||
entryElement.classList.remove("has-session", "streaming", "has-remote-control");
|
||||
delete entryElement._consumerSession;
|
||||
}
|
||||
});
|
||||
|
||||
session.addEventListener("streamsChanged", () => {
|
||||
if (entryElement._consumerSession === session) {
|
||||
const streams = session.streams;
|
||||
if (streams.length > 0) {
|
||||
videoElement.srcObject = streams[0];
|
||||
videoElement.play().catch(() => {});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
session.addEventListener("remoteControllerChanged", () => {
|
||||
if (entryElement._consumerSession === session) {
|
||||
const remoteController = session.remoteController;
|
||||
if (remoteController) {
|
||||
entryElement.classList.add("has-remote-control");
|
||||
remoteController.attachVideoElement(videoElement);
|
||||
} else {
|
||||
entryElement.classList.remove("has-remote-control");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
entryElement.classList.add("has-session");
|
||||
session.connect();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
producerRemoved: function(producer) {
|
||||
const element = document.getElementById(producer.id);
|
||||
if (element) {
|
||||
if (element._consumerSession) {
|
||||
element._consumerSession.close();
|
||||
}
|
||||
|
||||
element.remove();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
api.registerProducersListener(listener);
|
||||
for (const producer of api.getAvailableProducers()) {
|
||||
listener.producerAdded(producer);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
document.addEventListener("click", (event) => {
|
||||
if (event.target.matches("div.video>div.fullscreen:hover>span")) {
|
||||
event.preventDefault();
|
||||
event.target.parentNode.previousElementSibling.requestFullscreen();
|
||||
}
|
||||
});
|
||||
|
||||
const signalingProtocol = window.location.protocol.startsWith("https") ? "wss" : "ws";
|
||||
const gstWebRTCConfig = {
|
||||
meta: { name: `WebClient-${Date.now()}` },
|
||||
signalingServerUrl: `ws://127.0.0.1:8443`,
|
||||
};
|
||||
|
||||
const api = new GstWebRTCAPI(gstWebRTCConfig);
|
||||
// initCapture(api);
|
||||
initRemoteStreams(api);
|
||||
});
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
30
ui/static/package-lock.json
generated
Normal file
30
ui/static/package-lock.json
generated
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"name": "vcs-controller",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "vcs-controller",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tauri-apps/api": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-1.6.0.tgz",
|
||||
"integrity": "sha512-rqI++FWClU5I2UBp4HXFvl+sBWkdigBkxnpJDQUWttNyG7IZP4FwQGhTNL5EOw0vI8i6eSAJ5frLqO7n7jbJdg==",
|
||||
"engines": {
|
||||
"node": ">= 14.6.0",
|
||||
"npm": ">= 6.6.0",
|
||||
"yarn": ">= 1.19.1"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/tauri"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
ui/static/package.json
Normal file
14
ui/static/package.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "vcs-controller",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^1.6.0"
|
||||
}
|
||||
}
|
|
@ -1,4 +1,6 @@
|
|||
|
||||
import { emit, listen } from '@tauri-apps/api/event';
|
||||
|
||||
const videoview = document.getElementById("remoteview");
|
||||
|
||||
const config = {
|
||||
|
@ -6,11 +8,8 @@ const config = {
|
|||
};
|
||||
const polite = true;
|
||||
|
||||
const signaler = new SignalingChannel();
|
||||
const pc = new RTCPeerConnection(config);
|
||||
|
||||
const constraints = { audio: false, video: true };
|
||||
|
||||
pc.ontrack = ({ track, streams }) => {
|
||||
track.onunmute = () => {
|
||||
if (remoteview.srcObject) {
|
||||
|
@ -26,7 +25,7 @@ pc.onnegotionationneeded = async () => {
|
|||
try {
|
||||
makingOffer = true;
|
||||
await pc.setLocalDescription();
|
||||
signaler.send({ description: pc.localDescription });
|
||||
emit("webrtc-event", { description: pc.localDescription });
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
} finally {
|
||||
|
@ -34,7 +33,7 @@ pc.onnegotionationneeded = async () => {
|
|||
}
|
||||
};
|
||||
|
||||
pc.onicecandidate = ({ candidate }) => signaler.send({ candidate });
|
||||
pc.onicecandidate = ({ candidate }) => emit("webrtc-event", { candidate });
|
||||
pc.oniceconnectionstatechange = () => {
|
||||
if (pc.iceConnectionState === "failed") {
|
||||
pc.restartIce();
|
||||
|
@ -43,7 +42,12 @@ pc.oniceconnectionstatechange = () => {
|
|||
|
||||
let ignoreOffer = false;
|
||||
|
||||
signaler.onmessage = async ({ data: { description, candidate } }) => {
|
||||
const application_message = await listen('frontend_message', async (event) => {
|
||||
console.log("Event: ");
|
||||
console.log(event);
|
||||
|
||||
const { description, candidate } = event.payload.data;
|
||||
|
||||
try {
|
||||
if (description) {
|
||||
const offerCollision =
|
||||
|
@ -59,7 +63,7 @@ signaler.onmessage = async ({ data: { description, candidate } }) => {
|
|||
await pc.setRemoteDescription(description);
|
||||
if (description.type === "offer") {
|
||||
await pc.setLocalDescription();
|
||||
signaler.send({ description: pc.localDescription });
|
||||
emit( "webrtc-event", { description: pc.localDescription });
|
||||
}
|
||||
} else if (candidate) {
|
||||
try {
|
||||
|
@ -73,7 +77,9 @@ signaler.onmessage = async ({ data: { description, candidate } }) => {
|
|||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
async function start_rtc_connection() {
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue