add switch to use go2rtc ffmpeg mode

This commit is contained in:
Josh Hawkins 2025-11-03 09:22:31 -06:00
parent d44340eca6
commit 2d7ee99207
4 changed files with 71 additions and 23 deletions

View File

@ -174,9 +174,7 @@ export default function CameraWizardDialog({
...(friendlyName && { friendly_name: friendlyName }), ...(friendlyName && { friendly_name: friendlyName }),
ffmpeg: { ffmpeg: {
inputs: wizardData.streams.map((stream, index) => { inputs: wizardData.streams.map((stream, index) => {
const isRestreamed = if (stream.restream) {
wizardData.restreamIds?.includes(stream.id) ?? false;
if (isRestreamed) {
const go2rtcStreamName = const go2rtcStreamName =
wizardData.streams!.length === 1 wizardData.streams!.length === 1
? finalCameraName ? finalCameraName
@ -234,7 +232,11 @@ export default function CameraWizardDialog({
wizardData.streams!.length === 1 wizardData.streams!.length === 1
? finalCameraName ? finalCameraName
: `${finalCameraName}_${index + 1}`; : `${finalCameraName}_${index + 1}`;
go2rtcStreams[streamName] = [stream.url];
const streamUrl = stream.useFfmpeg
? `ffmpeg:${stream.url}`
: stream.url;
go2rtcStreams[streamName] = [streamUrl];
}); });
if (Object.keys(go2rtcStreams).length > 0) { if (Object.keys(go2rtcStreams).length > 0) {

View File

@ -201,16 +201,12 @@ export default function Step2StreamConfig({
const setRestream = useCallback( const setRestream = useCallback(
(streamId: string) => { (streamId: string) => {
const currentIds = wizardData.restreamIds || []; const stream = streams.find((s) => s.id === streamId);
const isSelected = currentIds.includes(streamId); if (!stream) return;
const newIds = isSelected
? currentIds.filter((id) => id !== streamId) updateStream(streamId, { restream: !stream.restream });
: [...currentIds, streamId];
onUpdate({
restreamIds: newIds,
});
}, },
[wizardData.restreamIds, onUpdate], [streams, updateStream],
); );
const hasDetectRole = streams.some((s) => s.roles.includes("detect")); const hasDetectRole = streams.some((s) => s.roles.includes("detect"));
@ -435,9 +431,7 @@ export default function Step2StreamConfig({
{t("cameraWizard.step2.go2rtc")} {t("cameraWizard.step2.go2rtc")}
</span> </span>
<Switch <Switch
checked={(wizardData.restreamIds || []).includes( checked={stream.restream || false}
stream.id,
)}
onCheckedChange={() => setRestream(stream.id)} onCheckedChange={() => setRestream(stream.id)}
/> />
</div> </div>

View File

@ -1,7 +1,13 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Switch } from "@/components/ui/switch";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { LuRotateCcw } from "react-icons/lu"; import { LuRotateCcw, LuInfo } from "react-icons/lu";
import { useState, useCallback, useMemo, useEffect } from "react"; import { useState, useCallback, useMemo, useEffect } from "react";
import ActivityIndicator from "@/components/indicators/activity-indicator"; import ActivityIndicator from "@/components/indicators/activity-indicator";
import axios from "axios"; import axios from "axios";
@ -216,7 +222,6 @@ export default function Step3Validation({
brandTemplate: wizardData.brandTemplate, brandTemplate: wizardData.brandTemplate,
customUrl: wizardData.customUrl, customUrl: wizardData.customUrl,
streams: wizardData.streams, streams: wizardData.streams,
restreamIds: wizardData.restreamIds,
}; };
onSave(configData); onSave(configData);
@ -322,6 +327,51 @@ export default function Step3Validation({
</div> </div>
)} )}
{result?.success && (
<div className="mb-3 flex items-center justify-between">
<div className="flex items-center gap-2">
<span className="text-sm">
{t("cameraWizard.step3.ffmpegModule")}
</span>
<Popover>
<PopoverTrigger asChild>
<Button
variant="ghost"
size="sm"
className="h-4 w-4 p-0"
>
<LuInfo className="size-3" />
</Button>
</PopoverTrigger>
<PopoverContent className="pointer-events-auto w-80 text-xs">
<div className="space-y-2">
<div className="font-medium">
{t("cameraWizard.step3.ffmpegModule")}
</div>
<div className="text-muted-foreground">
{t(
"cameraWizard.step3.ffmpegModuleDescription",
)}
</div>
</div>
</PopoverContent>
</Popover>
</div>
<Switch
checked={stream.useFfmpeg || false}
onCheckedChange={(checked) => {
onUpdate({
streams: streams.map((s) =>
s.id === stream.id
? { ...s, useFfmpeg: checked }
: s,
),
});
}}
/>
</div>
)}
<div className="mb-2 flex flex-col justify-between gap-1 md:flex-row md:items-center"> <div className="mb-2 flex flex-col justify-between gap-1 md:flex-row md:items-center">
<span className="break-all text-sm text-muted-foreground"> <span className="break-all text-sm text-muted-foreground">
{stream.url} {stream.url}
@ -491,8 +541,7 @@ function StreamIssues({
// Restreaming check // Restreaming check
if (stream.roles.includes("record")) { if (stream.roles.includes("record")) {
const restreamIds = wizardData.restreamIds || []; if (stream.restream) {
if (restreamIds.includes(stream.id)) {
result.push({ result.push({
type: "warning", type: "warning",
message: t("cameraWizard.step3.issues.restreamingWarning"), message: t("cameraWizard.step3.issues.restreamingWarning"),
@ -660,9 +709,10 @@ function StreamPreview({ stream, onBandwidthUpdate }: StreamPreviewProps) {
useEffect(() => { useEffect(() => {
// Register stream with go2rtc // Register stream with go2rtc
const streamUrl = stream.useFfmpeg ? `ffmpeg:${stream.url}` : stream.url;
axios axios
.put(`go2rtc/streams/${streamId}`, null, { .put(`go2rtc/streams/${streamId}`, null, {
params: { src: stream.url }, params: { src: streamUrl },
}) })
.then(() => { .then(() => {
// Add small delay to allow go2rtc api to run and initialize the stream // Add small delay to allow go2rtc api to run and initialize the stream
@ -680,7 +730,7 @@ function StreamPreview({ stream, onBandwidthUpdate }: StreamPreviewProps) {
// do nothing on cleanup errors - go2rtc won't consume the streams // do nothing on cleanup errors - go2rtc won't consume the streams
}); });
}; };
}, [stream.url, streamId]); }, [stream.url, stream.useFfmpeg, streamId]);
const resolution = stream.testResult?.resolution; const resolution = stream.testResult?.resolution;
let aspectRatio = "16/9"; let aspectRatio = "16/9";

View File

@ -85,6 +85,8 @@ export type StreamConfig = {
quality?: string; quality?: string;
testResult?: TestResult; testResult?: TestResult;
userTested?: boolean; userTested?: boolean;
useFfmpeg?: boolean;
restream?: boolean;
}; };
export type TestResult = { export type TestResult = {
@ -105,7 +107,6 @@ export type WizardFormData = {
brandTemplate?: CameraBrand; brandTemplate?: CameraBrand;
customUrl?: string; customUrl?: string;
streams?: StreamConfig[]; streams?: StreamConfig[];
restreamIds?: string[];
}; };
// API Response Types // API Response Types
@ -146,6 +147,7 @@ export type CameraConfigData = {
inputs: { inputs: {
path: string; path: string;
roles: string[]; roles: string[];
input_args?: string;
}[]; }[];
}; };
live?: { live?: {