TT#142700 CSC: Create a PoC integration of the VoIP(NGCP) call using Javascript library JsSip in combination with Kamailio's WebSocket module
- Enable and disable camera during the call - Enable and disable screen during the call - Switch from camera to screen and back - Send in-band DTMF - Send "603 Decline" on termination Change-Id: Ife56ca49cadade44ee9b70b77b3f345b262be9d9pull/9/head
parent
3d1c2a7a6a
commit
238f78cb05
@ -0,0 +1,298 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
EventEmitter
|
||||||
|
} from 'events'
|
||||||
|
import jssip from 'jssip'
|
||||||
|
|
||||||
|
let $baseWebSocketUrl = null
|
||||||
|
let $subscriber = null
|
||||||
|
let $socket = null
|
||||||
|
let $userAgent = null
|
||||||
|
let $outgoingRtcSession = null
|
||||||
|
let $incomingRtcSession = null
|
||||||
|
let $localMediaStream = null
|
||||||
|
let $remoteMediaStream = null
|
||||||
|
let $isVideoScreen = false
|
||||||
|
|
||||||
|
const TERMINATION_OPTIONS = {
|
||||||
|
status_code: 603,
|
||||||
|
reason_phrase: 'Decline'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const callEvent = new EventEmitter()
|
||||||
|
|
||||||
|
function handleRemoteMediaStream (trackEvent) {
|
||||||
|
const stream = trackEvent.streams[0]
|
||||||
|
if (!$remoteMediaStream) {
|
||||||
|
$remoteMediaStream = stream
|
||||||
|
callEvent.emit('remoteStream', $remoteMediaStream)
|
||||||
|
} else if ($remoteMediaStream && $remoteMediaStream.id !== stream.id) {
|
||||||
|
$remoteMediaStream = stream
|
||||||
|
callEvent.emit('remoteStream', $remoteMediaStream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSubscriberUri () {
|
||||||
|
return 'sip:' + $subscriber.username + '@' + $subscriber.domain
|
||||||
|
}
|
||||||
|
|
||||||
|
function callCreateSocket () {
|
||||||
|
return new jssip.WebSocketInterface($baseWebSocketUrl + '/' + $subscriber.username)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callConfigure ({ baseWebSocketUrl }) {
|
||||||
|
$baseWebSocketUrl = baseWebSocketUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function callInitialize ({ subscriber }) {
|
||||||
|
$subscriber = subscriber
|
||||||
|
callRegister()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callRegister () {
|
||||||
|
if (!$socket) {
|
||||||
|
$socket = callCreateSocket()
|
||||||
|
const config = {
|
||||||
|
sockets: [$socket],
|
||||||
|
uri: getSubscriberUri(),
|
||||||
|
password: $subscriber.password
|
||||||
|
}
|
||||||
|
$userAgent = new jssip.UA(config)
|
||||||
|
const delegateEvent = (eventName) => {
|
||||||
|
$userAgent.on(eventName, (event) => callEvent.emit(eventName, event))
|
||||||
|
}
|
||||||
|
delegateEvent('connected')
|
||||||
|
delegateEvent('disconnected')
|
||||||
|
delegateEvent('newRTCSession')
|
||||||
|
delegateEvent('newMessage')
|
||||||
|
delegateEvent('registered')
|
||||||
|
delegateEvent('unregistered')
|
||||||
|
delegateEvent('registrationFailed')
|
||||||
|
$userAgent.on('newRTCSession', (event) => {
|
||||||
|
if (event.originator === 'remote') {
|
||||||
|
$incomingRtcSession = event.session
|
||||||
|
$incomingRtcSession.on('peerconnection', () => {
|
||||||
|
$incomingRtcSession.connection.ontrack = handleRemoteMediaStream
|
||||||
|
})
|
||||||
|
$incomingRtcSession.on('failed', (failedEvent) => {
|
||||||
|
callEvent.emit('incomingFailed', failedEvent)
|
||||||
|
})
|
||||||
|
$incomingRtcSession.on('ended', (failedEvent) => {
|
||||||
|
callEvent.emit('incomingEnded', failedEvent)
|
||||||
|
$incomingRtcSession = null
|
||||||
|
})
|
||||||
|
callEvent.emit('incoming', $incomingRtcSession)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$userAgent.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callUnregister () {
|
||||||
|
if ($userAgent) {
|
||||||
|
$userAgent.unregister({
|
||||||
|
all: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function callStart ({ number }) {
|
||||||
|
$localMediaStream = await callCreateLocalAudioStream()
|
||||||
|
callEvent.emit('localStream', $localMediaStream)
|
||||||
|
$outgoingRtcSession = $userAgent.call(number, {
|
||||||
|
eventHandlers: {
|
||||||
|
progress (event) {
|
||||||
|
callEvent.emit('outgoingProgress', event)
|
||||||
|
},
|
||||||
|
failed (event) {
|
||||||
|
callEvent.emit('outgoingFailed', event)
|
||||||
|
},
|
||||||
|
confirmed (event) {
|
||||||
|
callEvent.emit('outgoingConfirmed', event)
|
||||||
|
},
|
||||||
|
ended (event) {
|
||||||
|
callEvent.emit('outgoingEnded', event)
|
||||||
|
$outgoingRtcSession = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mediaStream: $localMediaStream
|
||||||
|
})
|
||||||
|
$outgoingRtcSession.connection.ontrack = handleRemoteMediaStream
|
||||||
|
const delegateEvent = (eventName, newName) => {
|
||||||
|
$outgoingRtcSession.on(eventName, (event) => callEvent.emit(newName, event))
|
||||||
|
}
|
||||||
|
delegateEvent('failed', 'outgoingFailed')
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function callAccept () {
|
||||||
|
$localMediaStream = await callCreateLocalAudioStream()
|
||||||
|
callEvent.emit('localStream', $localMediaStream)
|
||||||
|
if ($incomingRtcSession) {
|
||||||
|
$incomingRtcSession.answer({
|
||||||
|
mediaStream: $localMediaStream
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function callCreateLocalAudioStream () {
|
||||||
|
return await navigator.mediaDevices.getUserMedia({
|
||||||
|
video: false,
|
||||||
|
audio: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callGetRemoteMediaStream () {
|
||||||
|
return $remoteMediaStream
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callGetRemoteMediaStreamId () {
|
||||||
|
return $remoteMediaStream?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callGetLocalMediaStream () {
|
||||||
|
return $localMediaStream
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callGetLocalMediaStreamId () {
|
||||||
|
return $localMediaStream?.id
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callGetRtcSession () {
|
||||||
|
if ($outgoingRtcSession) {
|
||||||
|
return $outgoingRtcSession
|
||||||
|
} else if ($incomingRtcSession) {
|
||||||
|
return $incomingRtcSession
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function callChangeVideoStream (stream) {
|
||||||
|
if ($localMediaStream && callGetRtcSession()) {
|
||||||
|
callGetRtcSession().connection.getSenders().forEach(
|
||||||
|
sender => callGetRtcSession().connection.removeTrack(sender)
|
||||||
|
)
|
||||||
|
$localMediaStream.getTracks().forEach(track => track.stop())
|
||||||
|
$localMediaStream = stream
|
||||||
|
$localMediaStream.getTracks().forEach(
|
||||||
|
track => callGetRtcSession().connection.addTrack(track, $localMediaStream)
|
||||||
|
)
|
||||||
|
await callRenegotiate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function callAddCamera () {
|
||||||
|
await callChangeVideoStream(await navigator.mediaDevices.getUserMedia({
|
||||||
|
video: {
|
||||||
|
width: {
|
||||||
|
ideal: 4096
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
ideal: 2160
|
||||||
|
}
|
||||||
|
},
|
||||||
|
audio: true
|
||||||
|
}))
|
||||||
|
$isVideoScreen = false
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function callAddScreen () {
|
||||||
|
$isVideoScreen = true
|
||||||
|
await callChangeVideoStream(await navigator.mediaDevices.getDisplayMedia({
|
||||||
|
video: {
|
||||||
|
width: {
|
||||||
|
ideal: 4096
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
ideal: 2160
|
||||||
|
}
|
||||||
|
},
|
||||||
|
audio: true
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function callRemoveVideo () {
|
||||||
|
$isVideoScreen = false
|
||||||
|
await callChangeVideoStream(await navigator.mediaDevices.getUserMedia({
|
||||||
|
video: false,
|
||||||
|
audio: true
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function callRenegotiate () {
|
||||||
|
callGetRtcSession().renegotiate({
|
||||||
|
useUpdate: false
|
||||||
|
}, () => {
|
||||||
|
callEvent.emit('renegotiationSucceeded')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callHasRemoteVideo () {
|
||||||
|
return $remoteMediaStream?.getVideoTracks?.()?.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callHasLocalVideo () {
|
||||||
|
return $localMediaStream?.getVideoTracks?.()?.length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callHasLocalCamera () {
|
||||||
|
return callHasLocalVideo() && !$isVideoScreen
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callHasLocalScreen () {
|
||||||
|
return callHasLocalVideo() && $isVideoScreen
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callToggleMicrophone () {
|
||||||
|
const config = {
|
||||||
|
audio: true,
|
||||||
|
video: false
|
||||||
|
}
|
||||||
|
const rtcSession = callGetRtcSession()
|
||||||
|
const muted = rtcSession?.isMuted()
|
||||||
|
if (muted.audio) {
|
||||||
|
rtcSession.unmute(config)
|
||||||
|
} else {
|
||||||
|
rtcSession.mute(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callIsMuted () {
|
||||||
|
return callGetRtcSession()?.isMuted()?.audio
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callSendDTMF (tone, transport = 'RFC2833') {
|
||||||
|
const rtcSession = callGetRtcSession()
|
||||||
|
if (rtcSession) {
|
||||||
|
rtcSession.sendDTMF(tone, {
|
||||||
|
transportType: transport
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callToggleRemoteAudio () {
|
||||||
|
if ($remoteMediaStream && $remoteMediaStream.getAudioTracks()[0]) {
|
||||||
|
$remoteMediaStream.getAudioTracks()[0].enabled = !$remoteMediaStream?.getAudioTracks()[0]?.enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callIsRemoteAudioMuted () {
|
||||||
|
return !$remoteMediaStream?.getAudioTracks()[0]?.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
export function callEnd () {
|
||||||
|
if ($outgoingRtcSession && !$outgoingRtcSession.isEnded()) {
|
||||||
|
$outgoingRtcSession.terminate(TERMINATION_OPTIONS)
|
||||||
|
$outgoingRtcSession = null
|
||||||
|
}
|
||||||
|
if ($incomingRtcSession && !$incomingRtcSession.isEnded()) {
|
||||||
|
$incomingRtcSession.terminate(TERMINATION_OPTIONS)
|
||||||
|
$incomingRtcSession = null
|
||||||
|
}
|
||||||
|
if ($localMediaStream) {
|
||||||
|
$localMediaStream.getTracks().forEach(track => track.stop())
|
||||||
|
$localMediaStream = null
|
||||||
|
}
|
||||||
|
if ($remoteMediaStream) {
|
||||||
|
$remoteMediaStream.getTracks().forEach(track => track.stop())
|
||||||
|
$remoteMediaStream = null
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
v4
|
||||||
|
} from 'uuid'
|
||||||
|
import _ from 'lodash'
|
||||||
|
import {
|
||||||
|
callConfigure,
|
||||||
|
callEnd,
|
||||||
|
callEvent,
|
||||||
|
callHasLocalCamera,
|
||||||
|
callHasLocalScreen,
|
||||||
|
callHasRemoteVideo,
|
||||||
|
callIsMuted,
|
||||||
|
callIsRemoteAudioMuted
|
||||||
|
} from 'src/api/ngcp-call'
|
||||||
|
import { errorVisibilityTimeout } from 'src/store/call/common'
|
||||||
|
|
||||||
|
export default async ({ Vue, app, store }) => {
|
||||||
|
callConfigure({
|
||||||
|
baseWebSocketUrl: app.$appConfig.baseWsUrl + '/wss/sip'
|
||||||
|
})
|
||||||
|
const callFailed = (event) => {
|
||||||
|
let cause = event.cause
|
||||||
|
const reason = event.message?.parseHeader?.('Reason')
|
||||||
|
if (reason?.text) {
|
||||||
|
cause = reason.text
|
||||||
|
}
|
||||||
|
if (event.originator !== 'local') {
|
||||||
|
callEnd()
|
||||||
|
store.commit('call/endCall', cause)
|
||||||
|
setTimeout(() => {
|
||||||
|
store.commit('call/inputNumber')
|
||||||
|
}, errorVisibilityTimeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
callEvent.on('connected', () => {
|
||||||
|
store.commit('call/enableCall')
|
||||||
|
})
|
||||||
|
callEvent.on('disconnected', () => {
|
||||||
|
store.commit('call/disableCall')
|
||||||
|
})
|
||||||
|
callEvent.on('outgoingProgress', (event) => {
|
||||||
|
store.commit('call/startRinging')
|
||||||
|
})
|
||||||
|
callEvent.on('outgoingFailed', callFailed)
|
||||||
|
callEvent.on('incomingFailed', callFailed)
|
||||||
|
callEvent.on('outgoingEnded', callFailed)
|
||||||
|
callEvent.on('incomingEnded', callFailed)
|
||||||
|
callEvent.on('incoming', (session) => {
|
||||||
|
store.commit('call/incomingCall', {
|
||||||
|
number: _.get(session, 'remote_identity.uri.user', 'Unknown')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
callEvent.on('localStream', () => {
|
||||||
|
store.commit('call/toggleMicrophone', !callIsMuted())
|
||||||
|
})
|
||||||
|
callEvent.on('remoteStream', () => {
|
||||||
|
store.commit('call/establishCall', {
|
||||||
|
mediaStreamId: v4(),
|
||||||
|
isLocalAudioMuted: callIsMuted(),
|
||||||
|
hasLocalCamera: callHasLocalCamera(),
|
||||||
|
hasLocalScreen: callHasLocalScreen(),
|
||||||
|
hasRemoteVideo: callHasRemoteVideo(),
|
||||||
|
isRemoteAudioMuted: callIsRemoteAudioMuted()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
@ -1,76 +0,0 @@
|
|||||||
import Vue from 'vue'
|
|
||||||
import getRtcEnginePlugin from 'src/plugins/rtc-engine'
|
|
||||||
import CallPlugin from 'src/plugins/call'
|
|
||||||
import ConferencePlugin from 'src/plugins/conference'
|
|
||||||
import { errorVisibilityTimeout } from 'src/store/call'
|
|
||||||
|
|
||||||
export default ({ Vue, store, app }) => {
|
|
||||||
const rtcPluginConfig = {
|
|
||||||
cdkScriptUrl: app.$appConfig.baseHttpUrl + '/rtc/files/dist/cdk-prod.js',
|
|
||||||
webSocketUrl: app.$appConfig.baseWsUrl + '/rtc/api',
|
|
||||||
ngcpApiBaseUrl: app.$appConfig.baseHttpUrl
|
|
||||||
// ngcpApiJwt: ... // Note: this value will be set in userInit action, with value from "getJwt" function
|
|
||||||
}
|
|
||||||
const RtcEnginePlugin = getRtcEnginePlugin(rtcPluginConfig)
|
|
||||||
Vue.use(RtcEnginePlugin)
|
|
||||||
Vue.use(CallPlugin)
|
|
||||||
Vue.use(ConferencePlugin)
|
|
||||||
|
|
||||||
rtcEngine(store)
|
|
||||||
call(store)
|
|
||||||
conference(store)
|
|
||||||
}
|
|
||||||
|
|
||||||
function rtcEngine (store) {
|
|
||||||
Vue.$rtcEngine.onSipNetworkConnected(() => {
|
|
||||||
store.commit('call/enableCall')
|
|
||||||
}).onSipNetworkDisconnected(() => {
|
|
||||||
store.commit('call/disableCall')
|
|
||||||
}).onConferenceNetworkConnected(() => {
|
|
||||||
store.commit('conference/enableConferencing')
|
|
||||||
}).onConferenceNetworkDisconnected(() => {
|
|
||||||
store.commit('conference/disableConferencing')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function call (store) {
|
|
||||||
Vue.$call.onIncoming(() => {
|
|
||||||
store.commit('call/incomingCall', {
|
|
||||||
number: Vue.$call.getNumber()
|
|
||||||
})
|
|
||||||
}).onRemoteMedia((remoteMediaStream) => {
|
|
||||||
store.commit('call/establishCall', remoteMediaStream)
|
|
||||||
}).onEnded((reason) => {
|
|
||||||
Vue.$call.end()
|
|
||||||
store.commit('call/endCall', reason)
|
|
||||||
setTimeout(() => {
|
|
||||||
store.commit('call/inputNumber')
|
|
||||||
}, errorVisibilityTimeout)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function conference (store) {
|
|
||||||
Vue.$conference.onConferenceParticipantJoined((participant) => {
|
|
||||||
store.commit('conference/participantJoined', participant)
|
|
||||||
participant.onMediaStream(() => {
|
|
||||||
store.commit('conference/removeRemoteMedia', participant.id)
|
|
||||||
store.commit('conference/addRemoteMedia', participant.id)
|
|
||||||
}).onMediaEnded(() => {
|
|
||||||
store.commit('conference/removeRemoteMedia', participant.id)
|
|
||||||
})
|
|
||||||
}).onConferenceParticipantLeft((participant) => {
|
|
||||||
store.commit('conference/participantLeft', participant)
|
|
||||||
store.commit('conference/removeRemoteMedia', participant.id)
|
|
||||||
store.commit('conference/setSelectedParticipant', participant.id)
|
|
||||||
}).onConferenceEvent((event) => {
|
|
||||||
store.commit('conference/event', event)
|
|
||||||
}).onConferenceMessage((message) => {
|
|
||||||
store.commit('conference/message', message)
|
|
||||||
}).onConferenceFile((file) => {
|
|
||||||
store.commit('conference/file', file)
|
|
||||||
}).onLocalMediaStreamEnded(() => {
|
|
||||||
store.commit('conference/disposeLocalMedia')
|
|
||||||
}).onConferenceEnded(() => {
|
|
||||||
store.dispatch('conference/leave')
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,327 +0,0 @@
|
|||||||
import EventEmitter from 'events'
|
|
||||||
|
|
||||||
export const LocalMedia = {
|
|
||||||
audioOnly: 'audioOnly',
|
|
||||||
audioVideo: 'audioVideo',
|
|
||||||
videoOnly: 'videoOnly',
|
|
||||||
audioScreen: 'audioScreen',
|
|
||||||
screenOnly: 'screenOnly'
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NetworkNotConnected {
|
|
||||||
constructor (network) {
|
|
||||||
this.name = 'NetworkNotConnected'
|
|
||||||
this.message = 'Network ' + network + ' is not connected'
|
|
||||||
this.network = network
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CallNotFound {
|
|
||||||
constructor () {
|
|
||||||
this.name = 'CallNotFound'
|
|
||||||
this.message = 'Call not found'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CallAlreadyExists {
|
|
||||||
constructor () {
|
|
||||||
this.name = 'CallAlreadyExists'
|
|
||||||
this.message = 'Call already exists'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let rtcEngineCallInstance = null
|
|
||||||
export class RtcEngineCall {
|
|
||||||
constructor () {
|
|
||||||
this.network = null
|
|
||||||
this.rtcEngine = null
|
|
||||||
this.localMedia = null
|
|
||||||
this.remoteMedia = null
|
|
||||||
this.currentCall = null
|
|
||||||
this.events = new EventEmitter()
|
|
||||||
this.endedReason = null
|
|
||||||
}
|
|
||||||
|
|
||||||
setRtcEngine (rtcEngine) {
|
|
||||||
if (this.rtcEngine === null) {
|
|
||||||
this.rtcEngine = rtcEngine
|
|
||||||
this.rtcEngine.onSipNetworkConnected(($network) => {
|
|
||||||
this.events.emit('connected')
|
|
||||||
this.network = $network
|
|
||||||
this.network.onIncomingCall((remoteCall) => {
|
|
||||||
if (this.network !== null && this.currentCall === null) {
|
|
||||||
this.currentCall = remoteCall
|
|
||||||
this.currentCall.onEnded(() => {
|
|
||||||
this.events.emit('ended', this.currentCall.endedReason)
|
|
||||||
}).onRemoteMedia((remoteMediaStream) => {
|
|
||||||
this.remoteMedia = remoteMediaStream
|
|
||||||
this.events.emit('remoteMedia', remoteMediaStream)
|
|
||||||
}).onRemoteMediaEnded(() => {
|
|
||||||
this.events.emit('remoteMediaEnded')
|
|
||||||
}).onError((err) => {
|
|
||||||
console.debug(err)
|
|
||||||
this.end()
|
|
||||||
this.events.emit('ended', err.message)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
this.events.emit('incoming')
|
|
||||||
})
|
|
||||||
}).onSipNetworkDisconnected(() => {
|
|
||||||
this.events.emit('disconnected')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isAvailable () {
|
|
||||||
return this.network !== null && this.network.isConnected
|
|
||||||
}
|
|
||||||
|
|
||||||
hasRunningCall () {
|
|
||||||
return this.currentCall !== null
|
|
||||||
}
|
|
||||||
|
|
||||||
createLocalMedia (localMedia) {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
const localMediaBuilder = cdk.media.create()
|
|
||||||
if (localMedia === LocalMedia.audioOnly || localMedia === LocalMedia.audioVideo ||
|
|
||||||
localMedia === LocalMedia.audioScreen) {
|
|
||||||
localMediaBuilder.enableMicrophone()
|
|
||||||
}
|
|
||||||
if (localMedia === LocalMedia.audioVideo || localMedia === LocalMedia.videoOnly) {
|
|
||||||
localMediaBuilder.enableCamera()
|
|
||||||
} else if (localMedia === LocalMedia.audioScreen || localMedia === LocalMedia.screenOnly) {
|
|
||||||
localMediaBuilder.enableScreen()
|
|
||||||
}
|
|
||||||
localMediaBuilder.build().then((localMediaStream) => {
|
|
||||||
this.localMedia = localMediaStream
|
|
||||||
resolve(this.localMedia)
|
|
||||||
}).catch((err) => {
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
start (peer, localMediaStream) {
|
|
||||||
if (this.network !== null && this.currentCall === null) {
|
|
||||||
peer = peer.replace(/(\s|\+)/, '')
|
|
||||||
this.currentCall = this.network.call(peer, {
|
|
||||||
localMediaStream: localMediaStream
|
|
||||||
})
|
|
||||||
this.currentCall.onEnded(() => {
|
|
||||||
this.events.emit('ended', this.currentCall.endedReason)
|
|
||||||
this.end()
|
|
||||||
}).onRemoteMedia((remoteMediaStream) => {
|
|
||||||
this.remoteMedia = remoteMediaStream
|
|
||||||
this.events.emit('remoteMedia', remoteMediaStream)
|
|
||||||
}).onRemoteMediaEnded(() => {
|
|
||||||
this.events.emit('remoteMediaEnded')
|
|
||||||
}).onAccepted(() => {
|
|
||||||
this.events.emit('accepted')
|
|
||||||
}).onPending(() => {
|
|
||||||
this.events.emit('pending')
|
|
||||||
}).onRingingStart(() => {
|
|
||||||
this.events.emit('ringingStart')
|
|
||||||
}).onRingingStop(() => {
|
|
||||||
this.events.emit('ringingStop')
|
|
||||||
}).onError((err) => {
|
|
||||||
console.debug(err)
|
|
||||||
this.end()
|
|
||||||
this.events.emit('ended', err.message)
|
|
||||||
})
|
|
||||||
} else if (this.network !== null) {
|
|
||||||
throw new CallAlreadyExists()
|
|
||||||
} else {
|
|
||||||
throw new NetworkNotConnected(this.networkTag) // TODO: "this.networkTag" is not defined. We should get this variable from somewhere
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getNumber () {
|
|
||||||
if (this.currentCall !== null) {
|
|
||||||
return this.currentCall.peer
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onIncoming (listener) {
|
|
||||||
this.events.on('incoming', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onAccepted (listener) {
|
|
||||||
this.events.on('accepted', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onPending (listener) {
|
|
||||||
this.events.on('pending', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onRingingStart (listener) {
|
|
||||||
this.events.on('ringingStart', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onRingingStop (listener) {
|
|
||||||
this.events.on('ringingStop', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onRemoteMedia (listener) {
|
|
||||||
this.events.on('remoteMedia', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onRemoteMediaEnded (listener) {
|
|
||||||
this.events.on('remoteMediaEnded', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onEnded (listener) {
|
|
||||||
this.events.on('ended', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onConnected (listener) {
|
|
||||||
this.events.on('connected', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onDisconnected (listener) {
|
|
||||||
this.events.on('disconnected', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
accept (localMediaStream) {
|
|
||||||
if (this.currentCall !== null) {
|
|
||||||
this.currentCall.accept(localMediaStream).then(() => {
|
|
||||||
this.events.emit('locallyAccepted')
|
|
||||||
}).catch((err) => {
|
|
||||||
console.debug(err)
|
|
||||||
this.end()
|
|
||||||
this.events.emit('ended', err.message)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
throw new Error('Remote call does not exist')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hangUp () {
|
|
||||||
this.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
end () {
|
|
||||||
if (this.currentCall !== null) {
|
|
||||||
this.currentCall.end()
|
|
||||||
this.currentCall = null
|
|
||||||
}
|
|
||||||
this.endMedia()
|
|
||||||
}
|
|
||||||
|
|
||||||
endMedia () {
|
|
||||||
if (this.localMedia !== null) {
|
|
||||||
this.localMedia.stop()
|
|
||||||
this.localMedia = null
|
|
||||||
}
|
|
||||||
if (this.remoteMedia !== null) {
|
|
||||||
this.remoteMedia.stop()
|
|
||||||
this.remoteMedia = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
disableAudio () {
|
|
||||||
if (this.currentCall !== null) {
|
|
||||||
this.currentCall.disableAudio()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enableAudio () {
|
|
||||||
if (this.currentCall !== null) {
|
|
||||||
this.currentCall.enableAudio()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
disableVideo () {
|
|
||||||
if (this.currentCall !== null) {
|
|
||||||
this.currentCall.disableVideo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enableVideo () {
|
|
||||||
if (this.currentCall !== null) {
|
|
||||||
this.currentCall.enableVideo()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sendDTMF (char) {
|
|
||||||
if (this.currentCall !== null) {
|
|
||||||
this.currentCall.sendDTMF(char)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getCall () {
|
|
||||||
if (this.currentCall !== null) {
|
|
||||||
return this.currentCall
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getLocalMediaId () {
|
|
||||||
if (this.localMedia !== null) {
|
|
||||||
return this.localMedia.getStream().id
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
getLocalMediaStream () {
|
|
||||||
if (this.localMedia !== null) {
|
|
||||||
return this.localMedia.getStream()
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
getRemoteMediaId () {
|
|
||||||
if (this.remoteMedia !== null) {
|
|
||||||
return this.remoteMedia.getStream().id
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
getRemoteMediaStream () {
|
|
||||||
if (this.remoteMedia !== null) {
|
|
||||||
return this.remoteMedia.getStream()
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
hasRemoteVideo () {
|
|
||||||
if (this.remoteMedia !== null) {
|
|
||||||
return this.remoteMedia.hasVideo()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
hasLocalVideo () {
|
|
||||||
if (this.localMedia !== null) {
|
|
||||||
return this.localMedia.hasVideo()
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
static getInstance () {
|
|
||||||
if (rtcEngineCallInstance === null) {
|
|
||||||
rtcEngineCallInstance = new RtcEngineCall()
|
|
||||||
}
|
|
||||||
return rtcEngineCallInstance
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
install (Vue) {
|
|
||||||
Vue.$call = RtcEngineCall.getInstance()
|
|
||||||
Vue.$call.setRtcEngine(Vue.$rtcEngine)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,163 +0,0 @@
|
|||||||
|
|
||||||
import EventEmitter from 'events'
|
|
||||||
|
|
||||||
let conferencePlugin = null
|
|
||||||
|
|
||||||
export class ConferencePlugin {
|
|
||||||
constructor () {
|
|
||||||
this.events = new EventEmitter()
|
|
||||||
this.rtcEngine = null
|
|
||||||
this.conference = null
|
|
||||||
this.localMediaStream = null
|
|
||||||
}
|
|
||||||
|
|
||||||
setRtcEngine (rtcEngine) {
|
|
||||||
if (this.rtcEngine === null) {
|
|
||||||
this.rtcEngine = rtcEngine
|
|
||||||
this.rtcEngine.onConferenceNetworkConnected((network) => {
|
|
||||||
this.events.emit('connected')
|
|
||||||
network
|
|
||||||
.onConferenceParticipantJoined((participant) => {
|
|
||||||
this.events.emit('participantJoined', participant)
|
|
||||||
})
|
|
||||||
.onConferenceParticipantLeft((participant) => {
|
|
||||||
this.events.emit('participantLeft', participant)
|
|
||||||
})
|
|
||||||
.onConferenceEvent((event) => {
|
|
||||||
this.events.emit('conferenceEvent', event)
|
|
||||||
})
|
|
||||||
.onConferenceMessage((message) => {
|
|
||||||
this.events.emit('conferenceMessage', message)
|
|
||||||
})
|
|
||||||
.onConferenceFile((file) => {
|
|
||||||
this.events.emit('conferenceFile', file)
|
|
||||||
})
|
|
||||||
.onConferenceEnded(() => {
|
|
||||||
this.events.emit('conferenceEnded')
|
|
||||||
})
|
|
||||||
}).onConferenceNetworkDisconnected(() => {
|
|
||||||
this.events.emit('disconnected')
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getNetwork () {
|
|
||||||
return this.rtcEngine.getConferenceNetwork()
|
|
||||||
}
|
|
||||||
|
|
||||||
async joinConference (options) {
|
|
||||||
options.localMediaStream = this.getLocalMediaStream()
|
|
||||||
this.conference = await this.getNetwork().joinConference(options)
|
|
||||||
return this.conference
|
|
||||||
}
|
|
||||||
|
|
||||||
async changeConferenceMedia () {
|
|
||||||
await this.getNetwork().changeConferenceMedia({
|
|
||||||
localMediaStream: this.getLocalMediaStream()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
async leaveConference () {
|
|
||||||
await this.getNetwork().leaveConference()
|
|
||||||
}
|
|
||||||
|
|
||||||
onConferenceParticipantJoined (listener) {
|
|
||||||
this.events.on('participantJoined', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onConferenceParticipantLeft (listener) {
|
|
||||||
this.events.on('participantLeft', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onConferenceEvent (listener) {
|
|
||||||
this.events.on('conferenceEvent', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onConferenceMessage (listener) {
|
|
||||||
this.events.on('conferenceMessage', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onConferenceFile (listener) {
|
|
||||||
this.events.on('conferenceFile', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onConferenceEnded (listener) {
|
|
||||||
this.events.on('conferenceEnded', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onError (listener) {
|
|
||||||
this.events.on('error', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
static getInstance () {
|
|
||||||
if (conferencePlugin === null) {
|
|
||||||
conferencePlugin = new ConferencePlugin()
|
|
||||||
}
|
|
||||||
return conferencePlugin
|
|
||||||
}
|
|
||||||
|
|
||||||
setLocalMediaStream (localMediaStream) {
|
|
||||||
this.removeLocalMediaStream()
|
|
||||||
this.localMediaStream = localMediaStream
|
|
||||||
this.localMediaStream.onEnded(() => {
|
|
||||||
this.events.emit('localMediaStreamEnded', this.localMediaStream)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getLocalMediaStream () {
|
|
||||||
return this.localMediaStream
|
|
||||||
}
|
|
||||||
|
|
||||||
onLocalMediaStreamEnded (listener) {
|
|
||||||
this.events.on('localMediaStreamEnded', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
hasLocalMediaStream () {
|
|
||||||
return this.localMediaStream !== null
|
|
||||||
}
|
|
||||||
|
|
||||||
removeLocalMediaStream () {
|
|
||||||
if (this.hasLocalMediaStream()) {
|
|
||||||
this.getLocalMediaStream().stop()
|
|
||||||
}
|
|
||||||
this.localMediaStream = null
|
|
||||||
}
|
|
||||||
|
|
||||||
getLocalMediaStreamNative () {
|
|
||||||
if (this.hasLocalMediaStream()) {
|
|
||||||
return this.getLocalMediaStream().getStream()
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
getLocalParticipant () {
|
|
||||||
if (this.conference !== null) {
|
|
||||||
return this.conference.getLocalParticipant()
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getRemoteParticipant (id) {
|
|
||||||
if (this.conference !== null) {
|
|
||||||
return this.conference.getRemoteParticipant(id)
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
install (Vue) {
|
|
||||||
Vue.$conference = ConferencePlugin.getInstance()
|
|
||||||
Vue.$conference.setRtcEngine(Vue.$rtcEngine)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,189 +0,0 @@
|
|||||||
|
|
||||||
import loadScript from 'load-script'
|
|
||||||
import EventEmitter from 'events'
|
|
||||||
|
|
||||||
const scriptId = 'cdk'
|
|
||||||
|
|
||||||
let rtcEnginePlugin = null
|
|
||||||
|
|
||||||
export class RtcEnginePlugin {
|
|
||||||
constructor ({
|
|
||||||
cdkScriptUrl = null,
|
|
||||||
webSocketUrl = null,
|
|
||||||
ngcpApiBaseUrl = null,
|
|
||||||
ngcpApiJwt = null
|
|
||||||
}) {
|
|
||||||
this.cdkScriptUrl = cdkScriptUrl
|
|
||||||
this.webSocketUrl = webSocketUrl
|
|
||||||
this.script = null
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @type {cdk.Client}
|
|
||||||
*/
|
|
||||||
this.client = null
|
|
||||||
this.sessionToken = null
|
|
||||||
this.ngcpApiJwt = ngcpApiJwt
|
|
||||||
this.ngcpApiBaseUrl = ngcpApiBaseUrl
|
|
||||||
this.events = new EventEmitter()
|
|
||||||
}
|
|
||||||
|
|
||||||
createMedia () {
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
return cdk.media.create()
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize () {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
Promise.resolve().then(() => {
|
|
||||||
return this.loadLibrary()
|
|
||||||
}).then(() => {
|
|
||||||
return this.createSession()
|
|
||||||
}).then(() => {
|
|
||||||
return this.connectClient()
|
|
||||||
}).then(() => {
|
|
||||||
resolve()
|
|
||||||
}).catch((err) => {
|
|
||||||
reject(err)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
setNgcpApiJwt (jwt) {
|
|
||||||
this.ngcpApiJwt = jwt
|
|
||||||
}
|
|
||||||
|
|
||||||
setNgcpApiBaseUrl (baseUrl) {
|
|
||||||
this.ngcpApiBaseUrl = baseUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
loadLibrary () {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (this.script === null) {
|
|
||||||
loadScript(this.cdkScriptUrl, {
|
|
||||||
attrs: {
|
|
||||||
id: scriptId
|
|
||||||
}
|
|
||||||
}, (err, script) => {
|
|
||||||
this.script = script
|
|
||||||
if (err) {
|
|
||||||
console.debug(err)
|
|
||||||
reject(new Error('Unable to load RTC:Engine client library'))
|
|
||||||
} else {
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
createSession () {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (this.ngcpApiJwt !== null && this.sessionToken === null) {
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
cdk.ngcp.setApiBaseUrl(this.ngcpApiBaseUrl)
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
cdk.ngcp.setApiJwt(this.ngcpApiJwt)
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
cdk.ngcp.createRTCEngineSession().then((sessionToken) => {
|
|
||||||
this.sessionToken = sessionToken
|
|
||||||
resolve()
|
|
||||||
}).catch((err) => {
|
|
||||||
console.error(err)
|
|
||||||
reject(new Error('Unable to create RTC:Engine session'))
|
|
||||||
})
|
|
||||||
} else if (this.ngcpApiJwt !== null && this.sessionToken !== null) {
|
|
||||||
resolve()
|
|
||||||
} else {
|
|
||||||
throw new Error('Can not create RTC:Engine session without a valid NGCP API JWT')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
connectClient () {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
if (this.client === null) {
|
|
||||||
// eslint-disable-next-line no-undef
|
|
||||||
this.client = new cdk.Client({
|
|
||||||
url: this.webSocketUrl,
|
|
||||||
userSession: this.sessionToken
|
|
||||||
})
|
|
||||||
this.client.onConnect(() => {
|
|
||||||
this.events.emit('connected')
|
|
||||||
try {
|
|
||||||
const conferenceNetwork = this.client.getNetworkByTag('conference')
|
|
||||||
conferenceNetwork.onConnect(() => {
|
|
||||||
this.events.emit('conference-network-connected', conferenceNetwork)
|
|
||||||
}).onDisconnect(() => {
|
|
||||||
this.events.emit('conference-network-disconnected', conferenceNetwork)
|
|
||||||
})
|
|
||||||
const sipNetwork = this.client.getNetworkByTag('sip')
|
|
||||||
sipNetwork.onConnect(() => {
|
|
||||||
this.events.emit('sip-network-connected', sipNetwork)
|
|
||||||
}).onDisconnect(() => {
|
|
||||||
this.events.emit('sip-network-disconnected', sipNetwork)
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
reject(new Error('Unable to connect to a specific network by RTCEngine client'))
|
|
||||||
}
|
|
||||||
resolve()
|
|
||||||
})
|
|
||||||
this.client.onDisconnect(() => {
|
|
||||||
reject(new Error('Unable to connect RTCEngine client'))
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onSipNetworkConnected (listener) {
|
|
||||||
this.events.on('sip-network-connected', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onSipNetworkDisconnected (listener) {
|
|
||||||
this.events.on('sip-network-disconnected', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onConferenceNetworkConnected (listener) {
|
|
||||||
this.events.on('conference-network-connected', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onConferenceNetworkDisconnected (listener) {
|
|
||||||
this.events.on('conference-network-disconnected', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onConnected (listener) {
|
|
||||||
this.events.on('connected', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
onDisconnected (listener) {
|
|
||||||
this.events.on('disconnected', listener)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
getConferenceNetwork () {
|
|
||||||
return this.client.getNetworkByTag('conference')
|
|
||||||
}
|
|
||||||
|
|
||||||
static getInstance (rtcConfig = {}) {
|
|
||||||
if (rtcEnginePlugin === null) {
|
|
||||||
rtcEnginePlugin = new RtcEnginePlugin(rtcConfig)
|
|
||||||
}
|
|
||||||
return rtcEnginePlugin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function getVuePlugin (rtcConfig) {
|
|
||||||
return {
|
|
||||||
install (Vue) {
|
|
||||||
Vue.$rtcEngine = RtcEnginePlugin.getInstance(rtcConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,54 @@
|
|||||||
|
import loadScript from 'load-script'
|
||||||
|
|
||||||
|
const RTC_ENGINE_LIBRARY_ID = 'ngcp-rtc-engine-library'
|
||||||
|
|
||||||
|
export const LocalMedia = {
|
||||||
|
audioOnly: 'audioOnly',
|
||||||
|
audioVideo: 'audioVideo',
|
||||||
|
videoOnly: 'videoOnly',
|
||||||
|
audioScreen: 'audioScreen',
|
||||||
|
screenOnly: 'screenOnly'
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadRtcEngineLibrary ({ scriptUrl }) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const script = document.getElementById(RTC_ENGINE_LIBRARY_ID)
|
||||||
|
if (!script) {
|
||||||
|
loadScript(scriptUrl, {
|
||||||
|
attrs: {
|
||||||
|
id: RTC_ENGINE_LIBRARY_ID
|
||||||
|
}
|
||||||
|
}, (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(new Error('Unable to load RTC:Engine client library'))
|
||||||
|
} else {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unloadRtcEngineLibrary () {
|
||||||
|
const script = document.getElementById(RTC_ENGINE_LIBRARY_ID)
|
||||||
|
if (script) {
|
||||||
|
script.remove()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function rtcEngineCreateMedia (localMedia) {
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
const localMediaBuilder = cdk.media.create()
|
||||||
|
if (localMedia === LocalMedia.audioOnly || localMedia === LocalMedia.audioVideo ||
|
||||||
|
localMedia === LocalMedia.audioScreen) {
|
||||||
|
localMediaBuilder.enableMicrophone()
|
||||||
|
}
|
||||||
|
if (localMedia === LocalMedia.audioVideo || localMedia === LocalMedia.videoOnly) {
|
||||||
|
localMediaBuilder.enableCamera()
|
||||||
|
} else if (localMedia === LocalMedia.audioScreen || localMedia === LocalMedia.screenOnly) {
|
||||||
|
localMediaBuilder.enableScreen()
|
||||||
|
}
|
||||||
|
return await localMediaBuilder.build()
|
||||||
|
}
|
@ -1,374 +0,0 @@
|
|||||||
|
|
||||||
import _ from 'lodash'
|
|
||||||
import Vue from 'vue'
|
|
||||||
import {
|
|
||||||
normalizeDestination
|
|
||||||
} from '../filters/number-format'
|
|
||||||
import {
|
|
||||||
startCase
|
|
||||||
} from '../filters/string'
|
|
||||||
import {
|
|
||||||
i18n
|
|
||||||
} from 'src/boot/i18n'
|
|
||||||
|
|
||||||
export var CallState = {
|
|
||||||
input: 'input',
|
|
||||||
initiating: 'initiating',
|
|
||||||
ringing: 'ringing',
|
|
||||||
incoming: 'incoming',
|
|
||||||
established: 'established',
|
|
||||||
ended: 'ended'
|
|
||||||
}
|
|
||||||
export var CallStateTitle = {
|
|
||||||
get input () { return i18n.t('Start new call') },
|
|
||||||
get initiating () { return i18n.t('Calling') },
|
|
||||||
get ringing () { return i18n.t('Ringing at') },
|
|
||||||
get incoming () { return i18n.t('Incoming call from') },
|
|
||||||
get established () { return i18n.t('In call with') },
|
|
||||||
get ended () { return i18n.t('Call ended') }
|
|
||||||
}
|
|
||||||
|
|
||||||
export var MediaType = {
|
|
||||||
audioOnly: 'audioOnly',
|
|
||||||
audioVideo: 'audioVideo',
|
|
||||||
audioScreen: 'audioScreen'
|
|
||||||
}
|
|
||||||
|
|
||||||
export const errorVisibilityTimeout = 5000
|
|
||||||
export const reinitializeTimeout = 5000
|
|
||||||
|
|
||||||
function handleUserMediaError (context, err) {
|
|
||||||
const errName = _.get(err, 'name', null)
|
|
||||||
const errMessage = _.get(err, 'message', null)
|
|
||||||
if (errName === 'UserMediaError' && errMessage === 'Permission denied') {
|
|
||||||
context.commit('endCall', 'userMediaPermissionDenied')
|
|
||||||
}
|
|
||||||
if (errMessage === 'plugin not detected') {
|
|
||||||
context.commit('desktopSharingInstall')
|
|
||||||
context.commit('hangUpCall')
|
|
||||||
} else if (errMessage === 'PermissionDenied') {
|
|
||||||
context.commit('endCall', 'desktopSharingPermissionDenied')
|
|
||||||
} else {
|
|
||||||
context.commit('endCall', errName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
namespaced: true,
|
|
||||||
state: {
|
|
||||||
callEnabled: false,
|
|
||||||
endedReason: null,
|
|
||||||
callState: CallState.input,
|
|
||||||
number: '',
|
|
||||||
numberInput: '',
|
|
||||||
localMediaStream: null,
|
|
||||||
remoteMediaStream: null,
|
|
||||||
caller: false,
|
|
||||||
callee: false,
|
|
||||||
desktopSharingInstall: false,
|
|
||||||
microphoneEnabled: true,
|
|
||||||
cameraEnabled: true,
|
|
||||||
remoteVolumeEnabled: true,
|
|
||||||
maximized: false,
|
|
||||||
dialpadOpened: false
|
|
||||||
},
|
|
||||||
getters: {
|
|
||||||
isCallEnabled (state) {
|
|
||||||
return state.callEnabled
|
|
||||||
},
|
|
||||||
endedReason (state) {
|
|
||||||
return state.endedReason
|
|
||||||
},
|
|
||||||
callNumber (state) {
|
|
||||||
return state.number
|
|
||||||
},
|
|
||||||
callNumberInput (state) {
|
|
||||||
return state.numberInput
|
|
||||||
},
|
|
||||||
// isNetworkConnected(state) {
|
|
||||||
// return state.initializationState === RequestState.succeeded;
|
|
||||||
// },
|
|
||||||
// isCallAvailable(state, getters) {
|
|
||||||
// return getters.isNetworkConnected;
|
|
||||||
// },
|
|
||||||
// isCallInitializing(state, getters, rootState, rootGetters) {
|
|
||||||
// return state.initializationState === RequestState.requesting ||
|
|
||||||
// rootGetters['user/userDataRequesting'];
|
|
||||||
// },
|
|
||||||
// isCallInitialized(state) {
|
|
||||||
// return state.initializationState === RequestState.succeeded
|
|
||||||
// },
|
|
||||||
// hasCallInitError(state) {
|
|
||||||
// return state.initializationError !== null;
|
|
||||||
// },
|
|
||||||
// callInitError(state) {
|
|
||||||
// return state.initializationError;
|
|
||||||
// },
|
|
||||||
isCallInitializing (state, getters, rootState, rootGetters) {
|
|
||||||
return rootGetters['user/isRtcEngineInitializing']
|
|
||||||
},
|
|
||||||
isPreparing (state) {
|
|
||||||
return state.callState === CallState.input
|
|
||||||
},
|
|
||||||
isInitiating (state) {
|
|
||||||
return state.callState === CallState.initiating
|
|
||||||
},
|
|
||||||
isIncoming (state) {
|
|
||||||
return state.callState === CallState.incoming
|
|
||||||
},
|
|
||||||
isTrying (state) {
|
|
||||||
return state.callState === CallState.initiating ||
|
|
||||||
state.callState === CallState.ringing
|
|
||||||
},
|
|
||||||
isRinging (state) {
|
|
||||||
return state.callState === CallState.ringing
|
|
||||||
},
|
|
||||||
isCalling (state) {
|
|
||||||
return state.callState === CallState.initiating ||
|
|
||||||
state.callState === CallState.ringing ||
|
|
||||||
state.callState === CallState.established ||
|
|
||||||
state.callState === CallState.incoming ||
|
|
||||||
state.callState === CallState.ended
|
|
||||||
},
|
|
||||||
isEstablished (state) {
|
|
||||||
return state.callState === CallState.established
|
|
||||||
},
|
|
||||||
isEnded (state) {
|
|
||||||
return state.callState === CallState.ended
|
|
||||||
},
|
|
||||||
hasRtcEngineCapability (state, getters, rootState, rootGetters) {
|
|
||||||
return rootGetters['user/hasRtcEngineCapability']
|
|
||||||
},
|
|
||||||
hasRtcEngineCapabilityEnabled (state, getters, rootState, rootGetters) {
|
|
||||||
return rootGetters['user/hasRtcEngineCapabilityEnabled']
|
|
||||||
},
|
|
||||||
hasRemoteVideo (state) {
|
|
||||||
if (state.remoteMediaStream !== null) {
|
|
||||||
return Vue.$call.hasRemoteVideo()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hasLocalVideo (state) {
|
|
||||||
if (state.localMediaStream !== null) {
|
|
||||||
return Vue.$call.hasLocalVideo()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hasVideo (state, getters) {
|
|
||||||
return getters.hasLocalVideo || getters.hasRemoteVideo
|
|
||||||
},
|
|
||||||
isAudioEnabled (state) {
|
|
||||||
return state.audioEnabled
|
|
||||||
},
|
|
||||||
isVideoEnabled (state) {
|
|
||||||
return state.videoEnabled
|
|
||||||
},
|
|
||||||
isCaller (state) {
|
|
||||||
return state.caller
|
|
||||||
},
|
|
||||||
isCallee (state) {
|
|
||||||
return state.callee
|
|
||||||
},
|
|
||||||
callState (state) {
|
|
||||||
return state.callState
|
|
||||||
},
|
|
||||||
desktopSharingInstall (state) {
|
|
||||||
return state.desktopSharingInstall
|
|
||||||
},
|
|
||||||
localMediaStream (state) {
|
|
||||||
if (state.localMediaStream !== null) {
|
|
||||||
return Vue.$call.getLocalMediaStream()
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
},
|
|
||||||
remoteMediaStream (state) {
|
|
||||||
if (state.remoteMediaStream !== null) {
|
|
||||||
return Vue.$call.getRemoteMediaStream()
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
},
|
|
||||||
isMicrophoneEnabled (state) {
|
|
||||||
return state.microphoneEnabled
|
|
||||||
},
|
|
||||||
isCameraEnabled (state) {
|
|
||||||
return state.cameraEnabled
|
|
||||||
},
|
|
||||||
isRemoteVolumeEnabled (state) {
|
|
||||||
return state.remoteVolumeEnabled
|
|
||||||
},
|
|
||||||
isMaximized (state) {
|
|
||||||
return state.maximized
|
|
||||||
},
|
|
||||||
isDialpadOpened (state) {
|
|
||||||
return state.dialpadOpened
|
|
||||||
},
|
|
||||||
callNumberFormatted (state, getters) {
|
|
||||||
return normalizeDestination(getters.callNumber)
|
|
||||||
},
|
|
||||||
callEndedReasonFormatted (state, getters) {
|
|
||||||
return startCase(getters.endedReason)
|
|
||||||
},
|
|
||||||
callStateTitle (state) {
|
|
||||||
return CallStateTitle[state.callState]
|
|
||||||
},
|
|
||||||
callStateSubtitle (state, getters) {
|
|
||||||
if (state.callState === CallState.initiating ||
|
|
||||||
state.callState === CallState.ringing ||
|
|
||||||
state.callState === CallState.incoming ||
|
|
||||||
state.callState === CallState.established) {
|
|
||||||
return getters.callNumberFormatted
|
|
||||||
} else if (state.callState === CallState.ended) {
|
|
||||||
return getters.callEndedReasonFormatted
|
|
||||||
} else {
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mutations: {
|
|
||||||
numberInputChanged (state, numberInput) {
|
|
||||||
state.numberInput = numberInput
|
|
||||||
},
|
|
||||||
inputNumber (state) {
|
|
||||||
state.callState = CallState.input
|
|
||||||
state.number = ''
|
|
||||||
state.numberInput = ''
|
|
||||||
state.endedReason = null
|
|
||||||
},
|
|
||||||
startCalling (state, number) {
|
|
||||||
state.number = number
|
|
||||||
state.callState = CallState.initiating
|
|
||||||
state.caller = true
|
|
||||||
state.callee = false
|
|
||||||
state.endedReason = null
|
|
||||||
},
|
|
||||||
localMediaSuccess (state) {
|
|
||||||
state.localMediaStream = Vue.$call.getLocalMediaId()
|
|
||||||
},
|
|
||||||
startRinging (state) {
|
|
||||||
state.callState = CallState.ringing
|
|
||||||
},
|
|
||||||
stopRinging (state) {
|
|
||||||
state.callState = CallState.established
|
|
||||||
},
|
|
||||||
establishCall (state) {
|
|
||||||
state.remoteMediaStream = Vue.$call.getRemoteMediaId()
|
|
||||||
state.callState = CallState.established
|
|
||||||
state.microphoneEnabled = true
|
|
||||||
state.cameraEnabled = true
|
|
||||||
state.remoteVolumeEnabled = true
|
|
||||||
},
|
|
||||||
incomingCall (state, options) {
|
|
||||||
state.callState = CallState.incoming
|
|
||||||
state.number = options.number
|
|
||||||
state.callee = true
|
|
||||||
state.caller = false
|
|
||||||
state.endedReason = null
|
|
||||||
},
|
|
||||||
hangUpCall (state) {
|
|
||||||
state.callState = CallState.input
|
|
||||||
state.number = ''
|
|
||||||
state.numberInput = ''
|
|
||||||
state.endedReason = null
|
|
||||||
Vue.$call.hangUp()
|
|
||||||
},
|
|
||||||
endCall (state, reason) {
|
|
||||||
if (state.endedReason === null) {
|
|
||||||
state.callState = CallState.ended
|
|
||||||
state.endedReason = reason
|
|
||||||
}
|
|
||||||
Vue.$call.end()
|
|
||||||
},
|
|
||||||
sendDTMF (state, value) {
|
|
||||||
state.dtmf = value
|
|
||||||
},
|
|
||||||
toggleMicrophone (state) {
|
|
||||||
state.microphoneEnabled = !state.microphoneEnabled
|
|
||||||
},
|
|
||||||
toggleCamera (state) {
|
|
||||||
state.cameraEnabled = !state.cameraEnabled
|
|
||||||
},
|
|
||||||
toggleRemoteVolume (state) {
|
|
||||||
state.remoteVolumeEnabled = !state.remoteVolumeEnabled
|
|
||||||
},
|
|
||||||
maximize (state) {
|
|
||||||
state.dialpadOpened = false
|
|
||||||
state.maximized = true
|
|
||||||
},
|
|
||||||
minimize (state) {
|
|
||||||
state.dialpadOpened = false
|
|
||||||
state.maximized = false
|
|
||||||
},
|
|
||||||
toggleDialpad (state) {
|
|
||||||
state.dialpadOpened = !state.dialpadOpened
|
|
||||||
},
|
|
||||||
enableCall (state) {
|
|
||||||
state.callEnabled = true
|
|
||||||
},
|
|
||||||
disableCall (state) {
|
|
||||||
state.callEnabled = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
start (context, localMedia) {
|
|
||||||
const number = context.getters.callNumberInput.replaceAll('(', '')
|
|
||||||
.replaceAll(')', '')
|
|
||||||
.replaceAll(' ', '')
|
|
||||||
.replaceAll('-', '')
|
|
||||||
context.commit('startCalling', number)
|
|
||||||
Promise.resolve().then(() => {
|
|
||||||
return Vue.$call.createLocalMedia(localMedia)
|
|
||||||
}).then((localMediaStream) => {
|
|
||||||
context.commit('localMediaSuccess')
|
|
||||||
Vue.$call.onRingingStart(() => {
|
|
||||||
context.commit('startRinging')
|
|
||||||
}).onRingingStop(() => {
|
|
||||||
context.commit('stopRinging')
|
|
||||||
}).start(number, localMediaStream)
|
|
||||||
}).catch((err) => {
|
|
||||||
Vue.$call.end()
|
|
||||||
handleUserMediaError(context, err)
|
|
||||||
setTimeout(() => {
|
|
||||||
context.commit('inputNumber')
|
|
||||||
}, errorVisibilityTimeout)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
accept (context, localMedia) {
|
|
||||||
Vue.$call.createLocalMedia(localMedia).then((localMediaStream) => {
|
|
||||||
Vue.$call.accept(localMediaStream)
|
|
||||||
context.commit('localMediaSuccess')
|
|
||||||
}).catch((err) => {
|
|
||||||
Vue.$call.end()
|
|
||||||
handleUserMediaError(context, err)
|
|
||||||
setTimeout(() => {
|
|
||||||
context.commit('inputNumber')
|
|
||||||
}, errorVisibilityTimeout)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
end (context) {
|
|
||||||
Vue.$call.end()
|
|
||||||
context.commit('hangUpCall')
|
|
||||||
},
|
|
||||||
sendDTMF (context, value) {
|
|
||||||
if (Vue.$call.hasRunningCall()) {
|
|
||||||
Vue.$call.sendDTMF(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
toggleMicrophone (context) {
|
|
||||||
if (context.getters.isMicrophoneEnabled) {
|
|
||||||
Vue.$call.disableAudio()
|
|
||||||
} else {
|
|
||||||
Vue.$call.enableAudio()
|
|
||||||
}
|
|
||||||
context.commit('toggleMicrophone')
|
|
||||||
},
|
|
||||||
toggleCamera (context) {
|
|
||||||
if (context.getters.isCameraEnabled) {
|
|
||||||
Vue.$call.disableVideo()
|
|
||||||
} else {
|
|
||||||
Vue.$call.enableVideo()
|
|
||||||
}
|
|
||||||
context.commit('toggleCamera')
|
|
||||||
},
|
|
||||||
toggleRemoteVolume (context) {
|
|
||||||
context.commit('toggleRemoteVolume')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,73 @@
|
|||||||
|
import {
|
||||||
|
callGetLocalMediaStreamId,
|
||||||
|
callAccept,
|
||||||
|
callEnd,
|
||||||
|
callStart,
|
||||||
|
callAddCamera,
|
||||||
|
callAddScreen,
|
||||||
|
callRemoveVideo,
|
||||||
|
callHasLocalVideo,
|
||||||
|
callToggleMicrophone,
|
||||||
|
callIsMuted,
|
||||||
|
callSendDTMF,
|
||||||
|
callToggleRemoteAudio,
|
||||||
|
callIsRemoteAudioMuted, callHasLocalScreen, callHasLocalCamera
|
||||||
|
} from 'src/api/ngcp-call'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
async start (context, localMedia) {
|
||||||
|
const number = context.getters.callNumberInput.replaceAll('(', '')
|
||||||
|
.replaceAll(')', '')
|
||||||
|
.replaceAll(' ', '')
|
||||||
|
.replaceAll('-', '')
|
||||||
|
context.commit('startCalling', number)
|
||||||
|
await callStart({
|
||||||
|
number,
|
||||||
|
localMedia
|
||||||
|
})
|
||||||
|
context.commit('localMediaSuccess', callGetLocalMediaStreamId())
|
||||||
|
},
|
||||||
|
async accept (context, localMedia) {
|
||||||
|
await callAccept({
|
||||||
|
localMedia
|
||||||
|
})
|
||||||
|
context.commit('localMediaSuccess', callGetLocalMediaStreamId())
|
||||||
|
},
|
||||||
|
async toggleMicrophone (context) {
|
||||||
|
callToggleMicrophone()
|
||||||
|
context.commit('toggleMicrophone', !callIsMuted())
|
||||||
|
},
|
||||||
|
async toggleCamera (context) {
|
||||||
|
if (!callHasLocalVideo() || callHasLocalScreen()) {
|
||||||
|
await callAddCamera()
|
||||||
|
context.commit('disableVideo')
|
||||||
|
context.commit('enableCamera')
|
||||||
|
} else {
|
||||||
|
await callRemoveVideo()
|
||||||
|
context.commit('disableVideo')
|
||||||
|
}
|
||||||
|
context.commit('localMediaSuccess', callGetLocalMediaStreamId())
|
||||||
|
},
|
||||||
|
async toggleScreen (context) {
|
||||||
|
if (!callHasLocalVideo() || callHasLocalCamera()) {
|
||||||
|
await callAddScreen()
|
||||||
|
context.commit('disableVideo')
|
||||||
|
context.commit('enableScreen')
|
||||||
|
} else {
|
||||||
|
await callRemoveVideo()
|
||||||
|
context.commit('disableVideo')
|
||||||
|
}
|
||||||
|
context.commit('localMediaSuccess', callGetLocalMediaStreamId())
|
||||||
|
},
|
||||||
|
end (context) {
|
||||||
|
callEnd()
|
||||||
|
context.commit('hangUpCall')
|
||||||
|
},
|
||||||
|
sendDTMF (context, tone) {
|
||||||
|
callSendDTMF(tone)
|
||||||
|
},
|
||||||
|
toggleRemoteAudio (context) {
|
||||||
|
callToggleRemoteAudio()
|
||||||
|
context.commit('toggleRemoteAudio', !callIsRemoteAudioMuted())
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
import {
|
||||||
|
i18n
|
||||||
|
} from 'boot/i18n'
|
||||||
|
|
||||||
|
export const CallState = {
|
||||||
|
input: 'input',
|
||||||
|
initiating: 'initiating',
|
||||||
|
ringing: 'ringing',
|
||||||
|
incoming: 'incoming',
|
||||||
|
established: 'established',
|
||||||
|
ended: 'ended'
|
||||||
|
}
|
||||||
|
export const CallStateTitle = {
|
||||||
|
get input () { return i18n.t('Start new call') },
|
||||||
|
get initiating () { return i18n.t('Calling') },
|
||||||
|
get ringing () { return i18n.t('Ringing at') },
|
||||||
|
get incoming () { return i18n.t('Incoming call from') },
|
||||||
|
get established () { return i18n.t('In call with') },
|
||||||
|
get ended () { return i18n.t('Call ended') }
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MediaType = {
|
||||||
|
audioOnly: 'audioOnly',
|
||||||
|
audioVideo: 'audioVideo',
|
||||||
|
audioScreen: 'audioScreen'
|
||||||
|
}
|
||||||
|
|
||||||
|
export const errorVisibilityTimeout = 5000
|
||||||
|
export const reinitializeTimeout = 5000
|
@ -0,0 +1,138 @@
|
|||||||
|
import {
|
||||||
|
callGetLocalMediaStream,
|
||||||
|
callGetRemoteMediaStream,
|
||||||
|
callHasRemoteVideo
|
||||||
|
} from 'src/api/ngcp-call'
|
||||||
|
import {
|
||||||
|
normalizeDestination
|
||||||
|
} from 'src/filters/number-format'
|
||||||
|
import {
|
||||||
|
startCase
|
||||||
|
} from 'src/filters/string'
|
||||||
|
import {
|
||||||
|
CallState,
|
||||||
|
CallStateTitle
|
||||||
|
} from 'src/store/call/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
isCallEnabled (state) {
|
||||||
|
return state.callEnabled
|
||||||
|
},
|
||||||
|
endedReason (state) {
|
||||||
|
return state.endedReason
|
||||||
|
},
|
||||||
|
callNumber (state) {
|
||||||
|
return state.number
|
||||||
|
},
|
||||||
|
callNumberInput (state) {
|
||||||
|
return state.numberInput
|
||||||
|
},
|
||||||
|
isPreparing (state) {
|
||||||
|
return state.callState === CallState.input
|
||||||
|
},
|
||||||
|
isInitiating (state) {
|
||||||
|
return state.callState === CallState.initiating
|
||||||
|
},
|
||||||
|
isIncoming (state) {
|
||||||
|
return state.callState === CallState.incoming
|
||||||
|
},
|
||||||
|
isTrying (state) {
|
||||||
|
return state.callState === CallState.initiating ||
|
||||||
|
state.callState === CallState.ringing
|
||||||
|
},
|
||||||
|
isRinging (state) {
|
||||||
|
return state.callState === CallState.ringing
|
||||||
|
},
|
||||||
|
isCalling (state) {
|
||||||
|
return state.callState === CallState.initiating ||
|
||||||
|
state.callState === CallState.ringing ||
|
||||||
|
state.callState === CallState.established ||
|
||||||
|
state.callState === CallState.incoming ||
|
||||||
|
state.callState === CallState.ended
|
||||||
|
},
|
||||||
|
isEstablished (state) {
|
||||||
|
return state.callState === CallState.established
|
||||||
|
},
|
||||||
|
isEnded (state) {
|
||||||
|
return state.callState === CallState.ended
|
||||||
|
},
|
||||||
|
hasRtcEngineCapability (state, getters, rootState, rootGetters) {
|
||||||
|
return rootGetters['user/hasRtcEngineCapability']
|
||||||
|
},
|
||||||
|
hasRtcEngineCapabilityEnabled (state, getters, rootState, rootGetters) {
|
||||||
|
return rootGetters['user/hasRtcEngineCapabilityEnabled']
|
||||||
|
},
|
||||||
|
hasRemoteVideo (state) {
|
||||||
|
if (state.remoteMediaStream !== null) {
|
||||||
|
return callHasRemoteVideo()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasLocalVideo (state, getters) {
|
||||||
|
if (state.localMediaStream !== null) {
|
||||||
|
return getters.isScreenEnabled || getters.isCameraEnabled
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hasVideo (state, getters) {
|
||||||
|
return getters.hasLocalVideo || getters.hasRemoteVideo
|
||||||
|
},
|
||||||
|
isCaller (state) {
|
||||||
|
return state.caller
|
||||||
|
},
|
||||||
|
isCallee (state) {
|
||||||
|
return state.callee
|
||||||
|
},
|
||||||
|
callState (state) {
|
||||||
|
return state.callState
|
||||||
|
},
|
||||||
|
localMediaStream (state) {
|
||||||
|
if (state.localMediaStream) {
|
||||||
|
return callGetLocalMediaStream()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
remoteMediaStream (state) {
|
||||||
|
if (state.remoteMediaStream) {
|
||||||
|
return callGetRemoteMediaStream()
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
|
isMicrophoneEnabled (state) {
|
||||||
|
return state.microphoneEnabled
|
||||||
|
},
|
||||||
|
isCameraEnabled (state) {
|
||||||
|
return state.cameraEnabled
|
||||||
|
},
|
||||||
|
isScreenEnabled (state) {
|
||||||
|
return state.screenEnabled
|
||||||
|
},
|
||||||
|
isRemoteVolumeEnabled (state) {
|
||||||
|
return state.remoteAudioEnabled
|
||||||
|
},
|
||||||
|
isMaximized (state) {
|
||||||
|
return state.maximized
|
||||||
|
},
|
||||||
|
isDialpadOpened (state) {
|
||||||
|
return state.dialpadOpened
|
||||||
|
},
|
||||||
|
callNumberFormatted (state, getters) {
|
||||||
|
return normalizeDestination(getters.callNumber)
|
||||||
|
},
|
||||||
|
callEndedReasonFormatted (state, getters) {
|
||||||
|
return startCase(getters.endedReason)
|
||||||
|
},
|
||||||
|
callStateTitle (state) {
|
||||||
|
return CallStateTitle[state.callState]
|
||||||
|
},
|
||||||
|
callStateSubtitle (state, getters) {
|
||||||
|
if (state.callState === CallState.initiating ||
|
||||||
|
state.callState === CallState.ringing ||
|
||||||
|
state.callState === CallState.incoming ||
|
||||||
|
state.callState === CallState.established) {
|
||||||
|
return getters.callNumberFormatted
|
||||||
|
} else if (state.callState === CallState.ended) {
|
||||||
|
return getters.callEndedReasonFormatted
|
||||||
|
} else {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
import state from './state'
|
||||||
|
import getters from './getters'
|
||||||
|
import mutations from './mutations'
|
||||||
|
import actions from './actions'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
namespaced: true,
|
||||||
|
state: state,
|
||||||
|
getters: getters,
|
||||||
|
mutations: mutations,
|
||||||
|
actions: actions
|
||||||
|
}
|
@ -0,0 +1,110 @@
|
|||||||
|
import {
|
||||||
|
CallState
|
||||||
|
} from 'src/store/call/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
numberInputChanged (state, numberInput) {
|
||||||
|
state.numberInput = numberInput
|
||||||
|
},
|
||||||
|
inputNumber (state) {
|
||||||
|
state.callState = CallState.input
|
||||||
|
state.number = ''
|
||||||
|
state.numberInput = ''
|
||||||
|
state.endedReason = null
|
||||||
|
},
|
||||||
|
startCalling (state, number) {
|
||||||
|
state.number = number
|
||||||
|
state.callState = CallState.initiating
|
||||||
|
state.caller = true
|
||||||
|
state.callee = false
|
||||||
|
state.endedReason = null
|
||||||
|
},
|
||||||
|
localMediaSuccess (state, localMediaStreamId) {
|
||||||
|
state.localMediaStream = localMediaStreamId
|
||||||
|
},
|
||||||
|
startRinging (state) {
|
||||||
|
state.callState = CallState.ringing
|
||||||
|
},
|
||||||
|
stopRinging (state) {
|
||||||
|
state.callState = CallState.established
|
||||||
|
},
|
||||||
|
establishCall (state, {
|
||||||
|
mediaStreamId,
|
||||||
|
isLocalAudioMuted = false,
|
||||||
|
hasLocalCamera = false,
|
||||||
|
hasLocalScreen = false,
|
||||||
|
hasRemoteVideo = false,
|
||||||
|
isRemoteAudioMuted = false
|
||||||
|
}) {
|
||||||
|
state.remoteMediaStream = mediaStreamId
|
||||||
|
state.callState = CallState.established
|
||||||
|
state.microphoneEnabled = !isLocalAudioMuted
|
||||||
|
state.cameraEnabled = hasLocalCamera
|
||||||
|
state.screenEnabled = hasLocalScreen
|
||||||
|
state.remoteAudioEnabled = !isRemoteAudioMuted
|
||||||
|
state.remoteVideoEnabled = hasRemoteVideo
|
||||||
|
},
|
||||||
|
incomingCall (state, options) {
|
||||||
|
state.callState = CallState.incoming
|
||||||
|
state.number = options.number
|
||||||
|
state.callee = true
|
||||||
|
state.caller = false
|
||||||
|
state.endedReason = null
|
||||||
|
},
|
||||||
|
hangUpCall (state) {
|
||||||
|
state.callState = CallState.input
|
||||||
|
state.number = ''
|
||||||
|
state.numberInput = ''
|
||||||
|
state.endedReason = null
|
||||||
|
},
|
||||||
|
endCall (state, reason) {
|
||||||
|
if (state.endedReason === null) {
|
||||||
|
state.callState = CallState.ended
|
||||||
|
state.endedReason = reason
|
||||||
|
state.localMediaStream = null
|
||||||
|
state.remoteMediaStream = null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sendDTMF (state, value) {
|
||||||
|
state.dtmf = value
|
||||||
|
},
|
||||||
|
toggleMicrophone (state, enabled) {
|
||||||
|
state.microphoneEnabled = enabled
|
||||||
|
},
|
||||||
|
setCamera (state, enabled) {
|
||||||
|
state.cameraEnabled = enabled
|
||||||
|
},
|
||||||
|
enableCamera (state) {
|
||||||
|
state.cameraEnabled = true
|
||||||
|
},
|
||||||
|
enableScreen (state) {
|
||||||
|
state.screenEnabled = true
|
||||||
|
},
|
||||||
|
setScreen (state, enabled) {
|
||||||
|
state.screenEnabled = enabled
|
||||||
|
},
|
||||||
|
disableVideo (state) {
|
||||||
|
state.cameraEnabled = false
|
||||||
|
state.screenEnabled = false
|
||||||
|
},
|
||||||
|
toggleRemoteAudio (state, enabled) {
|
||||||
|
state.remoteAudioEnabled = enabled
|
||||||
|
},
|
||||||
|
maximize (state) {
|
||||||
|
state.dialpadOpened = false
|
||||||
|
state.maximized = true
|
||||||
|
},
|
||||||
|
minimize (state) {
|
||||||
|
state.dialpadOpened = false
|
||||||
|
state.maximized = false
|
||||||
|
},
|
||||||
|
toggleDialpad (state) {
|
||||||
|
state.dialpadOpened = !state.dialpadOpened
|
||||||
|
},
|
||||||
|
enableCall (state) {
|
||||||
|
state.callEnabled = true
|
||||||
|
},
|
||||||
|
disableCall (state) {
|
||||||
|
state.callEnabled = false
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
|
||||||
|
import {
|
||||||
|
CallState
|
||||||
|
} from 'src/store/call/common'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
callEnabled: false,
|
||||||
|
endedReason: null,
|
||||||
|
callState: CallState.input,
|
||||||
|
number: '',
|
||||||
|
numberInput: '',
|
||||||
|
localMediaStream: null,
|
||||||
|
remoteMediaStream: null,
|
||||||
|
caller: false,
|
||||||
|
callee: false,
|
||||||
|
microphoneEnabled: true,
|
||||||
|
cameraEnabled: false,
|
||||||
|
screenEnabled: false,
|
||||||
|
remoteAudioEnabled: true,
|
||||||
|
remoteVideoEnabled: true,
|
||||||
|
maximized: false,
|
||||||
|
dialpadOpened: false
|
||||||
|
}
|
Loading…
Reference in new issue