diff --git a/docs/docs/configuration/camera_specific.md b/docs/docs/configuration/camera_specific.md index cd5d4ff7f8..7bc9b0b9c3 100644 --- a/docs/docs/configuration/camera_specific.md +++ b/docs/docs/configuration/camera_specific.md @@ -170,6 +170,7 @@ The [Add Camera Wizard](cameras.md#adding-a-camera-with-the-add-camera-wizard) i 1. Click **Add Camera** in . 2. Choose **Manual selection** as the stream detection method and select **Reolink** as the camera brand. 3. The wizard queries the camera and automatically uses an http-flv stream for cameras 5MP and lower. For higher resolution cameras, it tries the http-flv stream first and falls back to RTSP when the camera does not support it. +4. The wizard turns on **Use stream compatibility mode** for the http-flv stream it selects. Enable it for any other http-flv stream you add, such as the sub stream. If you use the **Probe camera** method instead, the discovered stream URLs will be RTSP. For Reolink cameras 5MP and lower, where http-flv is recommended, the wizard will show a warning in the validation step. @@ -202,20 +203,20 @@ go2rtc: your_reolink_camera: - "ffmpeg:http://reolink_ip/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=username&password=password#video=copy#audio=copy#audio=opus" your_reolink_camera_sub: - - "http://reolink_ip/flv?port=1935&app=bcs&stream=channel0_ext.bcs&user=username&password=password" + - "ffmpeg:http://reolink_ip/flv?port=1935&app=bcs&stream=channel0_ext.bcs&user=username&password=password" # example for connecting to a Reolink camera that supports two way talk your_reolink_camera_twt: - "ffmpeg:http://reolink_ip/flv?port=1935&app=bcs&stream=channel0_main.bcs&user=username&password=password#video=copy#audio=copy#audio=opus" - "rtsp://username:password@reolink_ip/Preview_01_sub" your_reolink_camera_twt_sub: - - "http://reolink_ip/flv?port=1935&app=bcs&stream=channel0_ext.bcs&user=username&password=password" + - "ffmpeg:http://reolink_ip/flv?port=1935&app=bcs&stream=channel0_ext.bcs&user=username&password=password" - "rtsp://username:password@reolink_ip/Preview_01_sub" # example for connecting to a Reolink NVR your_reolink_camera_via_nvr: - - "http://reolink_nvr_ip/flv?port=1935&app=bcs&stream=channel3_main.bcs&user=username&password=password" # channel numbers are 0-15 + - "ffmpeg:http://reolink_nvr_ip/flv?port=1935&app=bcs&stream=channel3_main.bcs&user=username&password=password" # channel numbers are 0-15 - "ffmpeg:your_reolink_camera_via_nvr#audio=aac" your_reolink_camera_via_nvr_sub: - - "http://reolink_nvr_ip/flv?port=1935&app=bcs&stream=channel3_ext.bcs&user=username&password=password" + - "ffmpeg:http://reolink_nvr_ip/flv?port=1935&app=bcs&stream=channel3_ext.bcs&user=username&password=password" cameras: your_reolink_camera: diff --git a/docs/docs/troubleshooting/go2rtc.md b/docs/docs/troubleshooting/go2rtc.md index 12ddf72d65..2ee8bd756e 100644 --- a/docs/docs/troubleshooting/go2rtc.md +++ b/docs/docs/troubleshooting/go2rtc.md @@ -190,14 +190,14 @@ If you rename or remove a go2rtc stream while experimenting and the live stream Several camera brands have well-known quirks with go2rtc. Rather than repeat them here, see the [camera-specific configuration](/configuration/camera_specific) page, which covers them in detail. The highlights: -- **Reolink**: RTSP is unreliable on many models; the **http-flv** stream is recommended, and you must enable HTTP/RTMP in the camera and **reboot** it. 6MP+ models stream H.265 over http-flv-enhanced, which requires FFmpeg 8.0. See [Reolink Cameras](/configuration/camera_specific#reolink-cameras). +- **Reolink**: RTSP is unreliable on many models; the **http-flv** stream through the FFmpeg module is recommended, and you must enable HTTP/RTMP in the camera and **reboot** it. 6MP+ models stream H.265 over http-flv-enhanced, which requires FFmpeg 8.0. See [Reolink Cameras](/configuration/camera_specific#reolink-cameras). - **TP-Link Tapo**: use go2rtc's native `tapo://` source for stability and two-way audio; a stale RTSP credential can often be revived by clicking play once in the go2rtc web UI. - **Ubiquiti/UniFi Protect**: use the `rtspx://` scheme (not `rtsps://…?enableSrtp`). - **Amcrest/Dahua**: use the `/cam/realmonitor?channel=1&subtype=N` scheme, where `subtype=0` is the main stream. See [Amcrest & Dahua](/configuration/camera_specific#amcrest--dahua). ## Non-RTSP sources and the FFmpeg module -go2rtc's native zero-copy handling only supports well-formed RTSP H.264/H.265. Anything else (MJPEG, RTMP, or unusual codecs) must be handed to the FFmpeg module by prefixing the source with `ffmpeg:`. This is also necessary for some camera streams to be parsed at all, at the cost of slightly slower startup. MJPEG and other non-H.264 sources additionally need `#video=h264` (with `#hardware`) before they can be used for the `record`, `detect`, or restream roles. See [MJPEG Cameras](/configuration/camera_specific#mjpeg-cameras) for a complete example. +go2rtc's native zero-copy handling only supports well-formed RTSP H.264/H.265. Anything else (MJPEG, HTTP/HTTP-FLV, RTMP, or unusual codecs) must be handed to the FFmpeg module by prefixing the source with `ffmpeg:`. This is also necessary for some camera streams to be parsed at all, at the cost of slightly slower startup. MJPEG and other non-H.264 sources additionally need `#video=h264` (with `#hardware`) before they can be used for the `record`, `detect`, or restream roles. See [MJPEG Cameras](/configuration/camera_specific#mjpeg-cameras) for a complete example. ## Hardware-accelerated transcoding with FFmpeg 8 diff --git a/web/e2e/specs/settings/camera-wizard-reolink.spec.ts b/web/e2e/specs/settings/camera-wizard-reolink.spec.ts index d03bd78081..cacc0bfcc0 100644 --- a/web/e2e/specs/settings/camera-wizard-reolink.spec.ts +++ b/web/e2e/specs/settings/camera-wizard-reolink.spec.ts @@ -3,7 +3,8 @@ * * The wizard asks the camera for its resolution, then probes http-flv first * above 5MP and falls back to RTSP. The Step 4 RTSP warning is only for - * cameras that should be on http-flv. + * cameras that should be on http-flv. An http-flv stream the wizard selects + * is registered with go2rtc through the ffmpeg module. */ import { test, expect } from "../../fixtures/frigate-test"; @@ -12,6 +13,7 @@ import type { Page } from "@playwright/test"; const FLV_PATH = "channel0_main.bcs"; const RTSP_PATH = "Preview_01_main"; const RTSP_WARNING = "Reolink RTSP is not recommended"; +const HTTP_WARNING = "Reolink HTTP streams should use FFmpeg"; const FFPROBE_OK = [ { @@ -105,6 +107,20 @@ test.describe("Camera wizard Reolink stream selection @medium @mobile", () => { expect(probed).toEqual([FLV_PATH]); await expect(dialog.locator(`input[value*="${FLV_PATH}"]`)).toBeVisible(); + + const registered: string[] = []; + await frigateApp.page.route("**/api/go2rtc/streams/**", (route) => { + const src = new URL(route.request().url()).searchParams.get("src"); + if (src) registered.push(src); + return route.fulfill({ json: {} }); + }); + + await dialog.getByRole("button", { name: /^Next$/i }).click(); + await expect( + dialog.getByRole("button", { name: /Save New Camera/i }), + ).toBeVisible(); + await expect.poll(() => registered[0]).toMatch(/^ffmpeg:http:\/\//); + await expect(dialog.getByText(HTTP_WARNING)).toHaveCount(0); }); test("above 5MP falls back to RTSP without a warning", async ({ diff --git a/web/public/locales/en/views/settings.json b/web/public/locales/en/views/settings.json index 6246fe0629..5bbe7b80d1 100644 --- a/web/public/locales/en/views/settings.json +++ b/web/public/locales/en/views/settings.json @@ -505,7 +505,8 @@ "audioCodecRequired": "An audio stream is required to support audio detection.", "restreamingWarning": "Reducing connections to the camera for the record stream may increase CPU usage slightly.", "brands": { - "reolink-rtsp": "Reolink RTSP is not recommended. Enable HTTP in the camera's firmware settings and restart the wizard." + "reolink-rtsp": "Reolink RTSP is not recommended. Enable HTTP in the camera's firmware settings and restart the wizard.", + "reolink-http": "Reolink HTTP streams should use FFmpeg for better compatibility. Enable 'Use stream compatibility mode' for this stream." }, "dahua": { "substreamWarning": "Substream 1 is locked to a low resolution. Many Dahua / Amcrest / EmpireTech cameras support additional substreams that need to be enabled in the camera's settings. It is recommended to check and utilize those streams if available." diff --git a/web/src/components/settings/wizard/Step2ProbeOrSnapshot.tsx b/web/src/components/settings/wizard/Step2ProbeOrSnapshot.tsx index 84818d9d40..f1cfdac4ea 100644 --- a/web/src/components/settings/wizard/Step2ProbeOrSnapshot.tsx +++ b/web/src/components/settings/wizard/Step2ProbeOrSnapshot.tsx @@ -393,6 +393,9 @@ export default function Step2ProbeOrSnapshot({ url: streamUrl, roles: ["detect"] as StreamRole[], testResult: result, + useFfmpeg: + wizardData.brandTemplate === "reolink" && + streamUrl.startsWith("http://"), }, ], }); diff --git a/web/src/components/settings/wizard/Step4Validation.tsx b/web/src/components/settings/wizard/Step4Validation.tsx index 4516379d68..a6d21aba16 100644 --- a/web/src/components/settings/wizard/Step4Validation.tsx +++ b/web/src/components/settings/wizard/Step4Validation.tsx @@ -500,6 +500,7 @@ function StreamIssues({ roles: stream.roles, brand: wizardData.brandTemplate, reolinkProtocol: wizardData.reolinkProtocol, + useFfmpeg: stream.useFfmpeg, restream: stream.restream, testResult: stream.testResult, }, diff --git a/web/src/utils/streamHealth.ts b/web/src/utils/streamHealth.ts index 4dd1fc1d7b..6fe72d0a2b 100644 --- a/web/src/utils/streamHealth.ts +++ b/web/src/utils/streamHealth.ts @@ -12,7 +12,7 @@ import { } from "@/utils/streamIssues"; // rules the add camera wizard shows that do not belong on the Health tab -const WIZARD_ONLY_RULES = new Set(["restream", "reolink-rtsp"]); +const WIZARD_ONLY_RULES = new Set(["restream", "reolink-rtsp", "reolink-http"]); /** * Whether the record output keeps the camera's audio codec. The default @@ -115,6 +115,7 @@ export function streamHealth( url, roles: input.roles as StreamRole[], brand: inferCameraBrand(url), + useFfmpeg: restream?.useFfmpeg, restream: !!restream, testResult: result, }, diff --git a/web/src/utils/streamIssues.ts b/web/src/utils/streamIssues.ts index d5b848b008..f763cc9c6d 100644 --- a/web/src/utils/streamIssues.ts +++ b/web/src/utils/streamIssues.ts @@ -14,6 +14,7 @@ export type StreamIssueInput = { roles: StreamRole[]; brand?: CameraBrand; reolinkProtocol?: "http-flv" | "rtsp" | null; + useFfmpeg?: boolean; restream?: boolean; testResult?: TestResult; }; @@ -94,18 +95,27 @@ export function getStreamIssues( const result: StreamIssue[] = []; const { roles, testResult } = input; - if ( - input.brand === "reolink" && - input.reolinkProtocol !== "rtsp" && - input.url.toLowerCase().startsWith("rtsp://") - ) { - result.push({ - type: "warning", - rule: "reolink-rtsp", - message: t("cameraWizard.step4.issues.brands.reolink-rtsp", { - ns: "views/settings", - }), - }); + if (input.brand === "reolink") { + const streamUrl = input.url.toLowerCase(); + if (streamUrl.startsWith("rtsp://") && input.reolinkProtocol !== "rtsp") { + result.push({ + type: "warning", + rule: "reolink-rtsp", + message: t("cameraWizard.step4.issues.brands.reolink-rtsp", { + ns: "views/settings", + }), + }); + } + + if (streamUrl.startsWith("http://") && !input.useFfmpeg) { + result.push({ + type: "warning", + rule: "reolink-http", + message: t("cameraWizard.step4.issues.brands.reolink-http", { + ns: "views/settings", + }), + }); + } } if (testResult?.videoCodec) { @@ -259,7 +269,7 @@ export function getStreamIssues( export function resolveRestreamSource( path: string, streams: Record | undefined, -): { url: string } | undefined { +): { url: string; useFfmpeg: boolean } | undefined { const name = parseRestreamStreamName(path); if (!name || !streams) { @@ -279,8 +289,11 @@ export function resolveRestreamSource( } if (source.startsWith("ffmpeg:")) { - return { url: source.slice("ffmpeg:".length).split("#")[0] }; + return { + url: source.slice("ffmpeg:".length).split("#")[0], + useFfmpeg: true, + }; } - return { url: source }; + return { url: source, useFfmpeg: false }; }