Skip to content

Commit 60ad052

Browse files
authored
runfix: Allow blurred background to be enabled before the call starts (#17605)
1 parent e71004f commit 60ad052

File tree

4 files changed

+47
-43
lines changed

4 files changed

+47
-43
lines changed

src/script/calling/CallingRepository.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ import {EventSource} from '../event/EventSource';
8282
import type {MediaDevicesHandler} from '../media/MediaDevicesHandler';
8383
import type {MediaStreamHandler} from '../media/MediaStreamHandler';
8484
import {MediaType} from '../media/MediaType';
85-
import {applyBlur} from '../media/VideoBackgroundBlur';
8685
import {APIClient} from '../service/APIClientSingleton';
8786
import {Core} from '../service/CoreSingleton';
8887
import {TeamState} from '../team/TeamState';
@@ -131,6 +130,7 @@ export class CallingRepository {
131130
private readonly acceptVersionWarning: (conversationId: QualifiedId) => void;
132131
private readonly callLog: string[];
133132
private readonly logger: Logger;
133+
private enableBackgroundBlur = false;
134134
private avsVersion: number = 0;
135135
private incomingCallCallback: (call: Call) => void;
136136
private isReady: boolean = false;
@@ -276,13 +276,8 @@ export class CallingRepository {
276276
if (!videoFeed) {
277277
return;
278278
}
279-
let newVideoFeed = videoFeed;
280-
if (enable) {
281-
const blurredVideoStream = await applyBlur(videoFeed);
282-
// Keep a reference to the blurred stream in order to release it when the blur is disabled
283-
selfParticipant.blurredVideoStream(blurredVideoStream);
284-
newVideoFeed = blurredVideoStream.stream;
285-
}
279+
this.enableBackgroundBlur = enable;
280+
const newVideoFeed = enable ? ((await selfParticipant.setBlurredBackground(true)) as MediaStream) : videoFeed;
286281
this.changeMediaSource(newVideoFeed, MediaType.VIDEO, false);
287282
}
288283

@@ -530,10 +525,12 @@ export class CallingRepository {
530525
private async warmupMediaStreams(call: Call, audio: boolean, camera: boolean): Promise<boolean> {
531526
// if it's a video call we query the video user media in order to display the video preview
532527
try {
528+
const selfParticipant = call.getSelfParticipant();
533529
camera = this.teamState.isVideoCallingEnabled() ? camera : false;
534530
const mediaStream = await this.getMediaStream({audio, camera}, call.isGroupOrConference);
535531
if (call.state() !== CALL_STATE.NONE) {
536-
call.getSelfParticipant().updateMediaStream(mediaStream, true);
532+
selfParticipant.updateMediaStream(mediaStream, true);
533+
await selfParticipant.setBlurredBackground(this.enableBackgroundBlur);
537534
if (camera) {
538535
call.getSelfParticipant().videoState(VIDEO_STATE.STARTED);
539536
}
@@ -1780,8 +1777,9 @@ export class CallingRepository {
17801777
}
17811778
const mediaStream = await this.getMediaStream(missingStreams, call.isGroupOrConference);
17821779
this.mediaStreamQuery = undefined;
1783-
const newStream = selfParticipant.updateMediaStream(mediaStream, true);
1784-
return newStream;
1780+
selfParticipant.updateMediaStream(mediaStream, true);
1781+
await selfParticipant.setBlurredBackground(this.enableBackgroundBlur);
1782+
return selfParticipant.getMediaStream();
17851783
} catch (error) {
17861784
this.mediaStreamQuery = undefined;
17871785
this.logger.warn('Could not get mediaStream for call', error);

src/script/calling/Participant.ts

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,68 +18,72 @@
1818
*/
1919

2020
import {QualifiedId} from '@wireapp/api-client/lib/user';
21-
import ko from 'knockout';
21+
import ko, {observable, pureComputed} from 'knockout';
2222

2323
import {VIDEO_STATE} from '@wireapp/avs';
2424

2525
import {matchQualifiedIds} from 'Util/QualifiedId';
2626

2727
import {User} from '../entity/User';
28+
import {applyBlur} from '../media/VideoBackgroundBlur';
2829

2930
export type UserId = string;
3031
export type ClientId = string;
3132

3233
export class Participant {
3334
// Video
34-
public videoState: ko.Observable<VIDEO_STATE>;
35-
public videoStream: ko.Observable<MediaStream | undefined>;
36-
public blurredVideoStream = ko.observable<{stream: MediaStream; release: () => void} | undefined>(undefined);
37-
public hasActiveVideo: ko.PureComputed<boolean>;
38-
public hasPausedVideo: ko.PureComputed<boolean>;
39-
public sharesScreen: ko.PureComputed<boolean>;
40-
public sharesCamera: ko.PureComputed<boolean>;
41-
public startedScreenSharingAt: ko.Observable<number>;
42-
public isActivelySpeaking: ko.Observable<boolean>;
43-
public isSendingVideo: ko.PureComputed<boolean>;
44-
public isAudioEstablished: ko.Observable<boolean>;
35+
public readonly videoState = observable(VIDEO_STATE.STOPPED);
36+
public readonly videoStream = observable<MediaStream | undefined>();
37+
public readonly blurredVideoStream = observable<{stream: MediaStream; release: () => void} | undefined>();
38+
public readonly hasActiveVideo: ko.PureComputed<boolean>;
39+
public readonly hasPausedVideo: ko.PureComputed<boolean>;
40+
public readonly sharesScreen: ko.PureComputed<boolean>;
41+
public readonly sharesCamera: ko.PureComputed<boolean>;
42+
public readonly startedScreenSharingAt = observable<number>(0);
43+
public readonly isActivelySpeaking = observable(false);
44+
public readonly isSendingVideo: ko.PureComputed<boolean>;
45+
public readonly isAudioEstablished = observable(false);
4546

4647
// Audio
47-
public audioStream: ko.Observable<MediaStream | undefined>;
48-
public isMuted: ko.Observable<boolean>;
48+
public readonly audioStream = observable<MediaStream | undefined>();
49+
public readonly isMuted = observable(false);
4950

5051
constructor(
51-
public user: User,
52-
public clientId: ClientId,
52+
public readonly user: User,
53+
public readonly clientId: ClientId,
5354
) {
54-
this.videoState = ko.observable(VIDEO_STATE.STOPPED);
55-
this.hasActiveVideo = ko.pureComputed(() => {
55+
this.hasActiveVideo = pureComputed(() => {
5656
return (this.sharesCamera() || this.sharesScreen()) && !!this.videoStream();
5757
});
58-
this.sharesScreen = ko.pureComputed(() => {
58+
this.sharesScreen = pureComputed(() => {
5959
return this.videoState() === VIDEO_STATE.SCREENSHARE;
6060
});
61-
this.sharesCamera = ko.pureComputed(() => {
61+
this.sharesCamera = pureComputed(() => {
6262
return [VIDEO_STATE.STARTED, VIDEO_STATE.PAUSED].includes(this.videoState());
6363
});
64-
this.hasPausedVideo = ko.pureComputed(() => {
64+
this.hasPausedVideo = pureComputed(() => {
6565
return this.videoState() === VIDEO_STATE.PAUSED;
6666
});
67-
this.videoStream = ko.observable();
68-
this.audioStream = ko.observable();
69-
this.isActivelySpeaking = ko.observable(false);
70-
this.startedScreenSharingAt = ko.observable();
71-
this.isMuted = ko.observable(false);
72-
this.isSendingVideo = ko.pureComputed(() => {
67+
this.isSendingVideo = pureComputed(() => {
7368
return this.videoState() !== VIDEO_STATE.STOPPED;
7469
});
75-
this.isAudioEstablished = ko.observable(false);
7670
}
7771

7872
public releaseBlurredVideoStream(): void {
7973
this.blurredVideoStream()?.release();
8074
this.blurredVideoStream(undefined);
8175
}
8276

77+
public async setBlurredBackground(isBlurred: boolean) {
78+
const originalVideoStream = this.videoStream();
79+
if (isBlurred && originalVideoStream) {
80+
this.blurredVideoStream(await applyBlur(originalVideoStream));
81+
} else {
82+
this.releaseBlurredVideoStream();
83+
}
84+
return this.blurredVideoStream()?.stream;
85+
}
86+
8387
readonly doesMatchIds = (userId: QualifiedId, clientId: ClientId): boolean =>
8488
matchQualifiedIds(userId, this.user.qualifiedId) && clientId === this.clientId;
8589

@@ -89,6 +93,7 @@ export class Participant {
8993
}
9094

9195
setVideoStream(videoStream: MediaStream, stopTracks: boolean): void {
96+
this.releaseBlurredVideoStream();
9297
this.releaseStream(this.videoStream(), stopTracks);
9398
this.videoStream(videoStream);
9499
}
@@ -104,8 +109,9 @@ export class Participant {
104109
}
105110

106111
getMediaStream(): MediaStream {
107-
const audioTracks: MediaStreamTrack[] = this.audioStream() ? this.audioStream().getTracks() : [];
108-
const videoTracks: MediaStreamTrack[] = this.videoStream() ? this.videoStream().getTracks() : [];
112+
const audioTracks: MediaStreamTrack[] = this.audioStream()?.getTracks() ?? [];
113+
const videoTracks: MediaStreamTrack[] =
114+
this.blurredVideoStream()?.stream.getTracks() ?? this.videoStream()?.getTracks() ?? [];
109115
return new MediaStream(audioTracks.concat(videoTracks));
110116
}
111117

src/script/media/BackgroundBlurrer/fragmentShader.glsl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#define BLUR_QUALITY 8 //The higher the value, the more blur. Must be an even number.
1+
#define BLUR_QUALITY 14 //The higher the value, the more blur. Must be an even number.
22
#define SMOOTH 3 // This will create a smooth transition between the blurred and the clear part (the higher the value, the smoother)
33

44
precision mediump float;

src/script/media/VideoBackgroundBlur.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ enum FRAMERATE {
3232

3333
const QualitySettings = {
3434
segmentationModel: SEGMENTATION_MODEL.PERFORMANCE,
35-
framerate: FRAMERATE.HIGH,
35+
framerate: FRAMERATE.LOW,
3636
};
3737

3838
// Calculate the FPS interval

0 commit comments

Comments
 (0)