Skip to content

Commit

Permalink
Add go2rtc and add restream role / live source (blakeblackshear#4082)
Browse files Browse the repository at this point in the history
* Pull go2rtc dependency

* Add go2rtc to local services and add to s6

* Add relay controller for go2rtc

* Add restream role

* Add restream role

* Add restream to nginx

* Add camera live source config

* Disable RTMP by default and use restream

* Use go2rtc for camera config

* Fix go2rtc move

* Start restream on frigate start

* Send restream to camera level

* Fix restream

* Make sure jsmpeg works as expected

* Make view rspect live size config

* Tweak player options to fit live view

* Adjust VideoPlayer to accept live option which disables irrelevant controls

* Add multiple options from restream live view

* Add base for webrtc option

* Setup specific restream modules

* Make mp4 the default streaming for now

* Expose 8554 for rtsp relay from go2rtc

* Formatting

* Update docs to suggest new restream method.

* Update docs to reflect restream role

* Update docs to reflect restream role

* Add webrtc player

* Improvements to webRTC

* Support webrtc

* Cleanup

* Adjust rtmp test and add restream test

* Fix tests

* Add restream tests

* Add live view docs and show different options

* Small docs tweak

* Support all stream types

* Update to beta 9 of go2rtc

* Formatting

* Make jsmpeg the default

* Support wss if made from https

* Support wss if made from https

* Use onEffect

* Set url outside onEffect

* Fix passed deps

* Update docs about required host mode

* Try memo instead

* Close websocket on changing camera

* Formatting

* Close pc connection

* Set video source to null on cleanup

* Use full path since go2rtc can't see PATH var

* Adjust audio codec to enable browser audio by default

* Cleanup stream creation

* Add restream tests

* Format tests

* Mock requests

* Adjust paths

* Move stream configs to restream

* Remove live source

* Remove live config

* Use live persistence for which view to use on each camera

* Fix live sizes

* Only use jsmpeg sizes for jsmpeg live

* Set max live size

* Remove access of live config

* Add selector for live view source in web view

* Remove RTMP from default list of roles

* Update docs

* Fix tests

* Fix docs for live view modes

* make default undefined to avoid race condition

* Wait until camera source is loaded to avoid race condition

* Fix tests

* Add config to go2rtc

* Work with config

* Set full path for config

* Set to use stun

* Check for mounted file

* Look for frigate-go2rtc

* Update docs to reflect webRTC configuration.

* Add link to go2rtc config

* Update docs to be more clear

* Update docs to be more clear

* Update format

Co-authored-by: Felipe Santos <[email protected]>

* Update live docs

* Improve bash startup script

* Add option to force audio compatibility

* Formatting

* Fix mapping

* Fix broken link

* Update go2rtc version

* Get go2rtc webui working

* Add support for mse

* Remove mp4 option

* Undo changes to video player

* Update docs for new live view options

* Make separate path for mse

* Remove unused

* Remove mp4 path

* Try to get go2rtc proxy working

* Try to get go2rtc proxy working

* Remove unused callback

* Allow websocket on restrea dashboard

* Make mse default stream option

* Fix mse sizing

* don't assume roles is defined

* Remove nginx mapping to go2rtc ui

Co-authored-by: Felipe Santos <[email protected]>
Co-authored-by: Blake Blackshear <[email protected]>
  • Loading branch information
3 people authored Nov 2, 2022
1 parent b4d4adb commit d8123d2
Show file tree
Hide file tree
Showing 26 changed files with 614 additions and 86 deletions.
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ services:
- "5000:5000"
- "5001:5001"
- "8080:8080"
- "8554:8554"
entrypoint: ["sudo", "/init"]
command: /bin/sh -c "while sleep 1000; do :; done"
mqtt:
Expand Down
8 changes: 8 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ RUN apt-get -qq update \

ENV PATH=$PATH:/usr/lib/btbn-ffmpeg/bin

# install go2rtc
RUN wget -O go2rtc "https://github.com/AlexxIT/go2rtc/releases/download/v0.1-beta.10/go2rtc_linux_${TARGETARCH}" \
&& chmod +x go2rtc \
&& mkdir -p /usr/local/go2rtc/sbin/ \
&& mv go2rtc /usr/local/go2rtc/sbin/go2rtc

COPY --from=nginx /usr/local/nginx/ /usr/local/nginx/

# get model and labels
Expand All @@ -142,6 +148,8 @@ RUN S6_ARCH="${TARGETARCH}" \

EXPOSE 5000
EXPOSE 1935
EXPOSE 8554
EXPOSE 8555

ENTRYPOINT ["/init"]

Expand Down
5 changes: 5 additions & 0 deletions docker/rootfs/etc/services.d/go2rtc/finish
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/execlineb -S1
if { s6-test ${1} -ne 0 }
if { s6-test ${1} -ne 256 }

s6-svscanctl -t /var/run/s6/services
12 changes: 12 additions & 0 deletions docker/rootfs/etc/services.d/go2rtc/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

# https://gist.github.com/mohanpedala/1e2ff5661761d3abd0385e8223e16425?permalink_comment_id=3945021
set -euo pipefail

if [[ -f "/config/frigate-go2rtc.yaml" ]]; then
CONFIG_PATH=/config/frigate-go2rtc.yaml
else
CONFIG_PATH=/usr/local/go2rtc/sbin/go2rtc.yaml
fi

exec /usr/local/go2rtc/sbin/go2rtc -config="$CONFIG_PATH"
4 changes: 4 additions & 0 deletions docker/rootfs/usr/local/go2rtc/sbin/go2rtc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
webrtc:
listen: ":8555"
candidates:
- stun:8555
23 changes: 22 additions & 1 deletion docker/rootfs/usr/local/nginx/conf/nginx.conf
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ http {
keepalive 1024;
}

upstream go2rtc {
server 127.0.0.1:1984;
keepalive 1024;
}

server {
listen 5000;

Expand Down Expand Up @@ -165,14 +170,30 @@ http {
proxy_set_header Host $host;
}

location /live/ {
location /live/jsmpeg/ {
proxy_pass https://jsmpeg/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}

location /live/mse/ {
proxy_pass https://go2rtc/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}

location /live/webrtc/ {
proxy_pass https://go2rtc/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
}

location ~* /api/.*\.(jpg|jpeg|png)$ {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
Expand Down
11 changes: 6 additions & 5 deletions docs/docs/configuration/cameras.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ Several inputs can be configured for each camera and the role of each input can

Each role can only be assigned to one input per camera. The options for roles are as follows:

| Role | Description |
| -------- | ----------------------------------------------------------------------------------------------- |
| `detect` | Main feed for object detection |
| `record` | Saves segments of the video feed based on configuration settings. [docs](/configuration/record) |
| `rtmp` | Broadcast as an RTMP feed for other services to consume. [docs](/configuration/rtmp) |
| Role | Description |
| ---------- | ---------------------------------------------------------------------------------------------------- |
| `detect` | Main feed for object detection |
| `record` | Saves segments of the video feed based on configuration settings. [docs](/configuration/record) |
| `restream` | Broadcast as RTSP feed and use the full res stream for live view. [docs](/configuration/restream) |
| `rtmp` | Deprecated: Broadcast as an RTMP feed for other services to consume. [docs](/configuration/restream) |

```yaml
mqtt:
Expand Down
36 changes: 22 additions & 14 deletions docs/docs/configuration/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ cameras:
- path: rtsp:https://viewer:{FRIGATE_RTSP_PASSWORD}@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
roles:
- detect
- rtmp
- restream
detect:
width: 1280
height: 720
Expand Down Expand Up @@ -336,21 +336,28 @@ snapshots:
person: 15
# Optional: RTMP configuration
# NOTE: RTMP is deprecated in favor of restream
# NOTE: Can be overridden at the camera level
rtmp:
# Optional: Enable the RTMP stream (default: True)
enabled: True
# Optional: Enable the RTMP stream (default: False)
enabled: False
# Optional: Live stream configuration for WebUI
# Optional: Restream configuration
# NOTE: Can be overridden at the camera level
live:
# Optional: Set the height of the live stream. (default: 720)
# This must be less than or equal to the height of the detect stream. Lower resolutions
# reduce bandwidth required for viewing the live stream. Width is computed to match known aspect ratio.
height: 720
# Optional: Set the encode quality of the live stream (default: shown below)
# 1 is the highest quality, and 31 is the lowest. Lower quality feeds utilize less CPU resources.
quality: 8
restream:
# Optional: Enable the restream (default: True)
enabled: True
# Optional: Force audio compatibility with browsers (default: shown below)
force_audio: False
# Optional: jsmpeg stream configuration for WebUI
jsmpeg:
# Optional: Set the height of the jsmpeg stream. (default: 720)
# This must be less than or equal to the height of the detect stream. Lower resolutions
# reduce bandwidth required for viewing the jsmpeg stream. Width is computed to match known aspect ratio.
height: 720
# Optional: Set the encode quality of the jsmpeg stream (default: shown below)
# 1 is the highest quality, and 31 is the lowest. Lower quality feeds utilize less CPU resources.
quality: 8
# Optional: in-feed timestamp style configuration
# NOTE: Can be overridden at the camera level
Expand Down Expand Up @@ -387,11 +394,12 @@ cameras:
# Required: the path to the stream
# NOTE: path may include environment variables, which must begin with 'FRIGATE_' and be referenced in {}
- path: rtsp:https://viewer:{FRIGATE_RTSP_PASSWORD}@10.0.10.10:554/cam/realmonitor?channel=1&subtype=2
# Required: list of roles for this stream. valid values are: detect,record,rtmp
# NOTICE: In addition to assigning the record, and rtmp roles,
# Required: list of roles for this stream. valid values are: detect,record,restream,rtmp
# NOTICE: In addition to assigning the record, restream, and rtmp roles,
# they must also be enabled in the camera config.
roles:
- detect
- restream
- rtmp
# Optional: stream specific global args (default: inherit)
# global_args:
Expand Down
41 changes: 41 additions & 0 deletions docs/docs/configuration/live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
id: live
title: Live View
---

Frigate has different live view options, some of which require [restream](restream.md) to be enabled.

## Live View Options

Live view options can be selected while viewing the live stream. The options are:

| Source | Latency | Frame Rate | Resolution | Audio | Requires Restream | Other Limitations |
| ------ | ------- | -------------------------------------- | -------------- | ---------------------------- | ----------------- | --------------------- |
| jsmpeg | low | same as `detect -> fps`, capped at 10 | same as detect | no | no | none |
| mse | low | native | native | yes (depends on audio codec) | yes | none |
| webrtc | lowest | native | native | yes (depends on audio codec) | yes | requires extra config |

### WebRTC extra configuration:

webRTC works by creating a websocket connection on extra ports. One of the following is required for webRTC to work:
* Frigate is run with `network_mode: host` to support automatic UDP port pass through locally and remotely. See https://github.com/AlexxIT/go2rtc#module-webrtc for more details
* Frigate is run with `network_mode: bridge` and has:
* Router setup to forward port `8555` to port `8555` on the frigate device.
* For local webRTC, you will need to create your own go2rtc config:

```yaml
webrtc:
listen: ":8555"
candidates:
- <frigate host ip address>:8555 # <--- enter frigate host IP here
- stun:8555
```
and pass that config to frigate via docker or `frigate-go2rtc.yaml` for addon users:

See https://github.com/AlexxIT/go2rtc#module-webrtc for more details

```yaml
volumes:
- /path/to/your/go2rtc.yaml:/config/frigate-go2rtc.yaml:ro
```
12 changes: 12 additions & 0 deletions docs/docs/configuration/restream.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
id: restream
title: Restream
---

### RTSP

Frigate can restream your video feed as an RTSP feed for other applications such as Home Assistant to utilize it at `rtsp:https://<frigate_host>:8554/<camera_name>`. Port 8554 must be open. This allows you to use a video feed for detection in frigate and Home Assistant live view at the same time without having to make two separate connections to the camera. The video feed is copied from the original video feed directly to avoid re-encoding. This feed does not include any annotation by Frigate.

### RTMP (Deprecated)

In previous Frigate versions RTMP was used for re-streaming. RTMP has disadvantages however including being incompatible with H.265, high bitrates, and certain audio codecs. RTMP is deprecated and it is recommended to move to the new restream role.
8 changes: 0 additions & 8 deletions docs/docs/configuration/rtmp.md

This file was deleted.

3 changes: 2 additions & 1 deletion docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ module.exports = {
"configuration/record",
"configuration/snapshots",
"configuration/objects",
"configuration/rtmp",
"configuration/restream",
"configuration/live",
"configuration/zones",
"configuration/birdseye",
"configuration/stationary_objects",
Expand Down
6 changes: 6 additions & 0 deletions frigate/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from frigate.output import output_frames
from frigate.plus import PlusApi
from frigate.record import RecordingCleanup, RecordingMaintainer
from frigate.restream import RestreamApi
from frigate.stats import StatsEmitter, stats_init
from frigate.storage import StorageMaintainer
from frigate.version import VERSION
Expand Down Expand Up @@ -163,6 +164,10 @@ def init_web_server(self) -> None:
self.plus_api,
)

def init_restream(self) -> None:
self.restream = RestreamApi(self.config)
self.restream.add_cameras()

def init_mqtt(self) -> None:
self.mqtt_client = create_mqtt_client(self.config, self.camera_metrics)

Expand Down Expand Up @@ -363,6 +368,7 @@ def start(self) -> None:
self.start_camera_capture_processes()
self.init_stats()
self.init_web_server()
self.init_restream()
self.start_mqtt_relay()
self.start_event_processor()
self.start_event_cleanup()
Expand Down
47 changes: 36 additions & 11 deletions frigate/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ class FfmpegConfig(FrigateBaseModel):

class CameraRoleEnum(str, Enum):
record = "record"
restream = "restream"
rtmp = "rtmp"
detect = "detect"

Expand Down Expand Up @@ -513,12 +514,22 @@ class CameraMqttConfig(FrigateBaseModel):


class RtmpConfig(FrigateBaseModel):
enabled: bool = Field(default=True, title="RTMP restreaming enabled.")
enabled: bool = Field(default=False, title="RTMP restreaming enabled.")


class CameraLiveConfig(FrigateBaseModel):
height: int = Field(default=720, title="Live camera view height")
quality: int = Field(default=8, ge=1, le=31, title="Live camera view quality")
class JsmpegStreamConfig(FrigateBaseModel):
height: int = Field(default=720, title="Live camera view height.")
quality: int = Field(default=8, ge=1, le=31, title="Live camera view quality.")


class RestreamConfig(FrigateBaseModel):
enabled: bool = Field(default=True, title="Restreaming enabled.")
force_audio: bool = Field(
default=False, title="Force audio compatibility with the browser."
)
jsmpeg: JsmpegStreamConfig = Field(
default_factory=JsmpegStreamConfig, title="Jsmpeg Stream Configuration."
)


class CameraUiConfig(FrigateBaseModel):
Expand All @@ -544,8 +555,8 @@ class CameraConfig(FrigateBaseModel):
rtmp: RtmpConfig = Field(
default_factory=RtmpConfig, title="RTMP restreaming configuration."
)
live: CameraLiveConfig = Field(
default_factory=CameraLiveConfig, title="Live playback settings."
restream: RestreamConfig = Field(
default_factory=RestreamConfig, title="Restreaming configuration."
)
snapshots: SnapshotsConfig = Field(
default_factory=SnapshotsConfig, title="Snapshot configuration."
Expand Down Expand Up @@ -582,7 +593,16 @@ def __init__(self, **config):

# add roles to the input if there is only one
if len(config["ffmpeg"]["inputs"]) == 1:
config["ffmpeg"]["inputs"][0]["roles"] = ["record", "rtmp", "detect"]
has_rtmp = "rtmp" in config["ffmpeg"]["inputs"][0].get("roles", [])

config["ffmpeg"]["inputs"][0]["roles"] = [
"record",
"detect",
"restream",
]

if has_rtmp:
config["ffmpeg"]["inputs"][0]["roles"].append("rtmp")

super().__init__(**config)

Expand Down Expand Up @@ -763,12 +783,12 @@ class FrigateConfig(FrigateBaseModel):
snapshots: SnapshotsConfig = Field(
default_factory=SnapshotsConfig, title="Global snapshots configuration."
)
live: CameraLiveConfig = Field(
default_factory=CameraLiveConfig, title="Global live configuration."
)
rtmp: RtmpConfig = Field(
default_factory=RtmpConfig, title="Global RTMP restreaming configuration."
)
restream: RestreamConfig = Field(
default_factory=RestreamConfig, title="Global restream configuration."
)
birdseye: BirdseyeConfig = Field(
default_factory=BirdseyeConfig, title="Birdseye configuration."
)
Expand Down Expand Up @@ -805,8 +825,8 @@ def runtime_config(self) -> FrigateConfig:
"birdseye": ...,
"record": ...,
"snapshots": ...,
"live": ...,
"rtmp": ...,
"restream": ...,
"objects": ...,
"motion": ...,
"detect": ...,
Expand Down Expand Up @@ -893,6 +913,11 @@ def runtime_config(self) -> FrigateConfig:
f"Camera {name} has rtmp enabled, but rtmp is not assigned to an input."
)

if camera_config.restream.enabled and not "restream" in assigned_roles:
raise ValueError(
f"Camera {name} has restream enabled, but restream is not assigned to an input."
)

# backwards compatibility for retain_days
if not camera_config.record.retain_days is None:
logger.warning(
Expand Down
Loading

0 comments on commit d8123d2

Please sign in to comment.