From 391f8bae3623b93cd71225f240c23bd9039c8b97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrian=20Wei=C3=9F?= <77456193+aweiss-dev@users.noreply.github.com> Date: Wed, 4 Dec 2024 13:37:37 +0100 Subject: [PATCH 1/2] fix: make zod errors not hard crash (#18418) --- .../backup/CrossPlatformBackup/CPB.export.ts | 174 ++++++++++-------- .../backup/CrossPlatformBackup/data.schema.ts | 2 +- 2 files changed, 100 insertions(+), 76 deletions(-) diff --git a/src/script/backup/CrossPlatformBackup/CPB.export.ts b/src/script/backup/CrossPlatformBackup/CPB.export.ts index f2ec4418e59..0d1baf636c3 100644 --- a/src/script/backup/CrossPlatformBackup/CPB.export.ts +++ b/src/script/backup/CrossPlatformBackup/CPB.export.ts @@ -63,103 +63,127 @@ export const exportCPBHistoryFromDatabase = async ({ } // Taking care of conversations - const conversationsData = ConversationTableSchema.parse( + const { + success: conversationsSuccess, + data: conversationsData, + error: conversationsError, + } = ConversationTableSchema.safeParse( await exportTable({ backupService, table: conversationTable, preprocessor: streamProgress(preprocessConversations), }), ); - conversationsData.forEach(conversationData => - backupExporter.addConversation( - new BackUpConversation( - new BackupQualifiedId(conversationData.id, conversationData.domain), - conversationData.name ?? '', + if (conversationsSuccess) { + conversationsData.forEach(conversationData => + backupExporter.addConversation( + new BackUpConversation( + new BackupQualifiedId(conversationData.id, conversationData.domain), + conversationData.name ?? '', + ), ), - ), - ); + ); + } else { + CPBLogger.log('Conversation data schema validation failed', conversationsError); + } // Taking care of users - const usersData = UserTableSchema.parse( + const { + success: usersSuccess, + data: usersData, + error: usersError, + } = UserTableSchema.safeParse( await exportTable({backupService, table: usersTable, preprocessor: streamProgress(preprocessUsers)}), ); - usersData.forEach(userData => - backupExporter.addUser( - new BackupUser( - new BackupQualifiedId(userData?.qualified_id?.id ?? userData.id, userData?.qualified_id?.domain ?? ''), - userData.name, - userData.handle ?? '', + if (usersSuccess) { + usersData.forEach(userData => + backupExporter.addUser( + new BackupUser( + new BackupQualifiedId(userData?.qualified_id?.id ?? userData.id, userData?.qualified_id?.domain ?? ''), + userData.name, + userData.handle ?? '', + ), ), - ), - ); + ); + } else { + CPBLogger.log('User data schema validation failed', usersError); + } // Taking care of events - const eventsData = EventTableSchema.parse( + const { + success: eventsSuccess, + data: eventsData, + error: eventsError, + } = EventTableSchema.safeParse( await exportTable({backupService, table: eventsTable, preprocessor: streamProgress(preprocessEvents)}), ); - eventsData.forEach(eventData => { - const {type} = eventData; - // ToDo: Add support for other types of messages and different types of content. Also figure out which fields are required. - if (!isSupportedEventType(type)) { - // eslint-disable-next-line no-console - CPBLogger.log('Unsupported message type', type); - return; - } - if (!eventData.id) { - // eslint-disable-next-line no-console - CPBLogger.log('Event without id', eventData); - return; - } - - const id = eventData.id; - const conversationId = new BackupQualifiedId( - eventData.qualified_conversation.id, - eventData.qualified_conversation.domain ?? '', - ); - const senderUserId = new BackupQualifiedId( - eventData.qualified_from?.id ?? eventData.from ?? '', - eventData.qualified_from?.domain ?? '', - ); - const senderClientId = eventData.from_client_id ?? ''; - const creationDate = new BackupDateTime(new Date(eventData.time)); - // for debugging purposes - const webPrimaryKey = eventData.primary_key; - - if (isAssetAddEvent(type)) { - const {success, error, data} = AssetContentSchema.safeParse(eventData.data); - if (!success) { - CPBLogger.log('Asset data schema validation failed', error); + if (eventsSuccess) { + eventsData.forEach(eventData => { + const {type} = eventData; + // ToDo: Add support for other types of messages and different types of content. Also figure out which fields are required. + if (!isSupportedEventType(type)) { + // eslint-disable-next-line no-console + CPBLogger.log('Unsupported message type', type); + return; + } + if (!eventData.id) { + // eslint-disable-next-line no-console + CPBLogger.log('Event without id', eventData); return; } - const metaData = buildMetaData(data.content_type, data.info); - - CPBLogger.log('metaData', metaData, data.content_type); - - const asset = new BackupMessageContent.Asset( - data.content_type, - data.content_length, - data.info.name, - transformObjectToArray(data.otr_key), - transformObjectToArray(data.sha256), - data.key, - data.token, - data.domain, - null, - metaData, + const id = eventData.id; + const conversationId = new BackupQualifiedId( + eventData.qualified_conversation.id, + eventData.qualified_conversation.domain ?? '', ); - backupExporter.addMessage( - new BackupMessage(id, conversationId, senderUserId, senderClientId, creationDate, asset, webPrimaryKey), + const senderUserId = new BackupQualifiedId( + eventData.qualified_from?.id ?? eventData.from ?? '', + eventData.qualified_from?.domain ?? '', ); - } + const senderClientId = eventData.from_client_id ?? ''; + const creationDate = new BackupDateTime(new Date(eventData.time)); + // for debugging purposes + const webPrimaryKey = eventData.primary_key; + + if (isAssetAddEvent(type)) { + const {success, error, data} = AssetContentSchema.safeParse(eventData.data); + if (!success) { + CPBLogger.log('Asset data schema validation failed', error); + return; + } + + const metaData = buildMetaData(data.content_type, data.info); + + CPBLogger.log('metaData', metaData, data.content_type); + + const asset = new BackupMessageContent.Asset( + data.content_type, + data.content_length, + data.info.name, + transformObjectToArray(data.otr_key), + transformObjectToArray(data.sha256), + data.key, + data.token, + data.domain, + null, + metaData, + ); + backupExporter.addMessage( + new BackupMessage(id, conversationId, senderUserId, senderClientId, creationDate, asset, webPrimaryKey), + ); + } - if (isMessageAddEvent(type) && eventData.data?.content) { - const text = new BackupMessageContent.Text(eventData.data.content); - backupExporter.addMessage( - new BackupMessage(id, conversationId, senderUserId, senderClientId, creationDate, text, webPrimaryKey), - ); - } - }); + if (isMessageAddEvent(type) && eventData.data?.content) { + const text = new BackupMessageContent.Text(eventData.data.content); + backupExporter.addMessage( + new BackupMessage(id, conversationId, senderUserId, senderClientId, creationDate, text, webPrimaryKey), + ); + } + }); + } else { + CPBLogger.log('Event data schema validation failed', eventsError); + } return backupExporter.serialize(); }; diff --git a/src/script/backup/CrossPlatformBackup/data.schema.ts b/src/script/backup/CrossPlatformBackup/data.schema.ts index f4700ac0700..cd6d3066146 100644 --- a/src/script/backup/CrossPlatformBackup/data.schema.ts +++ b/src/script/backup/CrossPlatformBackup/data.schema.ts @@ -25,7 +25,7 @@ const ConversationSchema = zod.object({ accessRoleV2: zod.string().optional(), archived_state: zod.boolean(), archived_timestamp: zod.number(), - cipher_suite: zod.string().optional(), + cipher_suite: zod.number().optional(), creator: zod.string(), domain: zod.string(), group_id: zod.string().optional(), From ce47d8fd5d906e4321c1a3c8059497cec51ad8c1 Mon Sep 17 00:00:00 2001 From: Enrico Schwendig Date: Wed, 4 Dec 2024 15:10:00 +0100 Subject: [PATCH 2/2] feat: Automatic Video Quality Adjustments [WPB-11479] (#18253) * feat: request the streams quality whenever the video display resolution changes * feat: add track debugger and fix load resolution on max mode --- package.json | 3 +- src/script/calling/Call.ts | 2 +- src/script/calling/CallState.ts | 13 ++- src/script/calling/CallingRepository.ts | 81 +++++++++++++++++-- src/script/calling/Participant.ts | 19 ++++- .../ConfigToolbar/ConfigToolbar.tsx | 27 +++++++ .../components/calling/GroupVideoGridTile.tsx | 30 ++++--- src/script/team/TeamState.ts | 1 + src/script/util/DebugUtil.ts | 39 +++++++++ .../calling/videoGridHandlerSpec.js | 2 +- yarn.lock | 38 +++++++-- 11 files changed, 227 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 2277deadefc..4d1ff4528ec 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "@lexical/history": "0.20.2", "@lexical/react": "0.20.2", "@mediapipe/tasks-vision": "0.10.18", - "@wireapp/avs": "9.10.16", + "@wireapp/avs": "10.0.4", + "@wireapp/avs-debugger": "0.0.5", "@wireapp/commons": "5.4.0", "@wireapp/core": "46.11.4", "@wireapp/react-ui-kit": "9.28.0", diff --git a/src/script/calling/Call.ts b/src/script/calling/Call.ts index 460247d8698..348a7caa5b2 100644 --- a/src/script/calling/Call.ts +++ b/src/script/calling/Call.ts @@ -60,7 +60,7 @@ export class Call { public blockMessages: boolean = false; public currentPage: ko.Observable = ko.observable(0); public pages: ko.ObservableArray = ko.observableArray(); - readonly maximizedParticipant: ko.Observable; + public readonly maximizedParticipant: ko.Observable; public readonly isActive: ko.PureComputed; private readonly audios: Record = {}; diff --git a/src/script/calling/CallState.ts b/src/script/calling/CallState.ts index f87c2cccf95..234412ce3e1 100644 --- a/src/script/calling/CallState.ts +++ b/src/script/calling/CallState.ts @@ -70,8 +70,9 @@ export class CallState { public readonly activeCalls: ko.PureComputed; public readonly joinedCall: ko.PureComputed; public readonly activeCallViewTab = ko.observable(CallViewTab.ALL); - readonly hasAvailableScreensToShare: ko.PureComputed; - readonly isSpeakersViewActive: ko.PureComputed; + public readonly hasAvailableScreensToShare: ko.PureComputed; + public readonly isSpeakersViewActive: ko.PureComputed; + public readonly isMaximisedViewActive: ko.PureComputed; public readonly viewMode = ko.observable(CallingViewMode.MINIMIZED); public readonly detachedWindow = ko.observable(null); public readonly isScreenSharingSourceFromDetachedWindow = ko.observable(false); @@ -96,6 +97,14 @@ export class CallState { }); this.isSpeakersViewActive = ko.pureComputed(() => this.activeCallViewTab() === CallViewTab.SPEAKERS); + this.isMaximisedViewActive = ko.pureComputed(() => { + const call = this.joinedCall(); + if (!call) { + return false; + } + return call.maximizedParticipant() !== null; + }); + this.hasAvailableScreensToShare = ko.pureComputed( () => this.selectableScreens().length > 0 || this.selectableWindows().length > 0, ); diff --git a/src/script/calling/CallingRepository.ts b/src/script/calling/CallingRepository.ts index 92ee7520302..128e0ba33bc 100644 --- a/src/script/calling/CallingRepository.ts +++ b/src/script/calling/CallingRepository.ts @@ -43,6 +43,7 @@ import { LOG_LEVEL, QUALITY, REASON, + RESOLUTION, STATE as CALL_STATE, VIDEO_STATE, VSTREAMS, @@ -50,6 +51,7 @@ import { WcallClient, WcallMember, } from '@wireapp/avs'; +import {AvsDebugger} from '@wireapp/avs-debugger'; import {Runtime} from '@wireapp/commons'; import {WebAppEvents} from '@wireapp/webapp-events'; @@ -256,6 +258,7 @@ export class CallingRepository { this.onChooseScreen = (deviceId: string) => {}; + // Request the video streams whenever the mode changes to active speaker ko.computed(() => { const call = this.callState.joinedCall(); if (!call) { @@ -263,7 +266,39 @@ export class CallingRepository { } const isSpeakersViewActive = this.callState.isSpeakersViewActive(); if (isSpeakersViewActive) { - this.requestVideoStreams(call.conversation.qualifiedId, call.activeSpeakers()); + const videoQuality = call.activeSpeakers().length > 2 ? RESOLUTION.LOW : RESOLUTION.HIGH; + + const speakes = call.activeSpeakers(); + speakes.forEach(p => { + // 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. + p.isSwitchingVideoResolution(true); + window.setTimeout(() => { + p.isSwitchingVideoResolution(false); + }, 1000); + }); + + this.requestVideoStreams(call.conversation.qualifiedId, speakes, videoQuality); + } + }); + + // Request the video streams whenever toggle display maximised Participant. + ko.computed(() => { + const call = this.callState.joinedCall(); + if (!call) { + return; + } + 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. + window.setTimeout(() => { + maximizedParticipant.isSwitchingVideoResolution(false); + }, 1000); + this.requestVideoStreams(call.conversation.qualifiedId, [maximizedParticipant], RESOLUTION.HIGH); + } else { + this.requestCurrentPageVideoStreams(call); } }); } @@ -1204,6 +1239,7 @@ export class CallingRepository { const conversationIdStr = this.serializeQualifiedId(conversationId); this.wCall?.end(this.wUser, conversationIdStr); callingSubscriptions.removeCall(conversationId); + AvsDebugger.reset(); }; private readonly leaveMLSConference = async (conversationId: QualifiedId) => { @@ -1300,24 +1336,47 @@ export class CallingRepository { this.wCall?.reject(this.wUser, this.serializeQualifiedId(conversationId)); } + /** + * This method monitors every change in the call and is therefore the main method for handling video requests. + * These changes include mute/unmute, screen sharing, or camera switching, joining or leaving of participants, or... + * @param call + * @param newPage + */ changeCallPage(call: Call, newPage: number): void { call.currentPage(newPage); - if (!this.callState.isSpeakersViewActive()) { + if (!this.callState.isSpeakersViewActive() && !this.callState.isMaximisedViewActive()) { this.requestCurrentPageVideoStreams(call); } } + /** + * This method queries streams for the participants who are displayed on the active page! This can include up to nine + * participants and is used when flipping pages or starting a call. + * @param call + */ requestCurrentPageVideoStreams(call: Call): void { - const currentPageParticipants = call.pages()[call.currentPage()]; - this.requestVideoStreams(call.conversation.qualifiedId, currentPageParticipants); + const currentPageParticipants = call.pages()[call.currentPage()] ?? []; + const videoQuality: RESOLUTION = currentPageParticipants.length <= 2 ? RESOLUTION.HIGH : RESOLUTION.LOW; + this.requestVideoStreams(call.conversation.qualifiedId, currentPageParticipants, videoQuality); } - requestVideoStreams(conversationId: QualifiedId, participants: Participant[]) { + requestVideoStreams(conversationId: QualifiedId, participants: Participant[], videoQuality: RESOLUTION) { + if (participants.length === 0) { + return; + } + // Filter myself out and do not request my own stream. + const requestParticipants = participants.filter(p => !this.isSelfUser(p)); + if (requestParticipants.length === 0) { + return; + } + const convId = this.serializeQualifiedId(conversationId); + const payload = { - clients: participants.map(participant => ({ + clients: requestParticipants.map(participant => ({ clientid: participant.clientId, userid: this.serializeQualifiedId(participant.user.qualifiedId), + quality: videoQuality, })), convid: convId, }; @@ -1355,6 +1414,7 @@ export class CallingRepository { const conversationIdStr = this.serializeQualifiedId(conversationId); delete this.poorCallQualityUsers[conversationIdStr]; this.wCall?.end(this.wUser, conversationIdStr); + AvsDebugger.reset(); }; muteCall(call: Call, shouldMute: boolean, reason?: MuteState): void { @@ -2262,6 +2322,8 @@ export class CallingRepository { this.callState .calls() .forEach((call: Call) => this.wCall?.end(this.wUser, this.serializeQualifiedId(call.conversation.qualifiedId))); + + AvsDebugger.reset(); this.wCall?.destroy(this.wUser); } @@ -2312,6 +2374,13 @@ export class CallingRepository { PrimaryModal.show(PrimaryModal.type.ACKNOWLEDGE, modalOptions); } + private isSelfUser(participant: Participant): boolean { + if (this.selfUser == null || this.selfClientId == null) { + return false; + } + return participant.doesMatchIds(this.selfUser.qualifiedId, this.selfClientId); + } + //############################################################################## // Logging //############################################################################## diff --git a/src/script/calling/Participant.ts b/src/script/calling/Participant.ts index cfe30e68719..0a6165e703c 100644 --- a/src/script/calling/Participant.ts +++ b/src/script/calling/Participant.ts @@ -18,9 +18,10 @@ */ import {QualifiedId} from '@wireapp/api-client/lib/user'; -import ko, {observable, pureComputed} from 'knockout'; +import ko, {computed, observable, pureComputed} from 'knockout'; import {VIDEO_STATE} from '@wireapp/avs'; +import {AvsDebugger} from '@wireapp/avs-debugger'; import {matchQualifiedIds} from 'Util/QualifiedId'; @@ -39,6 +40,7 @@ export class Participant { public readonly hasPausedVideo: ko.PureComputed; public readonly sharesScreen: ko.PureComputed; public readonly sharesCamera: ko.PureComputed; + public readonly isSwitchingVideoResolution = observable(false); public readonly startedScreenSharingAt = observable(0); public readonly isActivelySpeaking = observable(false); public readonly isSendingVideo: ko.PureComputed; @@ -67,6 +69,18 @@ export class Participant { this.isSendingVideo = pureComputed(() => { return this.videoState() !== VIDEO_STATE.STOPPED; }); + this.isSwitchingVideoResolution(false); + + computed(() => { + const stream = this.videoStream(); + + if (stream && stream.getVideoTracks().length > 0) { + if (AvsDebugger.hasTrack(this.user.id)) { + AvsDebugger.removeTrack(this.user.id); + } + AvsDebugger.addTrack(this.user.id, this.user.name(), stream.getVideoTracks()[0]); + } + }); } public releaseBlurredVideoStream(): void { @@ -141,6 +155,9 @@ export class Participant { track.stop(); } mediaStream.removeTrack(track); + if (track.kind == 'video' && AvsDebugger.hasTrack(this.user.id)) { + AvsDebugger.removeTrack(this.user.id); + } }); } } diff --git a/src/script/components/ConfigToolbar/ConfigToolbar.tsx b/src/script/components/ConfigToolbar/ConfigToolbar.tsx index d8dda991200..78cf4200caa 100644 --- a/src/script/components/ConfigToolbar/ConfigToolbar.tsx +++ b/src/script/components/ConfigToolbar/ConfigToolbar.tsx @@ -37,6 +37,7 @@ export function ConfigToolbar() { const messageCountRef = useRef(0); // For the message count const [prefix, setPrefix] = useState('Message -'); // Prefix input const wrapperRef = useRef(null); + const [avsDebuggerEnabled, setAvsDebuggerEnabled] = useState(!!window.wire?.app?.debug?.isEnabledAvsDebugger()); // // Toggle config tool on 'cmd/ctrl + shift + 2' useEffect(() => { @@ -160,6 +161,25 @@ export function ConfigToolbar() { useClickOutside(wrapperRef, () => setShowConfig(false)); + const handleAvsEnable = (isChecked: boolean) => { + setAvsDebuggerEnabled(!!window.wire?.app?.debug?.enableAvsDebugger(isChecked)); + }; + + const renderAvsSwitch = (value: boolean) => { + return ( +
+ + handleAvsEnable(isChecked)} + /> +
+ ); + }; + if (!showConfig) { return null; } @@ -173,11 +193,18 @@ export function ConfigToolbar() {
{renderConfig(configFeaturesState)}
+
+

Debug Functions

+ +
{renderAvsSwitch(avsDebuggerEnabled)}
+ +
+

Message Automation

= ({ isMaximized, onTileDoubleClick, }) => { - const {isMuted, videoState, videoStream, blurredVideoStream, isActivelySpeaking, isAudioEstablished} = - useKoSubscribableChildren(participant, [ - 'isMuted', - 'videoStream', - 'blurredVideoStream', - 'isActivelySpeaking', - 'videoState', - 'isAudioEstablished', - ]); + const { + isMuted, + videoState, + videoStream, + blurredVideoStream, + isActivelySpeaking, + isAudioEstablished, + isSwitchingVideoResolution, + } = useKoSubscribableChildren(participant, [ + 'isMuted', + 'videoStream', + 'blurredVideoStream', + 'isActivelySpeaking', + 'videoState', + 'isAudioEstablished', + 'isSwitchingVideoResolution', + ]); const {name} = useKoSubscribableChildren(participant?.user, ['name']); const sharesScreen = videoState === VIDEO_STATE.SCREENSHARE; @@ -220,7 +228,7 @@ const GroupVideoGridTile: React.FC = ({ {nameContainer} - {hasPausedVideo && ( + {(hasPausedVideo || isSwitchingVideoResolution) && (
@@ -232,7 +240,7 @@ const GroupVideoGridTile: React.FC = ({ css={{fontsize: minimized ? '0.6875rem' : '0.875rem'}} data-uie-name="status-video-paused" > - {t('videoCallPaused')} + {hasPausedVideo ? t('videoCallPaused') : t('videoCallParticipantConnecting')}
{nameContainer}
diff --git a/src/script/team/TeamState.ts b/src/script/team/TeamState.ts index f2293502164..abbb2b2bf00 100644 --- a/src/script/team/TeamState.ts +++ b/src/script/team/TeamState.ts @@ -120,6 +120,7 @@ export class TeamState { this.isConferenceCallingEnabled = ko.pureComputed( () => this.teamFeatures()?.conferenceCalling?.status === FeatureStatus.ENABLED, ); + this.isGuestLinkEnabled = ko.pureComputed( () => this.teamFeatures()?.conversationGuestLinks?.status === FeatureStatus.ENABLED, ); diff --git a/src/script/util/DebugUtil.ts b/src/script/util/DebugUtil.ts index 0dc1a6f04cb..2dea7977747 100644 --- a/src/script/util/DebugUtil.ts +++ b/src/script/util/DebugUtil.ts @@ -37,7 +37,10 @@ import keyboardjs from 'keyboardjs'; import {$createTextNode, $getRoot, LexicalEditor} from 'lexical'; import {container} from 'tsyringe'; +import {AvsDebugger} from '@wireapp/avs-debugger'; + import {showAppNotification} from 'Components/AppNotification'; +import {getStorage} from 'Util/localStorage'; import {getLogger, Logger} from 'Util/Logger'; import {KEY} from './KeyboardUtil'; @@ -112,6 +115,9 @@ export class DebugUtil { this.logger = getLogger('DebugUtil'); keyboardjs.bind(['command+shift+1', 'ctrl+shift+1'], this.toggleDebugUi); + + // If the debugger marked as active in the LocalStorage, install the Web Component + this.setupAvsDebugger(); } async importEvents() { @@ -253,6 +259,39 @@ export class DebugUtil { this.propertiesRepository.savePreference(PROPERTIES_TYPE.CALL.PUSH_TO_TALK_KEY, key); } + setupAvsDebugger() { + if (this.isEnabledAvsDebugger()) { + this.enableAvsDebugger(true); + } + } + + enableAvsDebugger(enable: boolean): boolean { + const storage = getStorage(); + + if (storage === undefined) { + return false; + } + if (enable) { + AvsDebugger.initTrackDebugger(); + } else { + AvsDebugger.destructTrackDebugger(); + } + + storage.setItem('avs-debugger-enabled', `${enable}`); + return enable; + } + + isEnabledAvsDebugger(): boolean { + const storage = getStorage(); + + if (storage === undefined) { + return false; + } + + const isEnabled = storage.getItem('avs-debugger-enabled'); + return isEnabled === 'true'; + } + /** Used by QA test automation. */ blockAllConnections(): Promise { const blockUsers = this.userState.users().map(userEntity => this.connectionRepository.blockUser(userEntity)); diff --git a/test/unit_tests/calling/videoGridHandlerSpec.js b/test/unit_tests/calling/videoGridHandlerSpec.js index a9f4a10d20d..4e66d582e1a 100644 --- a/test/unit_tests/calling/videoGridHandlerSpec.js +++ b/test/unit_tests/calling/videoGridHandlerSpec.js @@ -71,7 +71,7 @@ describe('videoGridHandler', () => { selfUser.isMe = true; const selfParticipant = new Participant(selfUser, 'selfdevice'); selfParticipant.videoState(VIDEO_STATE.STARTED); - selfParticipant.videoStream({}); + selfParticipant.videoStream({getVideoTracks: () => []}); const call = new Call('', '', undefined, selfParticipant, CALL_TYPE.NORMAL, { currentAvailableDeviceId: { audiooutput: ko.pureComputed(() => 'test'), diff --git a/yarn.lock b/yarn.lock index df279eb8a92..0578e3502e8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5969,10 +5969,21 @@ __metadata: languageName: node linkType: hard -"@wireapp/avs@npm:9.10.16": - version: 9.10.16 - resolution: "@wireapp/avs@npm:9.10.16" - checksum: 10/2ed7b43e5be5bfadfb8eb870cdfc2a758f835e87304e7c445114580831b63e720c6a96e12db7addd90b8581c2ffc424b1e65987ea4c34d9e19bb45aa5ba3701f +"@wireapp/avs-debugger@npm:0.0.5": + version: 0.0.5 + resolution: "@wireapp/avs-debugger@npm:0.0.5" + dependencies: + debug: "npm:^4.3.5" + uuid: "npm:^10.0.0" + webrtc-internals: "npm:^0.0.8" + checksum: 10/0ca37f7af760eab50c16ffafb964901190e4a4ed50d8ff3db8f3a1af25265c2534a1683cce2a52076c3b6994ad90e6e46c8b30845ba12d0c0f7a929f5f5d99de + languageName: node + linkType: hard + +"@wireapp/avs@npm:10.0.4": + version: 10.0.4 + resolution: "@wireapp/avs@npm:10.0.4" + checksum: 10/b584313806a95ffe732c9a4f62ea001f4871ee60e3a75830bd6dce176422c8eee43ae2be8ccff4642b1a3ed729508cc029f647219d04551523a77efe890370fe languageName: node linkType: hard @@ -18223,6 +18234,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^10.0.0": + version: 10.0.0 + resolution: "uuid@npm:10.0.0" + bin: + uuid: dist/bin/uuid + checksum: 10/35aa60614811a201ff90f8ca5e9ecb7076a75c3821e17f0f5ff72d44e36c2d35fcbc2ceee9c4ac7317f4cc41895da30e74f3885e30313bee48fda6338f250538 + languageName: node + linkType: hard + "v8-compile-cache-lib@npm:^3.0.1": version: 3.0.1 resolution: "v8-compile-cache-lib@npm:3.0.1" @@ -18525,6 +18545,13 @@ __metadata: languageName: node linkType: hard +"webrtc-internals@npm:^0.0.8": + version: 0.0.8 + resolution: "webrtc-internals@npm:0.0.8" + checksum: 10/df26c0d602c1807ba33d1ef53d363ad3acbcb854438257efd1dac33511cfedbdf23b94b2afee534a9e8b277d23b395cbe6b06bc0d293cec38391ed8e3d9e2490 + languageName: node + linkType: hard + "whatwg-encoding@npm:^2.0.0": version: 2.0.0 resolution: "whatwg-encoding@npm:2.0.0" @@ -18732,7 +18759,8 @@ __metadata: "@types/uuid": "npm:^10.0.0" "@types/webpack-env": "npm:1.18.5" "@types/wicg-file-system-access": "npm:^2023.10.5" - "@wireapp/avs": "npm:9.10.16" + "@wireapp/avs": "npm:10.0.4" + "@wireapp/avs-debugger": "npm:0.0.5" "@wireapp/commons": "npm:5.4.0" "@wireapp/copy-config": "npm:2.2.10" "@wireapp/core": "npm:46.11.4"