Skip to content

Commit a5f85ca

Browse files
committed
add subscription
1 parent 3ea785b commit a5f85ca

File tree

3 files changed

+53
-301
lines changed

3 files changed

+53
-301
lines changed

client-web/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
},
2727
"dependencies": {
2828
"@protobuf-ts/runtime": "^2.10.0",
29-
"lit": "^3.3.0"
29+
"lit": "^3.3.0",
30+
"nanostores": "^1.0.1"
3031
},
3132
"devDependencies": {
3233
"@protobuf-ts/plugin": "^2.10.0",

client-web/src/lib/core.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import {
66
ServerMessage,
77
VideoSubscription,
88
} from "./sfu.ts";
9+
import { atom, type PreinitializedWritableAtom } from "nanostores";
910

1011
const MAX_DOWNSTREAMS = 16;
1112
const LAST_N_AUDIO = 3;
13+
const DEBOUNCE_DELAY_MS = 500;
1214

1315
// Internal Ids
1416
type ParticipantId = string;
@@ -18,10 +20,12 @@ interface VideoSlot {
1820
participantId?: ParticipantId;
1921
}
2022

21-
interface ParticipantMeta {
23+
export interface ParticipantMeta {
2224
externalParticipantId: string;
2325
participantId: string;
2426
media: MediaConfig;
27+
stream: MediaStream;
28+
maxHeight: number;
2529
}
2630

2731
export interface ClientCoreConfig {
@@ -42,9 +46,10 @@ export class ClientCore {
4246
#audioSlots: RTCRtpTransceiver[];
4347

4448
#participants: Record<ParticipantId, ParticipantMeta>;
49+
#timeoutId: ReturnType<typeof setTimeout> | null;
4550

4651
onStateChanged = (state: RTCPeerConnectionState) => {};
47-
onTrack = (track: RTCPeerConnection) => {};
52+
onNewParticipant = (participant: ParticipantMeta) => {};
4853

4954
constructor(cfg: ClientCoreConfig) {
5055
this.#sfuUrl = cfg.sfuUrl;
@@ -57,6 +62,7 @@ export class ClientCore {
5762
this.#audioSlots = [];
5863
this.#participants = {};
5964
this.#sequence = 0;
65+
this.#timeoutId = null;
6066

6167
this.#pc = new RTCPeerConnection();
6268
this.#pc.onconnectionstatechange = () => {
@@ -162,6 +168,8 @@ export class ClientCore {
162168
externalParticipantId: stream.externalParticipantId,
163169
participantId: stream.participantId,
164170
media: stream.media,
171+
stream: new MediaStream(),
172+
maxHeight: 0, // default invisible until the UI tells us to render
165173
};
166174
this.#participants[stream.participantId] = meta;
167175
newParticipants.push(meta);
@@ -185,6 +193,8 @@ export class ClientCore {
185193

186194
slot.participantId = participant.participantId;
187195
}
196+
197+
this.#triggerSubscriptionFeedback();
188198
}
189199

190200
#close(error?: string) {
@@ -197,6 +207,37 @@ export class ClientCore {
197207
this.#closed = true;
198208
}
199209

210+
updateSubscription(participantId: string, maxHeight: number) {
211+
if (participantId in this.#participants) {
212+
this.#participants[participantId].maxHeight = maxHeight;
213+
}
214+
215+
this.#triggerSubscriptionFeedback();
216+
}
217+
218+
#triggerSubscriptionFeedback() {
219+
if (this.#timeoutId) {
220+
return;
221+
}
222+
223+
this.#timeoutId = setTimeout(() => {
224+
const subscriptions: ParticipantSubscription[] = Object.values(
225+
this.#participants,
226+
).map((p) => ({
227+
participantId: p.participantId,
228+
videoSettings: {
229+
maxHeight: p.maxHeight,
230+
},
231+
}));
232+
this.#sendRpc({
233+
oneofKind: "videoSubscription",
234+
videoSubscription: {
235+
subscriptions,
236+
},
237+
});
238+
}, DEBOUNCE_DELAY_MS);
239+
}
240+
200241
async connect(room: string, participant: string) {
201242
if (this.#closed) {
202243
const errorMessage =
@@ -260,6 +301,9 @@ export class ClientCore {
260301
}
261302

262303
disconnect() {
304+
if (this.#timeoutId) {
305+
clearTimeout(this.#timeoutId);
306+
}
263307
this.#pc.close();
264308
}
265309

0 commit comments

Comments
 (0)