简体   繁体   English

SIP 与 JS 中的呼叫会议集成

[英]SIP integration with call conference in JS

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.我正在开发一个将 React.js 集成为前端框架的 Electron 应用程序,它更像是一个调用应用程序。 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 服务器,并且为了集成该 SIP 服务器,在前端我们使用了一个名为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 主要为我们提供了所有预定义的功能,用于拨打电话、接听电话、静音、取消静音、盲转、有人值守转接等。但是在召开电话会议时,它没有适当的文档. 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. SIP.JS 向我们指定我们可以使用FreeSWITCHASTERISK来实现功能,但根据我们的特定要求,不需要集成额外的服务器。 We have also referred to rfc documentation for the call conference, but no such progress was there.我们也参考了电话会议的rfc 文档,但没有这样的进展。

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 DTMF 功能
  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.注意:一旦应用程序启动,就会调用 getUAConfig()。 dialerFun() is called when we want to dial a specific number. dialerFun() 在我们想要拨打特定号码时被调用。 sessionInfoAdd() is called in both getUAConfig and dialerFun, as they are codes for incoming and outgoing calls. sessionInfoAdd() 在 getUAConfig 和 dialerFun 中都被调用,因为它们是传入和传出呼叫的代码。 when sessionInfoAdd() is triggered, the particular session which we get in return is added in the sessionInfo (Array) for the maintenance of sessions.当 sessionInfoAdd() 被触发时,我们返回的特定会话被添加到 sessionInfo (Array) 中以维护会话。

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) SIP.JS 只是一个库,因此您必须在 FreeSWITCH 或 Asterisk 上进行会议设置(我认为 FreeSWITCH 更好)

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.)这样做相当简单,在您的应用程序级别,您需要一种方法,在检查访问 ID 和您要添加的任何身份验证(如 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 FreeSWITCH 软件有一个陡峭的学习曲线,但是当我做类似的事情时这对我有帮助: https : //freeswitch.org/confluence/display/FREESWITCH/mod_conference

You can also code you own conf if you wish.如果你愿意,你也可以编码你自己的 conf。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM