2024-02-10 15:30:53 +03:00
|
|
|
import { CameraConfig, FrigateConfig } from "@/types/frigateConfig";
|
2025-08-22 15:04:30 +03:00
|
|
|
import { useCallback, useEffect, useState, useMemo } from "react";
|
2024-02-10 15:30:53 +03:00
|
|
|
import useSWR from "swr";
|
2025-02-10 19:42:35 +03:00
|
|
|
import { LivePlayerMode, LiveStreamMetadata } from "@/types/live";
|
2024-02-10 15:30:53 +03:00
|
|
|
|
|
|
|
|
export default function useCameraLiveMode(
|
2024-08-17 21:16:48 +03:00
|
|
|
cameras: CameraConfig[],
|
|
|
|
|
windowVisible: boolean,
|
|
|
|
|
) {
|
2024-02-10 15:30:53 +03:00
|
|
|
const { data: config } = useSWR<FrigateConfig>("config");
|
2025-08-22 15:04:30 +03:00
|
|
|
|
|
|
|
|
// Get comma-separated list of restreamed stream names for SWR key
|
|
|
|
|
const restreamedStreamsKey = useMemo(() => {
|
|
|
|
|
if (!cameras || !config) return null;
|
|
|
|
|
|
|
|
|
|
const streamNames = new Set<string>();
|
|
|
|
|
cameras.forEach((camera) => {
|
|
|
|
|
const isRestreamed = Object.keys(config.go2rtc.streams || {}).includes(
|
|
|
|
|
Object.values(camera.live.streams)[0],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (isRestreamed) {
|
|
|
|
|
Object.values(camera.live.streams).forEach((streamName) => {
|
|
|
|
|
streamNames.add(streamName);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return streamNames.size > 0
|
|
|
|
|
? Array.from(streamNames).sort().join(",")
|
|
|
|
|
: null;
|
|
|
|
|
}, [cameras, config]);
|
|
|
|
|
|
|
|
|
|
const streamsFetcher = useCallback(async (key: string) => {
|
|
|
|
|
const streamNames = key.split(",");
|
|
|
|
|
const metadata: { [key: string]: LiveStreamMetadata } = {};
|
|
|
|
|
|
|
|
|
|
await Promise.all(
|
|
|
|
|
streamNames.map(async (streamName) => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(`/api/go2rtc/streams/${streamName}`);
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
metadata[streamName] = data;
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
console.error(`Failed to fetch metadata for ${streamName}:`, error);
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return metadata;
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const { data: allStreamMetadata = {} } = useSWR<{
|
2025-02-10 19:42:35 +03:00
|
|
|
[key: string]: LiveStreamMetadata;
|
2025-08-22 15:04:30 +03:00
|
|
|
}>(restreamedStreamsKey, streamsFetcher, { revalidateOnFocus: false });
|
2025-02-10 19:42:35 +03:00
|
|
|
|
2024-08-17 21:16:48 +03:00
|
|
|
const [preferredLiveModes, setPreferredLiveModes] = useState<{
|
|
|
|
|
[key: string]: LivePlayerMode;
|
|
|
|
|
}>({});
|
2025-02-10 19:42:35 +03:00
|
|
|
const [isRestreamedStates, setIsRestreamedStates] = useState<{
|
|
|
|
|
[key: string]: boolean;
|
|
|
|
|
}>({});
|
|
|
|
|
const [supportsAudioOutputStates, setSupportsAudioOutputStates] = useState<{
|
|
|
|
|
[key: string]: {
|
|
|
|
|
supportsAudio: boolean;
|
|
|
|
|
cameraName: string;
|
|
|
|
|
};
|
|
|
|
|
}>({});
|
2024-02-10 15:30:53 +03:00
|
|
|
|
2024-08-17 21:16:48 +03:00
|
|
|
useEffect(() => {
|
|
|
|
|
if (!cameras) return;
|
2024-02-10 15:30:53 +03:00
|
|
|
|
2024-08-17 21:16:48 +03:00
|
|
|
const mseSupported =
|
|
|
|
|
"MediaSource" in window || "ManagedMediaSource" in window;
|
|
|
|
|
|
2025-02-10 19:42:35 +03:00
|
|
|
const newPreferredLiveModes: { [key: string]: LivePlayerMode } = {};
|
|
|
|
|
const newIsRestreamedStates: { [key: string]: boolean } = {};
|
|
|
|
|
const newSupportsAudioOutputStates: {
|
|
|
|
|
[key: string]: { supportsAudio: boolean; cameraName: string };
|
|
|
|
|
} = {};
|
2024-08-17 21:16:48 +03:00
|
|
|
|
2025-02-10 19:42:35 +03:00
|
|
|
cameras.forEach((camera) => {
|
|
|
|
|
const isRestreamed =
|
|
|
|
|
config &&
|
|
|
|
|
Object.keys(config.go2rtc.streams || {}).includes(
|
|
|
|
|
Object.values(camera.live.streams)[0],
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
newIsRestreamedStates[camera.name] = isRestreamed ?? false;
|
|
|
|
|
|
|
|
|
|
if (!mseSupported) {
|
|
|
|
|
newPreferredLiveModes[camera.name] = isRestreamed ? "webrtc" : "jsmpeg";
|
|
|
|
|
} else {
|
|
|
|
|
newPreferredLiveModes[camera.name] = isRestreamed ? "mse" : "jsmpeg";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// check each stream for audio support
|
|
|
|
|
if (isRestreamed) {
|
|
|
|
|
Object.values(camera.live.streams).forEach((streamName) => {
|
|
|
|
|
const metadata = allStreamMetadata?.[streamName];
|
|
|
|
|
newSupportsAudioOutputStates[streamName] = {
|
|
|
|
|
supportsAudio: metadata
|
|
|
|
|
? metadata.producers.find(
|
|
|
|
|
(prod) =>
|
|
|
|
|
prod.medias &&
|
|
|
|
|
prod.medias.find((media) =>
|
|
|
|
|
media.includes("audio, recvonly"),
|
|
|
|
|
) !== undefined,
|
|
|
|
|
) !== undefined
|
|
|
|
|
: false,
|
|
|
|
|
cameraName: camera.name,
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
newSupportsAudioOutputStates[camera.name] = {
|
|
|
|
|
supportsAudio: false,
|
|
|
|
|
cameraName: camera.name,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
});
|
2024-08-17 21:16:48 +03:00
|
|
|
|
|
|
|
|
setPreferredLiveModes(newPreferredLiveModes);
|
2025-02-10 19:42:35 +03:00
|
|
|
setIsRestreamedStates(newIsRestreamedStates);
|
|
|
|
|
setSupportsAudioOutputStates(newSupportsAudioOutputStates);
|
|
|
|
|
}, [cameras, config, windowVisible, allStreamMetadata]);
|
2024-08-17 21:16:48 +03:00
|
|
|
|
|
|
|
|
const resetPreferredLiveMode = useCallback(
|
|
|
|
|
(cameraName: string) => {
|
|
|
|
|
const mseSupported =
|
|
|
|
|
"MediaSource" in window || "ManagedMediaSource" in window;
|
|
|
|
|
const isRestreamed =
|
|
|
|
|
config && Object.keys(config.go2rtc.streams || {}).includes(cameraName);
|
|
|
|
|
|
|
|
|
|
setPreferredLiveModes((prevModes) => {
|
|
|
|
|
const newModes = { ...prevModes };
|
|
|
|
|
|
|
|
|
|
if (!mseSupported) {
|
|
|
|
|
newModes[cameraName] = isRestreamed ? "webrtc" : "jsmpeg";
|
|
|
|
|
} else {
|
|
|
|
|
newModes[cameraName] = isRestreamed ? "mse" : "jsmpeg";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return newModes;
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
[config],
|
2024-02-10 15:30:53 +03:00
|
|
|
);
|
|
|
|
|
|
2025-02-10 19:42:35 +03:00
|
|
|
return {
|
|
|
|
|
preferredLiveModes,
|
|
|
|
|
setPreferredLiveModes,
|
|
|
|
|
resetPreferredLiveMode,
|
|
|
|
|
isRestreamedStates,
|
|
|
|
|
supportsAudioOutputStates,
|
|
|
|
|
};
|
2024-02-10 15:30:53 +03:00
|
|
|
}
|