1

I am developing an Electron application with the integration of React.js as a front-end framework, which will be more like a calling application. In that application-specific users can have multiple calls incoming, outgoing, mute | unmute calls, hold | unhold calls, etc. For this functionality to be achieved we have our own sip server, and for integrating that SIP server, on the frontend we are using a library which is known as SIP.JS. SIP.JS provides us mostly all the predefined functions to make a call, receive a call, mute, unmute, blind transfer, attended transfer, etc. But when it comes to having a call conference, it doesn't have proper documentation for that. SIP.JS specifies to us that we can use FreeSWITCH as well as ASTERISK in order to achieve the functionality, but with our specific requirements, no additional server needs to be integrated. We have also referred to rfc documentation for the call conference, but no such progress was there.

So far what we did is:

  1. Registered the userAgent
  2. Code for Incoming call integrated
  3. Code for outgoing calls integrated
  4. multiple session handling is achieved, for multiple calls
  5. mute | unmute, hold | unhold.
  6. DTMF functionality
  7. Blind Transfer, Attended Transfer
  8. Ring all Devices

In this scenario of call conference, I guess we have to make changes in Incoming and outgoing session handling functions.

  1. For registration and incoming call in context:
 const getUAConfig = async (_extension, _name) => {
    let alreadyLogin = '';
    try {
      alreadyLogin = 'yes';
      if (alreadyLogin == 'yes') {
        _displayname = _name;
        _sipUsername = _extension;
        _sipServer = 'SIP SERVER';
        _sipPassword = 'SIP PASSWORD';
        _wssServer = 'WSS SERVER;

        const uri = UserAgent.makeURI('sip:' + _sipUsername + '@' + _sipServer);
        const transportOptions = {
          wsServers: 'WSS SERVER',
          traceSip: true,
          maxReconnectionAttempts: 1,
        };

        const userAgentOptions = {
          uri: uri,
          transportOptions: transportOptions,
          userAgentString: 'App name',
          authorizationPassword: _sipPassword,
          sipExtension100rel: 'Supported',
          sipExtensionReplaces: 'Supported',
          register: true,
          contactTransport: 'wss',
          dtmfType: 'info',
          displayName: _name,
          sessionDescriptionHandlerFactoryOptions: {
            peerConnectionOptions: {
              rtcpMuxPolicy: 'negotiate',
              iceCheckingTimeout: 1000,
              iceTransportPolicy: 'all',
              iceServers: [{ urls: 'stun:stun.l.google.com:19302' }],
            },
          },
        };

        userAgent = await new UserAgent(userAgentOptions);
        const registerOptions = {
          extraContactHeaderParams: [],
        };
        registerer = await new Registerer(userAgent, registerOptions);

        registerer.stateChange.addListener((newState) => {
          
        });

        userAgent.start().then(async () => {
          console.log('Connected with WebSocket.');
          // Send REGISTER
          await registerer
            .register()
            .then((request) => {
              console.log('Successfully sent REGISTER, object is here');
              dispatch({
                type: USER_REGISTERED,
                payload: true,
              });
            })
            .catch((error) => {
              console.log('Failed to send REGISTER');
            });
        });
        return { userAgent, registerer };
      } else {
        return null;
      }
    } catch (error) {
      console.log(error.message + '');
      return null;
    }
  };
  1. Outgoing functionality:
    const dilaerFun = (inputNumber, userAgentInfo) => {
    var session;
    var uri = UserAgent.makeURI(
      `URI which we wanna call (sip number)`
    );
    session = new Inviter(userAgentInfo, uri);
    session
      .invite()
      .then((request) => {
        console.log('Successfully sent INVITE');
        sessionInfoAdd(session);
        session.stateChange.addListener(async (state) => {
          switch (state) {
            case 'Established':
              setMissedStatus(null);
              console.log('established outgoing....');
              //outgoing call log-----
              const mediaElement = document.getElementById(
                `mediaElement${session._id}`
              );
              const remoteStream = new MediaStream();

              session.sessionDescriptionHandler.peerConnection
                .getReceivers()
                .forEach((receiver) => {
                  if (receiver.track) {
                    remoteStream.addTrack(receiver.track);
                  }
                });
              mediaElement.srcObject = remoteStream;
              mediaElement.play();
              break;
            case 'Terminated':
              console.log('terminated');
              dispatch({
                type: DEMO_STATE,
                payload: session._id,
              });
              break;
            default:
              break;
          }
        });
      })
      .catch((error) => {
        console.error(' Failed to INVITE');
        console.error(error.toString());
      });
  };

  1. Array of sessions are maintained by:
  const sessionInfoAdd = (session) => {
    dispatch({
      type: SESSION_STORE,
      payload: session,
    });
  };
  1. Variable in which all sessions are stored is:
 sessionInfo:[]

NOTE: getUAConfig() is called as soon as the application is started. dialerFun() is called when we want to dial a specific number. sessionInfoAdd() is called in both getUAConfig and dialerFun, as they are codes for incoming and outgoing calls. when sessionInfoAdd() is triggered, the particular session which we get in return is added in the sessionInfo (Array) for the maintenance of sessions.

Yun
  • 3,056
  • 6
  • 9
  • 28

1 Answers1

0

SIP.JS is just a library so you will have to get the conference setup on the FreeSWITCH or Asterisk (FreeSWITCH is the better in my opinion)

Doing this is fairly straight forward, at your app level you need a way to get calls across to the box after checking the details like access ID and any auth you want to add, (like a PIN.)

Once you have that done, you can forward that to an extension specifically set for conferencing or have a dynamic conference setup by send from the app towards a specific gateway/dialplan to do this.

The FreeSWITCH software has a steep learning curve on it but this helped me when I was doing something similar: https://freeswitch.org/confluence/display/FREESWITCH/mod_conference

You can also code you own conf if you wish.