1- import { ClientMessage , MediaConfig , ServerMessage } from "./sfu.ts" ;
1+ import {
2+ ClientMessage ,
3+ MediaConfig ,
4+ ParticipantStream ,
5+ ParticipantSubscription ,
6+ ServerMessage ,
7+ VideoSubscription ,
8+ } from "./sfu.ts" ;
29
310const MAX_DOWNSTREAMS = 16 ;
411const LAST_N_AUDIO = 3 ;
@@ -13,7 +20,8 @@ interface VideoSlot {
1320
1421interface ParticipantMeta {
1522 externalParticipantId : string ;
16- media ?: MediaConfig ;
23+ participantId : string ;
24+ media : MediaConfig ;
1725}
1826
1927export interface ClientCoreConfig {
@@ -28,6 +36,7 @@ export class ClientCore {
2836 #videoSender: RTCRtpTransceiver ;
2937 #audioSender: RTCRtpTransceiver ;
3038 #closed: boolean ;
39+ #sequence: number ;
3140
3241 #videoSlots: VideoSlot [ ] ;
3342 #audioSlots: RTCRtpTransceiver [ ] ;
@@ -47,6 +56,7 @@ export class ClientCore {
4756 this . #videoSlots = [ ] ;
4857 this . #audioSlots = [ ] ;
4958 this . #participants = { } ;
59+ this . #sequence = 0 ;
5060
5161 this . #pc = new RTCPeerConnection ( ) ;
5262 this . #pc. onconnectionstatechange = ( ) => {
@@ -81,27 +91,13 @@ export class ClientCore {
8191
8292 switch ( msgKind ) {
8393 case "roomSnapshot" :
84- for ( const participant of msg . roomSnapshot . participants ) {
85- this . #participants[ participant . participantId ] = {
86- externalParticipantId : participant . externalParticipantId ,
87- media : participant . media ,
88- } ;
89- }
94+ this . #handleParticipantUpdates( msg . roomSnapshot . participants ) ;
9095 break ;
9196 case "streamUpdate" :
9297 if ( msg . streamUpdate . participantStream ) {
93- const stream = msg . streamUpdate . participantStream ;
94- if ( stream . participantId in this . #participants) {
95- const participant = this . #participants[ stream . participantId ] ;
96- participant . media = stream . media ;
97- participant . externalParticipantId =
98- stream . externalParticipantId ;
99- } else {
100- this . #participants[ stream . participantId ] = {
101- externalParticipantId : stream . externalParticipantId ,
102- media : stream . media ,
103- } ;
104- }
98+ this . #handleParticipantUpdates( [
99+ msg . streamUpdate . participantStream ,
100+ ] ) ;
105101 }
106102 break ;
107103 }
@@ -138,6 +134,59 @@ export class ClientCore {
138134 }
139135 }
140136
137+ #sendRpc( msg : ClientMessage [ "msg" ] ) {
138+ this . #rpc. send ( ClientMessage . toBinary ( {
139+ sequence : this . #sequence,
140+ msg,
141+ } ) ) ;
142+ this . #sequence += 1 ;
143+ }
144+
145+ #handleParticipantUpdates( streams : ParticipantStream [ ] ) {
146+ const newParticipants : ParticipantMeta [ ] = [ ] ;
147+
148+ for ( const stream of streams ) {
149+ if ( ! stream . media ) {
150+ // participant has left
151+ delete this . #participants[ stream . participantId ] ;
152+ continue ;
153+ }
154+
155+ if ( stream . participantId in this . #participants) {
156+ const participant = this . #participants[ stream . participantId ] ;
157+ participant . media = stream . media ;
158+ participant . externalParticipantId = stream . externalParticipantId ;
159+ participant . participantId = stream . participantId ;
160+ } else {
161+ const meta : ParticipantMeta = {
162+ externalParticipantId : stream . externalParticipantId ,
163+ participantId : stream . participantId ,
164+ media : stream . media ,
165+ } ;
166+ this . #participants[ stream . participantId ] = meta ;
167+ newParticipants . push ( meta ) ;
168+ }
169+ }
170+
171+ // TODO: should we bin pack the old participants first?
172+ for ( const slot of this . #videoSlots) {
173+ if ( slot . participantId ) {
174+ if ( slot . participantId in this . #participants) {
175+ continue ;
176+ }
177+
178+ slot . participantId = undefined ;
179+ }
180+
181+ const participant = newParticipants . pop ( ) ;
182+ if ( ! participant ) {
183+ continue ;
184+ }
185+
186+ slot . participantId = participant . participantId ;
187+ }
188+ }
189+
141190 #close( error ?: string ) {
142191 if ( this . #closed) return ;
143192
0 commit comments