Стабилизация отображения камеры при повторном подключении
This commit is contained in:
@@ -123,9 +123,12 @@ export function FloatingObservationWindow({
|
|||||||
return () => observer.disconnect();
|
return () => observer.disconnect();
|
||||||
}, [boundsRef]);
|
}, [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;
|
const windowRect = rect ?? initialRect;
|
||||||
if (!windowRect) return null;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WorkspaceWindow
|
<WorkspaceWindow
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ let cameraTransportCallbackIsCurrent;
|
|||||||
let cameraBrowserTransportIdentity;
|
let cameraBrowserTransportIdentity;
|
||||||
let liveRerunRecoveryAuthorityIdentity;
|
let liveRerunRecoveryAuthorityIdentity;
|
||||||
let initialObservationWindowRect;
|
let initialObservationWindowRect;
|
||||||
|
let FloatingObservationWindow;
|
||||||
let ObservationTimeline;
|
let ObservationTimeline;
|
||||||
let shouldCaptureWorkspacePointer;
|
let shouldCaptureWorkspacePointer;
|
||||||
let normalizeTimelineRange;
|
let normalizeTimelineRange;
|
||||||
@@ -86,9 +87,11 @@ before(async () => {
|
|||||||
} = await server.ssrLoadModule(
|
} = await server.ssrLoadModule(
|
||||||
"/src/core/observation/liveCameraRecovery.ts",
|
"/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(
|
({ liveRerunRecoveryAuthorityIdentity } = await server.ssrLoadModule(
|
||||||
"/src/core/observation/liveReceiverWatchdog.ts",
|
"/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);
|
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", () => {
|
test("observation camera windows stack without overlap when the viewport is narrow", () => {
|
||||||
const bounds = { width: 420, height: 700 };
|
const bounds = { width: 420, height: 700 };
|
||||||
const lower = initialObservationWindowRect(0, 2, bounds);
|
const lower = initialObservationWindowRect(0, 2, bounds);
|
||||||
|
|||||||
Reference in New Issue
Block a user