"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "ts/services/calling.ts" between
Signal-Desktop-1.35.2.tar.gz and Signal-Desktop-1.36.1.tar.gz

About: Signal-Desktop is a cross-platform encrypted messaging service (also available for mobile devices).

calling.ts  (Signal-Desktop-1.35.2):calling.ts  (Signal-Desktop-1.36.1)
/* eslint-disable class-methods-use-this */
import { import {
Call, Call,
CallEndedReason, CallEndedReason,
CallId, CallId,
CallLogLevel, CallLogLevel,
CallSettings, CallSettings,
CallState, CallState,
CanvasVideoRenderer, CanvasVideoRenderer,
DeviceId, DeviceId,
GumVideoCapturer, GumVideoCapturer,
RingRTC, RingRTC,
UserId, UserId,
} from 'ringrtc'; } from 'ringrtc';
import is from '@sindresorhus/is';
import { import {
ActionsType as UxActionsType, ActionsType as UxActionsType,
CallDetailsType, CallDetailsType,
} from '../state/ducks/calling'; } from '../state/ducks/calling';
import { CallingMessageClass, EnvelopeClass } from '../textsecure.d'; import { CallingMessageClass, EnvelopeClass } from '../textsecure.d';
import { ConversationModelType } from '../model-types.d'; import { ConversationModelType } from '../model-types.d';
import is from '@sindresorhus/is'; import {
import { AudioDevice, MediaDeviceSettings } from '../types/Calling'; AudioDevice,
CallHistoryDetailsType,
MediaDeviceSettings,
} from '../types/Calling';
export { export {
CallState, CallState,
CanvasVideoRenderer, CanvasVideoRenderer,
GumVideoCapturer, GumVideoCapturer,
VideoCapturer, VideoCapturer,
VideoRenderer, VideoRenderer,
} from 'ringrtc'; } from 'ringrtc';
export type CallHistoryDetailsType = {
wasIncoming: boolean;
wasVideoCall: boolean;
wasDeclined: boolean;
acceptedTime?: number;
endedTime: number;
};
export class CallingClass { export class CallingClass {
readonly videoCapturer: GumVideoCapturer; readonly videoCapturer: GumVideoCapturer;
readonly videoRenderer: CanvasVideoRenderer; readonly videoRenderer: CanvasVideoRenderer;
private uxActions?: UxActionsType; private uxActions?: UxActionsType;
private lastMediaDeviceSettings?: MediaDeviceSettings; private lastMediaDeviceSettings?: MediaDeviceSettings;
private deviceReselectionTimer?: NodeJS.Timeout; private deviceReselectionTimer?: NodeJS.Timeout;
constructor() { constructor() {
this.videoCapturer = new GumVideoCapturer(640, 480, 30); this.videoCapturer = new GumVideoCapturer(640, 480, 30);
this.videoRenderer = new CanvasVideoRenderer(); this.videoRenderer = new CanvasVideoRenderer();
} }
initialize(uxActions: UxActionsType): void { initialize(uxActions: UxActionsType): void {
this.uxActions = uxActions; this.uxActions = uxActions;
if (!uxActions) { if (!uxActions) {
skipping to change at line 88 skipping to change at line 90
RingRTC.handleIncomingCall = this.handleIncomingCall.bind(this); RingRTC.handleIncomingCall = this.handleIncomingCall.bind(this);
RingRTC.handleAutoEndedIncomingCallRequest = this.handleAutoEndedIncomingCal lRequest.bind( RingRTC.handleAutoEndedIncomingCallRequest = this.handleAutoEndedIncomingCal lRequest.bind(
this this
); );
RingRTC.handleLogMessage = this.handleLogMessage.bind(this); RingRTC.handleLogMessage = this.handleLogMessage.bind(this);
} }
async startOutgoingCall( async startOutgoingCall(
conversation: ConversationModelType, conversation: ConversationModelType,
isVideoCall: boolean isVideoCall: boolean
) { ): Promise<void> {
if (!this.uxActions) { if (!this.uxActions) {
window.log.error('Missing uxActions, new call not allowed.'); window.log.error('Missing uxActions, new call not allowed.');
return; return;
} }
const remoteUserId = this.getRemoteUserIdFromConversation(conversation); const remoteUserId = this.getRemoteUserIdFromConversation(conversation);
if (!remoteUserId || !this.localDeviceId) { if (!remoteUserId || !this.localDeviceId) {
window.log.error('Missing identifier, new call not allowed.'); window.log.error('Missing identifier, new call not allowed.');
return; return;
} }
skipping to change at line 133 skipping to change at line 135
await this.startDeviceReselectionTimer(); await this.startDeviceReselectionTimer();
RingRTC.setVideoCapturer(call.callId, this.videoCapturer); RingRTC.setVideoCapturer(call.callId, this.videoCapturer);
RingRTC.setVideoRenderer(call.callId, this.videoRenderer); RingRTC.setVideoRenderer(call.callId, this.videoRenderer);
this.attachToCall(conversation, call); this.attachToCall(conversation, call);
this.uxActions.outgoingCall({ this.uxActions.outgoingCall({
callDetails: this.getUxCallDetails(conversation, call), callDetails: this.getUxCallDetails(conversation, call),
}); });
} }
async accept(callId: CallId, asVideoCall: boolean) { async accept(callId: CallId, asVideoCall: boolean): Promise<void> {
const haveMediaPermissions = await this.requestPermissions(asVideoCall); const haveMediaPermissions = await this.requestPermissions(asVideoCall);
if (haveMediaPermissions) { if (haveMediaPermissions) {
await this.startDeviceReselectionTimer(); await this.startDeviceReselectionTimer();
RingRTC.setVideoCapturer(callId, this.videoCapturer); RingRTC.setVideoCapturer(callId, this.videoCapturer);
RingRTC.setVideoRenderer(callId, this.videoRenderer); RingRTC.setVideoRenderer(callId, this.videoRenderer);
RingRTC.accept(callId, asVideoCall); RingRTC.accept(callId, asVideoCall);
} else { } else {
window.log.info('Permissions were denied, call not allowed, hanging up.'); window.log.info('Permissions were denied, call not allowed, hanging up.');
RingRTC.hangup(callId); RingRTC.hangup(callId);
} }
} }
decline(callId: CallId) { decline(callId: CallId): void {
RingRTC.decline(callId); RingRTC.decline(callId);
} }
hangup(callId: CallId) { hangup(callId: CallId): void {
RingRTC.hangup(callId); RingRTC.hangup(callId);
} }
setOutgoingAudio(callId: CallId, enabled: boolean) { setOutgoingAudio(callId: CallId, enabled: boolean): void {
RingRTC.setOutgoingAudio(callId, enabled); RingRTC.setOutgoingAudio(callId, enabled);
} }
setOutgoingVideo(callId: CallId, enabled: boolean) { setOutgoingVideo(callId: CallId, enabled: boolean): void {
RingRTC.setOutgoingVideo(callId, enabled); RingRTC.setOutgoingVideo(callId, enabled);
} }
private async startDeviceReselectionTimer(): Promise<void> { private async startDeviceReselectionTimer(): Promise<void> {
// Poll once // Poll once
await this.pollForMediaDevices(); await this.pollForMediaDevices();
// Start the timer // Start the timer
if (!this.deviceReselectionTimer) { if (!this.deviceReselectionTimer) {
this.deviceReselectionTimer = setInterval(async () => { this.deviceReselectionTimer = setInterval(async () => {
await this.pollForMediaDevices(); await this.pollForMediaDevices();
skipping to change at line 180 skipping to change at line 182
} }
} }
private stopDeviceReselectionTimer() { private stopDeviceReselectionTimer() {
if (this.deviceReselectionTimer) { if (this.deviceReselectionTimer) {
clearInterval(this.deviceReselectionTimer); clearInterval(this.deviceReselectionTimer);
this.deviceReselectionTimer = undefined; this.deviceReselectionTimer = undefined;
} }
} }
// tslint:disable-next-line cyclomatic-complexity
private mediaDeviceSettingsEqual( private mediaDeviceSettingsEqual(
a?: MediaDeviceSettings, a?: MediaDeviceSettings,
b?: MediaDeviceSettings b?: MediaDeviceSettings
): boolean { ): boolean {
if (!a && !b) { if (!a && !b) {
return true; return true;
} }
if (!a || !b) { if (!a || !b) {
return false; return false;
} }
if ( if (
a.availableCameras.length !== b.availableCameras.length || a.availableCameras.length !== b.availableCameras.length ||
a.availableMicrophones.length !== b.availableMicrophones.length || a.availableMicrophones.length !== b.availableMicrophones.length ||
a.availableSpeakers.length !== b.availableSpeakers.length a.availableSpeakers.length !== b.availableSpeakers.length
) { ) {
return false; return false;
} }
for (let i = 0; i < a.availableCameras.length; i++) { for (let i = 0; i < a.availableCameras.length; i += 1) {
if ( if (
a.availableCameras[i].deviceId !== b.availableCameras[i].deviceId || a.availableCameras[i].deviceId !== b.availableCameras[i].deviceId ||
a.availableCameras[i].groupId !== b.availableCameras[i].groupId || a.availableCameras[i].groupId !== b.availableCameras[i].groupId ||
a.availableCameras[i].label !== b.availableCameras[i].label a.availableCameras[i].label !== b.availableCameras[i].label
) { ) {
return false; return false;
} }
} }
for (let i = 0; i < a.availableMicrophones.length; i++) { for (let i = 0; i < a.availableMicrophones.length; i += 1) {
if ( if (
a.availableMicrophones[i].name !== b.availableMicrophones[i].name || a.availableMicrophones[i].name !== b.availableMicrophones[i].name ||
a.availableMicrophones[i].uniqueId !== a.availableMicrophones[i].uniqueId !==
b.availableMicrophones[i].uniqueId b.availableMicrophones[i].uniqueId
) { ) {
return false; return false;
} }
} }
for (let i = 0; i < a.availableSpeakers.length; i++) { for (let i = 0; i < a.availableSpeakers.length; i += 1) {
if ( if (
a.availableSpeakers[i].name !== b.availableSpeakers[i].name || a.availableSpeakers[i].name !== b.availableSpeakers[i].name ||
a.availableSpeakers[i].uniqueId !== b.availableSpeakers[i].uniqueId a.availableSpeakers[i].uniqueId !== b.availableSpeakers[i].uniqueId
) { ) {
return false; return false;
} }
} }
if ( if (
(a.selectedCamera && !b.selectedCamera) || (a.selectedCamera && !b.selectedCamera) ||
(!a.selectedCamera && b.selectedCamera) || (!a.selectedCamera && b.selectedCamera) ||
skipping to change at line 302 skipping to change at line 303
); );
const selectedSpeakerIndex = this.findBestMatchingDeviceIndex( const selectedSpeakerIndex = this.findBestMatchingDeviceIndex(
availableSpeakers, availableSpeakers,
preferredSpeaker preferredSpeaker
); );
const selectedSpeaker = const selectedSpeaker =
selectedSpeakerIndex !== undefined selectedSpeakerIndex !== undefined
? availableSpeakers[selectedSpeakerIndex] ? availableSpeakers[selectedSpeakerIndex]
: undefined; : undefined;
const availableCameras = await window.Signal.Services.calling.videoCapturer. enumerateDevices(); const availableCameras = await this.videoCapturer.enumerateDevices();
const preferredCamera = window.storage.get('preferred-video-input-device'); const preferredCamera = window.storage.get('preferred-video-input-device');
const selectedCamera = this.findBestMatchingCamera( const selectedCamera = this.findBestMatchingCamera(
availableCameras, availableCameras,
preferredCamera preferredCamera
); );
return { return {
availableMicrophones, availableMicrophones,
availableSpeakers, availableSpeakers,
selectedMicrophone, selectedMicrophone,
skipping to change at line 354 skipping to change at line 355
return available.length > 0 ? 0 : undefined; return available.length > 0 ? 0 : undefined;
} }
findBestMatchingCamera( findBestMatchingCamera(
available: Array<MediaDeviceInfo>, available: Array<MediaDeviceInfo>,
preferred?: string preferred?: string
): string | undefined { ): string | undefined {
const matchingId = available.filter(d => d.deviceId === preferred); const matchingId = available.filter(d => d.deviceId === preferred);
const nonInfrared = available.filter(d => !d.label.includes('IR Camera')); const nonInfrared = available.filter(d => !d.label.includes('IR Camera'));
/// By default, pick the first non-IR camera (but allow the user to pick the // By default, pick the first non-IR camera (but allow the user to pick the
infrared if they so desire) // infrared if they so desire)
if (matchingId.length > 0) { if (matchingId.length > 0) {
return matchingId[0].deviceId; return matchingId[0].deviceId;
} else if (nonInfrared.length > 0) { }
if (nonInfrared.length > 0) {
return nonInfrared[0].deviceId; return nonInfrared[0].deviceId;
} else {
return undefined;
} }
return undefined;
} }
setPreferredMicrophone(device: AudioDevice) { setPreferredMicrophone(device: AudioDevice): void {
window.log.info('MediaDevice: setPreferredMicrophone', device); window.log.info('MediaDevice: setPreferredMicrophone', device);
window.storage.put('preferred-audio-input-device', device); window.storage.put('preferred-audio-input-device', device);
RingRTC.setAudioInput(device.index); RingRTC.setAudioInput(device.index);
} }
setPreferredSpeaker(device: AudioDevice) { setPreferredSpeaker(device: AudioDevice): void {
window.log.info('MediaDevice: setPreferredSpeaker', device); window.log.info('MediaDevice: setPreferredSpeaker', device);
window.storage.put('preferred-audio-output-device', device); window.storage.put('preferred-audio-output-device', device);
RingRTC.setAudioOutput(device.index); RingRTC.setAudioOutput(device.index);
} }
async setPreferredCamera(device: string) { async setPreferredCamera(device: string): Promise<void> {
window.log.info('MediaDevice: setPreferredCamera', device); window.log.info('MediaDevice: setPreferredCamera', device);
window.storage.put('preferred-video-input-device', device); window.storage.put('preferred-video-input-device', device);
await this.videoCapturer.setPreferredDevice(device); await this.videoCapturer.setPreferredDevice(device);
} }
async handleCallingMessage( async handleCallingMessage(
envelope: EnvelopeClass, envelope: EnvelopeClass,
callingMessage: CallingMessageClass callingMessage: CallingMessageClass
) { ): Promise<void> {
const enableIncomingCalls = await window.getIncomingCallNotification(); const enableIncomingCalls = await window.getIncomingCallNotification();
if (callingMessage.offer && !enableIncomingCalls) { if (callingMessage.offer && !enableIncomingCalls) {
// Drop offers silently if incoming call notifications are disabled. // Drop offers silently if incoming call notifications are disabled.
window.log.info('Incoming calls are disabled, ignoring call offer.'); window.log.info('Incoming calls are disabled, ignoring call offer.');
return; return;
} }
const remoteUserId = envelope.source || envelope.sourceUuid; const remoteUserId = envelope.source || envelope.sourceUuid;
const remoteDeviceId = this.parseDeviceId(envelope.sourceDevice); const remoteDeviceId = this.parseDeviceId(envelope.sourceDevice);
if (!remoteUserId || !remoteDeviceId || !this.localDeviceId) { if (!remoteUserId || !remoteDeviceId || !this.localDeviceId) {
skipping to change at line 424 skipping to change at line 427
if ( if (
(!this.lastMediaDeviceSettings && settings.selectedCamera) || (!this.lastMediaDeviceSettings && settings.selectedCamera) ||
(this.lastMediaDeviceSettings && (this.lastMediaDeviceSettings &&
settings.selectedCamera && settings.selectedCamera &&
this.lastMediaDeviceSettings.selectedCamera !== settings.selectedCamera) this.lastMediaDeviceSettings.selectedCamera !== settings.selectedCamera)
) { ) {
window.log.info('MediaDevice: selecting camera', settings.selectedCamera); window.log.info('MediaDevice: selecting camera', settings.selectedCamera);
await this.videoCapturer.setPreferredDevice(settings.selectedCamera); await this.videoCapturer.setPreferredDevice(settings.selectedCamera);
} }
// Assume that the MediaDeviceSettings have been obtained very recently and // Assume that the MediaDeviceSettings have been obtained very recently and
the index is still valid (no devices have been plugged in in between). // the index is still valid (no devices have been plugged in in between).
if (settings.selectedMicrophone) { if (settings.selectedMicrophone) {
window.log.info( window.log.info(
'MediaDevice: selecting microphone', 'MediaDevice: selecting microphone',
settings.selectedMicrophone settings.selectedMicrophone
); );
RingRTC.setAudioInput(settings.selectedMicrophone.index); RingRTC.setAudioInput(settings.selectedMicrophone.index);
} }
if (settings.selectedSpeaker) { if (settings.selectedSpeaker) {
window.log.info( window.log.info(
skipping to change at line 471 skipping to change at line 475
} }
return true; return true;
} }
private async requestPermissions(isVideoCall: boolean): Promise<boolean> { private async requestPermissions(isVideoCall: boolean): Promise<boolean> {
const microphonePermission = await this.requestMicrophonePermissions(); const microphonePermission = await this.requestMicrophonePermissions();
if (microphonePermission) { if (microphonePermission) {
if (isVideoCall) { if (isVideoCall) {
return this.requestCameraPermissions(); return this.requestCameraPermissions();
} else {
return true;
} }
} else {
return false; return true;
} }
return false;
} }
private async handleOutgoingSignaling( private async handleOutgoingSignaling(
remoteUserId: UserId, remoteUserId: UserId,
message: CallingMessageClass message: CallingMessageClass
): Promise<boolean> { ): Promise<boolean> {
const conversation = window.ConversationController.get(remoteUserId); const conversation = window.ConversationController.get(remoteUserId);
const sendOptions = conversation const sendOptions = conversation
? conversation.getSendOptions() ? conversation.getSendOptions()
: undefined; : undefined;
skipping to change at line 586 skipping to change at line 590
} }
private attachToCall(conversation: ConversationModelType, call: Call): void { private attachToCall(conversation: ConversationModelType, call: Call): void {
const { uxActions } = this; const { uxActions } = this;
if (!uxActions) { if (!uxActions) {
return; return;
} }
let acceptedTime: number | undefined; let acceptedTime: number | undefined;
// eslint-disable-next-line no-param-reassign
call.handleStateChanged = () => { call.handleStateChanged = () => {
if (call.state === CallState.Accepted) { if (call.state === CallState.Accepted) {
acceptedTime = Date.now(); acceptedTime = Date.now();
} else if (call.state === CallState.Ended) { } else if (call.state === CallState.Ended) {
this.addCallHistoryForEndedCall(conversation, call, acceptedTime); this.addCallHistoryForEndedCall(conversation, call, acceptedTime);
this.stopDeviceReselectionTimer(); this.stopDeviceReselectionTimer();
this.lastMediaDeviceSettings = undefined; this.lastMediaDeviceSettings = undefined;
} }
uxActions.callStateChange({ uxActions.callStateChange({
callState: call.state, callState: call.state,
callDetails: this.getUxCallDetails(conversation, call), callDetails: this.getUxCallDetails(conversation, call),
}); });
}; };
// eslint-disable-next-line no-param-reassign
call.handleRemoteVideoEnabled = () => { call.handleRemoteVideoEnabled = () => {
uxActions.remoteVideoChange({ uxActions.remoteVideoChange({
remoteVideoEnabled: call.remoteVideoEnabled, remoteVideoEnabled: call.remoteVideoEnabled,
}); });
}; };
} }
private async handleLogMessage( private async handleLogMessage(
level: CallLogLevel, level: CallLogLevel,
fileName: string, fileName: string,
line: number, line: number,
message: string message: string
) { ) {
// info/warn/error are only needed to be logged for now.
// tslint:disable-next-line switch-default
switch (level) { switch (level) {
case CallLogLevel.Info: case CallLogLevel.Info:
window.log.info(`${fileName}:${line} ${message}`); window.log.info(`${fileName}:${line} ${message}`);
break; break;
case CallLogLevel.Warn: case CallLogLevel.Warn:
window.log.warn(`${fileName}:${line} ${message}`); window.log.warn(`${fileName}:${line} ${message}`);
break; break;
case CallLogLevel.Error: case CallLogLevel.Error:
window.log.error(`${fileName}:${line} ${message}`); window.log.error(`${fileName}:${line} ${message}`);
break;
default:
break;
} }
} }
private getRemoteUserIdFromConversation( private getRemoteUserIdFromConversation(
conversation: ConversationModelType conversation: ConversationModelType
): UserId | undefined { ): UserId | undefined {
const recipients = conversation.getRecipients(); const recipients = conversation.getRecipients();
if (recipients.length !== 1) { if (recipients.length !== 1) {
return undefined; return undefined;
} }
skipping to change at line 689 skipping to change at line 696
callId: call.callId, callId: call.callId,
isIncoming: call.isIncoming, isIncoming: call.isIncoming,
isVideoCall: call.isVideoCall, isVideoCall: call.isVideoCall,
}; };
} }
private addCallHistoryForEndedCall( private addCallHistoryForEndedCall(
conversation: ConversationModelType, conversation: ConversationModelType,
call: Call, call: Call,
acceptedTime: number | undefined acceptedTimeParam: number | undefined
) { ) {
let acceptedTime = acceptedTimeParam;
const { endedReason, isIncoming } = call; const { endedReason, isIncoming } = call;
const wasAccepted = Boolean(acceptedTime); const wasAccepted = Boolean(acceptedTime);
const isOutgoing = !isIncoming; const isOutgoing = !isIncoming;
const wasDeclined = const wasDeclined =
!wasAccepted && !wasAccepted &&
(endedReason === CallEndedReason.Declined || (endedReason === CallEndedReason.Declined ||
endedReason === CallEndedReason.DeclinedOnAnotherDevice || endedReason === CallEndedReason.DeclinedOnAnotherDevice ||
(isIncoming && endedReason === CallEndedReason.LocalHangup) || (isIncoming && endedReason === CallEndedReason.LocalHangup) ||
(isOutgoing && endedReason === CallEndedReason.RemoteHangup) || (isOutgoing && endedReason === CallEndedReason.RemoteHangup) ||
(isOutgoing && (isOutgoing &&
endedReason === CallEndedReason.RemoteHangupNeedPermission)); endedReason === CallEndedReason.RemoteHangupNeedPermission));
if (call.endedReason === CallEndedReason.AcceptedOnAnotherDevice) { if (call.endedReason === CallEndedReason.AcceptedOnAnotherDevice) {
// tslint:disable-next-line no-parameter-reassignment
acceptedTime = Date.now(); acceptedTime = Date.now();
} }
const callHistoryDetails: CallHistoryDetailsType = { const callHistoryDetails: CallHistoryDetailsType = {
wasIncoming: call.isIncoming, wasIncoming: call.isIncoming,
wasVideoCall: call.isVideoCall, wasVideoCall: call.isVideoCall,
wasDeclined, wasDeclined,
acceptedTime, acceptedTime,
endedTime: Date.now(), endedTime: Date.now(),
}; };
 End of changes. 38 change blocks. 
40 lines changed or deleted 46 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)