This commit is contained in:
legop3
2026-07-10 23:20:09 -04:00
parent ee393adc8e
commit d777e3a48e
27 changed files with 1363 additions and 162 deletions
+74
View File
@@ -0,0 +1,74 @@
// Hook: usePtzCameraSnapshot
// Purpose: Subscribes to the server-generated PTZ camera snapshot feed.
// Scope: Mirrors the rover snapshot object-URL lifecycle while using PTZ-specific socket events and authorization.
import { useEffect, useRef, useState } from 'react';
import { useSocket } from '../context/SocketContext.jsx';
export function usePtzCameraSnapshot(options = {}) {
const socket = useSocket();
const { enabled = true, version = null } = options;
const [feed, setFeed] = useState(null);
const objectUrlRef = useRef(null);
const [connectionNonce, setConnectionNonce] = useState(0);
useEffect(() => {
if (!socket) return undefined;
const handleConnect = () => setConnectionNonce((prev) => prev + 1);
socket.on('connect', handleConnect);
return () => socket.off('connect', handleConnect);
}, [socket]);
useEffect(() => {
if (objectUrlRef.current) {
URL.revokeObjectURL(objectUrlRef.current);
objectUrlRef.current = null;
}
setFeed(null);
}, [version]);
useEffect(() => {
if (!enabled || !socket) return undefined;
let cancelled = false;
const handleFrame = (meta = {}, buffer) => {
if (cancelled || !buffer) return;
const url = URL.createObjectURL(new Blob([buffer], { type: 'image/jpeg' }));
if (objectUrlRef.current) URL.revokeObjectURL(objectUrlRef.current);
objectUrlRef.current = url;
setFeed({
status: 'playing',
ts: meta.ts || Date.now(),
error: null,
objectUrl: url,
});
};
const handleStatus = (meta = {}) => {
if (cancelled) return;
setFeed((prev) => ({
...(prev || {}),
status: meta.error ? 'error' : prev?.status || 'connecting',
error: meta.error || null,
ts: meta.ts || prev?.ts || null,
objectUrl: prev?.objectUrl || null,
}));
};
socket.on('ptzCamera:snapshotFrame', handleFrame);
socket.on('ptzCamera:snapshotStatus', handleStatus);
socket.emit('ptzCamera:snapshotSubscribe', {}, () => {});
return () => {
cancelled = true;
socket.emit('ptzCamera:snapshotUnsubscribe');
socket.off('ptzCamera:snapshotFrame', handleFrame);
socket.off('ptzCamera:snapshotStatus', handleStatus);
if (objectUrlRef.current) {
URL.revokeObjectURL(objectUrlRef.current);
objectUrlRef.current = null;
}
};
}, [socket, enabled, version, connectionNonce]);
return feed;
}
+6 -1
View File
@@ -88,7 +88,12 @@ export function useVideoRequests(sourceList = [], options = {}) {
let cancelled = false;
function requestEntry(entry) {
const payload = entry.type === 'room' ? { roomCameraId: entry.id } : { roverId: entry.id };
const payload =
entry.type === 'room'
? { roomCameraId: entry.id }
: entry.type === 'ptz'
? { type: 'ptz', id: entry.id }
: { roverId: entry.id };
socket.emit('video:request', payload, (resp = {}) => {
if (cancelled) return;
if (resp?.error) {