1919
2020import org .jetbrains .annotations .*;
2121import org .jitsi .impl .protocol .xmpp .*;
22+ import org .jitsi .jicofo .conference .*;
2223import org .jitsi .jicofo .conference .source .*;
2324import org .jitsi .jicofo .xmpp .muc .*;
2425import org .jitsi .utils .*;
3334
3435import java .time .*;
3536import java .util .*;
37+ import java .util .concurrent .*;
3638
3739import static java .time .temporal .ChronoUnit .SECONDS ;
3840
@@ -110,6 +112,17 @@ public static String getEndpointId(ChatRoomMember chatRoomMember)
110112 */
111113 private final JitsiMeetConferenceImpl conference ;
112114
115+ /**
116+ * The task, if any, currently scheduled to signal queued remote sources.
117+ */
118+ private ScheduledFuture <?> signalQueuedSourcesTask ;
119+
120+ /**
121+ * The lock used when queueing remote sources to be signaled with a delay, i.e. when setting
122+ * {@link #signalQueuedSourcesTask}.
123+ */
124+ private final Object signalQueuedSourcesTaskSyncRoot = new Object ();
125+
113126 /**
114127 * Creates new {@link Participant} for given chat room member.
115128 *
@@ -126,6 +139,7 @@ public Participant(
126139 this .conference = conference ;
127140 this .roomMember = roomMember ;
128141 this .logger = parentLogger .createChildLogger (getClass ().getName ());
142+ logger .addContext ("participant" , getEndpointId ());
129143 }
130144
131145 /**
@@ -521,6 +535,151 @@ public void setMuted(MediaType mediaType, boolean value)
521535 }
522536 }
523537
538+ /**
539+ * Add a set of remote sources, which are to be signaled to the remote side. The sources may be signaled
540+ * immediately, or queued to be signaled later.
541+ * @param sources the sources to add.
542+ */
543+ public void addRemoteSources (ConferenceSourceMap sources )
544+ {
545+ if (!isSessionEstablished ())
546+ {
547+ logger .debug ("No Jingle session yet, queueing source-add." );
548+ queueRemoteSourcesToAdd (sources );
549+ // No need to schedule, the sources will be signaled when the session is established.
550+ return ;
551+ }
552+
553+ int delayMs = ConferenceConfig .config .getSourceSignalingDelayMs (conference .getParticipantCount ());
554+ if (delayMs > 0 )
555+ {
556+ synchronized (signalQueuedSourcesTaskSyncRoot )
557+ {
558+ queueRemoteSourcesToAdd (sources );
559+ scheduleSignalingOfQueuedSources (delayMs );
560+ }
561+ }
562+ else
563+ {
564+ OperationSetJingle jingle = conference .getJingle ();
565+ if (jingle == null )
566+ {
567+ logger .error ("Can not send Jingle source-add, no Jingle API available." );
568+ return ;
569+ }
570+ jingle .sendAddSourceIQ (
571+ sources ,
572+ getJingleSession (),
573+ ConferenceConfig .config .getUseJsonEncodedSources () && supportsJsonEncodedSources ());
574+ }
575+ }
576+
577+ /**
578+ * Schedule a task to signal all queued remote sources to the remote side. If a task is already scheduled, does
579+ * not schedule a new one (the existing task will send all latest queued sources).
580+ * @param delayMs the delay in milliseconds after which the task is to execute.
581+ */
582+ private void scheduleSignalingOfQueuedSources (int delayMs )
583+ {
584+ synchronized (signalQueuedSourcesTaskSyncRoot )
585+ {
586+ if (signalQueuedSourcesTask == null )
587+ {
588+ logger .debug ("Scheduling a task to signal queued remote sources after " + delayMs + " ms." );
589+ signalQueuedSourcesTask = TaskPools .getScheduledPool ().schedule (() ->
590+ {
591+ synchronized (signalQueuedSourcesTaskSyncRoot )
592+ {
593+ sendQueuedRemoteSources ();
594+ signalQueuedSourcesTask = null ;
595+ }
596+ },
597+ delayMs ,
598+ TimeUnit .MILLISECONDS );
599+ }
600+ }
601+ }
602+
603+
604+ /**
605+ * Removee a set of remote sources, which are to be signaled as removed to the remote side. The sources may be
606+ * signaled immediately, or queued to be signaled later.
607+ * @param sources the sources to remove.
608+ */
609+ public void removeRemoteSources (ConferenceSourceMap sources )
610+ {
611+ if (!isSessionEstablished ())
612+ {
613+ logger .debug ("No Jingle session yet, queueing source-remove." );
614+ queueRemoteSourcesToRemove (sources );
615+ // No need to schedule, the sources will be signaled when the session is established.
616+ return ;
617+ }
618+
619+ int delayMs = ConferenceConfig .config .getSourceSignalingDelayMs (conference .getParticipantCount ());
620+ if (delayMs > 0 )
621+ {
622+ synchronized (signalQueuedSourcesTaskSyncRoot )
623+ {
624+ queueRemoteSourcesToRemove (sources );
625+ scheduleSignalingOfQueuedSources (delayMs );
626+ }
627+ }
628+ else
629+ {
630+ OperationSetJingle jingle = conference .getJingle ();
631+ if (jingle == null )
632+ {
633+ logger .error ("Can not send Jingle source-remove, no Jingle API available." );
634+ return ;
635+ }
636+ jingle .sendRemoveSourceIQ (
637+ sources ,
638+ getJingleSession (),
639+ ConferenceConfig .config .getUseJsonEncodedSources () && supportsJsonEncodedSources ());
640+ }
641+ }
642+
643+ /**
644+ * Signal any queued remote source modifications (either addition or removal) to the remote side.
645+ */
646+ public void sendQueuedRemoteSources ()
647+ {
648+ OperationSetJingle jingle = conference .getJingle ();
649+ if (jingle == null )
650+ {
651+ logger .error ("Can not signal remote sources, no Jingle API available" );
652+ return ;
653+ }
654+
655+ if (!isSessionEstablished ())
656+ {
657+ logger .warn ("Can not singal remote sources, Jingle session not established." );
658+ return ;
659+ }
660+
661+ boolean encodeSourcesAsJson
662+ = ConferenceConfig .config .getUseJsonEncodedSources () && supportsJsonEncodedSources ();
663+
664+ for (SourcesToAddOrRemove sourcesToAddOrRemove : clearQueuedRemoteSourceChanges ())
665+ {
666+ AddOrRemove action = sourcesToAddOrRemove .getAction ();
667+ ConferenceSourceMap sources = sourcesToAddOrRemove .getSources ();
668+ logger .info ("Sending a queued source-" + action .toString ().toLowerCase () + ", sources:" + sources );
669+ if (action == AddOrRemove .Add )
670+ {
671+ jingle .sendAddSourceIQ (
672+ sourcesToAddOrRemove .getSources (),
673+ jingleSession ,
674+ encodeSourcesAsJson );
675+ }
676+ else if (action == AddOrRemove .Remove )
677+ {
678+ jingle .sendRemoveSourceIQ (sourcesToAddOrRemove .getSources (), jingleSession , encodeSourcesAsJson );
679+ }
680+ }
681+ }
682+
524683 /**
525684 * Checks whether the participant is muted.
526685 * @param mediaType the media type to check.
0 commit comments