summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock53
-rw-r--r--client-native-rift/src/main.rs38
-rw-r--r--client-web/public/assets/style/master.css11
-rw-r--r--client-web/public/assets/style/room.css14
-rw-r--r--client-web/public/start.html3
-rw-r--r--client-web/source/chat.ts9
-rw-r--r--client-web/source/helper.ts24
-rw-r--r--client-web/source/index.ts4
-rw-r--r--client-web/source/keybinds.ts13
-rw-r--r--client-web/source/menu.ts10
-rw-r--r--client-web/source/preferences/decl.ts2
-rw-r--r--client-web/source/preferences/ui.ts11
-rw-r--r--client-web/source/resource/track.ts55
-rw-r--r--makefile3
-rw-r--r--readme.md21
-rw-r--r--server/makefile2
-rw-r--r--server/src/main.rs11
-rw-r--r--todo.org5
18 files changed, 201 insertions, 88 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 18c51b3..a244571 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1496,6 +1496,19 @@ dependencies = [
]
[[package]]
+name = "env_logger"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0"
+dependencies = [
+ "humantime",
+ "is-terminal",
+ "log",
+ "regex",
+ "termcolor",
+]
+
+[[package]]
name = "epaint"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2252,9 +2265,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130"
[[package]]
name = "js-sys"
-version = "0.3.60"
+version = "0.3.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
+checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
dependencies = [
"wasm-bindgen",
]
@@ -2270,7 +2283,7 @@ dependencies = [
"crossbeam-channel",
"eframe",
"egui",
- "env_logger",
+ "env_logger 0.8.4",
"libmpv",
"log",
"tokio",
@@ -2284,7 +2297,7 @@ dependencies = [
"bytes",
"clap",
"client-native-lib",
- "env_logger",
+ "env_logger 0.8.4",
"log",
"tokio",
]
@@ -2293,7 +2306,7 @@ dependencies = [
name = "keks-meet-server"
version = "0.1.0"
dependencies = [
- "env_logger",
+ "env_logger 0.10.0",
"futures-util",
"hyper",
"listenfd",
@@ -3178,7 +3191,7 @@ dependencies = [
"bytes",
"clap",
"client-native-lib",
- "env_logger",
+ "env_logger 0.8.4",
"humansize",
"indicatif",
"log",
@@ -4192,9 +4205,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
-version = "0.2.83"
+version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
+checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
@@ -4202,9 +4215,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.83"
+version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
+checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
dependencies = [
"bumpalo",
"log",
@@ -4217,9 +4230,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.33"
+version = "0.4.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d"
+checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad"
dependencies = [
"cfg-if",
"js-sys",
@@ -4229,9 +4242,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.83"
+version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
+checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -4239,9 +4252,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.83"
+version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
+checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
dependencies = [
"proc-macro2",
"quote",
@@ -4252,9 +4265,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.83"
+version = "0.2.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
+checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
[[package]]
name = "wayland-client"
@@ -4343,9 +4356,9 @@ dependencies = [
[[package]]
name = "web-sys"
-version = "0.3.60"
+version = "0.3.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
+checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1"
dependencies = [
"js-sys",
"wasm-bindgen",
diff --git a/client-native-rift/src/main.rs b/client-native-rift/src/main.rs
index 5d0c7d6..45f720a 100644
--- a/client-native-rift/src/main.rs
+++ b/client-native-rift/src/main.rs
@@ -192,21 +192,29 @@ impl EventHandler for Handler {
let writer = writer.clone();
let pos = pos.clone();
Box::pin(async move {
- let pos = pos.fetch_add(mesg.data.len(), Ordering::Relaxed);
- info!(
- "recv {:?} ({} of {})",
- mesg.data.len(),
- humansize::format_size(pos, DECIMAL),
- humansize::format_size(resource.size.unwrap_or(0), DECIMAL),
- );
- writer
- .write()
- .await
- .as_mut()
- .unwrap()
- .write_all(&mesg.data)
- .await
- .unwrap();
+ // TODO
+ if mesg.is_string {
+ let s = String::from_utf8((&mesg.data).to_vec()).unwrap();
+ if &s == "end" {
+ info!("EOF reached")
+ }
+ } else {
+ let pos = pos.fetch_add(mesg.data.len(), Ordering::Relaxed);
+ info!(
+ "recv {:?} ({} of {})",
+ mesg.data.len(),
+ humansize::format_size(pos, DECIMAL),
+ humansize::format_size(resource.size.unwrap_or(0), DECIMAL),
+ );
+ writer
+ .write()
+ .await
+ .as_mut()
+ .unwrap()
+ .write_all(&mesg.data)
+ .await
+ .unwrap();
+ }
})
})
}
diff --git a/client-web/public/assets/style/master.css b/client-web/public/assets/style/master.css
index caa8c85..7a59d78 100644
--- a/client-web/public/assets/style/master.css
+++ b/client-web/public/assets/style/master.css
@@ -33,7 +33,7 @@ body {
justify-content: left;
}
-h2 {
+h1 {
font-weight: 700;
margin: 1em;
}
@@ -46,20 +46,24 @@ button {
border: 0px solid transparent;
border-radius: 3px;
}
+
input[type="button"]:hover,
button:hover {
filter: brightness(130%);
}
+
input[type="button"].active,
button.active {
filter: hue-rotate(20deg);
}
+
input[type="text"],
select,
input[type="number"] {
background-color: var(--bg-dark);
border: 1px solid var(--ac-light);
}
+
input:disabled,
button:disabled {
filter: sepia(90%);
@@ -71,15 +75,18 @@ button:disabled {
left: 50vw;
transform: translate(-50%, -50%);
}
+
.start-box p {
margin-bottom: 0.5em;
}
+
.start-box input[type="text"] {
margin: 0.5em;
font-size: xx-large;
}
+
.start-box input[type="button"] {
margin: 0.5em;
font-size: x-large;
width: calc(100% - 1em);
-}
+} \ No newline at end of file
diff --git a/client-web/public/assets/style/room.css b/client-web/public/assets/style/room.css
index 3f62685..ad84325 100644
--- a/client-web/public/assets/style/room.css
+++ b/client-web/public/assets/style/room.css
@@ -23,9 +23,11 @@
.user .info .name {
font-weight: 400;
}
+
.user.local .info .name {
text-decoration: underline;
}
+
.user .info {
margin-bottom: 1em;
}
@@ -44,6 +46,7 @@
.local-controls {
display: inline;
}
+
.local-controls::before {
content: "|";
}
@@ -51,6 +54,7 @@
.transfer-status {
background-color: #00000040;
}
+
.progress-bar {
z-index: -1;
padding: 0px;
@@ -59,3 +63,13 @@
border-radius: 3px;
background-color: var(--ac-light);
}
+
+
+.resource-track>div {
+ border: 2px solid transparent;
+ border-radius: 4px;
+}
+
+.resource-track>div.audio-active {
+ border: 2px solid var(--ac-light);
+}
diff --git a/client-web/public/start.html b/client-web/public/start.html
index afe771e..4766c77 100644
--- a/client-web/public/start.html
+++ b/client-web/public/start.html
@@ -8,7 +8,7 @@
</head>
<body>
<div class="start-box">
- <h2>keks-meet</h2>
+ <h1>keks-meet</h2>
<p>A web conferencing application using webrtc</p>
<p>
keks-meet is free software! It is licenced under the terms of
@@ -29,6 +29,7 @@
room_input.type = "text";
room_input.id = "room-id-input";
room_input.placeholder = "Room Secret";
+ room_input.ariaLabel = "Room Secret"
const submit = document.createElement("input");
submit.type = "button";
diff --git a/client-web/source/chat.ts b/client-web/source/chat.ts
index b7c572d..47b0678 100644
--- a/client-web/source/chat.ts
+++ b/client-web/source/chat.ts
@@ -6,7 +6,7 @@
/// <reference lib="dom" />
import { ChatMessage } from "../../common/packets.d.ts";
-import { ediv, espan, image_view, notify, OverlayUi } from "./helper.ts";
+import { ediv, esection, espan, image_view, notify, OverlayUi } from "./helper.ts";
import { log } from "./logger.ts";
import { PREFS } from "./preferences/mod.ts";
import { Room } from "./room.ts";
@@ -20,13 +20,15 @@ export class Chat extends OverlayUi {
constructor(public room: Room) {
const send = document.createElement("input")
+ send.ariaLabel = "send message"
send.type = "text"
+ send.placeholder = "send a message..."
- const messages = ediv({ class: "messages" })
+ const messages = ediv({ class: "messages", aria_live: "polite" })
const controls = ediv({ class: "controls" })
controls.append(send)
messages.append(document.createElement("hr"))
- super(ediv({ class: "chat" }, messages, controls))
+ super(esection({ class: "chat", aria_label: "chat", role: "dialog" }, messages, controls))
this.messages = messages
this.controls = controls
this.send_el = send
@@ -61,6 +63,7 @@ export class Chat extends OverlayUi {
}
}
+ on_show(): void { this.focus() }
focus() { this.send_el.focus() }
send(msg: ChatMessage) {
this.room.local_user.chat(msg)
diff --git a/client-web/source/helper.ts b/client-web/source/helper.ts
index c23beb6..1133afe 100644
--- a/client-web/source/helper.ts
+++ b/client-web/source/helper.ts
@@ -9,12 +9,25 @@ import { PREFS } from "./preferences/mod.ts";
const elem = <K extends keyof HTMLElementTagNameMap>(s: K): HTMLElementTagNameMap[K] => document.createElement(s)
-interface Opts<El> { class?: string[] | string, id?: string, src?: string, onclick?: (e: El) => void }
+interface Opts<El> {
+ class?: string[] | string,
+ id?: string, src?: string,
+ for?: string,
+ onclick?: (e: El) => void,
+ role?: "dialog"
+ aria_label?: string
+ aria_live?: "polite" | "assertive"
+ aria_modal?: boolean
+}
function apply_opts<El extends HTMLElement>(e: El, o: Opts<El> | undefined) {
if (!o) return
if (o.id) e.id = o.id
if (o.onclick) e.onclick = () => o.onclick!(e)
+ if (o.aria_label) e.ariaLabel = o.aria_label
+ if (o.aria_live) e.ariaLive = o.aria_live
+ if (o.for) (e as unknown as HTMLLabelElement).htmlFor = o.for
+ if (o.aria_modal) e.ariaModal = "true"
if (typeof o?.class == "string") e.classList.add(o.class)
if (typeof o?.class == "object") e.classList.add(...o.class)
}
@@ -42,6 +55,9 @@ export const eh5 = elem_with_content("h5")
export const eh6 = elem_with_content("h6")
export const epre = elem_with_content("pre")
export const ediv = elem_with_children("div")
+export const efooter = elem_with_children("footer")
+export const esection = elem_with_children("section")
+export const enav = elem_with_children("nav")
export const etr = elem_with_children("tr")
export const etd = elem_with_children("td")
export const eth = elem_with_children("th")
@@ -59,10 +75,12 @@ export class OverlayUi {
}
get shown() { return this._shown }
set shown(v: boolean) {
- if (v && !this._shown) OVERLAYS.append(this.el)
- if (!v && this._shown) OVERLAYS.removeChild(this.el)
+ if (v && !this._shown) OVERLAYS.append(this.el), this.on_show()
+ if (!v && this._shown) OVERLAYS.removeChild(this.el), this.on_hide()
this._shown = v
}
+ on_show() { }
+ on_hide() { }
}
export function image_view(url: string, opts?: Opts<HTMLElement>): HTMLElement {
diff --git a/client-web/source/index.ts b/client-web/source/index.ts
index bc52f8b..cc3492f 100644
--- a/client-web/source/index.ts
+++ b/client-web/source/index.ts
@@ -6,7 +6,7 @@
/// <reference lib="dom" />
import { init_serviceworker } from "./sw/init.ts";
-import { ediv, OVERLAYS } from "./helper.ts";
+import { esection, OVERLAYS } from "./helper.ts";
import { setup_keybinds } from "./keybinds.ts";
import { log, LOGGER_CONTAINER } from "./logger.ts"
import { BottomMenu, MenuBr } from "./menu.ts";
@@ -15,7 +15,7 @@ import { SignalingConnection } from "./protocol/mod.ts";
import { Room } from "./room.ts"
export const VERSION = "0.1.12"
-export const ROOM_CONTAINER = ediv({ class: "room" })
+export const ROOM_CONTAINER = esection({ class: "room", aria_label: "user list" })
export const RTC_CONFIG: RTCConfiguration = {
iceServers: [
diff --git a/client-web/source/keybinds.ts b/client-web/source/keybinds.ts
index 5463e47..d096501 100644
--- a/client-web/source/keybinds.ts
+++ b/client-web/source/keybinds.ts
@@ -10,28 +10,23 @@ import { Room } from "./room.ts"
import { update_serviceworker } from "./sw/init.ts";
export function setup_keybinds(room: Room) {
- let command_mode = false
+ // let command_mode = false
document.body.addEventListener("keydown", ev => {
// TODO is there a proper solution?
if (ev.target instanceof HTMLInputElement && !(ev.target.type == "button")) return
if (ev.repeat) return
- if (ev.code == "Enter") {
+ if (ev.code == "Enter" && ev.ctrlKey) {
room.chat.shown = !room.chat.shown
if (room.chat.shown) room.chat.focus()
ev.preventDefault() // so focused buttons dont trigger
}
- if (ev.code == "Space") {
- command_mode = true
- ev.preventDefault() // so focused buttons dont trigger
- return
- }
- if (command_mode) {
+ if (ev.shiftKey) {
if (ev.code == "KeyM" || ev.code == "KeyR") room.local_user.await_add_resource(create_mic_res())
if (ev.code == "KeyS") room.local_user.await_add_resource(create_screencast_res())
if (ev.code == "KeyC" && !ev.ctrlKey) room.local_user.await_add_resource(create_camera_res())
if (ev.code == "KeyC" && ev.ctrlKey) room.local_user.resources.forEach(t => t.destroy())
if (ev.code == "KeyU") if (window.confirm("really update?")) update_serviceworker()
}
- command_mode = false
+ // command_mode = false
})
}
diff --git a/client-web/source/menu.ts b/client-web/source/menu.ts
index 9126dc5..035f3aa 100644
--- a/client-web/source/menu.ts
+++ b/client-web/source/menu.ts
@@ -5,7 +5,7 @@
*/
/// <reference lib="dom" />
-import { ebutton, ediv, ep, OverlayUi } from "./helper.ts"
+import { ebutton, ediv, efooter, enav, ep, OverlayUi } from "./helper.ts"
import { VERSION } from "./index.ts"
import { PrefUi } from "./preferences/ui.ts"
import { create_file_res } from "./resource/file.ts";
@@ -26,7 +26,7 @@ export class MenuBr extends OverlayUi {
return p
}
- super(ediv({ class: "menu-br" },
+ super(efooter({ class: "menu-br" },
ep(`keks-meet ${VERSION}`, { class: "version" }),
item("License", "https://codeberg.org/metamuffin/keks-meet/raw/branch/master/COPYING"),
item("Source code", "https://codeberg.org/metamuffin/keks-meet"),
@@ -41,6 +41,7 @@ export class BottomMenu extends OverlayUi {
const chat_toggle = document.createElement("input")
chat_toggle.type = "button"
chat_toggle.value = "Chat"
+ chat_toggle.ariaHasPopup = "menu"
chat_toggle.onclick = () => {
room.chat.shown = !room.chat.shown
if (room.chat.shown) chat_toggle.classList.add("active")
@@ -50,6 +51,7 @@ export class BottomMenu extends OverlayUi {
const prefs_button = document.createElement("input")
prefs_button.type = "button"
prefs_button.value = "Settings"
+ prefs_button.ariaHasPopup = "menu"
const prefs = new PrefUi()
prefs_button.onclick = () => {
@@ -58,13 +60,13 @@ export class BottomMenu extends OverlayUi {
else prefs_button.classList.remove("active")
}
- const local_controls = ediv({ class: "local-controls" },
+ const local_controls = ediv({ class: "local-controls", aria_label: "local resources" },
ebutton("Microphone", { onclick: () => room.local_user.await_add_resource(create_mic_res()) }),
ebutton("Camera", { onclick: () => room.local_user.await_add_resource(create_camera_res()) }),
ebutton("Screen", { onclick: () => room.local_user.await_add_resource(create_screencast_res()) }),
ebutton("File", { onclick: () => room.local_user.await_add_resource(create_file_res()) }),
)
- super(ediv({ class: "bottom-menu" }, chat_toggle, prefs_button, local_controls))
+ super(enav({ class: "bottom-menu" }, chat_toggle, prefs_button, local_controls))
}
}
diff --git a/client-web/source/preferences/decl.ts b/client-web/source/preferences/decl.ts
index effd885..f3f8e84 100644
--- a/client-web/source/preferences/decl.ts
+++ b/client-web/source/preferences/decl.ts
@@ -32,6 +32,8 @@ export const PREF_DECLS = {
camera_facing_mode: { type: optional(string), possible_values: ["environment", "user"], description: "Prefer user-facing or env-facing camera" },
auto_gain_control: { type: bool, description: "Automatically adjust mic gain" },
echo_cancellation: { type: bool, description: "Cancel echo" },
+ audio_activity_threshold: { type: number, optional: true, default: 0.003, description: "Audio activity threshold" },
+
// TODO differenciate between mic, cam and screen
optional_audio_default_enable: { type: bool, default: true, description: "Enable audio tracks by default" },
optional_video_default_enable: { type: bool, default: false, description: "Enable video tracks by default" },
diff --git a/client-web/source/preferences/ui.ts b/client-web/source/preferences/ui.ts
index bc0d123..2b1d0c7 100644
--- a/client-web/source/preferences/ui.ts
+++ b/client-web/source/preferences/ui.ts
@@ -5,7 +5,7 @@
*/
/// <reference lib="dom" />
-import { ebr, ebutton, ediv, elabel, espan, etd, etr, OverlayUi } from "../helper.ts";
+import { ebr, ebutton, ediv, eh2, elabel, espan, etd, etr, OverlayUi } from "../helper.ts";
import { PREF_DECLS } from "./decl.ts";
import { change_pref, on_pref_changed, PrefDecl, PREFS } from "./mod.ts";
@@ -65,7 +65,7 @@ export class PrefUi extends OverlayUi {
if (decl.default === undefined || decl.optional) {
const use_opt = document.createElement("input")
use_opt.type = "checkbox"
- use_opt.id = id
+ use_opt.id = "enable-" + id
use_opt.checked = PREFS[key] !== undefined
if (prim_control) prim_control.disabled = !use_opt.checked
use_opt.onchange = () => {
@@ -79,10 +79,10 @@ export class PrefUi extends OverlayUi {
use_opt_ = use_opt;
}
- const label = elabel(decl.description ?? `[${key}]`, { id })
+ const label = elabel(decl.description ?? `[${key}]`, { for: id })
return etr({ class: "pref" }, etd({}, label), etd({}, use_opt_ ?? ""), etd({}, prim_control ?? ""))
})
-
+
const notification_perm = Notification.permission == "granted" ? ediv() : ediv({},
espan("For keks-meet to send notifications, it needs you to grant permission: "),
ebutton("Grant", { onclick: () => Notification.requestPermission() }),
@@ -95,7 +95,6 @@ export class PrefUi extends OverlayUi {
const table = document.createElement("table")
table.append(...rows)
- super(ediv({ class: "prefs-overlay" }, notification_perm, reset, ebr(), table))
+ super(ediv({ class: "prefs-overlay" }, eh2("Settings"), notification_perm, ebr(), table, ebr(), reset))
}
-
}
diff --git a/client-web/source/resource/track.ts b/client-web/source/resource/track.ts
index 7d53522..22af16f 100644
--- a/client-web/source/resource/track.ts
+++ b/client-web/source/resource/track.ts
@@ -5,7 +5,7 @@
*/
/// <reference lib="dom" />
import { ProvideInfo } from "../../../common/packets.d.ts";
-import { ebutton, ediv } from "../helper.ts";
+import { ebutton, ediv, elabel } from "../helper.ts";
import { log } from "../logger.ts";
import { on_pref_changed, PREFS } from "../preferences/mod.ts";
import { get_rnnoise_node } from "../rnnoise.ts";
@@ -48,11 +48,12 @@ export const resource_track: ResourceHandlerDecl = {
}
}
-export function new_local_track(info: ProvideInfo, track: TrackHandle): LocalResource {
+export function new_local_track(info: ProvideInfo, track: TrackHandle, ...extra_controls: HTMLElement[]): LocalResource {
return {
info,
el: ediv({},
- create_track_display(track)
+ create_track_display(track),
+ ...extra_controls
),
destroy() { track.end() },
on_request(_user, _create_channel) {
@@ -62,24 +63,55 @@ export function new_local_track(info: ProvideInfo, track: TrackHandle): LocalRes
}
function create_track_display(track: TrackHandle): HTMLElement {
- const el = document.createElement("div")
const is_video = track.kind == "video"
- const media_el = is_video ? document.createElement("video") : document.createElement("audio")
+ const is_audio = track.kind == "audio"
+
const stream = new MediaStream([track.track])
+
+ const el = document.createElement("div")
+
+ const media_el = is_video
+ ? document.createElement("video")
+ : document.createElement("audio")
+
media_el.srcObject = stream
media_el.classList.add("media")
media_el.autoplay = true
media_el.controls = true
media_el.addEventListener("pause", () => media_el.play())
+
if (track.local) media_el.muted = true
el.append(media_el)
track.addEventListener("ended", () => {
media_el.srcObject = null // TODO // TODO figure out why i wrote todo here
el.remove()
})
+
+ if (is_audio && PREFS.audio_activity_threshold !== undefined) check_volume(stream, vol => {
+ const active = vol > PREFS.audio_activity_threshold
+ if (active != el.classList.contains("audio-active")) {
+ if (active) el.classList.add("audio-active")
+ else el.classList.remove("audio-active")
+ }
+ })
+
return el
}
+function check_volume(track: MediaStream, cb: (vol: number) => void) {
+ const ctx = new AudioContext();
+ const s = ctx.createMediaStreamSource(track)
+ const a = ctx.createAnalyser()
+ s.connect(a)
+ const samples = new Float32Array(a.fftSize);
+ setInterval(() => {
+ a.getFloatTimeDomainData(samples);
+ let sum = 0.0;
+ for (const amplitude of samples) { sum += amplitude * amplitude; }
+ cb(Math.sqrt(sum / samples.length))
+ }, 1000 / 15)
+}
+
export async function create_camera_res() {
log("media", "requesting user media (camera)")
const user_media = await window.navigator.mediaDevices.getUserMedia({
@@ -141,5 +173,16 @@ export async function create_mic_res() {
clear_gain_cb()
destination.disconnect()
})
- return new_local_track({ id: t.id, kind: "track", track_kind: "audio", label: "Microphone" }, t)
+
+ const mute = document.createElement("input")
+ mute.type = "checkbox"
+ mute.onchange = () => {
+ log("media", mute.checked ? "muted" : "unmuted")
+ if (mute.checked) gain.gain.value = Number.MIN_VALUE
+ else gain.gain.value = PREFS.microphone_gain
+ }
+ const mute_label = elabel("Mute", { class: "check-button" })
+ mute_label.prepend(mute)
+
+ return new_local_track({ id: t.id, kind: "track", track_kind: "audio", label: "Microphone" }, t, mute_label)
}
diff --git a/makefile b/makefile
index 089afe0..80953e6 100644
--- a/makefile
+++ b/makefile
@@ -11,6 +11,9 @@ server-build:
watch:
make -C client-web watch &
make -C server watch
+watch-public:
+ make -C client-web watch &
+ make -C server watch-public
install:
cargo +nightly install --force --path server
cargo +nightly install --force --path client-native-gui
diff --git a/readme.md b/readme.md
index e9cba8a..94a1ca0 100644
--- a/readme.md
+++ b/readme.md
@@ -36,6 +36,9 @@ make run # or `make build`
When changing code, use `make watch` to re-build things automatically as needed.
(run `cargo install systemfd cargo-watch` if needed)
+The server's bind address can be controlled using the `BIND` environment
+variable.
+
If you use this project or have any suggestions, please
[contact me](https://metamuffin.org/contact)
@@ -74,15 +77,15 @@ system works as follows:
## Keybinds
-| Keybind | Action |
-| --------- | ------------------------------------------------------- |
-| `RET` | Toggle chat |
-| `SPC M` | Add microphone track |
-| `SPC R` | Add microphone track (but with your left hand) |
-| `SPC C` | Add camera track |
-| `SPC S` | Add screencast track |
-| `SPC C-c` | End all tracks |
-| `C-v`\* | Paste image in chat (does not require chat to be shown) |
+| Keybind | Action |
+| ------- | ------------------------------------------------------- |
+| `C-RET` | Toggle chat |
+| `M` | Add microphone track |
+| `R` | Add microphone track (but with your left hand) |
+| `C` | Add camera track |
+| `S` | Add screencast track |
+| `C-c` | End all tracks |
+| `C-v`\* | Paste image in chat (does not require chat to be shown) |
## Debugging
diff --git a/server/makefile b/server/makefile
index 6299482..6097a10 100644
--- a/server/makefile
+++ b/server/makefile
@@ -5,5 +5,7 @@ run:
cargo +nightly run --release
watch:
systemfd --no-pid -s http::8080 -- cargo watch -x '+nightly run'
+watch-public:
+ systemfd --no-pid -s http::0.0.0.0:8080 -- cargo watch -x '+nightly run'
target/release/keks-meet: $(shell find src) Cargo.toml
cargo +nightly build --release
diff --git a/server/src/main.rs b/server/src/main.rs
index 85f1854..413f23a 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -12,6 +12,8 @@ use log::{debug, error};
use room::Room;
use std::collections::HashMap;
use std::convert::Infallible;
+use std::net::SocketAddr;
+use std::str::FromStr;
use std::sync::Arc;
use tokio::sync::RwLock;
use warp::hyper::Server;
@@ -69,13 +71,8 @@ async fn run() {
Server::from_tcp(l).unwrap()
} else {
Server::bind(
- &(
- [127, 0, 0, 1],
- std::env::var("PORT")
- .map(|p| p.parse().unwrap())
- .unwrap_or(8080),
- )
- .into(),
+ &SocketAddr::from_str(&std::env::var("BIND").unwrap_or(String::from("127.0.0.1:8080")))
+ .unwrap(),
)
};
let service = warp::service(routes);
diff --git a/todo.org b/todo.org
index 0901e1d..4ddcd80 100644
--- a/todo.org
+++ b/todo.org
@@ -5,9 +5,12 @@
* client-web
** show who is watching your stream
+** DONE mute
+** DONE voice activity indicators
+** ABANDONED fix tabbing order for chat
** TODO Make the optional streams UI prettier
** ABANDONED Maybe group tracks in streams to make sure everything is in sync
-** How do we implement global hotkeys?
+** ABANDONED How do we implement global hotkeys?
** ABANDONED Dont use websocket to send images to not block anything else
** PARTIAL File transfers via data channel (rift)
CLOSED: [2022-10-28 Fri 21:48]