Skip to content

Commit 5b68aa8

Browse files
committed
It works..?
1 parent 67449be commit 5b68aa8

20 files changed

+436
-36
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ There's a few scripts in the [dev](dev) directory to help you get started.
3737
You can run them directly in separate terminals or use the `all` script to run them all at once.
3838

3939
```sh
40-
# Run the relay, publisher, and web server:
40+
# Run the relay, a demo movie, and web server:
4141
./dev/all
4242

4343
# Or run each individually in separate terminals:
4444
./dev/relay
45-
./dev/pub
45+
./dev/bbb
4646
./dev/web
4747
```
4848

dev/bbb

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Change directory to the root of the project
5+
cd "$(dirname "$0")/.."
6+
7+
# Download the Big Buck Bunny video if it doesn't exist
8+
if [ ! -f dev/bbb.fmp4 ]; then
9+
if [ ! -f dev/bbb.mp4 ]; then
10+
echo "Downloading ya boye Big Buck Bunny..."
11+
wget http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 -O dev/bbb.mp4
12+
fi
13+
14+
echo "Converting to a (properly) fragmented MP4..."
15+
ffmpeg -i dev/bbb.mp4 \
16+
-c copy \
17+
-f mp4 -movflags cmaf+separate_moof+delay_moov+skip_trailer+frag_every_frame \
18+
dev/bbb.fmp4
19+
fi
20+
21+
INPUT=dev/bbb.fmp4 NAME=demo/bbb ./dev/pub "$@"

dev/pub

+4-17
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,10 @@ set -euo pipefail
44
# Change directory to the root of the project
55
cd "$(dirname "$0")/.."
66

7-
# Download the Big Buck Bunny video if it doesn't exist
8-
if [ ! -f dev/bbb.fmp4 ]; then
9-
if [ ! -f dev/bbb.mp4 ]; then
10-
echo "Downloading ya boye Big Buck Bunny..."
11-
wget http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4 -O dev/bbb.mp4
12-
fi
13-
14-
echo "Converting to a (properly) fragmented MP4..."
15-
ffmpeg -i dev/bbb.mp4 \
16-
-c copy \
17-
-f mp4 -movflags cmaf+separate_moof+delay_moov+skip_trailer+frag_every_frame \
18-
dev/bbb.fmp4
7+
# Error if INPUT or NAME is unset
8+
if [ -z "${INPUT:-}" ] || [ -z "${NAME:-}" ]; then
9+
echo "Usage: use ./dev/bbb or ./dev/tos"
10+
exit 1
1911
fi
2012

2113
# Connect to localhost by default.
@@ -24,13 +16,8 @@ PORT="${PORT:-4443}"
2416
ADDR="${ADDR:-$HOST:$PORT}"
2517
SCHEME="${SCHEME:-http}"
2618

27-
# Use the default "demo/bbb" for the broadcast.
28-
NAME="${NAME:-demo/bbb}"
2919
URL="${URL:-"$SCHEME://$ADDR/$NAME"}"
3020

31-
# Default to a source video
32-
INPUT="${INPUT:-dev/bbb.fmp4}"
33-
3421
# A crude hack to let the relay grab the cargo lock first when using /dev/all
3522
sleep 0.5
3623

dev/tos

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Change directory to the root of the project
5+
cd "$(dirname "$0")/.."
6+
7+
# Download the Big Buck Bunny video if it doesn't exist
8+
if [ ! -f dev/tos.fmp4 ]; then
9+
if [ ! -f dev/tos.mp4 ]; then
10+
echo "Downloading a vastly inferior demo movie..."
11+
wget http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/TearsOfSteel.mp4 -O dev/tos.mp4
12+
fi
13+
14+
echo "Converting to a (properly) fragmented MP4..."
15+
ffmpeg -i dev/tos.mp4 \
16+
-c copy \
17+
-f mp4 -movflags cmaf+separate_moof+delay_moov+skip_trailer+frag_every_frame \
18+
dev/tos.fmp4
19+
fi
20+
21+
INPUT=dev/tos.fmp4 NAME=demo/tos ./dev/pub "$@"

moq-transfork/src/model/announced.rs

+8
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ impl AnnouncedProducer {
7070
AnnouncedConsumer::new(self.state.subscribe(), prefix)
7171
}
7272

73+
/// Clear all announced tracks.
74+
pub fn reset(&mut self) {
75+
self.state.send_modify(|state| {
76+
state.active.clear();
77+
state.live = false;
78+
});
79+
}
80+
7381
pub async fn closed(&self) {
7482
self.state.closed().await;
7583
}

moq-web/src/demo/index.html

+3-4
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@
1414
</head>
1515

1616
<body style="display: flex">
17-
<!--<moq-publish url="http://localhost:4443/demo/me">
18-
<video slot="preview" autoplay muted style="border-radius: 0.375rem;"></video>
19-
</moq-publish>-->
17+
<!--<moq-publish url="http://localhost:4443/demo/me" />-->
18+
<!--<moq-watch url="http://localhost:4443/demo/bbb" />-->
2019

21-
<moq-watch url="http://localhost:4443/demo/bbb" />
20+
<moq-meet room="http://localhost:4443/demo" />
2221
</body>
2322

2423
</html>

moq-web/src/demo/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export { MoqPublishElement } from "../element/publish";
22
export { MoqWatchElement } from "../element/watch";
33
export { MoqVideoElement } from "../element/video";
4+
export { MoqMeetElement } from "../element/meet";

moq-web/src/element/meet.ts

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
import type { RoomAnnounced } from "@dist/rust";
2+
import { Room, RoomAction } from "@dist/rust";
3+
import type { MoqPublishElement } from "./publish";
4+
import { MoqWatchElement } from "./watch";
5+
6+
export class MoqMeetElement extends HTMLElement {
7+
#room: Room;
8+
#publish?: MoqPublishElement;
9+
10+
#broadcasts: HTMLDivElement;
11+
12+
// Work-around to make sure we only have one instance of each user.
13+
#unique: Map<string, number> = new Map();
14+
15+
static get observedAttributes() {
16+
return ["room"];
17+
}
18+
19+
constructor() {
20+
super();
21+
22+
this.#room = new Room();
23+
24+
const announced = this.#room.announced();
25+
this.#runAnnounced(announced).finally(() => announced.free());
26+
27+
const shadow = this.attachShadow({ mode: "open" });
28+
shadow.innerHTML = `
29+
<style type="text/css">
30+
.broadcasts {
31+
display: flex;
32+
gap: 8px;
33+
align-items: center;
34+
35+
moq-watch, moq-publish {
36+
border-radius: 0.375rem;
37+
overflow: hidden;
38+
}
39+
}
40+
41+
</style>
42+
`;
43+
44+
this.#broadcasts = document.createElement("div");
45+
this.#broadcasts.className = "broadcasts";
46+
47+
shadow.appendChild(this.#broadcasts);
48+
}
49+
50+
connectedCallback() {
51+
for (const name of MoqMeetElement.observedAttributes) {
52+
const value = this.getAttribute(name);
53+
if (value !== undefined) {
54+
this.attributeChangedCallback(name, null, value);
55+
}
56+
}
57+
}
58+
59+
disconnectedCallback() {}
60+
61+
attributeChangedCallback(name: string, old: string | null, value: string | null) {
62+
if (old === value) {
63+
return;
64+
}
65+
66+
switch (name) {
67+
case "room":
68+
this.#room.url = value;
69+
break;
70+
}
71+
}
72+
73+
async #runAnnounced(announced: RoomAnnounced) {
74+
while (true) {
75+
const announce = await announced.next();
76+
if (!announce) {
77+
return;
78+
}
79+
80+
console.log(announce);
81+
82+
switch (announce.action) {
83+
case RoomAction.Join:
84+
this.#join(announce.name);
85+
break;
86+
case RoomAction.Leave:
87+
this.#leave(announce.name);
88+
break;
89+
case RoomAction.Live:
90+
// TODO
91+
break;
92+
}
93+
}
94+
}
95+
96+
#join(name: string) {
97+
const count = this.#unique.get(name);
98+
if (count) {
99+
this.#unique.set(name, count + 1);
100+
return;
101+
}
102+
103+
this.#unique.set(name, 1);
104+
105+
const watch = new MoqWatchElement();
106+
watch.id = name;
107+
watch.url = `${this.room}/${name}`;
108+
this.#broadcasts.appendChild(watch);
109+
}
110+
111+
#leave(name: string) {
112+
let count = this.#unique.get(name);
113+
const watch = this.#broadcasts.querySelector(`#${name}`) as MoqWatchElement | null;
114+
115+
if (!count || !watch) {
116+
throw new Error("user not found");
117+
}
118+
119+
count -= 1;
120+
121+
if (count === 0) {
122+
this.#unique.delete(name);
123+
watch.remove();
124+
} else {
125+
this.#unique.set(name, count);
126+
}
127+
}
128+
129+
get room(): string | null {
130+
return this.getAttribute("room");
131+
}
132+
133+
set room(value: string | null) {
134+
if (value === null || value === "") {
135+
this.removeAttribute("room");
136+
} else {
137+
this.setAttribute("room", value);
138+
}
139+
}
140+
}
141+
142+
customElements.define("moq-meet", MoqMeetElement);
143+
144+
declare global {
145+
interface HTMLElementTagNameMap {
146+
"moq-meet": MoqMeetElement;
147+
}
148+
}

moq-web/src/error.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ pub enum Error {
2121
#[error("karp error: {0}")]
2222
Karp(#[from] moq_karp::Error),
2323

24-
#[error("invalid url")]
25-
InvalidUrl,
24+
#[error("invalid url: {0}")]
25+
InvalidUrl(String),
2626

2727
#[error("invalid fingerprint")]
2828
InvalidFingerprint,

moq-web/src/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ export type { WatchState } from "./watch";
44

55
// Can't run in a Worker, so no wrapper yet.
66
export { Publish, PublishState } from "@dist/rust";
7+
8+
// Too simple for a wrapper at the moment.
9+
export { Room, RoomAnnounce, RoomAnnounced, RoomAction } from "@dist/rust";

moq-web/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
mod error;
22
mod publish;
3+
mod room;
34
mod session;
45
mod watch;
56

67
pub use error::*;
78
pub use publish::*;
9+
pub use room::*;
810
pub use watch::*;
911

1012
pub(crate) use session::*;
@@ -18,7 +20,7 @@ pub fn start() {
1820
console_error_panic_hook::set_once();
1921

2022
let config = wasm_tracing::WASMLayerConfigBuilder::new()
21-
.set_max_level(tracing::Level::WARN)
23+
.set_max_level(tracing::Level::DEBUG)
2224
.build();
2325
wasm_tracing::set_as_global_default_with_config(config);
2426
}

moq-web/src/publish/backend.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl Backend {
7474
addr.set_query(None);
7575
addr.set_path("");
7676

77-
self.path = url.path_segments().ok_or(Error::InvalidUrl)?.collect();
77+
self.path = url.path_segments().ok_or(Error::InvalidUrl(url.to_string()))?.collect();
7878
self.connect = Some(Connect::new(addr));
7979

8080
self.status.state.set(PublishState::Connecting);

moq-web/src/publish/frontend.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ impl Publish {
3434

3535
#[wasm_bindgen(setter)]
3636
pub fn set_url(&mut self, url: Option<String>) -> Result<()> {
37-
let url = url.map(|u| Url::parse(&u)).transpose().map_err(|_| Error::InvalidUrl)?;
37+
let url = match url {
38+
Some(url) => Url::parse(&url).map_err(|_| Error::InvalidUrl(url.to_string()))?.into(),
39+
None => None,
40+
};
3841
self.controls.url.set(url);
3942
Ok(())
4043
}

0 commit comments

Comments
 (0)