Remote participant microphone controls (mute / unmute)

While the moderator can mute a participant, they cannot unmute the participant due to privacy concerns. Enhancement is required to unmute the remote user. In this document, we will explain how we can add mute/unmute feature to the Jitsi-meet project. Repository and files changed in Jitsi Meet project are specified.

1. jicofo repository

Conference focus is mandatory component of Jitsi Meet conferencing system next to the videobridge. To make changes to Jicofo, follow the steps below.

  • First of all, clone jicofo locally or fork it.

    apt install maven
    git clone https://github.com/jitsi/jicofo.git
    cd jicofo
    
  • Inside the cloned jicofo project, there are the following folders and files;

    • checkstyle.xml
    • debian
    • doc
    • lib
    • LICENSE
    • pom.xml
    • README.md
    • resources
    • script
    • SECURITY.md
    • src
  • To make changes to the JitsiMeetConferenceImpl.java file in the project, go to the location of the file and make the following changes.

    cd jicofo/src/main/java/org/jitsi/jicofo
    vim JitsiMeetConferenceImpl.java
    
  • Edit the following code block in file JitsiMeetConferenceImpl.java. Remove this condition or comment (Line: 2422).

    // do not allow unmuting other participants even for the moderator
    // if (!doMute && !fromJid.equals(toBeMutedJid))
    // {
    //    logger.warn("Blocking an unmute request (jid not the same).");
    //    return false;
    // }
    
  • Go to the directory where the pom.xml file is located and perform the mvn install process to create the new jar files.

    cd ~/jicofo
    mvn install
    
  • A target folder will be created after mvn install. Inside this folder are java jar files that we will use as compressed.

    • archive-tmp
    • checkstyle-checker.xml
    • classes
    • generated-test-sources
    • jicofo-1.1-SNAPSHOT.jar
    • maven-archiver
    • surefire-reports
    • checkstyle-cachefile
    • checkstyle-result.xml
    • generated-sources
    • jicofo-1.1-SNAPSHOT-archive.zip
    • jicofo-1.1-SNAPSHOT-jar-with-dependencies.jar
    • maven-status
    • test-classes
  • We need to replace our new jar file with the old jicofo.jar file.

    cd target
    mv jicofo-1.1-SNAPSHOT-jar-with-dependencies.jar jicofo.jar
    cp jicofo.jar /usr/share/jicofo/
    
  • To restart services after changes;

    /etc/init.d/jicofo restart && /etc/init.d/jitsi-videobridge2 restart && /etc/init.d/prosody restart

  • In order to monitor Jicofo logs;

    tail -f /var/log/jitsi/jicofo.log

2. jitsi-meet repository

  • You can clone the repository the change was made.
    git clone --branch mute-unmute git@github.com:bayraktarulku/jitsi-meet.git
    
  • For files and information updated in the repository or if you want to make changes yourself; You can follow the steps below.
2.1 Create Unmute Button Component
  • First, the unmute feature has been added to the participant’s options menu.
    • UnMuteButton && AbstractUnMuteButton classes created

    • Export UnMuteButton Component

      • index.js

        export { default as UnMuteButton } from './UnMuteButton';
        export { default as UnMuteRemoteParticipantDialog }
        from './UnMuteRemoteParticipantDialog';
        
    • UnMuteButton Component added to remoteVideoOptionsMenu

      • RemoteVideoMenuTriggerButton.js

        • UnMuteButton added import list
        import {
            GrantModeratorButton,
            MuteButton,
            UnMuteButton,
            MuteEveryoneElseButton,
            KickButton,
            PrivateMessageMenuButton,
            RemoteControlButton,
            RemoteVideoMenu,
            VolumeSlider
        } from './';
        
        • added UnMuteButton component to moderator (_renderRemoteVideoMenu() -> if(_isModerator))
        buttons.push(
            <UnMuteButton
                isAudioMuted = { isAudioMuted }
                key = 'unmute'
                participantID = { participantID } />
        );
        
2.2 Create Unmute Button Dialog Component
  • Create AbstractUnMuteRemoteParticipantDialog. Then include it in the UnMuteRemoteParticipantDialog class.
    • AbstractUnMuteRemoteParticipantDialog.js

    • UnMuteRemoteParticipantDialog.js

      class UnMuteRemoteParticipantDialog extends AbstractUnMuteRemoteParticipantDialog {
          /**
              * Implements React's {@link Component#render()}.
              *
              * @inheritdoc
              * @returns {ReactElement}
              */
          render() {
              return (
                  <Dialog
                      okKey = 'dialog.unMuteParticipantButton'
                      onSubmit = { this._onSubmit }
                      titleKey = 'dialog.unMuteParticipantTitle'
                      width = 'small'>
                      <div>
                          { this.props.t('dialog.unMuteParticipantBody') }
                      </div>
                  </Dialog>
              );
          }
      
          _onSubmit: () => boolean;
      }
      
2.3 Controls of Unmute
  • Method created for unmute option unMuteRemoteParticipant imported.
    • action.js

      import {
          unMuteRemoteParticipant
      } from '../base/participants';
      
      export function unMuteRemote(participantId: string) {
          return (dispatch: Dispatch<any>) => {
              sendAnalytics(createRemoteMuteConfirmedEvent(participantId));
              dispatch(unMuteRemoteParticipant(participantId));
          };
      }
      
  • Added const variable -> UNMUTE_REMOTE_PARTICIPANT.
    • actionTypes.js

      export const UNMUTE_REMOTE_PARTICIPANT = 'UNMUTE_REMOTE_PARTICIPANT';
      
  • This option import list added.
    • action.js

      import {
          UNMUTE_REMOTE_PARTICIPANT,
      }
      
      export function unMuteRemoteParticipant(id) {
          return {
              type: UNMUTE_REMOTE_PARTICIPANT,
              id
          };
      }
      
  • Created new case (UNMUTE_REMOTE_PARTICIPANT).
    • middleware.js

      import {
          UNMUTE_REMOTE_PARTICIPANT,
      }
      
      case UNMUTE_REMOTE_PARTICIPANT: {
          const { conference } = store.getState()['features/base/conference'];
          conference.unMuteParticipant(action.id);
          break;
      }
      

3. lib-jitsi-meet repository

  • You can clone the repository the change was made.
    git clone --branch mute-unmute git@github.com:bayraktarulku/lib-jitsi-meet
    
  • For files and information updated in the repository or if you want to make changes yourself; You can follow the steps below.
  • Added new event for AUDIO_UNMUTED_BY_FOCUS -> XMPPEvents.js

    AUDIO_UNMUTED_BY_FOCUS: 'xmpp.audio_unmuted_by_focus'
    
  • Created event listener for AUDIO_UNMUTED_BY_FOCUS -> JitsiConferenceEventManager.js

    chatRoom.addListener(XMPPEvents.AUDIO_UNMUTED_BY_FOCUS,
        actor => {
            // TODO: Add a way to differentiate between commands which caused
            // us to mute and those that did not change our state (i.e. we were
            // already muted).
            Statistics.sendAnalytics(createRemotelyMutedEvent());
    
           // conference.mutedByFocusActor = actor;
    
            // set isMutedByFocus when setAudioMute Promise ends
            conference.rtc.setAudioMute(false).then(
                () => {
                    conference.isMutedByFocus = false;
                    conference.mutedByFocusActor = null;
                })
                .catch(
                    error => {
                        conference.mutedByFocusActor = null;
                        logger.warn(
                            'Error while audio unmuting due to focus request', error);
                });
        }
    );
    
  • Added prototype for unMuteParticipant -> JitsiConference.js

    JitsiConference.prototype.unMuteParticipant = function(id) {
        const participant = this.getParticipantById(id);
    
        if (!participant) {
            return;
        }
        this.room.muteParticipant(participant.getJid(), false);
    };
    
  • Added unMuteParticipant method and changed onMute method -> ChatRoom.js

    unMuteParticipant(jid, mute) {
        logger.info('set unmute', mute);
        const iqToFocus = $iq(
            { to: this.focusMucJid,
                type: 'set' })
            .c('mute', {
                xmlns: 'http://jitsi.org/jitmeet/audio',
                jid
            })
            .t(mute.toString())
            .up();
    
        this.connection.sendIQ(
            iqToFocus,
            result => logger.log('set mute', result),
            error => logger.log('set mute error', error));
    }
    
    onMute(iq) {
        const from = iq.getAttribute('from');
        if (from !== this.focusMucJid) {
            logger.warn('Ignored mute from non focus peer');
    
            return;
        }
        const mute = $(iq).find('mute');
        if (mute.length && mute.text() === 'true') {
            this.eventEmitter.emit(XMPPEvents.AUDIO_MUTED_BY_FOCUS, mute.attr('actor'));
        } else if (mute.length && mute.text() === 'false') { // added unmute condition
        this.eventEmitter.emit(XMPPEvents.AUDIO_UNMUTED_BY_FOCUS, mute.attr('actor'));
        } else {
                // XXX Why do we support anything but muting? Why do we encode the
                // value in the text of the element? Why do we use a separate XML
                // namespace?
                logger.warn('Ignoring a mute request which does not explicitly '
                    - 'specify a positive mute command.');
        }
    }
    
4 Likes

I’ll try this on my docker environment…

@bayraktarulku, thanks for this. I don’t personally need the ‘unmute’ remote participant feature, but I need a feature where participants stay muted once moderator invokes the “mute everyone” action, so they can’t unmute themselves. This is similar to what obtains in Zoom and a few other platforms. So participants stay muted unless unmuted by the Moderator (they cannot unmute themselves). Does this enhancement do that? If not, can you please make the change to enable that?

Thanks again for your work!

@Freddie sorry, this develop does not meet the feature you requested. A similar development needs to be made as logic.

Submit a request to unmute the participant for moderator approval. If the moderator approves, we plan a study to the unmute. but still in the planning stage.

We haven’t tested it on docker. i’m waiting for your return on this.

Thanks @bayraktarulku. No problem.

Just to be clear, the feature I’m requesting allows participants to unmute themselves UNLESS they are muted by the moderator. So, if a participant mutes herself, she would be able to unmute herself. However, if muted by the moderator, she won’t be able to (unless moderator permits that action).

Take a look at how the feature is implemented in Zoom for instance:

Screen Shot 2020-09-07 at 12.22.40 AM

Screen Shot 2020-09-07 at 12.24.32 AM

I’d be very much interested in this feature if you can implement it. Thanks again for your effort!

@bayraktarulku I am confused where to put the files from section 2.1 in my docker-environment. Looking for the same in my web container. Lets see

Path error fixed in item 2.3

Thankyou so much sir, I will try this .
What is Jicofo sir ?

abc@abc-ThinkPad-W530:~/jicofo/target$ cp jicofo.jar /usr/share/jicofo/
cp: cannot create regular file ‘/usr/share/jicofo/’: Not a directory
I am unalbe to do it

I am able to click on the Unmute button but after clicking on UnmuteDialogbox button then nothing is happpening . please tell me what I am doing wrong and I am testing it locally on localhost8080

If you do not make changes in jicofo, you cannot trigger the unmute method. (Due to ethical concerns it is not allowed to open someone else’s voice & camera).

You must be a root user if you are working locally.

ok sir thankyou, I have clone jitsimeet from your repostory and then again clone lib-jitsimeet then after running it’s giving error like below
“Metro bundelr has encountered an error : while resolving module ‘lib-jitsi-meet/lib-jitsi-meet.min’”

Don’t use yarn.
You can use install-local ( npm i -g install-local ) to install your modified version of lib-jitsi-meet as follows: cd jitsi-meet; install-local …/lib-jitsi-meet

ok , but where am I able to see this Jicofo actually in my local machine I have not installed it still jitsi-meet is working . What I have did , I have downloaded jitsi-meet from official github link for jitsi.

will you please tell me where I can see this jicofo and what is exactly jicofo.
/etc/init.d/jicofo restart && /etc/init.d/jitsi-videobridge2 restart && /etc/init.d/prosody restart this commands are not running in my ubuntu system its saying folder is not avaialble.

Since Jicofo Jitsi Meet conference system is a mandatory component next to the video bridge and you set up jitsi-meet, you shouldn’t have any problems.

However, because you have problems;
Would you try again according to this document ?:


& then you can make the change.