Стабилизация отображения камеры при повторном подключении
This commit is contained in:
@@ -123,9 +123,12 @@ export function FloatingObservationWindow({
|
||||
return () => observer.disconnect();
|
||||
}, [boundsRef]);
|
||||
|
||||
const initialRect = bounds ? initialObservationWindowRect(index, count, bounds) : null;
|
||||
// Camera media admission must not wait for the viewport observer. A source
|
||||
// can arrive in the same commit as a workspace transition; mounting it with
|
||||
// the canonical fallback geometry keeps the browser transport alive until
|
||||
// the real bounds are measured immediately afterward.
|
||||
const initialRect = initialObservationWindowRect(index, count, bounds ?? undefined);
|
||||
const windowRect = rect ?? initialRect;
|
||||
if (!windowRect) return null;
|
||||
|
||||
return (
|
||||
<WorkspaceWindow
|
||||
|
||||
@@ -29,6 +29,7 @@ let cameraTransportCallbackIsCurrent;
|
||||
let cameraBrowserTransportIdentity;
|
||||
let liveRerunRecoveryAuthorityIdentity;
|
||||
let initialObservationWindowRect;
|
||||
let FloatingObservationWindow;
|
||||
let ObservationTimeline;
|
||||
let shouldCaptureWorkspacePointer;
|
||||
let normalizeTimelineRange;
|
||||
@@ -86,9 +87,11 @@ before(async () => {
|
||||
} = await server.ssrLoadModule(
|
||||
"/src/core/observation/liveCameraRecovery.ts",
|
||||
));
|
||||
({ initialObservationWindowRect, shouldCaptureWorkspacePointer } = await server.ssrLoadModule(
|
||||
"/src/components/FloatingObservationWindow.tsx",
|
||||
));
|
||||
({
|
||||
FloatingObservationWindow,
|
||||
initialObservationWindowRect,
|
||||
shouldCaptureWorkspacePointer,
|
||||
} = await server.ssrLoadModule("/src/components/FloatingObservationWindow.tsx"));
|
||||
({ liveRerunRecoveryAuthorityIdentity } = await server.ssrLoadModule(
|
||||
"/src/core/observation/liveReceiverWatchdog.ts",
|
||||
));
|
||||
@@ -133,6 +136,59 @@ test("observation camera windows tile from the bottom-right above the live timel
|
||||
assert.ok(right.y + right.height <= bounds.height - 64);
|
||||
});
|
||||
|
||||
test("live camera window keeps its player mounted before viewport measurement", () => {
|
||||
const source = {
|
||||
id: "k1:sensor.camera.right",
|
||||
sourceId: "sensor.camera.right",
|
||||
label: "Правая камера",
|
||||
description: "Live camera",
|
||||
modality: "video",
|
||||
availability: "streaming",
|
||||
transport: "websocket",
|
||||
endpointLabel: "Локальный видеопоток",
|
||||
previewUrl: null,
|
||||
delivery: {
|
||||
id: "camera-preview-4",
|
||||
kind: "mse-fmp4-websocket",
|
||||
url: "/camera-preview/4",
|
||||
mediaType: 'video/mp4; codecs="avc1.641028"',
|
||||
},
|
||||
activation: {
|
||||
groupId: "k1:device-session:camera.preview.decoder",
|
||||
maxActive: 1,
|
||||
selected: true,
|
||||
controllable: false,
|
||||
},
|
||||
binding: {
|
||||
deviceId: "k1-a",
|
||||
deviceSessionId: "device-session",
|
||||
acquisitionId: "acquisition-quick",
|
||||
},
|
||||
capabilities: {
|
||||
defaultVisible: true,
|
||||
overlay: true,
|
||||
resizable: true,
|
||||
timelineMode: "live-only",
|
||||
},
|
||||
};
|
||||
const noop = () => undefined;
|
||||
const markup = renderToStaticMarkup(createElement(FloatingObservationWindow, {
|
||||
source,
|
||||
index: 0,
|
||||
count: 1,
|
||||
boundsRef: { current: null },
|
||||
maximized: false,
|
||||
active: true,
|
||||
onRectChange: noop,
|
||||
onMaximizedChange: noop,
|
||||
onActivate: noop,
|
||||
onClose: noop,
|
||||
}));
|
||||
|
||||
assert.match(markup, /<video/);
|
||||
assert.match(markup, /Правая камера/);
|
||||
});
|
||||
|
||||
test("observation camera windows stack without overlap when the viewport is narrow", () => {
|
||||
const bounds = { width: 420, height: 700 };
|
||||
const lower = initialObservationWindowRect(0, 2, bounds);
|
||||
|
||||
Reference in New Issue
Block a user