mirror of
https://github.com/vector-im/element-call.git
synced 2026-10-16 23:45:50 +00:00
Ask one question about whether background effects can run
- The menu asked the browser; the pipeline asked the browser and for a desktop. On a phone whose browser can run them, blur was offered and then refused: the toggle moved and the video did not change. - Both now ask one function, and it asks the browser alone. The platform was the wrong measure: a phone on the fast route held 99% of its frame rate and was refused, a desktop on the slow one held 73% and was allowed. - The in-call device switcher is still withheld on a phone; that is the device menu's own rule and is unchanged. - The provider tests run as a phone, and fail with the desktop test back. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,10 @@ import type { CallViewModel } from "../state/CallViewModel/CallViewModel";
|
|||||||
import type { Alignment, Layout } from "../state/layout-types";
|
import type { Alignment, Layout } from "../state/layout-types";
|
||||||
import type { SpotlightTileViewModel } from "../state/TileViewModel";
|
import type { SpotlightTileViewModel } from "../state/TileViewModel";
|
||||||
import type { DeviceLabel } from "../state/MediaDevices";
|
import type { DeviceLabel } from "../state/MediaDevices";
|
||||||
import { createCallFooterViewModel } from "./CallFooterViewModel";
|
import {
|
||||||
|
createCallFooterViewModel,
|
||||||
|
createLobbyFooterViewModel,
|
||||||
|
} from "./CallFooterViewModel";
|
||||||
import { HeaderStyle } from "../UrlParams";
|
import { HeaderStyle } from "../UrlParams";
|
||||||
|
|
||||||
const platformMock = vi.hoisted(() => vi.fn(() => "desktop"));
|
const platformMock = vi.hoisted(() => vi.fn(() => "desktop"));
|
||||||
@@ -24,10 +27,10 @@ vi.mock("../Platform", () => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Prevent supportsBackgroundProcessors from throwing in jsdom – it is not
|
// The SDK's own check needs WebGL and canvas APIs jsdom does not have.
|
||||||
// exercised by these tests (only used in `videoToggles`, not `videoOptions`).
|
const sdkSupportMock = vi.hoisted(() => vi.fn(() => false));
|
||||||
vi.mock("@livekit/track-processors", () => ({
|
vi.mock("@livekit/track-processors", () => ({
|
||||||
supportsBackgroundProcessors: (): boolean => false,
|
supportsBackgroundProcessors: (): boolean => sdkSupportMock(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const outputSelectionMock = vi.hoisted(() => vi.fn(() => true));
|
const outputSelectionMock = vi.hoisted(() => vi.fn(() => true));
|
||||||
@@ -222,4 +225,55 @@ describe("createCallFooterViewModel", () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("background effects", () => {
|
||||||
|
// The lobby, because in a call the whole device switcher is already
|
||||||
|
// withheld on a phone, whatever the verdict says.
|
||||||
|
function lobbyOn(
|
||||||
|
platform: string,
|
||||||
|
): ReturnType<typeof createLobbyFooterViewModel> {
|
||||||
|
platformMock.mockReturnValue(platform);
|
||||||
|
return createLobbyFooterViewModel(
|
||||||
|
testScope(),
|
||||||
|
mockMuteStates(),
|
||||||
|
twoMicsAndOneCamMediaDevices,
|
||||||
|
/* openSettings */ undefined,
|
||||||
|
/* hangup */ undefined,
|
||||||
|
/* showLogo */ false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
it("offers them on a phone whose browser can run them", () => {
|
||||||
|
sdkSupportMock.mockReturnValue(true);
|
||||||
|
expect(lobbyOn("ios").toggleBlur$.value).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("offers them where the pipeline will honour them", () => {
|
||||||
|
sdkSupportMock.mockReturnValue(true);
|
||||||
|
expect(lobbyOn("desktop").toggleBlur$.value).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("offers nothing where the browser itself cannot run them", () => {
|
||||||
|
sdkSupportMock.mockReturnValue(false);
|
||||||
|
expect(lobbyOn("desktop").toggleBlur$.value).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("availability is the same before and during a call", () => {
|
||||||
|
for (const supported of [true, false]) {
|
||||||
|
sdkSupportMock.mockReturnValue(supported);
|
||||||
|
const lobby = lobbyOn("desktop");
|
||||||
|
const inCall = createCallFooterViewModel(
|
||||||
|
testScope(),
|
||||||
|
buildMinimalCallViewModel(gridLayout),
|
||||||
|
mockMuteStates(),
|
||||||
|
twoMicsAndOneCamMediaDevices,
|
||||||
|
/* reactionIdentifier */ undefined,
|
||||||
|
{ showControls: true, header: HeaderStyle.Standard },
|
||||||
|
);
|
||||||
|
const offeredInLobby = lobby.toggleBlur$.value !== undefined;
|
||||||
|
expect(inCall.toggleBlur$.value !== undefined).toBe(offeredInLobby);
|
||||||
|
expect(offeredInLobby).toBe(supported);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ Please see LICENSE in the repository root for full details.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { combineLatest, map, type Observable, switchMap } from "rxjs";
|
import { combineLatest, map, type Observable, switchMap } from "rxjs";
|
||||||
import { supportsBackgroundProcessors } from "@livekit/track-processors";
|
|
||||||
import { supportsAudioOutputSelection } from "livekit-client";
|
import { supportsAudioOutputSelection } from "livekit-client";
|
||||||
|
|
||||||
|
import { supportsBackgroundProcessors } from "../livekit/backgroundProcessing";
|
||||||
|
|
||||||
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
|
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
|
||||||
import { type MenuOptions } from "./MediaMuteAndSwitchButton";
|
import { type MenuOptions } from "./MediaMuteAndSwitchButton";
|
||||||
import { type MediaDevices } from "../state/MediaDevices";
|
import { type MediaDevices } from "../state/MediaDevices";
|
||||||
|
|||||||
@@ -48,10 +48,11 @@ vi.mock("@livekit/track-processors", () => ({
|
|||||||
}),
|
}),
|
||||||
supportsBackgroundProcessors: (): boolean => true,
|
supportsBackgroundProcessors: (): boolean => true,
|
||||||
}));
|
}));
|
||||||
|
// A phone: the pipeline must not ask.
|
||||||
|
vi.mock("../Platform", () => ({ platform: "ios" }));
|
||||||
vi.mock("./BackgroundEffectTransformer", () => ({
|
vi.mock("./BackgroundEffectTransformer", () => ({
|
||||||
BackgroundEffectTransformer: vi.fn(),
|
BackgroundEffectTransformer: vi.fn(),
|
||||||
}));
|
}));
|
||||||
vi.mock("../Platform", () => ({ platform: "desktop" }));
|
|
||||||
|
|
||||||
const processor = {} as ProcessorWrapper<BackgroundOptions>;
|
const processor = {} as ProcessorWrapper<BackgroundOptions>;
|
||||||
|
|
||||||
@@ -193,6 +194,15 @@ describe("ProcessorProvider", () => {
|
|||||||
expect(track.stopProcessor).not.toHaveBeenCalled();
|
expect(track.stopProcessor).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("runs on a phone whose browser can run it", async () => {
|
||||||
|
const track = cameraTrack();
|
||||||
|
render(surfaces(track));
|
||||||
|
await blur(true);
|
||||||
|
|
||||||
|
expect(latest().supported).toBe(true);
|
||||||
|
expect(track.setProcessor).toHaveBeenCalledWith(latest().processor);
|
||||||
|
});
|
||||||
|
|
||||||
it("preview and call share one pipeline", async () => {
|
it("preview and call share one pipeline", async () => {
|
||||||
const preview = cameraTrack();
|
const preview = cameraTrack();
|
||||||
const call = cameraTrack();
|
const call = cameraTrack();
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ Please see LICENSE in the repository root for full details.
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
type ProcessorWrapper,
|
type ProcessorWrapper,
|
||||||
supportsBackgroundProcessors as supportsBackgroundProcessorsLivekitSdk,
|
|
||||||
type BackgroundOptions,
|
type BackgroundOptions,
|
||||||
} from "@livekit/track-processors";
|
} from "@livekit/track-processors";
|
||||||
import {
|
import {
|
||||||
@@ -26,11 +25,11 @@ import { combineLatest } from "rxjs";
|
|||||||
import { backgroundBlur as backgroundBlurSettings } from "../settings/settings";
|
import { backgroundBlur as backgroundBlurSettings } from "../settings/settings";
|
||||||
import { BackgroundEffectTransformer } from "./BackgroundEffectTransformer";
|
import { BackgroundEffectTransformer } from "./BackgroundEffectTransformer";
|
||||||
import { OneStepPipeline } from "./OneStepPipeline";
|
import { OneStepPipeline } from "./OneStepPipeline";
|
||||||
|
import { supportsBackgroundProcessors } from "./backgroundProcessing";
|
||||||
import { type Behavior } from "../state/Behavior";
|
import { type Behavior } from "../state/Behavior";
|
||||||
import { ObservableScope } from "../state/ObservableScope";
|
import { ObservableScope } from "../state/ObservableScope";
|
||||||
import { BackgroundEffects } from "../state/BackgroundEffects";
|
import { BackgroundEffects } from "../state/BackgroundEffects";
|
||||||
import { useBehavior } from "../useBehavior";
|
import { useBehavior } from "../useBehavior";
|
||||||
import { platform } from "../Platform";
|
|
||||||
|
|
||||||
//TODO-MULTI-SFU: This is not yet fully there.
|
//TODO-MULTI-SFU: This is not yet fully there.
|
||||||
// it is a combination of exposing observable and react hooks.
|
// it is a combination of exposing observable and react hooks.
|
||||||
@@ -122,10 +121,6 @@ interface Props {
|
|||||||
children: JSX.Element;
|
children: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
function supportsBackgroundProcessors(): boolean {
|
|
||||||
return supportsBackgroundProcessorsLivekitSdk() && platform === "desktop";
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ProcessorProvider: FC<Props> = ({ children }) => {
|
export const ProcessorProvider: FC<Props> = ({ children }) => {
|
||||||
const [effects, setEffects] = useState<BackgroundEffects | null>(null);
|
const [effects, setEffects] = useState<BackgroundEffects | null>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2026 Element Creations Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { supportsBackgroundProcessors as supportsBackgroundProcessorsLivekitSdk } from "@livekit/track-processors";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this browser can run background effects: the one answer for the
|
||||||
|
* controls and the pipeline, so nothing is offered that the pipeline refuses.
|
||||||
|
*/
|
||||||
|
export function supportsBackgroundProcessors(): boolean {
|
||||||
|
return supportsBackgroundProcessorsLivekitSdk();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user