| 
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -25,7 +25,10 @@ import { PnServer } from '../../pushnotification';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import { setIsRinging } from './RingingManager';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				export type InputItem = { deviceId: string; label: string };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// tslint:disable: function-name
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				const maxWidth = 1920;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				const maxHeight = 1080;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				/**
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * This uuid is set only once we accepted a call or started one.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 */
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -166,6 +169,28 @@ if (typeof navigator !== 'undefined') {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				const silence = () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const ctx = new AudioContext();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const oscillator = ctx.createOscillator();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const dst = oscillator.connect(ctx.createMediaStreamDestination());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  oscillator.start();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  return Object.assign((dst as any).stream.getAudioTracks()[0], { enabled: false });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				const black = () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const canvas = Object.assign(document.createElement('canvas'), {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    width: maxWidth,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    height: maxHeight,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  canvas.getContext('2d')?.fillRect(0, 0, maxWidth, maxHeight);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const stream = (canvas as any).captureStream();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  return Object.assign(stream.getVideoTracks()[0], { enabled: false });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				const getBlackSilenceMediaStream = () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  return new MediaStream([black(), silence()]);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				async function updateConnectedDevices() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // Get the set of cameras connected
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const videoCameras = await getConnectedDevices('videoinput');
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -219,6 +244,14 @@ export async function selectCameraByDeviceId(cameraDeviceId: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (sender?.track) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      sender.track.enabled = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // do the same changes locally
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    localStream?.getVideoTracks().forEach(t => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      t.stop();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      localStream?.removeTrack(t);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    localStream?.addTrack(getBlackSilenceMediaStream().getVideoTracks()[0]);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    sendVideoStatusViaDataChannel();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    callVideoListeners();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return;
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -235,24 +268,23 @@ export async function selectCameraByDeviceId(cameraDeviceId: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    try {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const newVideoStream = await navigator.mediaDevices.getUserMedia(devicesConfig);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const videoTrack = newVideoStream.getVideoTracks()[0];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (!peerConnection) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        throw new Error('cannot selectCameraByDeviceId without a peer connection');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // video might be completely off on start. adding a track like this triggers a negotationneeded event
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.log.info('adding/replacing video track');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const sender = peerConnection.getSenders().find(s => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return s.track?.kind === videoTrack.kind;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.log.info('replacing video track');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const videoSender = peerConnection
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        .getTransceivers()
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        .find(t => t.sender.track?.kind === 'video')?.sender;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      videoTrack.enabled = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (sender) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // this should not trigger a negotationneeded event
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // and it is needed for when the video cam was never turn on
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        await sender.replaceTrack(videoTrack);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (videoSender) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        await videoSender.replaceTrack(videoTrack);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // this will trigger a negotiationeeded event
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        peerConnection.addTrack(videoTrack, newVideoStream);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        throw new Error(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				          'We should always have a videoSender as we are using a black video when no camera are in use'
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // do the same changes locally
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -266,6 +298,7 @@ export async function selectCameraByDeviceId(cameraDeviceId: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      callVideoListeners();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    } catch (e) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.log.warn('selectCameraByDeviceId failed with', e.message);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      ToastUtils.pushToastError('selectCamera', e.message);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      callVideoListeners();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -281,6 +314,12 @@ export async function selectAudioInputByDeviceId(audioInputDeviceId: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (sender?.track) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      sender.track.enabled = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // do the same changes locally
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    localStream?.getAudioTracks().forEach(t => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      t.stop();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      localStream?.removeTrack(t);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    localStream?.addTrack(getBlackSilenceMediaStream().getAudioTracks()[0]);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    callVideoListeners();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -295,6 +334,7 @@ export async function selectAudioInputByDeviceId(audioInputDeviceId: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    try {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const newAudioStream = await navigator.mediaDevices.getUserMedia(devicesConfig);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const audioTrack = newAudioStream.getAudioTracks()[0];
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (!peerConnection) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        throw new Error('cannot selectAudioInputByDeviceId without a peer connection');
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -331,18 +371,15 @@ export async function selectAudioOutputByDeviceId(audioOutputDeviceId: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				async function handleNegotiationNeededEvent(recipient: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				async function createOfferAndSendIt(recipient: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  try {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    makingOffer = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    window.log.info('got handleNegotiationNeeded event. creating offer');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const offer = await peerConnection?.createOffer({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      offerToReceiveAudio: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      offerToReceiveVideo: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    window.log.info('got createOfferAndSendIt event. creating offer');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    await (peerConnection as any)?.setLocalDescription();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const offer = peerConnection?.localDescription;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!offer) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      throw new Error('Could not create an offer');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    await peerConnection?.setLocalDescription(offer);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!currentCallUUID) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.log.warn('cannot send offer without a currentCallUUID');
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -357,18 +394,18 @@ async function handleNegotiationNeededEvent(recipient: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        uuid: currentCallUUID,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.log.info(`sending OFFER MESSAGE with callUUID: ${currentCallUUID}`);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const negotationOfferSendResult = await getMessageQueue().sendToPubKeyNonDurably(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.log.info(`sending '${offer.type}'' with callUUID: ${currentCallUUID}`);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const negotiationOfferSendResult = await getMessageQueue().sendToPubKeyNonDurably(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        PubKey.cast(recipient),
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        offerMessage
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (typeof negotationOfferSendResult === 'number') {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (typeof negotiationOfferSendResult === 'number') {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // window.log?.warn('setting last sent timestamp');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        lastOutgoingOfferTimestamp = negotationOfferSendResult;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        lastOutgoingOfferTimestamp = negotiationOfferSendResult;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  } catch (err) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    window.log?.error(`Error on handling negotiation needed ${err}`);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    window.log?.error(`Error createOfferAndSendIt ${err}`);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  } finally {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    makingOffer = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -390,23 +427,13 @@ async function openMediaDevicesAndAddTracks() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    selectedAudioInputId = audioInputsList[0].deviceId;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    selectedAudioInputId = DEVICE_DISABLED_DEVICE_ID; //audioInputsList[0].deviceId;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    selectedCameraId = DEVICE_DISABLED_DEVICE_ID;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    window.log.info(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      `openMediaDevices videoDevice:${selectedCameraId} audioDevice:${selectedAudioInputId}`
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const devicesConfig = {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      audio: {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        deviceId: { exact: selectedAudioInputId },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        echoCancellation: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      // we don't need a video stream on start
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      video: false,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    localStream = await navigator.mediaDevices.getUserMedia(devicesConfig);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    localStream = getBlackSilenceMediaStream();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    localStream.getTracks().map(track => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (localStream) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        peerConnection?.addTrack(track, localStream);
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -420,7 +447,6 @@ async function openMediaDevicesAndAddTracks() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  callVideoListeners();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// tslint:disable-next-line: function-name
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				export async function USER_callRecipient(recipient: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (!getCallMediaPermissionsSettings()) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ToastUtils.pushVideoCallPermissionNeeded();
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -457,6 +483,7 @@ export async function USER_callRecipient(recipient: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  await openMediaDevicesAndAddTracks();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  setIsRinging(true);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  await createOfferAndSendIt(recipient);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				const iceCandidates: Array<RTCIceCandidate> = new Array();
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -579,7 +606,6 @@ function closeVideoCall() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  window.inboxStore?.dispatch(endCall());
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  remoteVideoStreamIsMuted = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  timestampAcceptedCall = undefined;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  makingOffer = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  ignoreOffer = false;
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -626,7 +652,7 @@ function onDataChannelOnOpen() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  sendVideoStatusViaDataChannel();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function createOrGetPeerConnection(withPubkey: string, isAcceptingCall = false) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				function createOrGetPeerConnection(withPubkey: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  if (peerConnection) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    return peerConnection;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
	
		
			
				
					| 
						
						
						
							
								
							
						
					 | 
				
			
			 | 
			 | 
			
				@ -640,21 +666,7 @@ function createOrGetPeerConnection(withPubkey: string, isAcceptingCall = false)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  dataChannel.onmessage = onDataChannelReceivedMessage;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  dataChannel.onopen = onDataChannelOnOpen;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  peerConnection.onnegotiationneeded = async () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const shouldTriggerAnotherNeg =
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      isAcceptingCall && timestampAcceptedCall && Date.now() - timestampAcceptedCall > 1000;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!isAcceptingCall || shouldTriggerAnotherNeg) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      await handleNegotiationNeededEvent(withPubkey);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.log.info(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        'should negotaite again but we accepted the call recently, so swallowing this one'
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  peerConnection.onsignalingstatechange = handleSignalingStateChangeEvent;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  peerConnection.ontrack = event => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    event.track.onunmute = () => {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      remoteStream?.addTrack(event.track);
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -694,9 +706,6 @@ function createOrGetPeerConnection(withPubkey: string, isAcceptingCall = false)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  return peerConnection;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				let timestampAcceptedCall: number | undefined;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// tslint:disable-next-line: function-name
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				export async function USER_acceptIncomingCallRequest(fromSender: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  window.log.info('USER_acceptIncomingCallRequest');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  setIsRinging(false);
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -730,8 +739,7 @@ export async function USER_acceptIncomingCallRequest(fromSender: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  currentCallUUID = lastOfferMessage.uuid;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  timestampAcceptedCall = Date.now();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  peerConnection = createOrGetPeerConnection(fromSender, true);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  peerConnection = createOrGetPeerConnection(fromSender);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  await openMediaDevicesAndAddTracks();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -783,7 +791,6 @@ export async function rejectCallAlreadyAnotherCall(fromSender: string, forcedUUI
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  clearCallCacheFromPubkeyAndUUID(fromSender, forcedUUID);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// tslint:disable-next-line: function-name
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				export async function USER_rejectIncomingCallRequest(fromSender: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  setIsRinging(false);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // close the popup call
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -822,7 +829,6 @@ async function sendCallMessageAndSync(callmessage: CallMessage, user: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  ]);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				// tslint:disable-next-line: function-name
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				export async function USER_hangup(fromSender: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  window.log.info('USER_hangup');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -889,16 +895,12 @@ async function buildAnswerAndSendIt(sender: string) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.log.warn('cannot send answer without a currentCallUUID');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const answer = await peerConnection.createAnswer({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      offerToReceiveAudio: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      offerToReceiveVideo: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    await (peerConnection as any).setLocalDescription();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const answer = peerConnection.localDescription;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (!answer?.sdp || answer.sdp.length === 0) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.log.warn('failed to create answer');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    await peerConnection.setLocalDescription(answer);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const answerSdp = answer.sdp;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const callAnswerMessage = new CallMessage({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      timestamp: Date.now(),
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -955,25 +957,26 @@ export async function handleCallTypeOffer(
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      !makingOffer && (peerConnection?.signalingState === 'stable' || isSettingRemoteAnswerPending);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const polite = lastOutgoingOfferTimestamp < incomingOfferTimestamp;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const offerCollision = !readyForOffer;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    ignoreOffer = !polite && offerCollision;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (ignoreOffer) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.log?.warn('Received offer when unready for offer; Ignoring offer.');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (remoteCallUUID === currentCallUUID && currentCallUUID) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    if (peerConnection && remoteCallUUID === currentCallUUID && currentCallUUID) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.log.info('Got a new offer message from our ongoing call');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      isSettingRemoteAnswerPending = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const remoteDesc = new RTCSessionDescription({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      const remoteOfferDesc = new RTCSessionDescription({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        type: 'offer',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        sdp: callMessage.sdps[0],
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      isSettingRemoteAnswerPending = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      if (peerConnection) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        await peerConnection.setRemoteDescription(remoteDesc); // SRD rolls back as needed
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        await buildAnswerAndSendIt(sender);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      await peerConnection.setRemoteDescription(remoteOfferDesc); // SRD rolls back as needed
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      isSettingRemoteAnswerPending = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      await buildAnswerAndSendIt(sender);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      window.inboxStore?.dispatch(incomingCall({ pubkey: sender }));
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
							
								
							
						
						
					 | 
				
			
			 | 
			 | 
			
				@ -1103,19 +1106,21 @@ export async function handleCallTypeAnswer(sender: string, callMessage: SignalSe
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      pubkey: sender,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    })
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  const remoteDesc = new RTCSessionDescription({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    type: 'answer',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    sdp: callMessage.sdps[0],
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  // window.log?.info('Setting remote answer pending');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  isSettingRemoteAnswerPending = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  try {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    isSettingRemoteAnswerPending = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    const remoteDesc = new RTCSessionDescription({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      type: 'answer',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				      sdp: callMessage.sdps[0],
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    });
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    await peerConnection?.setRemoteDescription(remoteDesc); // SRD rolls back as needed
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  } catch (e) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    window.log.warn('setRemoteDescription failed:', e);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    window.log.warn('setRemoteDescriptio failed:', e);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  } finally {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    isSettingRemoteAnswerPending = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				  isSettingRemoteAnswerPending = false;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				export async function handleCallTypeIceCandidates(
 | 
			
		
		
	
	
		
			
				
					| 
						
							
								
							
						
						
						
					 | 
				
			
			 | 
			 | 
			
				
 
 |