From b84df84e539461de196f2b6703427d37af365a66 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Sat, 3 May 2025 13:06:28 -0300 Subject: [PATCH 01/32] Update README --- README.md | 86 ++++++++++++++++++++++----------------------- website/schema.json | 1 + 2 files changed, 44 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 90a2537f..62f5b6a7 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Ultimate camera streaming application with support RTSP, WebRTC, HomeKit, FFmpeg * [Source: Kasa](#source-kasa) * [Source: GoPro](#source-gopro) * [Source: Ivideon](#source-ivideon) - * [Source: Hass](#source-hass) + * [Source: Home Assistant](#source-homeassistant) * [Source: ISAPI](#source-isapi) * [Source: Nest](#source-nest) * [Source: Roborock](#source-roborock) @@ -82,7 +82,7 @@ Ultimate camera streaming application with support RTSP, WebRTC, HomeKit, FFmpeg * [Module: HomeKit](#module-homekit) * [Module: WebTorrent](#module-webtorrent) * [Module: ngrok](#module-ngrok) - * [Module: Hass](#module-hass) + * [Module: Home Assistant](#module-homeassistant) * [Module: MP4](#module-mp4) * [Module: HLS](#module-hls) * [Module: MJPEG](#module-mjpeg) @@ -142,7 +142,7 @@ The Docker container [`alexxit/go2rtc`](https://hub.docker.com/r/alexxit/go2rtc) 1. Install Add-On: - Settings > Add-ons > Plus > Repositories > Add `https://github.com/AlexxIT/hassio-addons` - go2rtc > Install > Start -2. Setup [Integration](#module-hass) +2. Setup [Integration](#module-home-assistant) ### go2rtc: Home Assistant Integration @@ -154,7 +154,7 @@ Latest, but maybe unstable version: - Binary: [latest nightly release](https://nightly.link/AlexxIT/go2rtc/workflows/build/master) - Docker: `alexxit/go2rtc:master` or `alexxit/go2rtc:master-hardware` versions -- Hass Add-on: `go2rtc master` or `go2rtc master hardware` versions +- Home Assistant Add-on: `go2rtc master` or `go2rtc master hardware` versions ## Configuration @@ -177,7 +177,7 @@ Available modules: - [mjpeg](#module-mjpeg) - MJPEG Server - [ffmpeg](#source-ffmpeg) - FFmpeg integration - [ngrok](#module-ngrok) - ngrok integration (external access for private network) -- [hass](#module-hass) - Home Assistant integration +- [hass](#module-home-assistant) - Home Assistant integration - [log](#module-log) - logs config ### Module: Streams @@ -202,7 +202,7 @@ Available source types: - [kasa](#source-tapo) - TP-Link Kasa cameras - [gopro](#source-gopro) - GoPro cameras - [ivideon](#source-ivideon) - public cameras from [Ivideon](https://tv.ivideon.com/) service -- [hass](#source-hass) - Home Assistant integration +- [hass](#source-home-assistant) - Home Assistant integration - [isapi](#source-isapi) - two way audio for Hikvision (ISAPI) cameras - [roborock](#source-roborock) - Roborock vacuums with cameras - [webrtc](#source-webrtc) - WebRTC/WHEP sources @@ -329,8 +329,8 @@ streams: You can get any stream or file or device via FFmpeg and push it to go2rtc. The app will automatically start FFmpeg with the proper arguments when someone starts watching the stream. -- FFmpeg preistalled for **Docker** and **Hass Add-on** users -- **Hass Add-on** users can target files from [/media](https://www.home-assistant.io/more-info/local-media/setup-media/) folder +- FFmpeg preistalled for **Docker** and **Home Assistant Add-on** users +- **Home Assistant Add-on** users can target files from [/media](https://www.home-assistant.io/more-info/local-media/setup-media/) folder Format: `ffmpeg:{input}#{param1}#{param2}#{param3}`. Examples: @@ -441,7 +441,7 @@ streams: Some sources may have a dynamic link. And you will need to get it using a bash or python script. Your script should echo a link to the source. RTSP, FFmpeg or any of the [supported sources](#module-streams). -**Docker** and **Hass Add-on** users has preinstalled `python3`, `curl`, `jq`. +**Docker** and **Home Assistant Add-on** users has preinstalled `python3`, `curl`, `jq`. Check examples in [wiki](https://github.com/AlexxIT/go2rtc/wiki/Source-Echo-examples). @@ -464,7 +464,7 @@ Like `echo` source, but uses the built-in [expr](https://github.com/antonmedv/ex - HomeKit device can be paired with only one ecosystem. So, if you have paired it to an iPhone (Apple Home) - you can't pair it with Home Assistant or go2rtc. Or if you have paired it to go2rtc - you can't pair it with iPhone - HomeKit device should be in same network with working [mDNS](https://en.wikipedia.org/wiki/Multicast_DNS) between device and go2rtc -go2rtc support import paired HomeKit devices from [Home Assistant](#source-hass). So you can use HomeKit camera with Hass and go2rtc simultaneously. If you using Hass, I recommend pairing devices with it, it will give you more options. +go2rtc support import paired HomeKit devices from [Home Assistant](#source-home-assistant). So you can use HomeKit camera with Home Assistant and go2rtc simultaneously. If you using Home Assistant, I recommend pairing devices with it, it will give you more options. You can pair device with go2rtc on the HomeKit page. If you can't see your devices - reload the page. Also try reboot your HomeKit device (power off). If you still can't see it - you have a problems with mDNS. @@ -580,7 +580,7 @@ streams: quailcam: ivideon:100-tu5dkUPct39cTp9oNEN2B6/0 ``` -#### Source: Hass +#### Source: Home Assistant Support import camera links from [Home Assistant](https://www.home-assistant.io/) config files: @@ -591,7 +591,7 @@ Support import camera links from [Home Assistant](https://www.home-assistant.io/ ```yaml hass: - config: "/config" # skip this setting if you Hass Add-on user + config: "/homeassistant" # skip this setting if you are a Home Assistant Add-on user streams: generic_camera: hass:Camera1 # Settings > Integrations > Integration Name @@ -607,14 +607,14 @@ Any cameras in WebRTC format are supported. But at the moment Home Assistant onl ```yaml streams: # link to Home Assistant Supervised - hass-webrtc1: hass://supervisor?entity_id=camera.nest_doorbell - # link to external Hass with Long-Lived Access Tokens - hass-webrtc2: hass://192.168.1.123:8123?entity_id=camera.nest_doorbell&token=eyXYZ... + ha-webrtc1: hass://supervisor?entity_id=camera.nest_doorbell + # link to external Home Assistant with Long-Lived Access Tokens + ha-webrtc2: hass://192.168.1.123:8123?entity_id=camera.nest_doorbell&token=eyXYZ... ``` **RTSP Cameras** -By default, the Home Assistant API does not allow you to get dynamic RTSP link to a camera stream. So more cameras, like [Tuya](https://www.home-assistant.io/integrations/tuya/), and possibly others can also be imported by using [this method](https://github.com/felipecrs/hass-expose-camera-stream-source#importing-home-assistant-cameras-to-go2rtc-andor-frigate). +By default, the Home Assistant API does not allow you to get dynamic RTSP link to a camera stream. So more cameras, like [Tuya](https://www.home-assistant.io/integrations/tuya/), and possibly others can also be imported by using [this method](https://github.com/felipecrs/hass-expose-camera-stream-source#importing-cameras-from-home-assistant-to-go2rtc-and-frigate). #### Source: ISAPI @@ -635,7 +635,7 @@ streams: Currently only WebRTC cameras are supported. -For simplicity, it is recommended to connect the Nest/WebRTC camera to the [Home Assistant](#source-hass). But if you can somehow get the below parameters - Nest/WebRTC source will work without Hass. +For simplicity, it is recommended to connect the Nest/WebRTC camera to the [Home Assistant](#source-home-assistant). But if you can somehow get the below parameters - Nest/WebRTC source will work without Home Assistant. ```yaml streams: @@ -863,7 +863,7 @@ api: You can get any stream as RTSP-stream: `rtsp://192.168.1.123:8554/{stream_name}` -You can enable external password protection for your RTSP streams. Password protection always disabled for localhost calls (ex. FFmpeg or Hass on same server). +You can enable external password protection for your RTSP streams. Password protection always disabled for localhost calls (ex. FFmpeg or Home Assistant on same server). ```yaml rtsp: @@ -1048,7 +1048,7 @@ TODO: article how it works... With ngrok integration you can get external access to your streams in situations when you have Internet with private IP-address. -- ngrok is pre-installed for **Docker** and **Hass Add-on** users +- ngrok is pre-installed for **Docker** and **Home Assistant Add-on** users - you may need external access for two different things: - WebRTC stream, so you need tunnel WebRTC TCP port (ex. 8555) - go2rtc web interface, so you need tunnel API HTTP port (ex. 1984) @@ -1100,9 +1100,9 @@ tunnels: See the [ngrok agent documentation](https://ngrok.com/docs/agent/config/) for more details on the ngrok configuration file. -### Module: Hass +### Module: Home Assistant -The best and easiest way to use go2rtc inside the Home Assistant is to install the custom integration [WebRTC Camera](#go2rtc-home-assistant-integration) and custom lovelace card. +The best and easiest way to use go2rtc inside the Home Assistant is to install the [WebRTC Camera](#go2rtc-home-assistant-integration) custom integration and card. But go2rtc is also compatible and can be used with [RTSPtoWebRTC](https://www.home-assistant.io/integrations/rtsp_to_webrtc/) built-in integration. @@ -1112,20 +1112,20 @@ You have several options on how to add a camera to Home Assistant: 2. Camera [any source](#module-streams) => [go2rtc config](#configuration) => [Generic Camera](https://www.home-assistant.io/integrations/generic/) - Install any [go2rtc](#fast-start) - Add your stream to [go2rtc config](#configuration) - - Hass > Settings > Integrations > Add Integration > [ONVIF](https://my.home-assistant.io/redirect/config_flow_start/?domain=onvif) > Host: `127.0.0.1`, Port: `1984` - - Hass > Settings > Integrations > Add Integration > [Generic Camera](https://my.home-assistant.io/redirect/config_flow_start/?domain=generic) > Stream Source URL: `rtsp://127.0.0.1:8554/camera1` (change to your stream name, leave everything else as is) + - Home Assistant > Settings > Integrations > Add Integration > [ONVIF](https://my.home-assistant.io/redirect/config_flow_start/?domain=onvif) > Host: `127.0.0.1`, Port: `1984` + - Home Assistant > Settings > Integrations > Add Integration > [Generic Camera](https://my.home-assistant.io/redirect/config_flow_start/?domain=generic) > Stream Source URL: `rtsp://127.0.0.1:8554/camera1` (change to your stream name, leave everything else as is) You have several options on how to watch the stream from the cameras in Home Assistant: 1. `Camera Entity` => `Picture Entity Card` => Technology `HLS`, codecs: `H264/H265/AAC`, poor latency. 2. `Camera Entity` => [RTSPtoWebRTC](https://www.home-assistant.io/integrations/rtsp_to_webrtc/) => `Picture Entity Card` => Technology `WebRTC`, codecs: `H264/PCMU/PCMA/OPUS`, best latency. - Install any [go2rtc](#fast-start) - - Hass > Settings > Integrations > Add Integration > [RTSPtoWebRTC](https://my.home-assistant.io/redirect/config_flow_start/?domain=rtsp_to_webrtc) > `http://127.0.0.1:1984/` + - Home Assistant > Settings > Integrations > Add Integration > [RTSPtoWebRTC](https://my.home-assistant.io/redirect/config_flow_start/?domain=rtsp_to_webrtc) > `http://127.0.0.1:1984/` - RTSPtoWebRTC > Configure > STUN server: `stun.l.google.com:19302` - - Use Picture Entity or Picture Glance lovelace card + - Use the default Picture Entity or Picture Glance card 3. `Camera Entity` or `Camera URL` => [WebRTC Camera](https://github.com/AlexxIT/WebRTC) => Technology: `WebRTC/MSE/MP4/MJPEG`, codecs: `H264/H265/AAC/PCMU/PCMA/OPUS`, best latency, best compatibility. - Install and add [WebRTC Camera](https://github.com/AlexxIT/WebRTC) custom integration - - Use WebRTC Camera custom lovelace card + - Use WebRTC Camera custom card in your dashboard You can add camera `entity_id` to [go2rtc config](#configuration) if you need transcoding: @@ -1134,9 +1134,9 @@ streams: "camera.hall": ffmpeg:{input}#video=copy#audio=opus ``` -**PS.** Default Home Assistant lovelace cards don't support 2-way audio. You can use 2-way audio from [Add-on Web UI](https://my.home-assistant.io/redirect/supervisor_addon/?addon=a889bffc_go2rtc&repository_url=https%3A%2F%2Fgithub.com%2FAlexxIT%2Fhassio-addons). But you need use HTTPS to access the microphone. This is a browser restriction and cannot be avoided. +**PS.** Default Home Assistant cards don't support 2-way audio. You can use 2-way audio from the [Add-on Web UI](https://my.home-assistant.io/redirect/supervisor_addon/?addon=a889bffc_go2rtc&repository_url=https%3A%2F%2Fgithub.com%2FAlexxIT%2Fhassio-addons). But you need use HTTPS to access the microphone. This is a browser restriction and cannot be avoided. -**PS.** There is also another nice card with go2rtc support - [Frigate Lovelace Card](https://github.com/dermotduffy/frigate-hass-card). +**PS.** There is also another nice card with 2-way audio support through go2rtc - [Advanced Camera Card](https://github.com/dermotduffy/advanced-camera-card). ### Module: MP4 @@ -1241,7 +1241,7 @@ webrtc: ``` - local access to RTSP is not a problem for [FFmpeg](#source-ffmpeg) integration, because it runs locally on your server -- local access to API is not a problem for [Home Assistant Add-on](#go2rtc-home-assistant-add-on), because Hass runs locally on same server and Add-on Web UI protected with Hass authorization ([Ingress feature](https://www.home-assistant.io/blog/2019/04/15/hassio-ingress/)) +- local access to API is not a problem for [Home Assistant Add-on](#go2rtc-home-assistant-add-on), because Home Assistant runs locally on same server and Add-on Web UI is protected with the Home Assistant authorization ([Ingress feature](https://www.home-assistant.io/blog/2019/04/15/hassio-ingress/)) - external access to WebRTC TCP port is not a problem, because it used only for transmit encrypted media data - anyway you need to open this port to your local network and to the Internet in order for WebRTC to work @@ -1263,7 +1263,7 @@ Without filters: Some examples: -- `rtsp://192.168.1.123:8554/camera1?mp4` - useful for recording as MP4 files (e.g. Hass or Frigate) +- `rtsp://192.168.1.123:8554/camera1?mp4` - useful for recording as MP4 files (e.g. Home Assistant or Frigate) - `rtsp://192.168.1.123:8554/camera1?video=h264,h265&audio=aac` - full version of the filter above - `rtsp://192.168.1.123:8554/camera1?video=h264&audio=aac&audio=opus` - H264 video codec and two separate audio tracks - `rtsp://192.168.1.123:8554/camera1?video&audio=all` - any video codec and all audio codecs as separate tracks @@ -1283,7 +1283,7 @@ Some examples: | Desktop Firefox | H264
PCMU, PCMA
OPUS | H264
AAC, FLAC*
OPUS | H264
AAC, FLAC*
OPUS | no | | - Desktop Safari 14+
- iPad Safari 14+
- iPhone Safari 17.1+ | H264, H265*
PCMU, PCMA
OPUS | H264, H265
AAC, FLAC* | **no!** | H264, H265
AAC, FLAC* | | iPhone Safari 14+ | H264, H265*
PCMU, PCMA
OPUS | **no!** | **no!** | H264, H265
AAC, FLAC* | -| macOS [Hass App][1] | no | no | no | H264, H265
AAC, FLAC* | +| macOS [Home Assistant App][1] | no | no | no | H264, H265
AAC, FLAC* | [1]: https://apps.apple.com/app/home-assistant/id1099568401 @@ -1342,7 +1342,7 @@ PCMU/xxx => PCMU/8000 => WebRTC **Important** -- FLAC codec not supported in a RTSP stream. If you using Frigate or Hass for recording MP4 files with PCMA/PCMU/PCM audio - you should setup transcoding to AAC codec. +- FLAC codec not supported in a RTSP stream. If you using Frigate or Home Assistant for recording MP4 files with PCMA/PCMU/PCM audio - you should setup transcoding to AAC codec. - PCMA and PCMU are VERY low quality codecs. Them support only 256! different sounds. Use them only when you have no other options. ## Codecs negotiation @@ -1376,8 +1376,8 @@ streams: ## Projects using go2rtc -- [Frigate 12+](https://frigate.video/) - open source NVR built around real-time AI object detection -- [Frigate Lovelace Card](https://github.com/dermotduffy/frigate-hass-card) - custom card for Home Assistant +- [Frigate](https://frigate.video/) - open source NVR built around real-time AI object detection +- [Advanced Camera Card](https://github.com/dermotduffy/advanced-camera-card) - custom card for Home Assistant - [OpenIPC](https://github.com/OpenIPC/firmware/tree/master/general/package/go2rtc) - Alternative IP Camera firmware from an open community - [wz_mini_hacks](https://github.com/gtxaspec/wz_mini_hacks) - Custom firmware for Wyze cameras - [EufyP2PStream](https://github.com/oischinger/eufyp2pstream) - A small project that provides a Video/Audio Stream from Eufy cameras that don't directly support RTSP @@ -1421,22 +1421,22 @@ streams: **Q. What's the difference between go2rtc, WebRTC Camera and RTSPtoWebRTC?** -**go2rtc** is a new version of the server-side [WebRTC Camera](https://github.com/AlexxIT/WebRTC) integration, completely rewritten from scratch, with a number of fixes and a huge number of new features. It is compatible with native Home Assistant [RTSPtoWebRTC](https://www.home-assistant.io/integrations/rtsp_to_webrtc/) integration. So you [can use](#module-hass) default lovelace Picture Entity or Picture Glance. +**go2rtc** is a new version of the server-side [WebRTC Camera](https://github.com/AlexxIT/WebRTC) integration, completely rewritten from scratch, with a number of fixes and a huge number of new features. It is compatible with native Home Assistant [RTSPtoWebRTC](https://www.home-assistant.io/integrations/rtsp_to_webrtc/) integration so you [can use](#module-home-assistant) the default Picture Entity or Picture Glance cards, although they don't support 2-way audio. -**Q. Should I use go2rtc addon or WebRTC Camera integration?** +**Q. Should I use go2rtc add-on or WebRTC Camera integration?** -**go2rtc** is more than just viewing your stream online with WebRTC/MSE/HLS/etc. You can use it all the time for your various tasks. But every time the Hass is rebooted - all integrations are also rebooted. So your streams may be interrupted if you use them in additional tasks. +**go2rtc** is more than just viewing your stream online with WebRTC/MSE/HLS/etc. You can use it all the time for your various tasks. But every time the Home Assistant is rebooted - all integrations are also rebooted. So your streams may be interrupted if you use them in additional tasks. -Basic users can use **WebRTC Camera** integration. Advanced users can use go2rtc addon or Frigate 12+ addon. +Basic users can use **WebRTC Camera** integration. Advanced users can use go2rtc add-on or Frigate add-on. -**Q. Which RTSP link should I use inside Hass?** +**Q. Which RTSP link should I use inside Home Assistant?** -You can use direct link to your cameras there (as you always do). **go2rtc** support zero-config feature. You may leave `streams` config section empty. And your streams will be created on the fly on first start from Hass. And your cameras will have multiple connections. Some from Hass directly and one from **go2rtc**. +You can use direct link to your cameras there (as you always do). **go2rtc** support zero-config feature. You may leave `streams` config section empty. And your streams will be created on the fly on first start from Home Assistant. And your cameras will have multiple connections. Some from Home Assistant directly and one from **go2rtc**. -Also you can specify your streams in **go2rtc** [config file](#configuration) and use RTSP links to this addon. With additional features: multi-source [codecs negotiation](#codecs-negotiation) or FFmpeg [transcoding](#source-ffmpeg) for unsupported codecs. Or use them as source for Frigate. And your cameras will have one connection from **go2rtc**. And **go2rtc** will have multiple connection - some from Hass via RTSP protocol, some from your browser via WebRTC/MSE/HLS protocols. +Also you can specify your streams in **go2rtc** [config file](#configuration) and use RTSP links to this add-on. With additional features: multi-source [codecs negotiation](#codecs-negotiation) or FFmpeg [transcoding](#source-ffmpeg) for unsupported codecs. Or use them as source for Frigate. And your cameras will have one connection from **go2rtc**. And **go2rtc** will have multiple connection - some from Home Assistant via RTSP protocol, some from your browser via WebRTC/MSE/HLS protocols. Use any config what you like. -**Q. What about lovelace card with support 2-way audio?** +**Q. What about a Home Assistant card with 2-way audio support?** -At this moment I am focused on improving stability and adding new features to **go2rtc**. Maybe someone could write such a card themselves. It's not difficult, I have [some sketches](https://github.com/AlexxIT/go2rtc/blob/master/www/webrtc.html). +No built-in card supports 2-way audio as of now in Home Assistant, but you can use [WebRTC Camera](https://github.com/AlexxIT/WebRTC/) or [Advanced Camera Card](https://github.com/dermotduffy/advanced-camera-card). diff --git a/website/schema.json b/website/schema.json index d5e19436..1aa2f320 100644 --- a/website/schema.json +++ b/website/schema.json @@ -109,6 +109,7 @@ "description": "Home Assistant config directory path", "type": "string", "examples": [ + "/homeassistant", "/config" ] } From f1faa95897da7a128fef07828fb2d670f96abfe6 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Sat, 3 May 2025 13:12:50 -0300 Subject: [PATCH 02/32] Fix some links --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 62f5b6a7..028f1b17 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ Ultimate camera streaming application with support RTSP, WebRTC, HomeKit, FFmpeg * [Source: Kasa](#source-kasa) * [Source: GoPro](#source-gopro) * [Source: Ivideon](#source-ivideon) - * [Source: Home Assistant](#source-homeassistant) + * [Source: Home Assistant](#source-home-assistant) * [Source: ISAPI](#source-isapi) * [Source: Nest](#source-nest) * [Source: Roborock](#source-roborock) @@ -82,7 +82,7 @@ Ultimate camera streaming application with support RTSP, WebRTC, HomeKit, FFmpeg * [Module: HomeKit](#module-homekit) * [Module: WebTorrent](#module-webtorrent) * [Module: ngrok](#module-ngrok) - * [Module: Home Assistant](#module-homeassistant) + * [Module: Home Assistant](#module-home-assistant) * [Module: MP4](#module-mp4) * [Module: HLS](#module-hls) * [Module: MJPEG](#module-mjpeg) From 4788011249802953d0ac71207d5259fc73eead0a Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Mon, 12 May 2025 19:51:31 -0300 Subject: [PATCH 03/32] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 028f1b17..8bf73988 100644 --- a/README.md +++ b/README.md @@ -329,7 +329,7 @@ streams: You can get any stream or file or device via FFmpeg and push it to go2rtc. The app will automatically start FFmpeg with the proper arguments when someone starts watching the stream. -- FFmpeg preistalled for **Docker** and **Home Assistant Add-on** users +- FFmpeg preinstalled for **Docker** and **Home Assistant Add-on** users - **Home Assistant Add-on** users can target files from [/media](https://www.home-assistant.io/more-info/local-media/setup-media/) folder Format: `ffmpeg:{input}#{param1}#{param2}#{param3}`. Examples: From 6e2e0d353affec26f0ed44ad7b93450ee4eb7472 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Thu, 22 May 2025 11:14:56 -0300 Subject: [PATCH 04/32] Revert Home Assistant config dir to /config in example --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8bf73988..43f10044 100644 --- a/README.md +++ b/README.md @@ -591,7 +591,7 @@ Support import camera links from [Home Assistant](https://www.home-assistant.io/ ```yaml hass: - config: "/homeassistant" # skip this setting if you are a Home Assistant Add-on user + config: "/config" # skip this setting if you are a Home Assistant Add-on user streams: generic_camera: hass:Camera1 # Settings > Integrations > Integration Name From 6f2afdc97b19d3e3f8637652eb08d342b5da9401 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Thu, 22 May 2025 11:16:54 -0300 Subject: [PATCH 05/32] Remove it from schema.json too --- website/schema.json | 1 - 1 file changed, 1 deletion(-) diff --git a/website/schema.json b/website/schema.json index 1aa2f320..d5e19436 100644 --- a/website/schema.json +++ b/website/schema.json @@ -109,7 +109,6 @@ "description": "Home Assistant config directory path", "type": "string", "examples": [ - "/homeassistant", "/config" ] } From 90217d61d05f52482bfe2016be7ee7681473e98a Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Mon, 7 Jul 2025 14:25:55 -0300 Subject: [PATCH 06/32] Restore ToC --- README.md | 58 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/README.md b/README.md index c177e21b..cabcd7e7 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,64 @@ Ultimate camera streaming application with support for RTSP, WebRTC, HomeKit, FF --- +- [Fast start](#fast-start) + - [go2rtc: Binary](#go2rtc-binary) + - [go2rtc: Docker](#go2rtc-docker) + - [go2rtc: Home Assistant add-on](#go2rtc-home-assistant-add-on) + - [go2rtc: Home Assistant Integration](#go2rtc-home-assistant-integration) + - [go2rtc: Dev version](#go2rtc-dev-version) +- [Configuration](#configuration) + - [Module: Streams](#module-streams) + - [Two-way audio](#two-way-audio) + - [Source: RTSP](#source-rtsp) + - [Source: RTMP](#source-rtmp) + - [Source: HTTP](#source-http) + - [Source: ONVIF](#source-onvif) + - [Source: FFmpeg](#source-ffmpeg) + - [Source: FFmpeg Device](#source-ffmpeg-device) + - [Source: Exec](#source-exec) + - [Source: Echo](#source-echo) + - [Source: Expr](#source-expr) + - [Source: HomeKit](#source-homekit) + - [Source: Bubble](#source-bubble) + - [Source: DVRIP](#source-dvrip) + - [Source: Tapo](#source-tapo) + - [Source: Kasa](#source-kasa) + - [Source: GoPro](#source-gopro) + - [Source: Ivideon](#source-ivideon) + - [Source: Hass](#source-hass) + - [Source: ISAPI](#source-isapi) + - [Source: Nest](#source-nest) + - [Source: Roborock](#source-roborock) + - [Source: WebRTC](#source-webrtc) + - [Source: WebTorrent](#source-webtorrent) + - [Incoming sources](#incoming-sources) + - [Incoming: Browser](#incoming-browser) + - [Incoming: WebRTC/WHIP](#incoming-webrtcwhip) + - [Stream to camera](#stream-to-camera) + - [Publish stream](#publish-stream) + - [Module: API](#module-api) + - [Module: RTSP](#module-rtsp) + - [Module: RTMP](#module-rtmp) + - [Module: WebRTC](#module-webrtc) + - [Module: HomeKit](#module-homekit) + - [Module: WebTorrent](#module-webtorrent) + - [Module: ngrok](#module-ngrok) + - [Module: Hass](#module-hass) + - [Module: MP4](#module-mp4) + - [Module: HLS](#module-hls) + - [Module: MJPEG](#module-mjpeg) + - [Module: Log](#module-log) +- [Security](#security) +- [Codecs filters](#codecs-filters) +- [Codecs madness](#codecs-madness) +- [Built-in transcoding](#built-in-transcoding) +- [Codecs negotiation](#codecs-negotiation) +- [Projects using go2rtc](#projects-using-go2rtc) +- [Camera experience](#camera-experience) +- [TIPS](#tips) +- [FAQ](#faq) + ## Fast start 1. Download [binary](#go2rtc-binary) or use [Docker](#go2rtc-docker) or Home Assistant [add-on](#go2rtc-home-assistant-add-on) or [Integration](#go2rtc-home-assistant-integration) From 6734492a2d92786753b6740692bce54a8ab1e808 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Mon, 7 Jul 2025 14:31:51 -0300 Subject: [PATCH 07/32] Change TIPS to Tips --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cabcd7e7..81239e71 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ Ultimate camera streaming application with support for RTSP, WebRTC, HomeKit, FF - [Codecs negotiation](#codecs-negotiation) - [Projects using go2rtc](#projects-using-go2rtc) - [Camera experience](#camera-experience) -- [TIPS](#tips) +- [Tips](#tips) - [FAQ](#faq) ## Fast start @@ -1416,7 +1416,7 @@ streams: - [TP-Link](https://www.tp-link.com/) - few streaming clients, packet loss? - Chinese cheap noname cameras, Wyze Cams, Xiaomi cameras with hacks (usually have `/live/ch00_1` in RTSP URL) - awful but usable RTSP protocol implementation, low stream quality, few settings, packet loss? -## TIPS +## Tips **Using apps for low RTSP delay** From 5e746e33672d1e38a3bb22a57047373dccd6745b Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Thu, 10 Jul 2025 16:15:14 -0300 Subject: [PATCH 08/32] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 81239e71..16854254 100644 --- a/README.md +++ b/README.md @@ -638,7 +638,7 @@ streams: Currently, only WebRTC cameras are supported. -For simplicity, it is recommended to connect the Nest/WebRTC camera to the [Home Assistant](#source-source-hass). But if you can somehow get the below parameters, Nest/WebRTC source will work without Home Assistant. +For simplicity, it is recommended to connect the Nest/WebRTC camera to the [Home Assistant](#source-hass). But if you can somehow get the below parameters, Nest/WebRTC source will work without Home Assistant. ```yaml streams: From ce4bee3be7f4c1a392395f251191d5c2de67273a Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Thu, 10 Jul 2025 16:15:42 -0300 Subject: [PATCH 09/32] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 16854254..1264819b 100644 --- a/README.md +++ b/README.md @@ -1144,7 +1144,7 @@ streams: "camera.hall": ffmpeg:{input}#video=copy#audio=opus ``` -**PS.** Default Home Assistant cards don't support two-way audio. You can use two-way audio from the [add-on Web UI](https://my.home-assistant.io/redirect/supervisor_addon/?addon=a889bffc_go2rtc&repository_url=https%3A%2F%2Fgithub.com%2FAlexxIT%2Fsource-hassio-addons), but you need to use HTTPS to access the microphone. This is a browser restriction and cannot be avoided. +**PS.** Default Home Assistant cards don't support two-way audio. You can use two-way audio from the [add-on Web UI](https://my.home-assistant.io/redirect/supervisor_addon/?addon=a889bffc_go2rtc&repository_url=https%3A%2F%2Fgithub.com%2FAlexxIT%2Fhassio-addons), but you need to use HTTPS to access the microphone. This is a browser restriction and cannot be avoided. **PS.** There is also another nice card with two-way audio support through go2rtc - [Advanced Camera Card](https://github.com/dermotduffy/advanced-camera-card). From b7557d750e508b794ff08980581b82f0303110b7 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Thu, 10 Jul 2025 16:16:50 -0300 Subject: [PATCH 10/32] Apply suggestions from code review --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1264819b..b4ca0481 100644 --- a/README.md +++ b/README.md @@ -610,9 +610,9 @@ Any cameras in WebRTC format are supported. But at the moment Home Assistant onl ```yaml streams: # link to Home Assistant Supervised - ha-webrtc1: hass://supervisor?entity_id=camera.nest_doorbell + hass-webrtc1: hass://supervisor?entity_id=camera.nest_doorbell # link to external Home Assistant with Long-Lived Access Tokens - ha-webrtc2: hass://192.168.1.123:8123?entity_id=camera.nest_doorbell&token=eyXYZ... + hass-webrtc2: hass://192.168.1.123:8123?entity_id=camera.nest_doorbell&token=eyXYZ... ``` **RTSP Cameras** From 88a2f33b7106708c491dc1a78f3756fb84ae1f38 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Thu, 10 Jul 2025 16:19:48 -0300 Subject: [PATCH 11/32] Fix table --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b4ca0481..e1eeff72 100644 --- a/README.md +++ b/README.md @@ -1293,7 +1293,7 @@ Some examples: | Desktop Firefox | H264
PCMU, PCMA
OPUS | H264
AAC, FLAC*
OPUS | H264
AAC, FLAC*
OPUS | no | | - Desktop Safari 14+
- iPad Safari 14+
- iPhone Safari 17.1+ | H264, H265*
PCMU, PCMA
OPUS | H264, H265
AAC, FLAC* | **no!** | H264, H265
AAC, FLAC* | | iPhone Safari 14+ | H264, H265*
PCMU, PCMA
OPUS | **no!** | **no!** | H264, H265
AAC, FLAC* | -| macOS [Home Assistant App][1] | no | no | no | H264, H265
AAC, FLAC* | +| macOS [Home Assistant App][1] | no | no | no | H264, H265
AAC, FLAC* | [1]: https://apps.apple.com/app/home-assistant/id1099568401 From 7c3d97b0a2187c8d486152e696e20e04ae3d2833 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Thu, 10 Jul 2025 16:28:37 -0300 Subject: [PATCH 12/32] Remove mentions of RTSPtoWebRTC --- README.md | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e1eeff72..1c89513c 100644 --- a/README.md +++ b/README.md @@ -1112,9 +1112,7 @@ See the [ngrok agent documentation](https://ngrok.com/docs/agent/config/) for mo ### Module: Hass -The best and easiest way to use go2rtc inside Home Assistant is to install the [WebRTC Camera](#go2rtc-home-assistant-integration) custom integration and card. - -But go2rtc is also compatible and can be used with the [RTSPtoWebRTC](https://www.home-assistant.io/integrations/rtsp_to_webrtc/) built-in integration. +While [go2rtc is used by default in Home Assistant](https://www.home-assistant.io/integrations/go2rtc/), the best and easiest way to have full control over it is to install the [WebRTC Camera](#go2rtc-home-assistant-integration) custom integration and card. You have several options on how to add a camera to Home Assistant: @@ -1127,13 +1125,8 @@ You have several options on how to add a camera to Home Assistant: You have several options on how to watch the stream from the cameras in Home Assistant: -1. `Camera Entity` => `Picture Entity Card` => Technology `HLS`, codecs: `H264/H265/AAC`, poor latency. -2. `Camera Entity` => [RTSPtoWebRTC](https://www.home-assistant.io/integrations/rtsp_to_webrtc/) => `Picture Entity Card` => Technology `WebRTC`, codecs: `H264/PCMU/PCMA/OPUS`, best latency. - - Install any [go2rtc](#fast-start) - - Home Assistant > Settings > Integrations > Add Integration > [RTSPtoWebRTC](https://my.home-assistant.io/redirect/config_flow_start/?domain=rtsp_to_webrtc) > `http://127.0.0.1:1984/` - - RTSPtoWebRTC > Configure > STUN server: `stun.l.google.com:19302` - - Use Picture Entity or Picture Glance Lovelace card -3. `Camera Entity` or `Camera URL` => [WebRTC Camera](https://github.com/AlexxIT/WebRTC) => Technology: `WebRTC/MSE/MP4/MJPEG`, codecs: `H264/H265/AAC/PCMU/PCMA/OPUS`, best latency, best compatibility. +1. `Camera Entity` => `Picture Entity Card` => Technology `WebRTC` (through [built-in go2rtc](https://www.home-assistant.io/integrations/go2rtc/)), codecs `H264/H265/AAC/PCMU/PCMA/OPUS`, best latency. Fallbacks to: Technology `HLS`, codecs: `H264/H265/AAC`, poor latency. +2. `Camera Entity` or `Camera URL` => [WebRTC Camera](https://github.com/AlexxIT/WebRTC) => Technology: `WebRTC/MSE/MP4/MJPEG`, codecs: `H264/H265/AAC/PCMU/PCMA/OPUS`, best latency, best compatibility. - Install and add [WebRTC Camera](https://github.com/AlexxIT/WebRTC) custom integration - Use WebRTC Camera custom card in your dashboard @@ -1429,7 +1422,7 @@ streams: ## FAQ -**Q. What's the difference between go2rtc, WebRTC Camera and RTSPtoWebRTC?** +**Q. What's the difference between go2rtc and WebRTC Camera?** **go2rtc** is a new version of the server-side [WebRTC Camera](https://github.com/AlexxIT/WebRTC) integration, completely rewritten from scratch, with a number of fixes and a huge number of new features. [go2rtc is used by default in Home Assistant](https://www.home-assistant.io/integrations/go2rtc/). From d4190290b796fbcd7c3ad6aa2fa9f94b33d51ebb Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Fri, 24 Oct 2025 12:20:54 -0300 Subject: [PATCH 13/32] Change back to "when" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1d62a2d0..f3280606 100644 --- a/README.md +++ b/README.md @@ -1082,7 +1082,7 @@ Link example: `https://alexxit.github.io/go2rtc/#share=02SNtgjKXY&pwd=wznEQqznxW ### Module: ngrok -With [ngrok](https://ngrok.com/) integration, you can get external access to your streams in situations where you have Internet with a private IP address ([read more](https://github.com/AlexxIT/go2rtc/blob/master/internal/ngrok/README.md)). +With [ngrok](https://ngrok.com/) integration, you can get external access to your streams in situations when you have Internet with a private IP address ([read more](https://github.com/AlexxIT/go2rtc/blob/master/internal/ngrok/README.md)). ### Module: Hass From d16d26067930f94f8fcd8060e4a38f7702597b25 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Fri, 24 Oct 2025 12:22:41 -0300 Subject: [PATCH 14/32] Use relative links (git tag-aware) --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f3280606..31155164 100644 --- a/README.md +++ b/README.md @@ -361,7 +361,7 @@ streams: rotate: ffmpeg:rtsp://12345678@192.168.1.123/av_stream/ch0#video=h264#rotate=90 ``` -All transcoding formats have [built-in templates](https://github.com/AlexxIT/go2rtc/blob/master/internal/ffmpeg/ffmpeg.go): `h264`, `h265`, `opus`, `pcmu`, `pcmu/16000`, `pcmu/48000`, `pcma`, `pcma/16000`, `pcma/48000`, `aac`, `aac/16000`. +All transcoding formats have [built-in templates](./internal/ffmpeg/ffmpeg.go): `h264`, `h265`, `opus`, `pcmu`, `pcmu/16000`, `pcmu/48000`, `pcma`, `pcma/16000`, `pcma/48000`, `aac`, `aac/16000`. But you can override them via YAML config. You can also add your own formats to the config and use them with source params. @@ -460,7 +460,7 @@ streams: *[New in v1.8.2](https://github.com/AlexxIT/go2rtc/releases/tag/v1.8.2)* -Like `echo` source, but uses the built-in [expr](https://github.com/antonmedv/expr) expression language ([read more](https://github.com/AlexxIT/go2rtc/blob/master/internal/expr/README.md)). +Like `echo` source, but uses the built-in [expr](https://github.com/antonmedv/expr) expression language ([read more](./internal/expr/README.md)). #### Source: HomeKit @@ -1082,7 +1082,7 @@ Link example: `https://alexxit.github.io/go2rtc/#share=02SNtgjKXY&pwd=wznEQqznxW ### Module: ngrok -With [ngrok](https://ngrok.com/) integration, you can get external access to your streams in situations when you have Internet with a private IP address ([read more](https://github.com/AlexxIT/go2rtc/blob/master/internal/ngrok/README.md)). +With [ngrok](https://ngrok.com/) integration, you can get external access to your streams in situations when you have Internet with a private IP address ([read more](./internal/ngrok/README.md)). ### Module: Hass @@ -1181,7 +1181,7 @@ API examples: - You can use `rotate` param with `90`, `180`, `270` or `-90` values - You can use `hardware`/`hw` param [read more](https://github.com/AlexxIT/go2rtc/wiki/Hardware-acceleration) -**PS.** This module also supports streaming to the server console (terminal) in the **animated ASCII art** format ([read more](https://github.com/AlexxIT/go2rtc/blob/master/internal/mjpeg/README.md)): +**PS.** This module also supports streaming to the server console (terminal) in the **animated ASCII art** format ([read more](./internal/mjpeg/README.md)): [![](https://img.youtube.com/vi/sHj_3h_sX7M/mqdefault.jpg)](https://www.youtube.com/watch?v=sHj_3h_sX7M) From af819952e8e9771a196d9829c4479b923165fcc6 Mon Sep 17 00:00:00 2001 From: Alex X Date: Sun, 18 Jan 2026 21:50:26 +0300 Subject: [PATCH 15/32] Improve ONVIF server support #1299 --- internal/onvif/onvif.go | 14 ++- pkg/onvif/envelope.go | 14 +-- pkg/onvif/server.go | 191 ++++++++++++++++++++++------------------ 3 files changed, 115 insertions(+), 104 deletions(-) diff --git a/internal/onvif/onvif.go b/internal/onvif/onvif.go index 65f8599a..c305b706 100644 --- a/internal/onvif/onvif.go +++ b/internal/onvif/onvif.go @@ -74,8 +74,10 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) { log.Trace().Msgf("[onvif] server request %s %s:\n%s", r.Method, r.RequestURI, b) switch operation { - case onvif.DeviceGetNetworkInterfaces, // important for Hass + case onvif.ServiceGetServiceCapabilities, // important for Hass + onvif.DeviceGetNetworkInterfaces, // important for Hass onvif.DeviceGetSystemDateAndTime, // important for Hass + onvif.DeviceSetSystemDateAndTime, // return just OK onvif.DeviceGetDiscoveryMode, onvif.DeviceGetDNS, onvif.DeviceGetHostname, @@ -83,8 +85,10 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) { onvif.DeviceGetNetworkProtocols, onvif.DeviceGetNTP, onvif.DeviceGetScopes, + onvif.MediaGetVideoEncoderConfiguration, onvif.MediaGetVideoEncoderConfigurations, onvif.MediaGetAudioEncoderConfigurations, + onvif.MediaGetVideoEncoderConfigurationOptions, onvif.MediaGetAudioSources, onvif.MediaGetAudioSourceConfigurations: b = onvif.StaticResponse(operation) @@ -100,11 +104,6 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) { // important for Hass: SerialNumber (unique server ID) b = onvif.GetDeviceInformationResponse("", "go2rtc", app.Version, r.Host) - case onvif.ServiceGetServiceCapabilities: - // important for Hass - // TODO: check path links to media - b = onvif.GetMediaServiceCapabilitiesResponse() - case onvif.DeviceSystemReboot: b = onvif.StaticResponse(operation) @@ -134,8 +133,7 @@ func onvifDeviceService(w http.ResponseWriter, r *http.Request) { case onvif.MediaGetStreamUri: host, _, err := net.SplitHostPort(r.Host) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return + host = r.Host // in case of Host without port } uri := "rtsp://" + host + ":" + rtsp.Port + "/" + onvif.FindTagValue(b, "ProfileToken") diff --git a/pkg/onvif/envelope.go b/pkg/onvif/envelope.go index f0e1b29c..76a41260 100644 --- a/pkg/onvif/envelope.go +++ b/pkg/onvif/envelope.go @@ -15,14 +15,9 @@ type Envelope struct { } const ( - prefix1 = ` - -` - prefix2 = ` -` - suffix = ` - -` + prefix1 = `` + prefix2 = `` + suffix = `` ) func NewEnvelope() *Envelope { @@ -54,8 +49,7 @@ func NewEnvelopeWithUser(user *url.Userinfo) *Envelope { %s - -`, +`, user.Username(), base64.StdEncoding.EncodeToString(h.Sum(nil)), base64.StdEncoding.EncodeToString([]byte(nonce)), diff --git a/pkg/onvif/server.go b/pkg/onvif/server.go index 54272798..94e42fc7 100644 --- a/pkg/onvif/server.go +++ b/pkg/onvif/server.go @@ -21,21 +21,24 @@ const ( DeviceGetScopes = "GetScopes" DeviceGetServices = "GetServices" DeviceGetSystemDateAndTime = "GetSystemDateAndTime" + DeviceSetSystemDateAndTime = "SetSystemDateAndTime" DeviceSystemReboot = "SystemReboot" ) const ( - MediaGetAudioEncoderConfigurations = "GetAudioEncoderConfigurations" - MediaGetAudioSources = "GetAudioSources" - MediaGetAudioSourceConfigurations = "GetAudioSourceConfigurations" - MediaGetProfile = "GetProfile" - MediaGetProfiles = "GetProfiles" - MediaGetSnapshotUri = "GetSnapshotUri" - MediaGetStreamUri = "GetStreamUri" - MediaGetVideoEncoderConfigurations = "GetVideoEncoderConfigurations" - MediaGetVideoSources = "GetVideoSources" - MediaGetVideoSourceConfiguration = "GetVideoSourceConfiguration" - MediaGetVideoSourceConfigurations = "GetVideoSourceConfigurations" + MediaGetAudioEncoderConfigurations = "GetAudioEncoderConfigurations" + MediaGetAudioSources = "GetAudioSources" + MediaGetAudioSourceConfigurations = "GetAudioSourceConfigurations" + MediaGetProfile = "GetProfile" + MediaGetProfiles = "GetProfiles" + MediaGetSnapshotUri = "GetSnapshotUri" + MediaGetStreamUri = "GetStreamUri" + MediaGetVideoEncoderConfiguration = "GetVideoEncoderConfiguration" + MediaGetVideoEncoderConfigurations = "GetVideoEncoderConfigurations" + MediaGetVideoEncoderConfigurationOptions = "GetVideoEncoderConfigurationOptions" + MediaGetVideoSources = "GetVideoSources" + MediaGetVideoSourceConfiguration = "GetVideoSourceConfiguration" + MediaGetVideoSourceConfigurations = "GetVideoSourceConfigurations" ) func GetRequestAction(b []byte) string { @@ -54,13 +57,13 @@ func GetRequestAction(b []byte) string { func GetCapabilitiesResponse(host string) []byte { e := NewEnvelope() - e.Append(` + e.Appendf(` - http://`, host, `/onvif/device_service + http://%s/onvif/device_service - http://`, host, `/onvif/media_service + http://%s/onvif/media_service false false @@ -68,24 +71,24 @@ func GetCapabilitiesResponse(host string) []byte { -`) +`, host, host) return e.Bytes() } func GetServicesResponse(host string) []byte { e := NewEnvelope() - e.Append(` + e.Appendf(` http://www.onvif.org/ver10/device/wsdl - http://`, host, `/onvif/device_service + http://%s/onvif/device_service 25 http://www.onvif.org/ver10/media/wsdl - http://`, host, `/onvif/media_service + http://%s/onvif/media_service 25 -`) +`, host, host) return e.Bytes() } @@ -120,30 +123,19 @@ func GetSystemDateAndTimeResponse() []byte { func GetDeviceInformationResponse(manuf, model, firmware, serial string) []byte { e := NewEnvelope() - e.Append(` - `, manuf, ` - `, model, ` - `, firmware, ` - `, serial, ` + e.Appendf(` + %s + %s + %s + %s 1.00 -`) - return e.Bytes() -} - -func GetMediaServiceCapabilitiesResponse() []byte { - e := NewEnvelope() - e.Append(` - - - -`) +`, manuf, model, firmware, serial) return e.Bytes() } func GetProfilesResponse(names []string) []byte { e := NewEnvelope() - e.Append(` -`) + e.Append(``) for _, name := range names { appendProfile(e, "Profiles", name) } @@ -153,38 +145,40 @@ func GetProfilesResponse(names []string) []byte { func GetProfileResponse(name string) []byte { e := NewEnvelope() - e.Append(` -`) + e.Append(``) appendProfile(e, "Profile", name) e.Append(``) return e.Bytes() } func appendProfile(e *Envelope, tag, name string) { - // empty `RateControl` important for UniFi Protect - e.Append(` - `, name, ` - - VSC - `, name, ` - - - - VEC - H264 - 19201080 - - - -`) + // go2rtc name = ONVIF Profile Name = ONVIF Profile token + e.Appendf(``, tag, name) + e.Appendf(`%s`, name) + appendVideoSourceConfiguration(e, "VideoSourceConfiguration", name) + appendVideoEncoderConfiguration(e, "VideoEncoderConfiguration") + e.Appendf(``, tag) +} + +func GetVideoSourcesResponse(names []string) []byte { + // go2rtc name = ONVIF VideoSource token + e := NewEnvelope() + e.Append(``) + for _, name := range names { + e.Appendf(` + 30.000000 + 19201080 +`, name) + } + e.Append(``) + return e.Bytes() } func GetVideoSourceConfigurationsResponse(names []string) []byte { e := NewEnvelope() - e.Append(` -`) + e.Append(``) for _, name := range names { - appendProfile(e, "Configurations", name) + appendVideoSourceConfiguration(e, "Configurations", name) } e.Append(``) return e.Bytes() @@ -192,46 +186,56 @@ func GetVideoSourceConfigurationsResponse(names []string) []byte { func GetVideoSourceConfigurationResponse(name string) []byte { e := NewEnvelope() - e.Append(` -`) + e.Append(``) appendVideoSourceConfiguration(e, "Configuration", name) e.Append(``) return e.Bytes() } func appendVideoSourceConfiguration(e *Envelope, tag, name string) { - e.Append(` + // go2rtc name = ONVIF VideoSourceConfiguration token + e.Appendf(` VSC - `, name, ` + %s - -`) +`, tag, name, name, tag) } -func GetVideoSourcesResponse(names []string) []byte { +func GetVideoEncoderConfigurationsResponse() []byte { e := NewEnvelope() - e.Append(` -`) - for _, name := range names { - e.Append(` - 30.000000 - 19201080 - -`) - } - e.Append(``) + e.Append(``) + appendVideoEncoderConfiguration(e, "VideoEncoderConfigurations") + e.Append(``) return e.Bytes() } +func GetVideoEncoderConfigurationResponse() []byte { + e := NewEnvelope() + e.Append(``) + appendVideoEncoderConfiguration(e, "VideoEncoderConfiguration") + e.Append(``) + return e.Bytes() +} + +func appendVideoEncoderConfiguration(e *Envelope, tag string) { + // empty `RateControl` important for UniFi Protect + e.Appendf(` + VEC + H264 + 19201080 + + `, tag, tag) +} + func GetStreamUriResponse(uri string) []byte { e := NewEnvelope() - e.Append(``, uri, ``) + e.Appendf(`%s`, uri) return e.Bytes() } func GetSnapshotUriResponse(uri string) []byte { e := NewEnvelope() - e.Append(``, uri, ``) + e.Appendf(`%s`, uri) return e.Bytes() } @@ -239,6 +243,10 @@ func StaticResponse(operation string) []byte { switch operation { case DeviceGetSystemDateAndTime: return GetSystemDateAndTimeResponse() + case MediaGetVideoEncoderConfiguration: + return GetVideoEncoderConfigurationResponse() + case MediaGetVideoEncoderConfigurations: + return GetVideoEncoderConfigurationsResponse() } e := NewEnvelope() @@ -247,11 +255,18 @@ func StaticResponse(operation string) []byte { } var responses = map[string]string{ + ServiceGetServiceCapabilities: ` + + + +`, + DeviceGetDiscoveryMode: `Discoverable`, DeviceGetDNS: ``, DeviceGetHostname: ``, DeviceGetNetworkDefaultGateway: ``, DeviceGetNTP: ``, + DeviceSetSystemDateAndTime: ``, DeviceSystemReboot: `OK`, DeviceGetNetworkInterfaces: ``, @@ -263,16 +278,20 @@ var responses = map[string]string{ Fixedonvif://www.onvif.org/type/Network_Video_Transmitter `, - MediaGetVideoEncoderConfigurations: ` - - VEC - H264 - 19201080 - - -`, - MediaGetAudioEncoderConfigurations: ``, MediaGetAudioSources: ``, MediaGetAudioSourceConfigurations: ``, + + MediaGetVideoEncoderConfigurationOptions: ` + + 16 + + 19201080 + 0100 + 130 + 1100 + Main + + +`, } From fc22b20896b182510debd2783a25f26aa89002c2 Mon Sep 17 00:00:00 2001 From: Alex X Date: Sun, 18 Jan 2026 22:43:12 +0300 Subject: [PATCH 16/32] Fix ONVIF server support for Unifi Protect #1994 --- pkg/onvif/server.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkg/onvif/server.go b/pkg/onvif/server.go index 94e42fc7..fe3ba8b3 100644 --- a/pkg/onvif/server.go +++ b/pkg/onvif/server.go @@ -221,9 +221,13 @@ func appendVideoEncoderConfiguration(e *Envelope, tag string) { // empty `RateControl` important for UniFi Protect e.Appendf(` VEC + 1 H264 19201080 - + 0 + 3018192 + 10Main + PT10S `, tag, tag) } From c64fcc55a5e76a7653e136b2839ed028a00c9a7c Mon Sep 17 00:00:00 2001 From: Alex X Date: Mon, 19 Jan 2026 11:28:09 +0300 Subject: [PATCH 17/32] Update dependencies --- go.mod | 38 ++++++++++++++++++++------------------ go.sum | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 1e649cae..485509e6 100644 --- a/go.mod +++ b/go.mod @@ -5,26 +5,27 @@ go 1.24.0 require ( github.com/asticode/go-astits v1.14.0 github.com/eclipse/paho.mqtt.golang v1.5.1 - github.com/expr-lang/expr v1.17.6 + github.com/expr-lang/expr v1.17.7 github.com/google/uuid v1.6.0 github.com/gorilla/websocket v1.5.3 github.com/mattn/go-isatty v0.0.20 - github.com/miekg/dns v1.1.69 - github.com/pion/ice/v4 v4.1.0 - github.com/pion/interceptor v0.1.42 + github.com/miekg/dns v1.1.70 + github.com/pion/dtls/v3 v3.0.10 + github.com/pion/ice/v4 v4.2.0 + github.com/pion/interceptor v0.1.43 github.com/pion/rtcp v1.2.16 - github.com/pion/rtp v1.8.26 - github.com/pion/sdp/v3 v3.0.16 - github.com/pion/srtp/v3 v3.0.9 - github.com/pion/stun/v3 v3.0.2 - github.com/pion/webrtc/v4 v4.1.8 + github.com/pion/rtp v1.10.0 + github.com/pion/sdp/v3 v3.0.17 + github.com/pion/srtp/v3 v3.0.10 + github.com/pion/stun/v3 v3.1.1 + github.com/pion/webrtc/v4 v4.2.3 github.com/rs/zerolog v1.34.0 github.com/sigurn/crc16 v0.0.0-20240131213347-83fcde1e29d1 github.com/sigurn/crc8 v0.0.0-20220107193325-2243fe600f9f github.com/stretchr/testify v1.11.1 github.com/tadglines/go-pkgs v0.0.0-20210623144937-b983b20f54f9 - golang.org/x/crypto v0.46.0 - golang.org/x/net v0.48.0 + golang.org/x/crypto v0.47.0 + golang.org/x/net v0.49.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -33,18 +34,19 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect - github.com/pion/datachannel v1.5.10 // indirect - github.com/pion/dtls/v3 v3.0.9 // indirect + github.com/pion/datachannel v1.6.0 // indirect github.com/pion/logging v0.2.4 // indirect github.com/pion/mdns/v2 v2.1.0 // indirect github.com/pion/randutil v0.1.0 // indirect - github.com/pion/sctp v1.8.41 // indirect + github.com/pion/sctp v1.9.2 // indirect github.com/pion/transport/v3 v3.1.1 // indirect - github.com/pion/turn/v4 v4.1.3 // indirect + github.com/pion/transport/v4 v4.0.1 // indirect + github.com/pion/turn/v4 v4.1.4 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/wlynxg/anet v0.0.5 // indirect - golang.org/x/mod v0.31.0 // indirect + golang.org/x/mod v0.32.0 // indirect golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.39.0 // indirect - golang.org/x/tools v0.40.0 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.41.0 // indirect ) diff --git a/go.sum b/go.sum index d251618d..897bb8a2 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2I github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU= github.com/expr-lang/expr v1.17.6 h1:1h6i8ONk9cexhDmowO/A64VPxHScu7qfSl2k8OlINec= github.com/expr-lang/expr v1.17.6/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= +github.com/expr-lang/expr v1.17.7 h1:Q0xY/e/2aCIp8g9s/LGvMDCC5PxYlvHgDZRQ4y16JX8= +github.com/expr-lang/expr v1.17.7/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -32,14 +34,24 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc= github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g= +github.com/miekg/dns v1.1.70 h1:DZ4u2AV35VJxdD9Fo9fIWm119BsQL5cZU1cQ9s0LkqA= +github.com/miekg/dns v1.1.70/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= +github.com/pion/datachannel v1.6.0 h1:XecBlj+cvsxhAMZWFfFcPyUaDZtd7IJvrXqlXD/53i0= +github.com/pion/datachannel v1.6.0/go.mod h1:ur+wzYF8mWdC+Mkis5Thosk+u/VOL287apDNEbFpsIk= github.com/pion/dtls/v3 v3.0.9 h1:4AijfFRm8mAjd1gfdlB1wzJF3fjjR/VPIpJgkEtvYmM= github.com/pion/dtls/v3 v3.0.9/go.mod h1:abApPjgadS/ra1wvUzHLc3o2HvoxppAh+NZkyApL4Os= +github.com/pion/dtls/v3 v3.0.10 h1:k9ekkq1kaZoxnNEbyLKI8DI37j/Nbk1HWmMuywpQJgg= +github.com/pion/dtls/v3 v3.0.10/go.mod h1:YEmmBYIoBsY3jmG56dsziTv/Lca9y4Om83370CXfqJ8= github.com/pion/ice/v4 v4.1.0 h1:YlxIii2bTPWyC08/4hdmtYq4srbrY0T9xcTsTjldGqU= github.com/pion/ice/v4 v4.1.0/go.mod h1:5gPbzYxqenvn05k7zKPIZFuSAufolygiy6P1U9HzvZ4= +github.com/pion/ice/v4 v4.2.0 h1:jJC8S+CvXCCvIQUgx+oNZnoUpt6zwc34FhjWwCU4nlw= +github.com/pion/ice/v4 v4.2.0/go.mod h1:EgjBGxDgmd8xB0OkYEVFlzQuEI7kWSCFu+mULqaisy4= github.com/pion/interceptor v0.1.42 h1:0/4tvNtruXflBxLfApMVoMubUMik57VZ+94U0J7cmkQ= github.com/pion/interceptor v0.1.42/go.mod h1:g6XYTChs9XyolIQFhRHOOUS+bGVGLRfgTCUzH29EfVU= +github.com/pion/interceptor v0.1.43 h1:6hmRfnmjogSs300xfkR0JxYFZ9k5blTEvCD7wxEDuNQ= +github.com/pion/interceptor v0.1.43/go.mod h1:BSiC1qKIJt1XVr3l3xQ2GEmCFStk9tx8fwtCZxxgR7M= github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= github.com/pion/logging v0.2.4/go.mod h1:DffhXTKYdNZU+KtJ5pyQDjvOAh/GsNSyv1lbkFbe3so= github.com/pion/mdns/v2 v2.1.0 h1:3IJ9+Xio6tWYjhN6WwuY142P/1jA0D5ERaIqawg/fOY= @@ -50,20 +62,36 @@ github.com/pion/rtcp v1.2.16 h1:fk1B1dNW4hsI78XUCljZJlC4kZOPk67mNRuQ0fcEkSo= github.com/pion/rtcp v1.2.16/go.mod h1:/as7VKfYbs5NIb4h6muQ35kQF/J0ZVNz2Z3xKoCBYOo= github.com/pion/rtp v1.8.26 h1:VB+ESQFQhBXFytD+Gk8cxB6dXeVf2WQzg4aORvAvAAc= github.com/pion/rtp v1.8.26/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM= +github.com/pion/rtp v1.10.0 h1:XN/xca4ho6ZEcijpdF2VGFbwuHUfiIMf3ew8eAAE43w= +github.com/pion/rtp v1.10.0/go.mod h1:rF5nS1GqbR7H/TCpKwylzeq6yDM+MM6k+On5EgeThEM= github.com/pion/sctp v1.8.41 h1:20R4OHAno4Vky3/iE4xccInAScAa83X6nWUfyc65MIs= github.com/pion/sctp v1.8.41/go.mod h1:2wO6HBycUH7iCssuGyc2e9+0giXVW0pyCv3ZuL8LiyY= +github.com/pion/sctp v1.9.2 h1:HxsOzEV9pWoeggv7T5kewVkstFNcGvhMPx0GvUOUQXo= +github.com/pion/sctp v1.9.2/go.mod h1:OTOlsQ5EDQ6mQ0z4MUGXt2CgQmKyafBEXhUVqLRB6G8= github.com/pion/sdp/v3 v3.0.16 h1:0dKzYO6gTAvuLaAKQkC02eCPjMIi4NuAr/ibAwrGDCo= github.com/pion/sdp/v3 v3.0.16/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx6IXo= +github.com/pion/sdp/v3 v3.0.17 h1:9SfLAW/fF1XC8yRqQ3iWGzxkySxup4k4V7yN8Fs8nuo= +github.com/pion/sdp/v3 v3.0.17/go.mod h1:9tyKzznud3qiweZcD86kS0ff1pGYB3VX+Bcsmkx6IXo= github.com/pion/srtp/v3 v3.0.9 h1:lRGF4G61xxj+m/YluB3ZnBpiALSri2lTzba0kGZMrQY= github.com/pion/srtp/v3 v3.0.9/go.mod h1:E+AuWd7Ug2Fp5u38MKnhduvpVkveXJX6J4Lq4rxUYt8= +github.com/pion/srtp/v3 v3.0.10 h1:tFirkpBb3XccP5VEXLi50GqXhv5SKPxqrdlhDCJlZrQ= +github.com/pion/srtp/v3 v3.0.10/go.mod h1:3mOTIB0cq9qlbn59V4ozvv9ClW/BSEbRp4cY0VtaR7M= github.com/pion/stun/v3 v3.0.2 h1:BJuGEN2oLrJisiNEJtUTJC4BGbzbfp37LizfqswblFU= github.com/pion/stun/v3 v3.0.2/go.mod h1:JFJKfIWvt178MCF5H/YIgZ4VX3LYE77vca4b9HP60SA= +github.com/pion/stun/v3 v3.1.1 h1:CkQxveJ4xGQjulGSROXbXq94TAWu8gIX2dT+ePhUkqw= +github.com/pion/stun/v3 v3.1.1/go.mod h1:qC1DfmcCTQjl9PBaMa5wSn3x9IPmKxSdcCsxBcDBndM= github.com/pion/transport/v3 v3.1.1 h1:Tr684+fnnKlhPceU+ICdrw6KKkTms+5qHMgw6bIkYOM= github.com/pion/transport/v3 v3.1.1/go.mod h1:+c2eewC5WJQHiAA46fkMMzoYZSuGzA/7E2FPrOYHctQ= +github.com/pion/transport/v4 v4.0.1 h1:sdROELU6BZ63Ab7FrOLn13M6YdJLY20wldXW2Cu2k8o= +github.com/pion/transport/v4 v4.0.1/go.mod h1:nEuEA4AD5lPdcIegQDpVLgNoDGreqM/YqmEx3ovP4jM= github.com/pion/turn/v4 v4.1.3 h1:jVNW0iR05AS94ysEtvzsrk3gKs9Zqxf6HmnsLfRvlzA= github.com/pion/turn/v4 v4.1.3/go.mod h1:TD/eiBUf5f5LwXbCJa35T7dPtTpCHRJ9oJWmyPLVT3A= +github.com/pion/turn/v4 v4.1.4 h1:EU11yMXKIsK43FhcUnjLlrhE4nboHZq+TXBIi3QpcxQ= +github.com/pion/turn/v4 v4.1.4/go.mod h1:ES1DXVFKnOhuDkqn9hn5VJlSWmZPaRJLyBXoOeO/BmQ= github.com/pion/webrtc/v4 v4.1.8 h1:ynkjfiURDQ1+8EcJsoa60yumHAmyeYjz08AaOuor+sk= github.com/pion/webrtc/v4 v4.1.8/go.mod h1:KVaARG2RN0lZx0jc7AWTe38JpPv+1/KicOZ9jN52J/s= +github.com/pion/webrtc/v4 v4.2.3 h1:RtdWDnkenNQGxUrZqWa5gSkTm5ncsLg5d+zu0M4cXt4= +github.com/pion/webrtc/v4 v4.2.3/go.mod h1:7vsyFzRzaKP5IELUnj8zLcglPyIT6wWwqTppBZ1k6Kc= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE= @@ -88,10 +116,16 @@ github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU= github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8= +golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A= golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= +golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c= +golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU= golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o= +golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -99,10 +133,17 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= +golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= +golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc= +golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From b5948cfb25404cc5cb37b166ecaa2dca20b11d4b Mon Sep 17 00:00:00 2001 From: Alex X Date: Mon, 19 Jan 2026 11:59:09 +0300 Subject: [PATCH 18/32] Update version to 1.9.14 --- main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.go b/main.go index def7ee35..ca70e84d 100644 --- a/main.go +++ b/main.go @@ -52,7 +52,7 @@ import ( func main() { // version will be set later from -buildvcs info, this used only as fallback - app.Version = "1.9.13" + app.Version = "1.9.14" type module struct { name string From a37fdf38d8c23251e7ab6b261c317e5910733cb5 Mon Sep 17 00:00:00 2001 From: Alex X Date: Mon, 19 Jan 2026 18:17:38 +0300 Subject: [PATCH 19/32] Add support xiaomi chuangmi.camera.v2 on old firmwares --- pkg/xiaomi/legacy/client.go | 63 +++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/pkg/xiaomi/legacy/client.go b/pkg/xiaomi/legacy/client.go index 242fda3d..ab17e031 100644 --- a/pkg/xiaomi/legacy/client.go +++ b/pkg/xiaomi/legacy/client.go @@ -33,7 +33,7 @@ func NewClient(rawURL string) (*Client, error) { `{"public_key":"%s","sign":"%s","account":"admin"}`, query.Get("client_public"), query.Get("sign"), ) - } else if model == ModelXiaobai { + } else if model == ModelMijia || model == ModelXiaobai { username = "admin" password = query.Get("password") } else if model == ModelXiaofang { @@ -114,17 +114,49 @@ func (c *Client) ReadPacket() (hdr, payload []byte, err error) { return } +const ( + cmdVideoStart = 0x01ff + cmdVideoStop = 0x02ff + cmdAudioStart = 0x0300 + cmdAudioStop = 0x0301 + cmdStreamCtrlReq = 0x0320 +) + +var empty = []byte(`{}`) + func (c *Client) StartMedia(video, audio string) error { switch c.model { case ModelAqaraG2: - return c.WriteCommand(0x01ff, []byte(`{}`)) + return c.WriteCommand(cmdVideoStart, empty) + + case ModelMijia: + // 0 - auto, 1 - low, 3 - hd + switch video { + case "", "hd": + video = "3" + case "sd": + video = "1" // 2 is also low quality + case "auto": + video = "0" + } + s := fmt.Sprintf(`{"videoquality":%s}`, video) + + // quality after start + return errors.Join( + c.WriteCommand(cmdAudioStart, empty), + c.WriteCommand(cmdVideoStart, empty), + c.WriteCommand(cmdStreamCtrlReq, []byte(s)), + ) case ModelXiaobai: // 00030000 7b7d audio on // 01030000 7b7d audio off - if err := c.WriteCommand(0x0300, []byte(`{}`)); err != nil { - return err - } + // 20030000 0000000001000000 fhd (1920x1080) + // 20030000 0000000002000000 hd (1280x720) + // 20030000 0000000004000000 low (640x360) + // 20030000 00000000ff000000 auto (1920x1080) + // ff010000 7b7d video tart + // ff020000 7b7d video stop var b byte switch video { @@ -137,17 +169,13 @@ func (c *Client) StartMedia(video, audio string) error { case "auto": b = 0xff } - // 20030000 0000000001000000 fhd (1920x1080) - // 20030000 0000000002000000 hd (1280x720) - // 20030000 0000000004000000 low (640x360) - // 20030000 00000000ff000000 auto (1920x1080) - if err := c.WriteCommand(0x0320, []byte{0, 0, 0, 0, b, 0, 0, 0}); err != nil { - return err - } - // ff010000 7b7d video tart - // ff020000 7b7d video stop - return c.WriteCommand(0x01ff, []byte(`{}`)) + // quality before start + return errors.Join( + c.WriteCommand(cmdAudioStart, empty), + c.WriteCommand(cmdStreamCtrlReq, []byte{0, 0, 0, 0, b, 0, 0, 0}), + c.WriteCommand(cmdVideoStart, empty), + ) case ModelXiaofang: // 00010000 4943414d 95010400000000000000000600000000000000d20400005a07 - 90k bitrate @@ -170,8 +198,8 @@ func (c *Client) StartMedia(video, audio string) error { func (c *Client) StopMedia() error { return errors.Join( - c.WriteCommand(0x02ff, []byte(`{}`)), - c.WriteCommand(0x02ff, make([]byte, 8)), + c.WriteCommand(cmdVideoStop, empty), + c.WriteCommand(cmdVideoStop, make([]byte, 8)), ) } @@ -205,6 +233,7 @@ func DecodeVideo(data, key []byte) ([]byte, error) { const ( ModelAqaraG2 = "lumi.camera.gwagl01" ModelLoockV1 = "loock.cateye.v01" + ModelMijia = "chuangmi.camera.v2" // support miss format for new fw and legacy format for old fw ModelXiaobai = "chuangmi.camera.xiaobai" ModelXiaofang = "isa.camera.isc5" ) From ae0646ea9ef2ec9bd5ed1755a159799098d50861 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Mon, 19 Jan 2026 14:16:12 -0300 Subject: [PATCH 20/32] Remove outdated section about Tuya cameras --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 02978500..1be6ee16 100644 --- a/README.md +++ b/README.md @@ -697,7 +697,7 @@ streams: **RTSP Cameras** -By default, the Home Assistant API does not allow you to get a dynamic RTSP link to a camera stream. So more cameras, like [Tuya](https://www.home-assistant.io/integrations/tuya/), and possibly others, can also be imported using [this method](https://github.com/felipecrs/hass-expose-camera-stream-source#importing-cameras-from-home-assistant-to-go2rtc-or-frigate). +By default, the Home Assistant API does not allow you to get a dynamic RTSP link to a camera stream. [This method](https://github.com/felipecrs/hass-expose-camera-stream-source#importing-cameras-from-home-assistant-to-go2rtc-or-frigate) can work around it. ## Source: ISAPI From 861bc9728b91d641ea2ff327be2fdd7d445fe871 Mon Sep 17 00:00:00 2001 From: Felipe Santos Date: Mon, 19 Jan 2026 14:24:59 -0300 Subject: [PATCH 21/32] Fix some other markdownlint violations --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1be6ee16..8c97ad38 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Ultimate camera streaming application with support for RTSP, WebRTC, HomeKit, FFmpeg, RTMP, etc. -![](assets/go2rtc.png) +![go2rtc overview](assets/go2rtc.png) - zero-dependency and zero-config [small app](#go2rtc-binary) for all OS (Windows, macOS, Linux, ARM) - zero-delay for many supported protocols (lowest possible streaming latency) @@ -114,8 +114,8 @@ Ultimate camera streaming application with support for RTSP, WebRTC, HomeKit, FF - [Codecs madness](#codecs-madness) - [Codecs negotiation](#codecs-negotiation) - [Projects using go2rtc](#projects-using-go2rtc) -- [Camera experience](#cameras-experience) -- [TIPS](#tips) +- [Camera experience](#camera-experience) +- [Tips](#tips) # Fast start @@ -160,7 +160,7 @@ The Docker container [`alexxit/go2rtc`](https://hub.docker.com/r/alexxit/go2rtc) ## go2rtc: Home Assistant add-on -[![](https://my.home-assistant.io/badges/supervisor_addon.svg)](https://my.home-assistant.io/redirect/supervisor_addon/?addon=a889bffc_go2rtc&repository_url=https%3A%2F%2Fgithub.com%2FAlexxIT%2Fhassio-addons) +[![Open your Home Assistant instance and show the add add-on repository dialog with a specific repository URL pre-filled.](https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2FAlexxIT%2Fhassio-addons) 1. Install Add-On: - Settings > Add-ons > Plus > Repositories > Add `https://github.com/AlexxIT/hassio-addons` @@ -1342,7 +1342,7 @@ Some examples: [1]: https://apps.apple.com/app/home-assistant/id1099568401 -- `HTTP*` - HTTP Progressive Streaming, not related to [progressive download](https://en.wikipedia.org/wiki/Progressive_download), because the file has no size and no end +- `HTTP*` - HTTP Progressive Streaming, not related to [progressive download](https://en.wikipedia.org/wiki/Progressive_download), because the file has no size and no end - `WebRTC H265` - supported in [Chrome 136+](https://developer.chrome.com/release-notes/136), supported in [Safari 18+](https://developer.apple.com/documentation/safari-release-notes/safari-18-release-notes) - `MSE iPhone` - supported in [iOS 17.1+](https://webkit.org/blog/14735/webkit-features-in-safari-17-1/) @@ -1422,7 +1422,7 @@ streams: **go2rtc** automatically matches codecs for your browser and all your stream sources. This is called **multi-source two-way codec negotiation**. And this is one of the main features of this app. -![](assets/codecs.svg) +![Codec negotiation](assets/codecs.svg) **PS.** You can select `PCMU` or `PCMA` codec in camera settings and not use transcoding at all. Or you can select `AAC` codec for main stream and `PCMU` codec for second stream and add both RTSP to YAML config, this also will work fine. From 06cbbe554332dba1d0877ee5d8665361c095d0ce Mon Sep 17 00:00:00 2001 From: Alex X Date: Mon, 19 Jan 2026 21:55:07 +0300 Subject: [PATCH 22/32] Add support xiaomi isa.camera.isc5c1 --- pkg/xiaomi/miss/client.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/xiaomi/miss/client.go b/pkg/xiaomi/miss/client.go index 6eaa06cf..c39c1c47 100644 --- a/pkg/xiaomi/miss/client.go +++ b/pkg/xiaomi/miss/client.go @@ -139,11 +139,14 @@ const ( ModelLoockV2 = "loock.cateye.v02" ModelC200 = "chuangmi.camera.046c04" ModelC300 = "chuangmi.camera.72ac1" + // ModelXiaofang looks like it has the same firmware as the ModelDafang. + // There is also an older model "isa.camera.isc5" that only works with the legacy protocol. + ModelXiaofang = "isa.camera.isc5c1" ) func (c *Client) StartMedia(channel, quality, audio string) error { switch c.model { - case ModelDafang: + case ModelDafang, ModelXiaofang: var q, a byte if quality == "sd" { q = 1 // 0 - hd, 1 - sd, default - hd @@ -207,7 +210,7 @@ func (c *Client) StartSpeaker() error { // SpeakerCodec if the camera model has a non-standard two-way codec. func (c *Client) SpeakerCodec() uint32 { switch c.model { - case ModelDafang, "isa.camera.hlc6": + case ModelDafang, ModelXiaofang, "isa.camera.hlc6": return codecPCM case "chuangmi.camera.72ac1": return codecOPUS @@ -240,7 +243,7 @@ func (c *Client) ReadPacket() (*Packet, error) { } switch c.model { - case ModelDafang, ModelLoockV2: + case ModelDafang, ModelXiaofang, ModelLoockV2: // Dafang has ts in sec // LoockV2 has ts in msec for video, but zero ts for audio pkt.Timestamp = uint64(time.Now().UnixMilli()) From 7793a5585369d8ddb4d6faa18b7a81ba815952b1 Mon Sep 17 00:00:00 2001 From: Alex X Date: Tue, 20 Jan 2026 14:18:03 +0300 Subject: [PATCH 23/32] Add support xiaomi lumi.camera.gwagl01 --- pkg/xiaomi/legacy/client.go | 40 +++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/pkg/xiaomi/legacy/client.go b/pkg/xiaomi/legacy/client.go index ab17e031..2f99f523 100644 --- a/pkg/xiaomi/legacy/client.go +++ b/pkg/xiaomi/legacy/client.go @@ -122,12 +122,31 @@ const ( cmdStreamCtrlReq = 0x0320 ) -var empty = []byte(`{}`) +func (c *Client) WriteCommandJSON(ctrlType uint32, format string, a ...any) error { + if len(a) > 0 { + format = fmt.Sprintf(format, a...) + } + return c.WriteCommand(ctrlType, []byte(format)) +} func (c *Client) StartMedia(video, audio string) error { switch c.model { case ModelAqaraG2: - return c.WriteCommand(cmdVideoStart, empty) + // 0 - 1920x1080, 1 - 1280x720, 2 - ? + switch video { + case "", "fhd": + video = "0" + case "hd": + video = "1" + case "sd": + video = "2" + } + + return errors.Join( + c.WriteCommandJSON(cmdVideoStart, `{}`), + c.WriteCommandJSON(0x0605, `{"channel":%s}`, video), + c.WriteCommandJSON(0x0704, `{}`), // don't know why + ) case ModelMijia: // 0 - auto, 1 - low, 3 - hd @@ -139,13 +158,12 @@ func (c *Client) StartMedia(video, audio string) error { case "auto": video = "0" } - s := fmt.Sprintf(`{"videoquality":%s}`, video) // quality after start return errors.Join( - c.WriteCommand(cmdAudioStart, empty), - c.WriteCommand(cmdVideoStart, empty), - c.WriteCommand(cmdStreamCtrlReq, []byte(s)), + c.WriteCommandJSON(cmdAudioStart, `{}`), + c.WriteCommandJSON(cmdVideoStart, `{}`), + c.WriteCommandJSON(cmdStreamCtrlReq, `{"videoquality":%s}`, video), ) case ModelXiaobai: @@ -172,9 +190,9 @@ func (c *Client) StartMedia(video, audio string) error { // quality before start return errors.Join( - c.WriteCommand(cmdAudioStart, empty), + c.WriteCommandJSON(cmdAudioStart, `{}`), c.WriteCommand(cmdStreamCtrlReq, []byte{0, 0, 0, 0, b, 0, 0, 0}), - c.WriteCommand(cmdVideoStart, empty), + c.WriteCommandJSON(cmdVideoStart, `{}`), ) case ModelXiaofang: @@ -198,7 +216,7 @@ func (c *Client) StartMedia(video, audio string) error { func (c *Client) StopMedia() error { return errors.Join( - c.WriteCommand(cmdVideoStop, empty), + c.WriteCommandJSON(cmdVideoStop, `{}`), c.WriteCommand(cmdVideoStop, make([]byte, 8)), ) } @@ -214,8 +232,8 @@ func DecodeVideo(data, key []byte) ([]byte, error) { } nonce8 := data[:8] - i1 := binary.LittleEndian.Uint16(data[9:]) - i2 := binary.LittleEndian.Uint16(data[13:]) + i1 := binary.LittleEndian.Uint32(data[9:]) + i2 := binary.LittleEndian.Uint32(data[13:]) data = data[17:] src := data[i1 : i1+i2] From 75bd1f1b2b690a4a3f3e2fd7782f80b9870e2efb Mon Sep 17 00:00:00 2001 From: Alex X Date: Tue, 20 Jan 2026 14:19:18 +0300 Subject: [PATCH 24/32] Improve packet loss on old TUTK proto --- pkg/tutk/session16.go | 23 +++++++++++++---------- pkg/tutk/session25.go | 6 +++--- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/pkg/tutk/session16.go b/pkg/tutk/session16.go index 47110dd3..5344bdbc 100644 --- a/pkg/tutk/session16.go +++ b/pkg/tutk/session16.go @@ -50,7 +50,8 @@ type Session16 struct { seqSendCmd1 uint16 seqSendAud uint16 - waitSeq uint16 + waitFSeq uint16 + waitCSeq uint16 waitSize int waitData []byte } @@ -183,7 +184,7 @@ func (s *Session16) SessionRead(chID byte, cmd []byte) int { } // 0 01030800 command + version - // 4 00000000 frame num + // 4 00000000 frame seq // 8 ac880100 total size // 12 6200 chunk seq // 14 2000 tail (pkt header) size @@ -197,25 +198,27 @@ func (s *Session16) SessionRead(chID byte, cmd []byte) int { switch cmd[1] { case 0x03: - seq := binary.LittleEndian.Uint16(cmd[12:]) - if seq != s.waitSeq { - s.waitSeq = 0 - return msgMediaLost - } - if seq == 0 { + frameSeq := binary.LittleEndian.Uint16(cmd[4:]) + chunkSeq := binary.LittleEndian.Uint16(cmd[12:]) + if chunkSeq == 0 { + s.waitFSeq = frameSeq + s.waitCSeq = 0 s.waitData = s.waitData[:0] payloadSize := binary.LittleEndian.Uint32(cmd[8:]) hdrSize := binary.LittleEndian.Uint16(cmd[14:]) s.waitSize = int(hdrSize) + int(payloadSize) + } else if frameSeq != s.waitFSeq || chunkSeq != s.waitCSeq { + s.waitCSeq = 0 + return msgMediaLost } s.waitData = append(s.waitData, cmd[24:]...) if n := len(s.waitData); n < s.waitSize { - s.waitSeq++ + s.waitCSeq++ return msgMediaChunk } - s.waitSeq = 0 + s.waitCSeq = 0 payloadSize := binary.LittleEndian.Uint32(cmd[8:]) packetData[0] = bytes.Clone(s.waitData[payloadSize:]) diff --git a/pkg/tutk/session25.go b/pkg/tutk/session25.go index fd1f16b4..dc79d3a7 100644 --- a/pkg/tutk/session25.go +++ b/pkg/tutk/session25.go @@ -171,15 +171,15 @@ func (s *Session25) handleChunk(cmd []byte, checkSeq bool) int { // "0x20 chunk seq for first chunk if only one chunk". if binary.LittleEndian.Uint16(cmd2[6:]) == 0 || binary.LittleEndian.Uint16(cmd2[4:]) == 1 { s.waitData = s.waitData[:0] - s.waitSeq = seq - } else if seq != s.waitSeq { + s.waitCSeq = seq + } else if seq != s.waitCSeq { return msgMediaLost } s.waitData = append(s.waitData, cmd2[20:]...) if flags&0b0001 == 0 { - s.waitSeq++ + s.waitCSeq++ return msgMediaChunk } From 6f75b2f75eeddfa1d9d9553cb6f114c6b2c5491c Mon Sep 17 00:00:00 2001 From: Alex X Date: Tue, 20 Jan 2026 14:19:51 +0300 Subject: [PATCH 25/32] Add trace log for xiaomi devices list --- internal/xiaomi/xiaomi.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/xiaomi/xiaomi.go b/internal/xiaomi/xiaomi.go index 1801fa86..a5b23420 100644 --- a/internal/xiaomi/xiaomi.go +++ b/internal/xiaomi/xiaomi.go @@ -16,6 +16,7 @@ import ( "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/xiaomi" "github.com/AlexxIT/go2rtc/pkg/xiaomi/crypto" + "github.com/rs/zerolog" ) func Init() { @@ -26,7 +27,7 @@ func Init() { tokens = v.Cfg - log := app.GetLogger("xiaomi") + log = app.GetLogger("xiaomi") streams.HandleFunc("xiaomi", func(rawURL string) (core.Producer, error) { u, err := url.Parse(rawURL) @@ -49,6 +50,8 @@ func Init() { api.HandleFunc("api/xiaomi", apiXiaomi) } +var log zerolog.Logger + var tokens map[string]string var clouds map[string]*xiaomi.Cloud var cloudsMu sync.Mutex @@ -250,6 +253,8 @@ func apiDeviceList(w http.ResponseWriter, r *http.Request) { List []*Device `json:"list"` } + log.Trace().Str("user", user).Msgf("[xiaomi] devices list: %s", res) + if err = json.Unmarshal(res, &v); err != nil { return err } From 366cbd200d3b30bea9c7d88b36385d832cd245b2 Mon Sep 17 00:00:00 2001 From: Alex X Date: Tue, 20 Jan 2026 14:24:27 +0300 Subject: [PATCH 26/32] Fix unknown msg 0xF0 for cs2 proto --- pkg/xiaomi/miss/cs2/conn.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/xiaomi/miss/cs2/conn.go b/pkg/xiaomi/miss/cs2/conn.go index 1bea07c6..2c1b395e 100644 --- a/pkg/xiaomi/miss/cs2/conn.go +++ b/pkg/xiaomi/miss/cs2/conn.go @@ -57,7 +57,8 @@ const ( msgDrwAck = 0xD1 msgPing = 0xE0 msgPong = 0xE1 - msgClose = 0xF1 + msgClose = 0xF0 + msgCloseAck = 0xF1 ) func handshake(host, transport string) (net.Conn, error) { @@ -162,7 +163,7 @@ func (c *Conn) worker() { case msgPing: _, _ = c.Conn.Write([]byte{magic, msgPong, 0, 0}) - case msgPong, msgP2PRdyUDP, msgP2PRdyTCP, msgClose: // skip it + case msgPong, msgP2PRdyUDP, msgP2PRdyTCP, msgClose, msgCloseAck: // skip it case msgDrwAck: // only for UDP if c.cmdAck != nil { c.cmdAck() From 2f9d13869255112df0c6683a90687184afa573bb Mon Sep 17 00:00:00 2001 From: Alex X Date: Tue, 20 Jan 2026 17:05:14 +0300 Subject: [PATCH 27/32] Fix zero channel for xiaomi source #2047 --- pkg/xiaomi/miss/client.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/xiaomi/miss/client.go b/pkg/xiaomi/miss/client.go index c39c1c47..95c3f9f3 100644 --- a/pkg/xiaomi/miss/client.go +++ b/pkg/xiaomi/miss/client.go @@ -184,9 +184,10 @@ func (c *Client) StartMedia(channel, quality, audio string) error { } data := binary.BigEndian.AppendUint32(nil, cmdVideoStart) - if channel == "" { + switch channel { + case "", "0": data = fmt.Appendf(data, `{"videoquality":%s,"enableaudio":%s}`, quality, audio) - } else { + default: data = fmt.Appendf(data, `{"videoquality":-1,"videoquality2":%s,"enableaudio":%s}`, quality, audio) } return c.WriteCommand(data) From 8f68f80bffdbfba8b75cae5224566f227df73cbf Mon Sep 17 00:00:00 2001 From: Alex X Date: Tue, 20 Jan 2026 20:51:40 +0300 Subject: [PATCH 28/32] Add support xiaomi chuangmi.camera.ipc019e --- pkg/xiaomi/legacy/client.go | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/pkg/xiaomi/legacy/client.go b/pkg/xiaomi/legacy/client.go index 2f99f523..8627b973 100644 --- a/pkg/xiaomi/legacy/client.go +++ b/pkg/xiaomi/legacy/client.go @@ -104,10 +104,11 @@ func (c *Client) ReadPacket() (hdr, payload []byte, err error) { return } if c.key != nil { - switch hdr[0] { - case tutk.CodecH264, tutk.CodecH265: + if c.model == ModelAqaraG2 && hdr[0] == tutk.CodecH265 { payload, err = DecodeVideo(payload, c.key) - case tutk.CodecAACLATM: + } else { + // ModelAqaraG2: audio AAC + // ModelIMILABA1: video HEVC, audio PCMA payload, err = crypto.Decode(payload, c.key) } } @@ -148,7 +149,7 @@ func (c *Client) StartMedia(video, audio string) error { c.WriteCommandJSON(0x0704, `{}`), // don't know why ) - case ModelMijia: + case ModelIMILABA1, ModelMijia: // 0 - auto, 1 - low, 3 - hd switch video { case "", "hd": @@ -209,9 +210,10 @@ func (c *Client) StartMedia(video, audio string) error { //if err := c.WriteCommand(0x100, data); err != nil { // return err //} + return nil } - return nil + return fmt.Errorf("xiaomi: unsupported model: %s", c.model) } func (c *Client) StopMedia() error { @@ -250,15 +252,17 @@ func DecodeVideo(data, key []byte) ([]byte, error) { const ( ModelAqaraG2 = "lumi.camera.gwagl01" + ModelIMILABA1 = "chuangmi.camera.ipc019e" ModelLoockV1 = "loock.cateye.v01" - ModelMijia = "chuangmi.camera.v2" // support miss format for new fw and legacy format for old fw ModelXiaobai = "chuangmi.camera.xiaobai" ModelXiaofang = "isa.camera.isc5" + // ModelMijia support miss format for new fw and legacy format for old fw + ModelMijia = "chuangmi.camera.v2" ) func Supported(model string) bool { switch model { - case ModelAqaraG2, ModelLoockV1, ModelXiaobai, ModelXiaofang: + case ModelAqaraG2, ModelIMILABA1, ModelLoockV1, ModelXiaobai, ModelXiaofang: return true } return false From de157eb14460301b45e15f32d0d34c2be0b64312 Mon Sep 17 00:00:00 2001 From: Alex X Date: Wed, 21 Jan 2026 17:14:14 +0300 Subject: [PATCH 29/32] Strip Userinfo from log entries and errors #2051 --- internal/api/ws/ws.go | 4 ++-- pkg/core/helpers.go | 12 ----------- pkg/creds/secrets.go | 49 ++++++++++++++++++++++++++----------------- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/internal/api/ws/ws.go b/internal/api/ws/ws.go index 981d1b41..02c2f90c 100644 --- a/internal/api/ws/ws.go +++ b/internal/api/ws/ws.go @@ -11,7 +11,7 @@ import ( "github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/app" - "github.com/AlexxIT/go2rtc/pkg/core" + "github.com/AlexxIT/go2rtc/pkg/creds" "github.com/gorilla/websocket" "github.com/rs/zerolog" ) @@ -133,7 +133,7 @@ func apiWS(w http.ResponseWriter, r *http.Request) { if handler := wsHandlers[msg.Type]; handler != nil { go func() { if err = handler(tr, msg); err != nil { - errMsg := core.StripUserinfo(err.Error()) + errMsg := creds.SecretString(err.Error()) tr.Write(&Message{Type: "error", Value: msg.Type + ": " + errMsg}) } }() diff --git a/pkg/core/helpers.go b/pkg/core/helpers.go index 52b969a7..45bbd0d5 100644 --- a/pkg/core/helpers.go +++ b/pkg/core/helpers.go @@ -2,7 +2,6 @@ package core import ( "crypto/rand" - "regexp" "runtime" "strconv" "strings" @@ -93,14 +92,3 @@ func Caller() string { _, file, line, _ := runtime.Caller(1) return file + ":" + strconv.Itoa(line) } - -const ( - unreserved = `A-Za-z0-9-._~` - subdelims = `!$&'()*+,;=` - userinfo = unreserved + subdelims + `%:` -) - -func StripUserinfo(s string) string { - sanitizer := regexp.MustCompile(`://[` + userinfo + `]+@`) - return sanitizer.ReplaceAllString(s, `://***@`) -} diff --git a/pkg/creds/secrets.go b/pkg/creds/secrets.go index a9a0094e..95ab4828 100644 --- a/pkg/creds/secrets.go +++ b/pkg/creds/secrets.go @@ -3,6 +3,7 @@ package creds import ( "io" "net/http" + "regexp" "slices" "strings" "sync" @@ -27,6 +28,7 @@ func AddSecret(value string) { var secrets []string var secretsMu sync.Mutex var secretsReplacer *strings.Replacer +var userinfoRegexp *regexp.Regexp func getReplacer() *strings.Replacer { secretsMu.Lock() @@ -40,14 +42,33 @@ func getReplacer() *strings.Replacer { secretsReplacer = strings.NewReplacer(oldnew...) } + if userinfoRegexp == nil { + userinfoRegexp = regexp.MustCompile(`://[` + userinfo + `]+@`) + } + return secretsReplacer } +// Uniform Resource Identifier (URI) +// https://datatracker.ietf.org/doc/html/rfc3986 +const ( + unreserved = `A-Za-z0-9-._~` + subdelims = `!$&'()*+,;=` + userinfo = unreserved + subdelims + `%:` +) + func SecretString(s string) string { re := getReplacer() + s = userinfoRegexp.ReplaceAllString(s, `://***@`) return re.Replace(s) } +func SecretWrite(w io.Writer, s string) (n int, err error) { + re := getReplacer() + s = userinfoRegexp.ReplaceAllString(s, `://***@`) + return re.WriteString(w, s) +} + func SecretWriter(w io.Writer) io.Writer { return &secretWriter{w} } @@ -57,27 +78,17 @@ type secretWriter struct { } func (s *secretWriter) Write(b []byte) (int, error) { - re := getReplacer() - return re.WriteString(s.w, string(b)) -} - -type secretResponse struct { - w http.ResponseWriter -} - -func (s *secretResponse) Header() http.Header { - return s.w.Header() -} - -func (s *secretResponse) Write(b []byte) (int, error) { - re := getReplacer() - return re.WriteString(s.w, string(b)) -} - -func (s *secretResponse) WriteHeader(statusCode int) { - s.w.WriteHeader(statusCode) + return SecretWrite(s.w, string(b)) } func SecretResponse(w http.ResponseWriter) http.ResponseWriter { return &secretResponse{w} } + +type secretResponse struct { + http.ResponseWriter +} + +func (s *secretResponse) Write(b []byte) (int, error) { + return SecretWrite(s.ResponseWriter, string(b)) +} From 6374366da5cb28b694607f4d4834d6b99077485a Mon Sep 17 00:00:00 2001 From: Alex X Date: Wed, 21 Jan 2026 22:26:16 +0300 Subject: [PATCH 30/32] Add support xiaomi isa.camera.df3 on old firmwares --- pkg/xiaomi/legacy/client.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkg/xiaomi/legacy/client.go b/pkg/xiaomi/legacy/client.go index 8627b973..57bd0a08 100644 --- a/pkg/xiaomi/legacy/client.go +++ b/pkg/xiaomi/legacy/client.go @@ -36,7 +36,7 @@ func NewClient(rawURL string) (*Client, error) { } else if model == ModelMijia || model == ModelXiaobai { username = "admin" password = query.Get("password") - } else if model == ModelXiaofang { + } else if model == ModelDafang || model == ModelXiaofang { username = "admin" } else { return nil, fmt.Errorf("xiaomi: unsupported model: %s", model) @@ -47,7 +47,7 @@ func NewClient(rawURL string) (*Client, error) { return nil, err } - if model == ModelXiaofang { + if model == ModelDafang || model == ModelXiaofang { err = xiaofangLogin(conn, query.Get("password")) if err != nil { _ = conn.Close() @@ -196,7 +196,7 @@ func (c *Client) StartMedia(video, audio string) error { c.WriteCommandJSON(cmdVideoStart, `{}`), ) - case ModelXiaofang: + case ModelDafang, ModelXiaofang: // 00010000 4943414d 95010400000000000000000600000000000000d20400005a07 - 90k bitrate // 00010000 4943414d 95010400000000000000000600000000000000d20400001e07 - 30k bitrate //var b byte @@ -258,6 +258,8 @@ const ( ModelXiaofang = "isa.camera.isc5" // ModelMijia support miss format for new fw and legacy format for old fw ModelMijia = "chuangmi.camera.v2" + // ModelDafang support miss format for new fw and legacy format for old fw + ModelDafang = "isa.camera.df3" ) func Supported(model string) bool { From 051aa664cd188967629d456dd62319dab8a58572 Mon Sep 17 00:00:00 2001 From: John E Date: Fri, 23 Jan 2026 17:17:53 -0500 Subject: [PATCH 31/32] Propose fix a couple of typos Signed-off-by: John E --- internal/expr/README.md | 2 +- internal/ffmpeg/hardware/README.md | 6 +++--- internal/webrtc/README.md | 14 +++++++------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/internal/expr/README.md b/internal/expr/README.md index db5a27d4..2ad005ce 100644 --- a/internal/expr/README.md +++ b/internal/expr/README.md @@ -136,7 +136,7 @@ streams: "ffmpeg:" + url3 + "#video=copy" ``` -## Comparsion +## Comparison | expr | python | js | |------------------------------|----------------------------|--------------------------------| diff --git a/internal/ffmpeg/hardware/README.md b/internal/ffmpeg/hardware/README.md index 2d7f21cf..df8d1d8f 100644 --- a/internal/ffmpeg/hardware/README.md +++ b/internal/ffmpeg/hardware/README.md @@ -22,7 +22,7 @@ You **NEED** hardware acceleration if you using `#video=h264`, `#video=h265`, `# streams: # auto select hardware encoder camera1_hw: ffmpeg:rtsp://rtsp:12345678@192.168.1.123/av_stream/ch0#video=h264#hardware - + # manual select hardware encoder (vaapi, cuda, v4l2m2m, dxva2, videotoolbox) camera1_vaapi: ffmpeg:rtsp://rtsp:12345678@192.168.1.123/av_stream/ch0#video=h264#hardware=vaapi ``` @@ -47,7 +47,7 @@ Read more [here](https://en.wikipedia.org/wiki/Intel_Quick_Sync_Video#Hardware_d Linux and Docker: - It may be important to have the latest version of the OS with the latest version of the Linux kernel. For example, on my **Debian 10 (kernel 4.19)** it did not work, but after update to **Debian 11 (kernel 5.10)** all was fine. -- In case of troube check you have `/dev/dri/` folder on your host. +- In case of trouble check you have `/dev/dri/` folder on your host. Docker users should add `--privileged` option to container for access to Hardware. @@ -79,7 +79,7 @@ Read more [here](https://docs.frigate.video/configuration/hardware_acceleration) **Supported on:** Linux binary, Docker, Hass Addon. -I don't recommend using transcoding on the Raspberry Pi 3. It's extreamly slow, even with hardware acceleration. Also it may fail when transcoding 2K+ stream. +I don't recommend using transcoding on the Raspberry Pi 3. It's extremely slow, even with hardware acceleration. Also it may fail when transcoding 2K+ stream. ## Raspberry Pi 4 diff --git a/internal/webrtc/README.md b/internal/webrtc/README.md index d50ed06c..22113023 100644 --- a/internal/webrtc/README.md +++ b/internal/webrtc/README.md @@ -10,8 +10,8 @@ What you should to know about WebRTC: If an external connection via STUN is used: - Uses [UDP hole punching](https://en.wikipedia.org/wiki/UDP_hole_punching) technology to bypass NAT even if you not open your server to the World -- For about 20% of users, the techology will not work because of the [Symmetric NAT](https://tomchen.github.io/symmetric-nat-test/) -- UDP is not suitable for transmitting 2K and 4K high bitrate video over open networks because of the high loss rate: +- For about 20% of users, the technology will not work because of the [Symmetric NAT](https://tomchen.github.io/symmetric-nat-test/) +- UDP is not suitable for transmitting 2K and 4K high bit rate video over open networks because of the high loss rate: - https://habr.com/ru/companies/flashphoner/articles/480006/ - https://www.youtube.com/watch?v=FXVg2ckuKfs @@ -26,7 +26,7 @@ webrtc: ## Config -**Important!** This example is not for copypasting! +**Important!** This example is not for copy pasting! ```yaml webrtc: @@ -49,13 +49,13 @@ webrtc: credential: your_pass # optional filter list for auto discovery logic - # some settings only make sense if you don't specify a fixed UDP port + # some settings only make sense if you don't specify a fixed UDP port filters: # list of host candidates from auto discovery to be sent # including candidates from the `listen` option # use `candidates: []` to remove all auto discovery candidates candidates: [ 192.168.1.123 ] - + # enable localhost candidates loopback: true @@ -84,7 +84,7 @@ Don't know why, but you can disable TCP port and leave only random UDP ports - ` ## Config filters -**Importan!** By default go2rtc exclude all Docker-like candidates (`172.16.0.0/12`). This can not be disabled. +**Important!** By default go2rtc exclude all Docker-like candidates (`172.16.0.0/12`). This can not be disabled. Filters allow you to exclude unnecessary candidates. Extra candidates don't make your connection worse or better. But the wrong filter settings can break everything. Skip this setting if you don't understand it. @@ -106,7 +106,7 @@ webrtc: candidates: [ 192.168.1.2:8555 ] # add manual host candidate (use docker port forwarding) ``` -## Userful links +## Useful links - https://www.ietf.org/archive/id/draft-ietf-wish-whip-01.html - https://www.ietf.org/id/draft-murillo-whep-01.html From a556bca7bd975d00afc829a5295493f0899e9870 Mon Sep 17 00:00:00 2001 From: Martin Bjeldbak Madsen Date: Sun, 25 Jan 2026 19:57:58 +1100 Subject: [PATCH 32/32] docs: fix link to updated JSONSchema --- internal/app/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/app/README.md b/internal/app/README.md index 9ec3d9fc..135bb7da 100644 --- a/internal/app/README.md +++ b/internal/app/README.md @@ -35,7 +35,7 @@ rtsp: Editors like [GoLand](https://www.jetbrains.com/go/) and [VS Code](https://code.visualstudio.com/) supports autocomplete and syntax validation. ```yaml -# yaml-language-server: $schema=https://raw.githubusercontent.com/AlexxIT/go2rtc/master/website/schema.json +# yaml-language-server: $schema=https://raw.githubusercontent.com/AlexxIT/go2rtc/master/www/schema.json ``` ## Defaults