Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit video senders #827

Merged
merged 6 commits into from
Nov 9, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions src/main/java/org/jitsi/impl/protocol/xmpp/ChatMemberImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,11 @@ public class ChatMemberImpl
*/
private String statsId;

/**
* Indicates whether the member's video sources are currently muted.
*/
private boolean isVideoMuted = true;

public ChatMemberImpl(EntityFullJid fullJid, ChatRoomImpl chatRoom,
int joinOrderNumber)
{
Expand Down Expand Up @@ -199,6 +204,9 @@ public boolean isJibri()
return isJibri;
}

@Override
public boolean isVideoMuted() { return isVideoMuted; }

/**
* Does presence processing.
*
Expand Down Expand Up @@ -287,6 +295,19 @@ void processPresence(Presence presence)
{
statsId = statsIdPacketExt.getStatsId();
}

boolean wasVideoMuted = isVideoMuted;
StandardExtensionElement videoMutedExt = presence.getExtension("videomuted", "jabber:client");
isVideoMuted = videoMutedExt == null || Boolean.valueOf(videoMutedExt.getText()); /* defaults to true */

if (isVideoMuted != wasVideoMuted)
{
logger.debug(() -> toString() + ". isMuted = " + isVideoMuted + ".");
if (isVideoMuted)
chatRoom.removeVideoSender();
else
chatRoom.addVideoSender();
}
}

/**
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/jitsi/impl/protocol/xmpp/ChatRoom.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ void join()
*/
int getMembersCount();

/**
* Returns the number of members that currently have their video sources unmuted.
*/
int getVideoSendersCount();

/**
* Grants ownership privileges to another user. Room owners may grant
* ownership privileges. Some room implementations will not allow to grant
Expand Down
36 changes: 36 additions & 0 deletions src/main/java/org/jitsi/impl/protocol/xmpp/ChatRoomImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ public class ChatRoomImpl
*/
private EventEmitter<ChatRoomListener> eventEmitter = new SyncEventEmitter<>();

/**
* The number of members that currently have their video sources unmuted.
*/
private int numVideoSenders;

/**
* Creates new instance of <tt>ChatRoomImpl</tt>.
*
Expand Down Expand Up @@ -577,6 +582,31 @@ private void sendLastPresence()
UtilKt.tryToSendStanza(xmppProvider.getXmppConnection(), lastPresenceSent);
}

@Override
public int getVideoSendersCount() { return numVideoSenders; }

public void addVideoSender()
{
++numVideoSenders;
logger.debug(() -> "The number of video senders has increased to " + numVideoSenders + ".");

eventEmitter.fireEvent(handler -> {
handler.numVideoSendersChanged(numVideoSenders);
return Unit.INSTANCE;
});
}

public void removeVideoSender()
{
--numVideoSenders;
logger.debug(() -> "The number of video senders has decreased to " + numVideoSenders + ".");

eventEmitter.fireEvent(handler -> {
handler.numVideoSendersChanged(numVideoSenders);
return Unit.INSTANCE;
});
}

/**
* Adds a new {@link ChatMemberImpl} with the given JID to {@link #members}.
* If a member with the given JID already exists, it returns the existing
Expand All @@ -597,6 +627,9 @@ private ChatMemberImpl addMember(EntityFullJid jid)

members.put(jid, newMember);

if (!newMember.isVideoMuted())
addVideoSender();

return newMember;
}
}
Expand Down Expand Up @@ -858,6 +891,9 @@ private ChatMemberImpl removeMember(EntityFullJid occupantJid)
logger.error(occupantJid + " not in " + roomJid);
}

if (!removed.isVideoMuted())
removeVideoSender();

return removed;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ public interface ChatRoomMember

boolean isJibri();

/**
* Returns whether the room member has its video sources muted.
*/
boolean isVideoMuted();

/**
* Gets the region (e.g. "us-east") of this {@link ChatRoomMember}.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public class JitsiMeetConferenceImpl
@NotNull
private final JitsiMeetConfig config;

private final ChatRoomListener chatRoomListener = new ChatRoomListenerImpl();
private final ChatRoomListener chatRoomListener = new ChatRoomListenerImpl(this);

/**
* Conference room chat instance.
Expand Down Expand Up @@ -228,6 +228,11 @@ public class JitsiMeetConferenceImpl
*/
private final ValidatingConferenceSourceMap conferenceSources = new ValidatingConferenceSourceMap();

/**
* Whether the limit on the number of video senders is currently hit.
*/
private boolean videoLimitReached = false;

/**
* Creates new instance of {@link JitsiMeetConferenceImpl}.
*
Expand Down Expand Up @@ -526,6 +531,19 @@ private void setConferenceProperty(String key, String value, boolean updatePrese
}
}

/**
* Process the new number of video senders reported by the chat room.
*/
private void numVideoSendersChanged(int numVideoSenders)
{
boolean newValue = numVideoSenders >= ConferenceConfig.config.getMaxVideoSenders();
if (videoLimitReached != newValue)
{
videoLimitReached = newValue;
setConferenceProperty("video-limit-reached", String.valueOf(videoLimitReached));
}
}

/**
* Leaves the conference room.
*/
Expand Down Expand Up @@ -1504,6 +1522,14 @@ public StanzaError onAddSource(@NotNull JingleSession jingleSession, List<Conten
return StanzaError.from(StanzaError.Condition.bad_request, e.getMessage()).build();
}

if (sourcesAdvertised.hasVideo() &&
chatRoom.getVideoSendersCount() >= ConferenceConfig.config.getMaxVideoSenders())
{
String errorMsg = "Source add rejected. Maximum number of video senders reached.";
logger.warn(() -> participantId + ": " + errorMsg);
return StanzaError.from(StanzaError.Condition.resource_constraint, errorMsg).build();
}

logger.debug(() -> "Received source-add from " + participantId + ": " + sourcesAdvertised);
logger.debug(() -> "Accepted sources from " + participantId + ": " + sourcesAccepted);

Expand Down Expand Up @@ -2417,6 +2443,12 @@ public void bridgeAdded(Bridge bridge)

private class ChatRoomListenerImpl implements ChatRoomListener
{
private final JitsiMeetConferenceImpl conference;
private ChatRoomListenerImpl(JitsiMeetConferenceImpl conference)
jqdrqgnq marked this conversation as resolved.
Show resolved Hide resolved
{
this.conference = conference;
}

@Override
public void roomDestroyed(@NotNull String reason)
{
Expand Down Expand Up @@ -2463,5 +2495,11 @@ public void localRoleChanged(@NotNull MemberRole newRole, @Nullable MemberRole o
public void memberPresenceChanged(@NotNull ChatRoomMember member)
{
}

@Override
public void numVideoSendersChanged(int numVideoSenders)
{
conference.numVideoSendersChanged(numVideoSenders);
}
}
}
4 changes: 4 additions & 0 deletions src/main/kotlin/org/jitsi/jicofo/ConferenceConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ class ConferenceConfig {
"jicofo.conference.min-participants".from(newConfig)
}

val maxVideoSenders: Int by config {
"jicofo.conference.max-video-senders".from(newConfig)
}

val enableLipSync: Boolean by config {
"jicofo.conference.enable-lip-sync".from(newConfig)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,16 @@ data class EndpointSourceSet(
*/
fun isEmpty() = sources.isEmpty() && ssrcGroups.isEmpty()

/**
* Whether there are any audio sources in this set.
*/
fun hasAudio() = sources.any { it.mediaType == AUDIO }
jqdrqgnq marked this conversation as resolved.
Show resolved Hide resolved

/**
* Whether there are any video sources in this set.
*/
fun hasVideo() = sources.any { it.mediaType == VIDEO }

/**
* Creates a list of Jingle [ContentPacketExtension]s that describe the sources in this [EndpointSourceSet].
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ interface ChatRoomListener {
fun roomDestroyed(reason: String) {}
fun startMutedChanged(startAudioMuted: Boolean, startVideoMuted: Boolean) {}
fun localRoleChanged(newRole: MemberRole, oldRole: MemberRole? = null) {}
fun numVideoSendersChanged(numVideoSenders: Int) {}
}

/** A class with the default kotlin method implementations (to avoid using @JvmDefault) **/
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@ jicofo {
// The minimum number of participants required for the conference to be started.
min-participants = 2

// The maximum number of participants that can send their video at the same time.
max-video-senders = 50

// Experimental.
enable-lip-sync = false

Expand Down