@@ -6,9 +6,11 @@ import {
66 ServerMessage ,
77 VideoSubscription ,
88} from "./sfu.ts" ;
9+ import { atom , type PreinitializedWritableAtom } from "nanostores" ;
910
1011const MAX_DOWNSTREAMS = 16 ;
1112const LAST_N_AUDIO = 3 ;
13+ const DEBOUNCE_DELAY_MS = 500 ;
1214
1315// Internal Ids
1416type 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
2731export 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