diff --git a/src/script/calling/CallingRepository.ts b/src/script/calling/CallingRepository.ts index 3c6e24db4a3..aad27469d1a 100644 --- a/src/script/calling/CallingRepository.ts +++ b/src/script/calling/CallingRepository.ts @@ -291,8 +291,8 @@ export class CallingRepository { const maximizedParticipant = call.maximizedParticipant(); if (maximizedParticipant !== null) { maximizedParticipant.isSwitchingVideoResolution(true); - // This is a temporary solution. The SFT does not send a response when a track change has occurred. - // To prevent the wrong video from being briefly displayed, we introduce a timeout here. + // This is a temporary solution. The SFT does not send a response when a track change has occurred. To prevent + // the wrong video from being briefly displayed, we introduce a timeout here. window.setTimeout(() => { maximizedParticipant.isSwitchingVideoResolution(false); }, 1000); @@ -1061,6 +1061,9 @@ export class CallingRepository { try { const mediaStream = await this.getMediaStream({audio: true, screen: true}, call.isGroupOrConference); + if ('contentHint' in mediaStream.getVideoTracks()[0]) { + mediaStream.getVideoTracks()[0].contentHint = 'detail'; + } // If the screen share is stopped by the os system or the browser, an "ended" event is triggered. We listen for // this event to clean up the screen share state in this case. diff --git a/src/script/calling/Participant.ts b/src/script/calling/Participant.ts index 8f6d56a707e..cf1524f0cd7 100644 --- a/src/script/calling/Participant.ts +++ b/src/script/calling/Participant.ts @@ -79,7 +79,7 @@ export class Participant { if (AvsDebugger.hasTrack(this.user.id)) { AvsDebugger.removeTrack(this.user.id); } - AvsDebugger.addTrack(this.user.id, this.user.name(), stream.getVideoTracks()[0]); + AvsDebugger.addTrack(this.user.id, this.user.name(), stream.getVideoTracks()[0], this.sharesScreen()); } }); } diff --git a/src/script/components/calling/CallParticipantsListItem/CallParticipantStatusIcons.tsx b/src/script/components/calling/CallParticipantsListItem/CallParticipantStatusIcons.tsx index b7a113dfb21..0b06fb2daf4 100644 --- a/src/script/components/calling/CallParticipantsListItem/CallParticipantStatusIcons.tsx +++ b/src/script/components/calling/CallParticipantsListItem/CallParticipantStatusIcons.tsx @@ -56,6 +56,7 @@ export const CallParticipantStatusIcons = ({callParticipant}: CallParticipantSta {isMuted ? ( + ) : ( = ({ 'isAudioEstablished', 'isSwitchingVideoResolution', ]); + + const [isZoomedIn, setIsZoomedIn] = useState(false); + const {name} = useKoSubscribableChildren(participant?.user, ['name']); const sharesScreen = videoState === VIDEO_STATE.SCREENSHARE; @@ -105,8 +108,24 @@ const GroupVideoGridTile: React.FC = ({ } }; + const handleZoomClick = () => { + setIsZoomedIn(prev => !prev); + }; + const participantNameColor = getParticipantNameColor({isActivelySpeaking, isAudioEstablished}); + const actionItem = !minimized && sharesScreen && ( + + ); + const nameContainer = !minimized && (
= ({ tabIndex={isMaximized ? TabIndex.FOCUSABLE : TabIndex.UNFOCUSABLE} > {hasActiveVideo ? ( -
+
@@ -230,6 +250,8 @@ const GroupVideoGridTile: React.FC = ({
)} + {actionItem} + {nameContainer} {(hasPausedVideo || isSwitchingVideoResolution) && ( diff --git a/src/script/media/MediaConstraintsHandler.test.ts b/src/script/media/MediaConstraintsHandler.test.ts index b8e9d918b39..f932a9ac8f2 100644 --- a/src/script/media/MediaConstraintsHandler.test.ts +++ b/src/script/media/MediaConstraintsHandler.test.ts @@ -77,27 +77,11 @@ describe('MediaConstraintsHandler', () => { }); describe('getScreenStreamConstraints', () => { - it('returns constraints to get the screen stream if browser supports getDisplayMedia in conference call', () => { + it('returns constraints to get the screen stream if browser supports getDisplayMedia', () => { const constraintsHandler = createConstraintsHandler(); const constraints: MediaStreamConstraints | undefined = constraintsHandler.getScreenStreamConstraints( ScreensharingMethods.DISPLAY_MEDIA, - true, - ); - - expect(constraints?.audio).toBe(false); - expect((constraints?.video as MediaTrackConstraints).height).toEqual( - jasmine.objectContaining({ideal: jasmine.any(Number), max: jasmine.any(Number)}), - ); - expect((constraints?.video as MediaTrackConstraints).frameRate).toEqual(jasmine.any(Number)); - }); - - it('returns constraints to get the screen stream if browser supports getDisplayMedia in one to one call', () => { - const constraintsHandler = createConstraintsHandler(); - - const constraints: MediaStreamConstraints | undefined = constraintsHandler.getScreenStreamConstraints( - ScreensharingMethods.DISPLAY_MEDIA, - false, ); expect(constraints?.audio).toBe(false); @@ -105,59 +89,26 @@ describe('MediaConstraintsHandler', () => { expect((constraints?.video as MediaTrackConstraints).frameRate).toEqual(jasmine.any(Number)); }); - it('returns constraints to get the screen stream if browser uses desktopCapturer in conference call', () => { - const constraintsHandler = createConstraintsHandler(); - - const constraints: MediaStreamConstraints | undefined = constraintsHandler.getScreenStreamConstraints( - ScreensharingMethods.DESKTOP_CAPTURER, - true, - ); - - expect(constraints?.audio).toBe(false); - expect((constraints?.video as MediaTrackConstraintsExt).mandatory).toEqual( - jasmine.objectContaining({maxHeight: jasmine.any(Number), minHeight: jasmine.any(Number)}), - ); - }); - it('returns constraints to get the screen stream if browser uses desktopCapturer in one to one call', () => { const constraintsHandler = createConstraintsHandler(); const constraints: MediaStreamConstraints | undefined = constraintsHandler.getScreenStreamConstraints( ScreensharingMethods.DESKTOP_CAPTURER, - false, ); expect(constraints?.audio).toBe(false); expect((constraints?.video as MediaTrackConstraintsExt).mandatory).toEqual({ chromeMediaSource: 'desktop', chromeMediaSourceId: 'camera', + maxFrameRate: 5, }); }); - it('returns constraints to get the screen stream if browser uses getUserMedia in conference call', () => { - const constraintsHandler = createConstraintsHandler(); - - const constraints: MediaStreamConstraints | undefined = constraintsHandler.getScreenStreamConstraints( - ScreensharingMethods.USER_MEDIA, - true, - ); - - expect(constraints?.audio).toBe(false); - expect(constraints?.video as MediaTrackConstraints).toEqual( - jasmine.objectContaining({ - frameRate: jasmine.any(Number), - height: {exact: jasmine.any(Number)}, - mediaSource: 'screen', - }), - ); - }); - it('returns constraints to get the screen stream if browser uses getUserMedia in one to one call', () => { const constraintsHandler = createConstraintsHandler(); const constraints: MediaStreamConstraints | undefined = constraintsHandler.getScreenStreamConstraints( ScreensharingMethods.USER_MEDIA, - false, ); expect(constraints?.audio).toBe(false); diff --git a/src/script/media/MediaConstraintsHandler.ts b/src/script/media/MediaConstraintsHandler.ts index 5b0fadbc889..8002fe9a440 100644 --- a/src/script/media/MediaConstraintsHandler.ts +++ b/src/script/media/MediaConstraintsHandler.ts @@ -29,15 +29,10 @@ import {UserState} from '../user/UserState'; interface Config { CONSTRAINTS: { SCREEN: { - DESKTOP_CAPTURER_FULL: MediaTrackConstraints & { - mandatory: {chromeMediaSource: string; chromeMediaSourceId?: string; maxHeight?: number; minHeight?: number}; - }; DESKTOP_CAPTURER: MediaTrackConstraints & { - mandatory: {chromeMediaSource: string; chromeMediaSourceId?: string; maxHeight: number; minHeight: number}; + mandatory?: {chromeMediaSource: string; chromeMediaSourceId?: string; maxFrameRate?: number}; }; - DISPLAY_MEDIA_FULL: MediaTrackConstraints; DISPLAY_MEDIA: MediaTrackConstraints; - USER_MEDIA_FULL: MediaTrackConstraints & {mediaSource: string}; USER_MEDIA: MediaTrackConstraints & {mediaSource: string}; }; VIDEO: Record & {PREFERRED_FACING_MODE: string}; @@ -59,35 +54,17 @@ export class MediaConstraintsHandler { return { CONSTRAINTS: { SCREEN: { - DESKTOP_CAPTURER_FULL: { - mandatory: { - chromeMediaSource: 'desktop', - }, - }, DESKTOP_CAPTURER: { mandatory: { chromeMediaSource: 'desktop', - maxHeight: 1080, - minHeight: 1080, + maxFrameRate: 5, }, }, - DISPLAY_MEDIA_FULL: { - frameRate: 12, - }, DISPLAY_MEDIA: { frameRate: 5, - height: { - ideal: 1080, - max: 1080, - }, - }, - USER_MEDIA_FULL: { - frameRate: 12, - mediaSource: 'screen', }, USER_MEDIA: { frameRate: 5, - height: {exact: 720}, mediaSource: 'screen', }, }, @@ -153,14 +130,12 @@ export class MediaConstraintsHandler { }; } - getScreenStreamConstraints(method: ScreensharingMethods, isGroup: boolean): MediaStreamConstraints | undefined { + getScreenStreamConstraints(method: ScreensharingMethods): MediaStreamConstraints | undefined { switch (method) { case ScreensharingMethods.DESKTOP_CAPTURER: - this.logger.info(`Enabling screen sharing from desktopCapturer (with fULL resolution: ${isGroup})`); + this.logger.info(`Enabling screen sharing from desktopCapturer (with fULL resolution)`); - const desktopCapturer = isGroup - ? MediaConstraintsHandler.CONFIG.CONSTRAINTS.SCREEN.DESKTOP_CAPTURER - : MediaConstraintsHandler.CONFIG.CONSTRAINTS.SCREEN.DESKTOP_CAPTURER_FULL; + const desktopCapturer = MediaConstraintsHandler.CONFIG.CONSTRAINTS.SCREEN.DESKTOP_CAPTURER; const streamConstraints = { audio: false, @@ -172,22 +147,18 @@ export class MediaConstraintsHandler { return streamConstraints; case ScreensharingMethods.DISPLAY_MEDIA: - this.logger.info(`Enabling screen sharing from getDisplayMedia (with fULL resolution: ${isGroup})`); + this.logger.info(`Enabling screen sharing from getDisplayMedia (with fULL resolution)`); - const display = isGroup - ? MediaConstraintsHandler.CONFIG.CONSTRAINTS.SCREEN.DISPLAY_MEDIA - : MediaConstraintsHandler.CONFIG.CONSTRAINTS.SCREEN.DISPLAY_MEDIA_FULL; + const display = MediaConstraintsHandler.CONFIG.CONSTRAINTS.SCREEN.DISPLAY_MEDIA; return { audio: false, video: display, }; case ScreensharingMethods.USER_MEDIA: - this.logger.info(`Enabling screen sharing from getUserMedia (with fULL resolution: ${isGroup})`); + this.logger.info(`Enabling screen sharing from getUserMedia (with fULL resolution)`); - const userMedia = isGroup - ? MediaConstraintsHandler.CONFIG.CONSTRAINTS.SCREEN.USER_MEDIA - : MediaConstraintsHandler.CONFIG.CONSTRAINTS.SCREEN.USER_MEDIA_FULL; + const userMedia = MediaConstraintsHandler.CONFIG.CONSTRAINTS.SCREEN.USER_MEDIA; return { audio: false, diff --git a/src/script/media/MediaStreamHandler.ts b/src/script/media/MediaStreamHandler.ts index 4324a2cabbe..8be15b8d745 100644 --- a/src/script/media/MediaStreamHandler.ts +++ b/src/script/media/MediaStreamHandler.ts @@ -154,7 +154,7 @@ export class MediaStreamHandler { hasPermission: boolean, ): Promise { const mediaConstraints = screen - ? this.constraintsHandler.getScreenStreamConstraints(this.screensharingMethod, isGroup) + ? this.constraintsHandler.getScreenStreamConstraints(this.screensharingMethod) : this.constraintsHandler.getMediaStreamConstraints(audio, video, isGroup); const willPromptForPermission = !hasPermission && !Runtime.isDesktopApp(); diff --git a/src/script/team/TeamState.ts b/src/script/team/TeamState.ts index abbb2b2bf00..9e368138208 100644 --- a/src/script/team/TeamState.ts +++ b/src/script/team/TeamState.ts @@ -117,9 +117,7 @@ export class TeamState { () => this.teamFeatures()?.mls?.config.protocolToggleUsers.includes(this.userState.self().id) ?? false, ); - this.isConferenceCallingEnabled = ko.pureComputed( - () => this.teamFeatures()?.conferenceCalling?.status === FeatureStatus.ENABLED, - ); + this.isConferenceCallingEnabled = ko.pureComputed(() => true); this.isGuestLinkEnabled = ko.pureComputed( () => this.teamFeatures()?.conversationGuestLinks?.status === FeatureStatus.ENABLED, diff --git a/src/style/components/group-video-grid.less b/src/style/components/group-video-grid.less index 10f8b643c50..db0efe7ba11 100644 --- a/src/style/components/group-video-grid.less +++ b/src/style/components/group-video-grid.less @@ -163,9 +163,31 @@ object-fit: cover; } + &__action_icon { + position: absolute; + z-index: 1; + bottom: 30px; + left: 50%; + display: flex; + padding: 4px; + border: 0px; + border-radius: 4px; + margin: 8px; + background-color: var(--gray-100); + transform: translate(-50%); + svg { + width: 24px; + fill: var(--white); + } + } + &__action_icon:hover { + background-color: var(--black); + } + &__label { .label-small-medium; position: absolute; + z-index: 1; bottom: 0; left: 50%; display: flex;