diff --git a/ts/components/session/calling/CallButtons.tsx b/ts/components/session/calling/CallButtons.tsx index bfb2b583b..8b14869dd 100644 --- a/ts/components/session/calling/CallButtons.tsx +++ b/ts/components/session/calling/CallButtons.tsx @@ -1,6 +1,6 @@ import { SessionIconButton } from '../icon'; import { animation, contextMenu, Item, Menu } from 'react-contexify'; -import { InputItem } from '../../../session/utils/CallManager'; +import { InputItem } from '../../../session/utils/calling/CallManager'; import { setFullScreenCall } from '../../../state/ducks/call'; import { CallManager, ToastUtils } from '../../../session/utils'; import React from 'react'; @@ -71,16 +71,16 @@ export const AudioInputButton = ({ export const AudioOutputButton = ({ currentConnectedAudioOutputs, -}: // isAudioOutputMuted, -// hideArrowIcon = false, -{ + isAudioOutputMuted, + hideArrowIcon = false, +}: { currentConnectedAudioOutputs: Array; isAudioOutputMuted: boolean; hideArrowIcon?: boolean; }) => { return ( <> - {/* { @@ -90,7 +90,7 @@ export const AudioOutputButton = ({ showAudioOutputMenu(currentConnectedAudioOutputs, e); }} hidePopoverArrow={hideArrowIcon} - /> */} + /> , -// e: React.MouseEvent -// ) => { -// if (currentConnectedAudioOutputs.length === 0) { -// ToastUtils.pushNoAudioOutputFound(); -// return; -// } -// contextMenu.show({ -// id: audioOutputTriggerId, -// event: e, -// }); -// }; +const showAudioOutputMenu = ( + currentConnectedAudioOutputs: Array, + e: React.MouseEvent +) => { + if (currentConnectedAudioOutputs.length === 0) { + ToastUtils.pushNoAudioOutputFound(); + return; + } + contextMenu.show({ + id: audioOutputTriggerId, + event: e, + }); +}; const showVideoInputMenu = ( currentConnectedCameras: Array, @@ -300,22 +300,22 @@ const handleMicrophoneToggle = async ( } }; -// const handleSpeakerToggle = async ( -// currentConnectedAudioOutputs: Array, -// isAudioOutputMuted: boolean -// ) => { -// if (!currentConnectedAudioOutputs.length) { -// ToastUtils.pushNoAudioInputFound(); +const handleSpeakerToggle = async ( + currentConnectedAudioOutputs: Array, + isAudioOutputMuted: boolean +) => { + if (!currentConnectedAudioOutputs.length) { + ToastUtils.pushNoAudioInputFound(); -// return; -// } -// if (isAudioOutputMuted) { -// // selects the first one -// await CallManager.selectAudioOutputByDeviceId(currentConnectedAudioOutputs[0].deviceId); -// } else { -// await CallManager.selectAudioOutputByDeviceId(CallManager.DEVICE_DISABLED_DEVICE_ID); -// } -// }; + return; + } + if (isAudioOutputMuted) { + // selects the first one + await CallManager.selectAudioOutputByDeviceId(currentConnectedAudioOutputs[0].deviceId); + } else { + await CallManager.selectAudioOutputByDeviceId(CallManager.DEVICE_DISABLED_DEVICE_ID); + } +}; const StyledCallWindowControls = styled.div` position: absolute; diff --git a/ts/components/session/calling/InConversationCallContainer.tsx b/ts/components/session/calling/InConversationCallContainer.tsx index d8c748ed0..f7d42944f 100644 --- a/ts/components/session/calling/InConversationCallContainer.tsx +++ b/ts/components/session/calling/InConversationCallContainer.tsx @@ -23,7 +23,7 @@ import { import { useModuloWithTripleDots } from '../../../hooks/useModuloWithTripleDots'; import { CallWindowControls } from './CallButtons'; import { SessionSpinner } from '../SessionSpinner'; -import { DEVICE_DISABLED_DEVICE_ID } from '../../../session/utils/CallManager'; +import { DEVICE_DISABLED_DEVICE_ID } from '../../../session/utils/calling/CallManager'; const VideoContainer = styled.div` height: 100%; @@ -156,10 +156,10 @@ export const InConversationCallContainer = () => { if (videoRefRemote.current) { if (currentSelectedAudioOutput === DEVICE_DISABLED_DEVICE_ID) { - videoRefLocal.current.muted = true; + videoRefRemote.current.muted = true; } else { - // void videoRefLocal.current.setSinkId(currentSelectedAudioOutput); - videoRefLocal.current.muted = false; + // void videoRefRemote.current.setSinkId(currentSelectedAudioOutput); + videoRefRemote.current.muted = false; } } } diff --git a/ts/hooks/useVideoEventListener.ts b/ts/hooks/useVideoEventListener.ts index 401420cde..515be62d7 100644 --- a/ts/hooks/useVideoEventListener.ts +++ b/ts/hooks/useVideoEventListener.ts @@ -2,12 +2,13 @@ import { useEffect, useState } from 'react'; import { useSelector } from 'react-redux'; // tslint:disable-next-line: no-submodule-imports import useMountedState from 'react-use/lib/useMountedState'; -import { CallManager } from '../session/utils'; import { + addVideoEventsListener, CallManagerOptionsType, DEVICE_DISABLED_DEVICE_ID, InputItem, -} from '../session/utils/CallManager'; + removeVideoEventsListener, +} from '../session/utils/calling/CallManager'; import { getSelectedConversationKey } from '../state/selectors/conversations'; import { getCallIsInFullScreen, getHasOngoingCallWithPubkey } from '../state/selectors/call'; @@ -40,7 +41,7 @@ export function useVideoCallEventsListener(uniqueId: string, onSame: boolean) { (onSame && ongoingCallPubkey === selectedConversationKey) || (!onSame && ongoingCallPubkey !== selectedConversationKey) ) { - CallManager.addVideoEventsListener(uniqueId, (options: CallManagerOptionsType) => { + addVideoEventsListener(uniqueId, (options: CallManagerOptionsType) => { const { audioInputsList, audioOutputsList, @@ -68,7 +69,7 @@ export function useVideoCallEventsListener(uniqueId: string, onSame: boolean) { } return () => { - CallManager.removeVideoEventsListener(uniqueId); + removeVideoEventsListener(uniqueId); }; }, [ongoingCallPubkey, selectedConversationKey, isFullScreen]); diff --git a/ts/session/utils/CallManager.ts b/ts/session/utils/calling/CallManager.ts similarity index 95% rename from ts/session/utils/CallManager.ts rename to ts/session/utils/calling/CallManager.ts index d4d068950..b84c0e776 100644 --- a/ts/session/utils/CallManager.ts +++ b/ts/session/utils/calling/CallManager.ts @@ -1,10 +1,10 @@ import _ from 'lodash'; -import { MessageUtils, ToastUtils, UserUtils } from '.'; -import { getCallMediaPermissionsSettings } from '../../components/session/settings/SessionSettings'; -import { getConversationById } from '../../data/data'; -import { MessageModelType } from '../../models/messageType'; -import { SignalService } from '../../protobuf'; -import { openConversationWithMessages } from '../../state/ducks/conversations'; +import { MessageUtils, ToastUtils, UserUtils } from '../'; +import { getCallMediaPermissionsSettings } from '../../../components/session/settings/SessionSettings'; +import { getConversationById } from '../../../data/data'; +import { MessageModelType } from '../../../models/messageType'; +import { SignalService } from '../../../protobuf'; +import { openConversationWithMessages } from '../../../state/ducks/conversations'; import { answerCall, callConnected, @@ -13,22 +13,23 @@ import { incomingCall, setFullScreenCall, startingCallWith, -} from '../../state/ducks/call'; -import { getConversationController } from '../conversations'; -import { CallMessage } from '../messages/outgoing/controlMessage/CallMessage'; -import { ed25519Str } from '../onions/onionPath'; -import { getMessageQueue, MessageSender } from '../sending'; -import { PubKey } from '../types'; +} from '../../../state/ducks/call'; +import { getConversationController } from '../../conversations'; +import { CallMessage } from '../../messages/outgoing/controlMessage/CallMessage'; +import { ed25519Str } from '../../onions/onionPath'; +import { PubKey } from '../../types'; import { v4 as uuidv4 } from 'uuid'; -import { PnServer } from '../../pushnotification'; -import { setIsRinging } from './RingingManager'; +import { PnServer } from '../../../pushnotification'; +import { setIsRinging } from '../RingingManager'; +import { getBlackSilenceMediaStream } from './Silence'; +import { getMessageQueue } from '../..'; +import { MessageSender } from '../../sending'; -export type InputItem = { deviceId: string; label: string }; // tslint:disable: function-name -const maxWidth = 1920; -const maxHeight = 1080; +export type InputItem = { deviceId: string; label: string }; + /** * This uuid is set only once we accepted a call or started one. */ @@ -169,28 +170,6 @@ 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'); @@ -339,12 +318,13 @@ export async function selectAudioInputByDeviceId(audioInputDeviceId: string) { if (!peerConnection) { throw new Error('cannot selectAudioInputByDeviceId without a peer connection'); } - const sender = peerConnection.getSenders().find(s => { + const audioSender = peerConnection.getSenders().find(s => { return s.track?.kind === audioTrack.kind; }); + window.log.info('replacing audio track'); - if (sender) { - await sender.replaceTrack(audioTrack); + if (audioSender) { + await audioSender.replaceTrack(audioTrack); // we actually do not need to toggle the track here, as toggling it here unmuted here locally (so we start to hear ourselves) } else { throw new Error('Failed to get sender for selectAudioInputByDeviceId '); @@ -562,6 +542,11 @@ function handleConnectionStateChanged(pubkey: string) { if (firstAudioInput) { void selectAudioInputByDeviceId(firstAudioInput); } + + const firstAudioOutput = audioOutputsList?.[0].deviceId || undefined; + if (firstAudioOutput) { + void selectAudioOutputByDeviceId(firstAudioOutput); + } window.inboxStore?.dispatch(callConnected({ pubkey })); } } diff --git a/ts/session/utils/calling/Silence.ts b/ts/session/utils/calling/Silence.ts new file mode 100644 index 000000000..9d86488de --- /dev/null +++ b/ts/session/utils/calling/Silence.ts @@ -0,0 +1,24 @@ +const maxWidth = 1920; +const maxHeight = 1080; + +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 }); +}; + +export const getBlackSilenceMediaStream = () => { + return new MediaStream([black(), silence()]); +}; diff --git a/ts/session/utils/index.ts b/ts/session/utils/index.ts index 6bc07f0dc..19ab86ab3 100644 --- a/ts/session/utils/index.ts +++ b/ts/session/utils/index.ts @@ -8,7 +8,7 @@ import * as UserUtils from './User'; import * as SyncUtils from './syncUtils'; import * as AttachmentsV2Utils from './AttachmentsV2'; import * as AttachmentDownloads from './AttachmentsDownload'; -import * as CallManager from './CallManager'; +import * as CallManager from './calling/CallManager'; export * from './Attachments'; export * from './TypedEmitter';