mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-03 09:45:22 +03:00
Try using RTSP for restream
This commit is contained in:
parent
d6731b17a4
commit
dcd195ade9
@ -356,6 +356,9 @@ restream:
|
|||||||
enabled: True
|
enabled: True
|
||||||
# Optional: Force audio compatibility with browsers (default: shown below)
|
# Optional: Force audio compatibility with browsers (default: shown below)
|
||||||
force_audio: True
|
force_audio: True
|
||||||
|
# Optional: Restream birdseye via RTSP (default: shown below)
|
||||||
|
# NOTE: Enabling this will set birdseye to run 24/7 which may increase CPU usage somewhat.
|
||||||
|
birdseye: False
|
||||||
# Optional: jsmpeg stream configuration for WebUI
|
# Optional: jsmpeg stream configuration for WebUI
|
||||||
jsmpeg:
|
jsmpeg:
|
||||||
# Optional: Set the height of the jsmpeg stream. (default: 720)
|
# Optional: Set the height of the jsmpeg stream. (default: 720)
|
||||||
|
|||||||
@ -12,7 +12,7 @@ Live view options can be selected while viewing the live stream. The options are
|
|||||||
| Source | Latency | Frame Rate | Resolution | Audio | Requires Restream | Other Limitations |
|
| 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 |
|
| 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 |
|
| mse | low | native | native | yes (depends on audio codec) | yes | not supported on iOS |
|
||||||
| webrtc | lowest | native | native | yes (depends on audio codec) | yes | requires extra config |
|
| webrtc | lowest | native | native | yes (depends on audio codec) | yes | requires extra config |
|
||||||
|
|
||||||
### WebRTC extra configuration:
|
### WebRTC extra configuration:
|
||||||
|
|||||||
@ -7,6 +7,14 @@ title: Restream
|
|||||||
|
|
||||||
Frigate can restream your video feed as an RTSP feed for other applications such as Home Assistant to utilize it at `rtsp://<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](#reduce-connections-to-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.
|
Frigate can restream your video feed as an RTSP feed for other applications such as Home Assistant to utilize it at `rtsp://<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](#reduce-connections-to-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.
|
||||||
|
|
||||||
|
#### Force Audio
|
||||||
|
|
||||||
|
Different live view technologies (ex: MSE, WebRTC) support different audio codecs. The `restream -> force_audio` flag tells the restream to make multiple streams available so that all live view technologies are supported. Some camera streams don't work well with this, in which case `restream -> force_audio` should be disabled.
|
||||||
|
|
||||||
|
#### Birdseye Restream
|
||||||
|
|
||||||
|
Birdseye RTSP restream can be enabled at `restream -> birdseye` and accessed at `rtsp://<frigate_host>:8554/birdseye`. Enabling the restream will cause birdseye to run 24/7 which may increase CPU usage somewhat.
|
||||||
|
|
||||||
### RTMP (Deprecated)
|
### 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.
|
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.
|
||||||
|
|||||||
@ -519,6 +519,7 @@ class RestreamConfig(FrigateBaseModel):
|
|||||||
force_audio: bool = Field(
|
force_audio: bool = Field(
|
||||||
default=True, title="Force audio compatibility with the browser."
|
default=True, title="Force audio compatibility with the browser."
|
||||||
)
|
)
|
||||||
|
birdseye: bool = Field(default=False, title="Restream the birdseye feed via RTSP.")
|
||||||
jsmpeg: JsmpegStreamConfig = Field(
|
jsmpeg: JsmpegStreamConfig = Field(
|
||||||
default_factory=JsmpegStreamConfig, title="Jsmpeg Stream Configuration."
|
default_factory=JsmpegStreamConfig, title="Jsmpeg Stream Configuration."
|
||||||
)
|
)
|
||||||
|
|||||||
@ -129,6 +129,51 @@ PRESETS_HW_ACCEL_SCALE = {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PRESETS_HW_ACCEL_ENCODE_TO_RTSP = {
|
||||||
|
"default": [
|
||||||
|
"-c:v",
|
||||||
|
"libx264",
|
||||||
|
"-g",
|
||||||
|
"50",
|
||||||
|
"-profile:v",
|
||||||
|
"high",
|
||||||
|
"-level:v",
|
||||||
|
"4.1",
|
||||||
|
"-preset:v",
|
||||||
|
"superfast",
|
||||||
|
"-tune:v",
|
||||||
|
"zerolatency",
|
||||||
|
],
|
||||||
|
"preset-rpi-64-h264": ["-c:v", "h264_v4l2m2m", "-g", "50", "-bf", "0"],
|
||||||
|
"preset-intel-vaapi": [
|
||||||
|
"-c:v",
|
||||||
|
"h264_vaapi",
|
||||||
|
"-g",
|
||||||
|
"50",
|
||||||
|
"-bf",
|
||||||
|
"0",
|
||||||
|
"-profile:v",
|
||||||
|
"high",
|
||||||
|
"-level:v",
|
||||||
|
"4.1",
|
||||||
|
],
|
||||||
|
"preset-intel-qsv-h264": ["-c:v", "h264_qsv"],
|
||||||
|
"preset-intel-qsv-h265": ["-c:v", "hevc_qsv"],
|
||||||
|
"preset-nvidia-h264": [
|
||||||
|
"-c:v",
|
||||||
|
"h264_nvenc",
|
||||||
|
"-g",
|
||||||
|
"50",
|
||||||
|
"-profile:v",
|
||||||
|
"high",
|
||||||
|
"-level:v",
|
||||||
|
"auto",
|
||||||
|
"-preset:v",
|
||||||
|
"p2",
|
||||||
|
"-tune:v",
|
||||||
|
"ll",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
def parse_preset_hardware_acceleration_decode(arg: Any) -> list[str]:
|
def parse_preset_hardware_acceleration_decode(arg: Any) -> list[str]:
|
||||||
"""Return the correct preset if in preset format otherwise return None."""
|
"""Return the correct preset if in preset format otherwise return None."""
|
||||||
@ -137,7 +182,6 @@ def parse_preset_hardware_acceleration_decode(arg: Any) -> list[str]:
|
|||||||
|
|
||||||
return PRESETS_HW_ACCEL_DECODE.get(arg, None)
|
return PRESETS_HW_ACCEL_DECODE.get(arg, None)
|
||||||
|
|
||||||
|
|
||||||
def parse_preset_hardware_acceleration_scale(
|
def parse_preset_hardware_acceleration_scale(
|
||||||
arg: Any,
|
arg: Any,
|
||||||
detect_args: list[str],
|
detect_args: list[str],
|
||||||
|
|||||||
@ -28,10 +28,74 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class FFMpegConverter:
|
class FFMpegConverter:
|
||||||
def __init__(self, in_width, in_height, out_width, out_height, quality):
|
def __init__(
|
||||||
ffmpeg_cmd = f"ffmpeg -f rawvideo -pix_fmt yuv420p -video_size {in_width}x{in_height} -i pipe: -f mpegts -s {out_width}x{out_height} -codec:v mpeg1video -q {quality} -bf 0 pipe:".split(
|
self,
|
||||||
" "
|
in_width: int,
|
||||||
)
|
in_height: int,
|
||||||
|
out_width: int,
|
||||||
|
out_height: int,
|
||||||
|
quality: int,
|
||||||
|
birdseye_rtsp: bool = False,
|
||||||
|
):
|
||||||
|
if birdseye_rtsp:
|
||||||
|
ffmpeg_cmd = [
|
||||||
|
"ffmpeg",
|
||||||
|
"-f",
|
||||||
|
"rawvideo",
|
||||||
|
"-pix_fmt",
|
||||||
|
"yuv420p",
|
||||||
|
"-video_size",
|
||||||
|
f"{in_width}x{in_height}",
|
||||||
|
"-i",
|
||||||
|
"pipe:",
|
||||||
|
"-an",
|
||||||
|
"-f",
|
||||||
|
"rtp_mpegts",
|
||||||
|
"-s",
|
||||||
|
f"{out_width}x{out_height}",
|
||||||
|
"-codec:v",
|
||||||
|
"mpeg1video",
|
||||||
|
"-q",
|
||||||
|
f"{quality}",
|
||||||
|
"-bf",
|
||||||
|
"0",
|
||||||
|
"rtp://127.0.0.1:1998",
|
||||||
|
"-f",
|
||||||
|
"mpegts",
|
||||||
|
"-s",
|
||||||
|
f"{out_width}x{out_height}",
|
||||||
|
"-codec:v",
|
||||||
|
"mpeg1video",
|
||||||
|
"-q",
|
||||||
|
f"{quality}",
|
||||||
|
"-bf",
|
||||||
|
"0",
|
||||||
|
"pipe:",
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
ffmpeg_cmd = [
|
||||||
|
"ffmpeg",
|
||||||
|
"-f",
|
||||||
|
"rawvideo",
|
||||||
|
"-pix_fmt",
|
||||||
|
"yuv420p",
|
||||||
|
"-video_size",
|
||||||
|
f"{in_width}x{in_height}",
|
||||||
|
"-i",
|
||||||
|
"pipe:",
|
||||||
|
"-f",
|
||||||
|
"mpegts",
|
||||||
|
"-s",
|
||||||
|
f"{out_width}x{out_height}",
|
||||||
|
"-codec:v",
|
||||||
|
"mpeg1video",
|
||||||
|
"-q",
|
||||||
|
f"{quality}",
|
||||||
|
"-bf",
|
||||||
|
"0",
|
||||||
|
"pipe:",
|
||||||
|
]
|
||||||
|
|
||||||
self.process = sp.Popen(
|
self.process = sp.Popen(
|
||||||
ffmpeg_cmd,
|
ffmpeg_cmd,
|
||||||
stdout=sp.PIPE,
|
stdout=sp.PIPE,
|
||||||
@ -386,6 +450,7 @@ def output_frames(config: FrigateConfig, video_output_queue):
|
|||||||
config.birdseye.width,
|
config.birdseye.width,
|
||||||
config.birdseye.height,
|
config.birdseye.height,
|
||||||
config.birdseye.quality,
|
config.birdseye.quality,
|
||||||
|
config.restream.birdseye,
|
||||||
)
|
)
|
||||||
broadcasters["birdseye"] = BroadcastThread(
|
broadcasters["birdseye"] = BroadcastThread(
|
||||||
"birdseye", converters["birdseye"], websocket_server
|
"birdseye", converters["birdseye"], websocket_server
|
||||||
@ -421,10 +486,12 @@ def output_frames(config: FrigateConfig, video_output_queue):
|
|||||||
# write to the converter for the camera if clients are listening to the specific camera
|
# write to the converter for the camera if clients are listening to the specific camera
|
||||||
converters[camera].write(frame.tobytes())
|
converters[camera].write(frame.tobytes())
|
||||||
|
|
||||||
# update birdseye if websockets are connected
|
if config.birdseye.enabled and (
|
||||||
if config.birdseye.enabled and any(
|
config.restream.birdseye
|
||||||
ws.environ["PATH_INFO"].endswith("birdseye")
|
or any(
|
||||||
for ws in websocket_server.manager
|
ws.environ["PATH_INFO"].endswith("birdseye")
|
||||||
|
for ws in websocket_server.manager
|
||||||
|
)
|
||||||
):
|
):
|
||||||
if birdseye_manager.update(
|
if birdseye_manager.update(
|
||||||
camera,
|
camera,
|
||||||
|
|||||||
@ -42,6 +42,11 @@ class RestreamApi:
|
|||||||
escape_special_characters(input.path)
|
escape_special_characters(input.path)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if self.config.restream.birdseye:
|
||||||
|
self.relays[
|
||||||
|
"birdseye"
|
||||||
|
] = "ffmpeg:rtp://127.0.0.1:1998#video=h264#audio=none"
|
||||||
|
|
||||||
for name, path in self.relays.items():
|
for name, path in self.relays.items():
|
||||||
params = {"src": path, "name": name}
|
params = {"src": path, "name": name}
|
||||||
requests.put("http://127.0.0.1:1984/api/streams", params=params)
|
requests.put("http://127.0.0.1:1984/api/streams", params=params)
|
||||||
|
|||||||
@ -1,14 +1,83 @@
|
|||||||
import { h } from 'preact';
|
import { h, Fragment } from 'preact';
|
||||||
|
import { usePersistence } from '../context';
|
||||||
|
import ActivityIndicator from '../components/ActivityIndicator';
|
||||||
import JSMpegPlayer from '../components/JSMpegPlayer';
|
import JSMpegPlayer from '../components/JSMpegPlayer';
|
||||||
import Heading from '../components/Heading';
|
import Heading from '../components/Heading';
|
||||||
|
import WebRtcPlayer from '../components/WebRtcPlayer';
|
||||||
|
import MsePlayer from '../components/MsePlayer';
|
||||||
|
import useSWR from 'swr';
|
||||||
|
import videojs from 'video.js';
|
||||||
|
|
||||||
export default function Birdseye() {
|
export default function Birdseye() {
|
||||||
|
const { data: config } = useSWR('config');
|
||||||
|
|
||||||
|
const [viewSource, setViewSource, sourceIsLoaded] = usePersistence('birdseye-source', 'mse');
|
||||||
|
const sourceValues = ['mse', 'webrtc', 'jsmpeg'];
|
||||||
|
|
||||||
|
if (!config || !sourceIsLoaded) {
|
||||||
|
return <ActivityIndicator />;
|
||||||
|
}
|
||||||
|
|
||||||
|
let player;
|
||||||
|
if (viewSource == 'mse' && config.restream.birdseye) {
|
||||||
|
if (videojs.browser.IS_IOS) {
|
||||||
|
player = (
|
||||||
|
<Fragment>
|
||||||
|
<div className="w-5xl text-center text-sm">
|
||||||
|
MSE is not supported on iOS devices. You'll need to use jsmpeg or webRTC. See the docs for more info.
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
player = (
|
||||||
|
<Fragment>
|
||||||
|
<div className="max-w-5xl">
|
||||||
|
<MsePlayer camera="birdseye" />
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (viewSource == 'webrtc' && config.restream.birdseye) {
|
||||||
|
player = (
|
||||||
|
<Fragment>
|
||||||
|
<div className="max-w-5xl">
|
||||||
|
<WebRtcPlayer camera="birdseye" />
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
player = (
|
||||||
|
<Fragment>
|
||||||
|
<div className="max-w-7xl">
|
||||||
|
<JSMpegPlayer camera="birdseye" />
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 p-2 px-4">
|
<div className="space-y-4 p-2 px-4">
|
||||||
<Heading size="2xl">Birdseye</Heading>
|
<div className="flex justify-between">
|
||||||
<div className="max-w-7xl">
|
<Heading className="p-2" size="2xl">
|
||||||
<JSMpegPlayer camera="birdseye" />
|
Birdseye
|
||||||
|
</Heading>
|
||||||
|
|
||||||
|
{config.restream.birdseye && (
|
||||||
|
<select
|
||||||
|
className="basis-1/8 cursor-pointer rounded dark:bg-slate-800"
|
||||||
|
value={viewSource}
|
||||||
|
onChange={(e) => setViewSource(e.target.value)}
|
||||||
|
>
|
||||||
|
{sourceValues.map((item) => (
|
||||||
|
<option key={item} value={item}>
|
||||||
|
{item}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{player}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user