UNPKG

react-native-pjsip

Version:
688 lines (628 loc) 21.7 kB
import React, {DeviceEventEmitter, NativeModules} from 'react-native'; import {EventEmitter} from 'events' import Call from './Call' import Message from './Message' import Account from './Account' /** * SIP headers object, where each key is a header name and value is a header value. * Example: * { * "X-Custom-Header": "Test Header Value", * "X-Custom-ID": "Awesome Header" * } * * @typedef {Object} PjSipHdrList */ /** * An additional information to be sent with outgoing SIP message. * It can (optionally) be specified for example * with #Endpoint.makeCall(), #Endpoint.answerCall(), #Endpoint.hangupCall(), * #Endpoint.holdCall() and many more. * * @typedef {Object} PjSipMsgData * @property {String} target_uri - Indicates whether the Courage component is present. * @property {PjSipHdrList} hdr_list - Additional message headers as linked list. * @property {String} content_type - MIME type of optional message body. * @property {String} msg_body - MIME type of optional message body. */ /** * An additional information to be sent with outgoing SIP message. * It can (optionally) be specified for example * with #Endpoint.makeCall(), #Endpoint.answerCall(), #Endpoint.hangupCall(), * #Endpoint.holdCall() and many more. * * @typedef {Object} PjSipCallSetttings * @property {number} flag - Bitmask of #pjsua_call_flag constants. * @property {number} req_keyframe_method - This flag controls what methods to request keyframe are allowed on the call. * @property {number} aud_cnt - Number of simultaneous active audio streams for this call. Setting this to zero will disable audio in this call. * @property {number} vid_cnt - Number of simultaneous active video streams for this call. Setting this to zero will disable video in this call. */ export default class Endpoint extends EventEmitter { constructor() { super(); // Subscribe to Accounts events DeviceEventEmitter.addListener('pjSipRegistrationChanged', this._onRegistrationChanged.bind(this)); // Subscribe to Calls events DeviceEventEmitter.addListener('pjSipCallReceived', this._onCallReceived.bind(this)); DeviceEventEmitter.addListener('pjSipCallChanged', this._onCallChanged.bind(this)); DeviceEventEmitter.addListener('pjSipCallTerminated', this._onCallTerminated.bind(this)); DeviceEventEmitter.addListener('pjSipCallScreenLocked', this._onCallScreenLocked.bind(this)); DeviceEventEmitter.addListener('pjSipMessageReceived', this._onMessageReceived.bind(this)); DeviceEventEmitter.addListener('pjSipConnectivityChanged', this._onConnectivityChanged.bind(this)); } /** * Returns a Promise that will be resolved once PjSip module is initialized. * Do not call any function while library is not initialized. * * @returns {Promise} */ start(configuration) { return new Promise(function(resolve, reject) { NativeModules.PjSipModule.start(configuration, (successful, data) => { if (successful) { let accounts = []; let calls = []; if (data.hasOwnProperty('accounts')) { for (let d of data['accounts']) { accounts.push(new Account(d)); } } if (data.hasOwnProperty('calls')) { for (let d of data['calls']) { calls.push(new Call(d)); } } let extra = {}; for (let key in data) { if (data.hasOwnProperty(key) && key != "accounts" && key != "calls") { extra[key] = data[key]; } } resolve({ accounts, calls, ...extra }); } else { reject(data); } }); }); } /** * @param configuration * @returns {Promise} */ changeNetworkConfiguration(configuration) { return new Promise(function(resolve, reject) { NativeModules.PjSipModule.changeNetworkConfiguration(configuration, (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * @param configuration * @returns {Promise} */ changeServiceConfiguration(configuration) { return new Promise(function(resolve, reject) { NativeModules.PjSipModule.changeServiceConfiguration(configuration, (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * Add a new account. If registration is configured for this account, this function would also start the * SIP registration session with the SIP registrar server. This SIP registration session will be maintained * internally by the library, and application doesn't need to do anything to maintain the registration session. * * An example configuration: * { * name: "John Doe", * username: "100", * domain: "pbx.com", * password: "XXXXXX", * * proxy: "192.168.100.1:5060", // default disabled. * transport: "TCP", // default TCP * regServer: "pbx.com", // default taken from domain * regTimeout: 300, // default 300 * } * * @param {Object} configuration * @returns {Promise} */ createAccount(configuration) { return new Promise(function(resolve, reject) { NativeModules.PjSipModule.createAccount(configuration, (successful, data) => { if (successful) { resolve(new Account(data)); } else { reject(data); } }); }); } replaceAccount(account, configuration) { throw new Error("Not implemented"); } /** * Update registration or perform unregistration. * If registration is configured for this account, then initial SIP REGISTER will be sent when the account is added. * Application normally only need to call this function if it wants to manually update the registration or to unregister from the server. * * @param {Account} account * @param bool renew If renew argument is zero, this will start unregistration process. * @returns {Promise} */ registerAccount(account, renew = true) { return new Promise(function(resolve, reject) { NativeModules.PjSipModule.registerAccount(account.getId(), renew, (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * Delete an account. This will unregister the account from the SIP server, if necessary, and terminate server side presence subscriptions associated with this account. * * @param {Account} account * @returns {Promise} */ deleteAccount(account) { return new Promise(function(resolve, reject) { NativeModules.PjSipModule.deleteAccount(account.getId(), (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * Make an outgoing call to the specified URI. * Available call settings: * - audioCount - Number of simultaneous active audio streams for this call. Setting this to zero will disable audio in this call. * - videoCount - Number of simultaneous active video streams for this call. Setting this to zero will disable video in this call. * - * * @param account {Account} * @param destination {String} Destination SIP URI. * @param callSettings {PjSipCallSetttings} Outgoing call settings. * @param msgSettings {PjSipMsgData} Outgoing call additional information to be sent with outgoing SIP message. */ makeCall(account, destination, callSettings, msgData) { destination = this._normalize(account, destination); return new Promise(function(resolve, reject) { NativeModules.PjSipModule.makeCall(account.getId(), destination, callSettings, msgData, (successful, data) => { if (successful) { resolve(new Call(data)); } else { reject(data); } }); }); } /** * Send response to incoming INVITE request. * * @param call {Call} Call instance * @returns {Promise} */ answerCall(call) { return new Promise((resolve, reject) => { NativeModules.PjSipModule.answerCall(call.getId(), (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * Hangup call by using method that is appropriate according to the call state. * * @param call {Call} Call instance * @returns {Promise} */ hangupCall(call) { // TODO: Add possibility to pass code and reason for hangup. return new Promise((resolve, reject) => { NativeModules.PjSipModule.hangupCall(call.getId(), (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * Hangup call by using Decline (603) method. * * @param call {Call} Call instance * @returns {Promise} */ declineCall(call) { return new Promise((resolve, reject) => { NativeModules.PjSipModule.declineCall(call.getId(), (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * Put the specified call on hold. This will send re-INVITE with the appropriate SDP to inform remote that the call is being put on hold. * * @param call {Call} Call instance * @returns {Promise} */ holdCall(call) { return new Promise((resolve, reject) => { NativeModules.PjSipModule.holdCall(call.getId(), (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * Release the specified call from hold. This will send re-INVITE with the appropriate SDP to inform remote that the call is resumed. * * @param call {Call} Call instance * @returns {Promise} */ unholdCall(call) { return new Promise((resolve, reject) => { NativeModules.PjSipModule.unholdCall(call.getId(), (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * @param call {Call} Call instance * @returns {Promise} */ muteCall(call) { return new Promise((resolve, reject) => { NativeModules.PjSipModule.muteCall(call.getId(), (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * @param call {Call} Call instance * @returns {Promise} */ unMuteCall(call) { return new Promise((resolve, reject) => { NativeModules.PjSipModule.unMuteCall(call.getId(), (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * @param call {Call} Call instance * @returns {Promise} */ useSpeaker(call) { return new Promise((resolve, reject) => { NativeModules.PjSipModule.useSpeaker(call.getId(), (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * @param call {Call} Call instance * @returns {Promise} */ useEarpiece(call) { return new Promise((resolve, reject) => { NativeModules.PjSipModule.useEarpiece(call.getId(), (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * Initiate call transfer to the specified address. * This function will send REFER request to instruct remote call party to initiate a new INVITE session to the specified destination/target. * * @param account {Account} Account associated with call. * @param call {Call} The call to be transferred. * @param destination URI of new target to be contacted. The URI may be in name address or addr-spec format. * @returns {Promise} */ xferCall(account, call, destination) { destination = this._normalize(account, destination); return new Promise((resolve, reject) => { NativeModules.PjSipModule.xferCall(call.getId(), destination, (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * Initiate attended call transfer. * This function will send REFER request to instruct remote call party to initiate new INVITE session to the URL of destCall. * The party at destCall then should "replace" the call with us with the new call from the REFER recipient. * * @param call {Call} The call to be transferred. * @param destCall {Call} The call to be transferred. * @returns {Promise} */ xferReplacesCall(call, destCall) { return new Promise((resolve, reject) => { NativeModules.PjSipModule.xferReplacesCall(call.getId(), destCall.getId(), (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * Redirect (forward) specified call to destination. * This function will send response to INVITE to instruct remote call party to redirect incoming call to the specified destination/target. * * @param account {Account} Account associated with call. * @param call {Call} The call to be transferred. * @param destination URI of new target to be contacted. The URI may be in name address or addr-spec format. * @returns {Promise} */ redirectCall(account, call, destination) { destination = this._normalize(account, destination); return new Promise((resolve, reject) => { NativeModules.PjSipModule.redirectCall(call.getId(), destination, (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * Send DTMF digits to remote using RFC 2833 payload formats. * * @param call {Call} Call instance * @param digits {String} DTMF string digits to be sent as described on RFC 2833 section 3.10. * @returns {Promise} */ dtmfCall(call, digits) { return new Promise((resolve, reject) => { NativeModules.PjSipModule.dtmfCall(call.getId(), digits, (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } activateAudioSession() { return new Promise((resolve, reject) => { NativeModules.PjSipModule.activateAudioSession((successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } deactivateAudioSession() { return new Promise((resolve, reject) => { NativeModules.PjSipModule.deactivateAudioSession((successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } changeOrientation(orientation) { const orientations = [ 'PJMEDIA_ORIENT_UNKNOWN', 'PJMEDIA_ORIENT_ROTATE_90DEG', 'PJMEDIA_ORIENT_ROTATE_270DEG', 'PJMEDIA_ORIENT_ROTATE_180DEG', 'PJMEDIA_ORIENT_NATURAL' ] if (orientations.indexOf(orientation) === -1) { throw new Error(`Invalid ${JSON.stringify(orientation)} device orientation, but expected ${orientations.join(", ")} values`) } NativeModules.PjSipModule.changeOrientation(orientation) } changeCodecSettings(codecSettings) { return new Promise(function(resolve, reject) { NativeModules.PjSipModule.changeCodecSettings(codecSettings, (successful, data) => { if (successful) { resolve(data); } else { reject(data); } }); }); } /** * @fires Endpoint#connectivity_changed * @private * @param data {Object} */ _onConnectivityChanged(data) { /** * Fires when registration status has changed. * * @event Endpoint#connectivity_changed * @property {Account} account */ this.emit("connectivity_changed", new Account(data)); } /** * @fires Endpoint#registration_changed * @private * @param data {Object} */ _onRegistrationChanged(data) { /** * Fires when registration status has changed. * * @event Endpoint#registration_changed * @property {Account} account */ this.emit("registration_changed", new Account(data)); } /** * @fires Endpoint#call_received * @private * @param data {Object} */ _onCallReceived(data) { /** * TODO * * @event Endpoint#call_received * @property {Call} call */ this.emit("call_received", new Call(data)); } /** * @fires Endpoint#call_changed * @private * @param data {Object} */ _onCallChanged(data) { /** * TODO * * @event Endpoint#call_changed * @property {Call} call */ this.emit("call_changed", new Call(data)); } /** * @fires Endpoint#call_terminated * @private * @param data {Object} */ _onCallTerminated(data) { /** * TODO * * @event Endpoint#call_terminated * @property {Call} call */ this.emit("call_terminated", new Call(data)); } /** * @fires Endpoint#call_screen_locked * @private * @param lock bool */ _onCallScreenLocked(lock) { /** * TODO * * @event Endpoint#call_screen_locked * @property bool lock */ this.emit("call_screen_locked", lock); } /** * @fires Endpoint#message_received * @private * @param data {Object} */ _onMessageReceived(data) { /** * TODO * * @event Endpoint#message_received * @property {Message} message */ this.emit("message_received", new Message(data)); } /** * @fires Endpoint#connectivity_changed * @private * @param available bool */ _onConnectivityChanged(available) { /** * @event Endpoint#connectivity_changed * @property bool available True if connectivity matches current Network settings, otherwise false. */ this.emit("connectivity_changed", available); } /** * Normalize Destination URI * * @param account * @param destination {string} * @returns {string} * @private */ _normalize(account, destination) { if (!destination.startsWith("sip:")) { let realm = account.getRegServer(); if (!realm) { realm = account.getDomain(); let s = realm.indexOf(":"); if (s > 0) { realm = realm.substr(0, s + 1); } } destination = "sip:" + destination + "@" + realm; } return destination; } // setUaConfig(UaConfig value) // setMaxCalls // setUserAgent // setNatTypeInSdp // setLogConfig(LogConfig value) // setLevel }