From 87a7551aaa2937f94abe5c0619aa2c4048b4e8b9 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 15 Oct 2026 21:16:44 -0500 Subject: [PATCH] probe webrtc before reporting it as not configured The WebRTC gate read only the Frigate config, so an install with no go2rtc candidates showed WebRTC as not configured, even though go2rtc fills in default candidates at startup (including the internal one under the HA app) and WebRTC often worked. Unset candidates now run the connectivity probe first, and only a failed probe reports not configured. The not-configured reason shows as a neutral note that names the setting and says WebRTC is optional, rather than as a red error. --- docs/docs/configuration/live.md | 2 +- .../specs/live-webrtc-availability.spec.ts | 65 +++++++++++++++++++ web/public/locales/en/views/live.json | 3 +- .../player/StreamTechnologySelect.tsx | 20 ++++-- web/src/hooks/use-webrtc-availability.ts | 28 ++++++-- 5 files changed, 106 insertions(+), 12 deletions(-) diff --git a/docs/docs/configuration/live.md b/docs/docs/configuration/live.md index 954787a08a..84aa337071 100644 --- a/docs/docs/configuration/live.md +++ b/docs/docs/configuration/live.md @@ -38,7 +38,7 @@ Frigate [defaults to MSE](#why-does-frigate-prefer-mse-over-webrtc-for-live-view - The choices are saved **per device, per camera** in your browser's local storage. - **WebRTC is only selectable when it can actually work for that stream.** When it can't, the option is shown disabled with the reason inline, and a more detailed reason (the failing codecs, or why the connectivity check failed) is logged to your browser's console. Common reasons: - - **Not configured**: no `candidates` or `ice_servers` are set under `go2rtc.webrtc` (see [WebRTC extra configuration](#webrtc-extra-configuration)). + - **Not configured**: no `candidates` or `ice_servers` are set under `go2rtc.webrtc`, and the connectivity check failed with go2rtc's default candidates (see [WebRTC extra configuration](#webrtc-extra-configuration)). This is a setup note rather than an error, since live view works without WebRTC. - **Could not connect**: e.g. port `8555` isn't reachable, or a STUN/TURN server is misconfigured. Frigate runs a one-time WebRTC connectivity check when the Live view opens; the option may briefly show as "checking" while it runs. - **Unsupported video codec**: the stream's video codec can't be played over WebRTC in your browser, most commonly H.265/HEVC in Firefox or Edge. - **Unsupported audio codec**: WebRTC needs opus or G.711 audio, so a stream whose playback audio is only AAC (without an added opus/G.711 track) can't carry audio over WebRTC. See [Audio Support](#audio-support) for how to add one. diff --git a/web/e2e/specs/live-webrtc-availability.spec.ts b/web/e2e/specs/live-webrtc-availability.spec.ts index b36944138d..49fc5668bd 100644 --- a/web/e2e/specs/live-webrtc-availability.spec.ts +++ b/web/e2e/specs/live-webrtc-availability.spec.ts @@ -110,6 +110,15 @@ test.describe("WebRTC availability gating @critical", () => { // distinguishes enabled from disabled options. const mseOption = frigateApp.page.getByRole("option", { name: /MSE/ }); await expect(mseOption).not.toHaveAttribute("aria-disabled", "true"); + + // a missing setting, shown as a neutral note rather than an error + await expect(webrtcOption).toContainText( + "under go2rtc → webrtc → candidates", + ); + await expect(webrtcOption).toContainText( + "Optional: live view works without it.", + ); + await expect(webrtcOption.locator(".text-danger")).toHaveCount(0); }); test("the unavailable reason is logged to the console once", async ({ @@ -214,6 +223,62 @@ test.describe("WebRTC availability gating @critical", () => { await expect(webrtcOption).toContainText(/Checking WebRTC availability/i); }); + test("desktop: unset candidates run the probe before reporting not configured", async ({ + frigateApp, + }) => { + test.skip(frigateApp.isMobile, "Desktop dropdown only"); + + // Count probe connections. The probe fails fast against the preview + // server, which with go2rtc's default candidates means setup is missing. + let probes = 0; + await frigateApp.page.routeWebSocket("**/live/webrtc/api/ws**", (ws) => { + probes++; + ws.close(); + }); + + await frigateApp.installDefaults({ + config: { + go2rtc: { + streams: { front_door: ["rtsp://127.0.0.1:8554/front_door"] }, + }, + }, + }); + + await frigateApp.page.route("**/api/go2rtc/streams/front_door**", (route) => + route.fulfill({ + json: { + producers: [{ medias: ["video, recvonly, H264"] }], + consumers: [], + }, + }), + ); + + await frigateApp.goto("/#front_door"); + + const live = new LivePage(frigateApp.page, true); + await expect(live.backButton).toBeVisible({ timeout: 10_000 }); + + await frigateApp.page.locator("button:has(svg)").last().click(); + + const menu = frigateApp.page + .locator('[role="menu"], [data-radix-menu-content]') + .first(); + await expect(menu).toBeVisible({ timeout: 3_000 }); + await menu + .locator('div:has(> label[for="streaming-mode"]) [role="combobox"]') + .first() + .click(); + + const webrtcOption = frigateApp.page.getByRole("option", { + name: /WebRTC/, + }); + await expect(webrtcOption).toBeVisible({ timeout: 3_000 }); + await expect(webrtcOption).toContainText( + "under go2rtc → webrtc → candidates", + ); + expect(probes).toBeGreaterThan(0); + }); + test("desktop: JSMpeg is no longer offered in the technology selector", async ({ frigateApp, }) => { diff --git a/web/public/locales/en/views/live.json b/web/public/locales/en/views/live.json index c5b370cea0..c4f3f91b0b 100644 --- a/web/public/locales/en/views/live.json +++ b/web/public/locales/en/views/live.json @@ -187,7 +187,8 @@ }, "unavailable": { "browser": "Your browser does not support WebRTC.", - "not-configured": "WebRTC is not configured. Set go2rtc webrtc candidates or ice_servers.", + "not-configured": "To enable, add this server's IP:8555 under go2rtc → webrtc → candidates.", + "notConfiguredOptional": "Optional: live view works without it. WebRTC is mainly needed for two-way talk.", "unreachable": "WebRTC could not connect. Check that port 8555 is reachable and any STUN/TURN server is correct.", "video-codec": "This stream's video codec is not supported by WebRTC in this browser.", "audio-codec": "This stream's audio codec is not supported by WebRTC. Transcode to opus or G.711 with go2rtc.", diff --git a/web/src/components/player/StreamTechnologySelect.tsx b/web/src/components/player/StreamTechnologySelect.tsx index fd08185510..36042c4b7e 100644 --- a/web/src/components/player/StreamTechnologySelect.tsx +++ b/web/src/components/player/StreamTechnologySelect.tsx @@ -7,7 +7,7 @@ import { } from "@/components/ui/select"; import { cn } from "@/lib/utils"; import { LivePlayerMode, WebRTCUnavailableReason } from "@/types/live"; -import { LuX } from "react-icons/lu"; +import { LuInfo, LuX } from "react-icons/lu"; import { useTranslation } from "react-i18next"; type StreamTechnologySelectProps = { @@ -28,6 +28,7 @@ export default function StreamTechnologySelect({ const { t } = useTranslation(["views/live"]); const isChecking = webRTCUnavailableReason === "checking"; + const isNotConfigured = webRTCUnavailableReason === "not-configured"; return (