2022-01-26 04:49:38 +08:00
|
|
|
import { Tracker } from 'meteor/tracker';
|
|
|
|
import VoiceCallStates from '/imports/api/voice-call-states';
|
|
|
|
import CallStateOptions from '/imports/api/voice-call-states/utils/callStates';
|
|
|
|
import logger from '/imports/startup/client/logger';
|
|
|
|
import Auth from '/imports/ui/services/auth';
|
2022-02-01 03:30:38 +08:00
|
|
|
import {
|
|
|
|
DEFAULT_INPUT_DEVICE_ID,
|
|
|
|
reloadAudioElement
|
|
|
|
} from '/imports/api/audio/client/bridge/service';
|
2022-01-26 04:49:38 +08:00
|
|
|
|
|
|
|
const MEDIA = Meteor.settings.public.media;
|
|
|
|
const BASE_BRIDGE_NAME = 'base';
|
|
|
|
const CALL_TRANSFER_TIMEOUT = MEDIA.callTransferTimeout;
|
|
|
|
const TRANSFER_TONE = '1';
|
2022-02-01 03:30:38 +08:00
|
|
|
const MEDIA_TAG = MEDIA.mediaTag;
|
2022-01-26 04:49:38 +08:00
|
|
|
|
2017-07-24 22:15:46 +08:00
|
|
|
export default class BaseAudioBridge {
|
2017-10-12 05:04:10 +08:00
|
|
|
constructor(userData) {
|
|
|
|
this.userData = userData;
|
|
|
|
|
|
|
|
this.baseErrorCodes = {
|
2017-10-19 03:40:01 +08:00
|
|
|
INVALID_TARGET: 'INVALID_TARGET',
|
|
|
|
CONNECTION_ERROR: 'CONNECTION_ERROR',
|
|
|
|
REQUEST_TIMEOUT: 'REQUEST_TIMEOUT',
|
|
|
|
GENERIC_ERROR: 'GENERIC_ERROR',
|
2017-10-27 01:14:56 +08:00
|
|
|
MEDIA_ERROR: 'MEDIA_ERROR',
|
2018-06-27 21:56:03 +08:00
|
|
|
WEBRTC_NOT_SUPPORTED: 'WEBRTC_NOT_SUPPORTED',
|
2018-06-29 02:14:35 +08:00
|
|
|
ICE_NEGOTIATION_FAILED: 'ICE_NEGOTIATION_FAILED',
|
2017-10-12 05:04:10 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
this.baseCallStates = {
|
|
|
|
started: 'started',
|
|
|
|
ended: 'ended',
|
|
|
|
failed: 'failed',
|
2019-06-13 05:01:20 +08:00
|
|
|
reconnecting: 'reconnecting',
|
2019-08-03 05:32:42 +08:00
|
|
|
autoplayBlocked: 'autoplayBlocked',
|
2017-10-12 05:04:10 +08:00
|
|
|
};
|
2022-01-26 04:49:38 +08:00
|
|
|
|
|
|
|
this.bridgeName = BASE_BRIDGE_NAME;
|
2017-07-24 22:15:46 +08:00
|
|
|
}
|
|
|
|
|
2019-11-30 05:48:04 +08:00
|
|
|
getPeerConnection() {
|
|
|
|
console.error('The Bridge must implement getPeerConnection');
|
|
|
|
}
|
|
|
|
|
2017-07-24 22:15:46 +08:00
|
|
|
exitAudio() {
|
2017-10-12 05:04:10 +08:00
|
|
|
console.error('The Bridge must implement exitAudio');
|
2017-07-24 22:15:46 +08:00
|
|
|
}
|
|
|
|
|
2017-09-29 21:38:10 +08:00
|
|
|
joinAudio() {
|
2017-10-12 05:04:10 +08:00
|
|
|
console.error('The Bridge must implement joinAudio');
|
2017-07-24 22:15:46 +08:00
|
|
|
}
|
2017-10-19 03:40:01 +08:00
|
|
|
|
|
|
|
changeInputDevice() {
|
|
|
|
console.error('The Bridge must implement changeInputDevice');
|
|
|
|
}
|
|
|
|
|
2022-01-26 04:49:38 +08:00
|
|
|
sendDtmf() {
|
|
|
|
console.error('The Bridge must implement sendDtmf');
|
|
|
|
}
|
|
|
|
|
2022-02-01 03:30:38 +08:00
|
|
|
setDefaultInputDevice() {
|
|
|
|
this.inputDeviceId = DEFAULT_INPUT_DEVICE_ID;
|
|
|
|
}
|
|
|
|
|
|
|
|
async changeInputDeviceId(inputDeviceId) {
|
|
|
|
if (!inputDeviceId) {
|
|
|
|
throw new Error();
|
|
|
|
}
|
|
|
|
|
|
|
|
this.inputDeviceId = inputDeviceId;
|
|
|
|
return inputDeviceId;
|
|
|
|
}
|
|
|
|
|
|
|
|
async changeOutputDevice(value, isLive) {
|
|
|
|
const audioElement = document.querySelector(MEDIA_TAG);
|
|
|
|
|
|
|
|
if (audioElement.setSinkId) {
|
|
|
|
try {
|
|
|
|
if (!isLive) {
|
|
|
|
audioElement.srcObject = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
await audioElement.setSinkId(value);
|
|
|
|
reloadAudioElement(audioElement);
|
|
|
|
logger.debug({
|
|
|
|
logCode: 'audio_reload_audio_element',
|
|
|
|
extraInfo: {
|
|
|
|
bridgeName: this.bridgeName,
|
|
|
|
},
|
|
|
|
}, 'Audio element reloaded after changing output device');
|
|
|
|
|
|
|
|
this.outputDeviceId = value;
|
|
|
|
} catch (error) {
|
|
|
|
logger.error({
|
|
|
|
logCode: 'audio_changeoutputdevice_error',
|
|
|
|
extraInfo: { error, callerIdName: this.user.callerIdName },
|
|
|
|
}, 'Change Output Device error');
|
|
|
|
throw new Error(this.baseErrorCodes.MEDIA_ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return this.outputDeviceId;
|
|
|
|
}
|
|
|
|
|
2022-01-26 04:49:38 +08:00
|
|
|
trackTransferState(transferCallback) {
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
let trackerControl = null;
|
|
|
|
|
|
|
|
const timeout = setTimeout(() => {
|
|
|
|
trackerControl.stop();
|
|
|
|
logger.warn({ logCode: 'audio_transfer_timed_out' },
|
|
|
|
'Timeout on transferring from echo test to conference');
|
|
|
|
this.callback({
|
|
|
|
status: this.baseCallStates.failed,
|
|
|
|
error: 1008,
|
|
|
|
bridgeError: 'Timeout on call transfer',
|
|
|
|
bridge: this.bridgeName,
|
|
|
|
});
|
|
|
|
|
|
|
|
this.exitAudio();
|
|
|
|
|
|
|
|
reject(this.baseErrorCodes.REQUEST_TIMEOUT);
|
|
|
|
}, CALL_TRANSFER_TIMEOUT);
|
|
|
|
|
|
|
|
this.sendDtmf(TRANSFER_TONE);
|
|
|
|
|
|
|
|
Tracker.autorun((c) => {
|
|
|
|
trackerControl = c;
|
|
|
|
const selector = { meetingId: Auth.meetingID, userId: Auth.userID };
|
|
|
|
const query = VoiceCallStates.find(selector);
|
|
|
|
|
|
|
|
query.observeChanges({
|
|
|
|
changed: (id, fields) => {
|
|
|
|
if (fields.callState === CallStateOptions.IN_CONFERENCE) {
|
|
|
|
clearTimeout(timeout);
|
|
|
|
transferCallback();
|
|
|
|
|
|
|
|
c.stop();
|
|
|
|
resolve();
|
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2017-07-24 22:15:46 +08:00
|
|
|
}
|