infobip-rtc
Version:
Infobip RTC JavaScript SDK - Infobip WebRTC API Implementation
148 lines • 6.58 kB
JavaScript
import HangupStatusFactory from "../util/HangupStatusFactory";
import { CallStatus } from "../CallStatus";
import { DefaultApplicationCall } from "./DefaultApplicationCall";
import { WsAction } from "../ws/WsAction";
import { DeclineType } from "../DeclineType";
import HangupReasonFactory from "./util/HangupReasonFactory";
import { ApplicationCallOptions } from "../options/ApplicationCallOptions";
import { START_VIDEO_FILTER_ERROR_DESCRIPTION } from '../options/filters/video/DefaultVideoFilterManager';
export class DefaultIncomingApplicationCall extends DefaultApplicationCall {
constructor(eventEmitter, gateway, logger, rtcConfig, device, caller, callsConfigurationId, remoteOffer, currentUserIdentity, token, apiUrl, callId) {
super(eventEmitter, gateway, logger, rtcConfig, device, callsConfigurationId, null, currentUserIdentity, token, apiUrl, callId);
this.caller = caller;
this.remoteOffer = remoteOffer;
this.callStatus = CallStatus.RINGING;
}
from() {
return this.caller.identity;
}
fromDisplayName() {
return this.caller.displayName;
}
accept(options) {
if (this.callStatus !== CallStatus.RINGING) {
return;
}
this.callStatus = CallStatus.CONNECTING;
this.applicationCallOptions = options || ApplicationCallOptions.builder().build();
this.localAudio = { active: this.applicationCallOptions.audio };
this.localCameraVideo = { active: this.applicationCallOptions.video };
if (this.applicationCallOptions.dataChannel) {
this.createDataChannel();
}
if (this.applicationCallOptions.audioOptions?.detectTalkingWhileMuted) {
this.createAudioStreamEnergyMonitor();
}
this.createAudioPeerConnection();
let deviceId = this.applicationCallOptions.videoOptions?.deviceId;
if (deviceId) {
this.setVideoInputDevice(deviceId);
}
this.negotiateAudio(this.applicationCallOptions);
}
async negotiateAudio(options) {
let mediaStream;
try {
let audio = options.audio || options.audioOptions?.detectTalkingWhileMuted;
mediaStream = await this.getLocalMediaStream(audio, options.video, options.videoOptions?.cameraOrientation, options.videoOptions?.cameraVideoFrameRate);
if (this.isFinished()) {
this.logger.warn(`Call is no longer connecting, aborting audio negotiation`, this.callId);
this.stopAllTracks(mediaStream);
return;
}
if (options.video && mediaStream.getVideoTracks().length > 0) {
let videoStream = new MediaStream([mediaStream.getVideoTracks()[0]]);
await this.setLocalVideoStream(videoStream);
}
let audioStream = audio ? new MediaStream([mediaStream.getAudioTracks()[0]]) : new MediaStream();
this.setLocalAudioStream(audioStream);
await this.setRemoteDescription();
if (this.isFinished()) {
this.logger.warn(`Call is no longer connecting, aborting audio negotiation`, this.callId);
return;
}
this.setRemoteCandidates();
this.addTracksToPc();
let answer = await this.audioPC.peerConnection.createAnswer();
answer = await this.setLocalDescription(this.audioPC.peerConnection, answer);
if (this.isFinished()) {
this.logger.warn(`Call is no longer connecting, aborting audio negotiation`, this.callId);
return;
}
this.sendAnswer(answer, options);
}
catch (error) {
if (mediaStream) {
this.stopAllTracks(mediaStream);
}
this.handleIncomingCallFlowError(error);
}
}
decline(options) {
if (this.callStatus !== CallStatus.RINGING) {
return;
}
this.callStatus = CallStatus.FINISHED;
this.declineCall(DeclineType.BUSY, 'Busy Here', options?.declineOnAllDevices);
}
async setRemoteDescription() {
return await this.audioPC.peerConnection.setRemoteDescription(this.remoteOffer);
}
addTracksToPc() {
let audioStream = this.emptyAudioStream ? this.emptyAudioStream.stream() : this.localAudioStream;
audioStream.getTracks().forEach((track) => this.audioPC.peerConnection.addTrack(track, this.localAudioStream));
this.localAudio.transceiver = this.audioPC.peerConnection.getTransceivers()
.find(transceiver => transceiver.receiver.track.kind === 'audio');
}
sendAnswer(answer, options) {
this.gateway.send({
action: WsAction.APP_CALL_ACCEPT,
callId: this.callId,
description: answer,
media: {
audio: {
muted: !this.localAudio.active,
},
video: {
camera: this.localCameraVideo.active
}
},
autoReconnect: options.autoReconnect
});
}
handleIncomingCallFlowError(error) {
this.logger.error(`Call flow error occurred: ${error} ${error?.stack}`, this.callId);
const errorCode = HangupStatusFactory.getApplicationHangupStatus(error);
if (this.isAcceptRetriableError(errorCode)) {
this.rollbackAccept();
this.emitErrorEvent(errorCode);
return;
}
let reason = HangupReasonFactory.getHangupReason(error);
this.declineCall(DeclineType.ERROR, reason);
this.eventEmitter.emit('call-error', { status: errorCode });
}
isAcceptRetriableError(errorCode) {
return errorCode?.description === START_VIDEO_FILTER_ERROR_DESCRIPTION;
}
rollbackAccept() {
this.callStatus = CallStatus.RINGING;
this.applicationCallOptions = null;
this.localAudio = { active: false };
this.localCameraVideo = { active: false };
this.dataChannelCleanup();
this.audioStreamEnergyMonitorCleanup();
this.audioCleanup();
}
declineCall(declineType, reason, declineOnAllDevices = false) {
let message = {
action: WsAction.APP_CALL_DECLINE,
callId: this.callId,
declineType: declineType,
reason: reason,
declineOnAllDevices: declineOnAllDevices
};
this.gateway.send(message);
}
}
//# sourceMappingURL=DefaultIncomingApplicationCall.js.map