Ability to set different codec for restream and use go2rtc hardware (#4876)

* Add video codec to restream config

* Add handling of encode engine and video codec

* Add test for video encoding

* Set in main configuration docs as well

* Add example to restream docs

* Put back patch
This commit is contained in:
Nicolas Mowen 2023-01-03 18:24:34 -07:00 committed by GitHub
parent 760d65b214
commit ea7d1aabba
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 90 additions and 8 deletions

View File

@ -356,6 +356,10 @@ 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: Video encoding to be used. By default the codec will be copied but
# it can be switched to another or an MJPEG stream can be encoded and restreamed
# as h264 (default: shown below)
video_encoding: "copy"
# Optional: Restream birdseye via RTSP (default: shown below) # Optional: Restream birdseye via RTSP (default: shown below)
# NOTE: Enabling this will set birdseye to run 24/7 which may increase CPU usage somewhat. # NOTE: Enabling this will set birdseye to run 24/7 which may increase CPU usage somewhat.
birdseye: False birdseye: False

View File

@ -15,6 +15,22 @@ Different live view technologies (ex: MSE, WebRTC) support different audio codec
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. 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.
#### Changing Restream Codec
Generally it is recommended to let the codec from the camera be copied. But there may be some cases where h265 needs to be transcoded as h264 or an MJPEG stream can be encoded and restreamed as h264. In this case the encoding will need to be set, if any hardware acceleration presets are set then that will be used to encode the stream.
```yaml
ffmpeg:
hwaccel_args: your-hwaccel-preset # <- highly recommended so the GPU is used
cameras:
mjpeg_cam:
ffmpeg:
...
restream:
video_encoding: h264
```
### 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.

View File

@ -514,8 +514,17 @@ class JsmpegStreamConfig(FrigateBaseModel):
quality: int = Field(default=8, ge=1, le=31, title="Live camera view quality.") quality: int = Field(default=8, ge=1, le=31, title="Live camera view quality.")
class RestreamCodecEnum(str, Enum):
copy = "copy"
h264 = "h264"
h265 = "h265"
class RestreamConfig(FrigateBaseModel): class RestreamConfig(FrigateBaseModel):
enabled: bool = Field(default=True, title="Restreaming enabled.") enabled: bool = Field(default=True, title="Restreaming enabled.")
video_encoding: RestreamCodecEnum = Field(
default=RestreamCodecEnum.copy, title="Method for encoding the restream."
)
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."
) )

View File

@ -230,6 +230,15 @@ PRESETS_HW_ACCEL_ENCODE = {
], ],
} }
PRESETS_HW_ACCEL_GO2RTC_ENGINE = {
"preset-intel-vaapi": "vaapi",
"preset-intel-qsv-h264": "vaapi", # go2rtc doesn't support qsv
"preset-intel-qsv-h265": "vaapi",
"preset-amd-vaapi": "vaapi",
"preset-nvidia-h264": "cuda",
"preset-nvidia-h265": "cuda",
}
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."""
@ -267,6 +276,14 @@ def parse_preset_hardware_acceleration_encode(arg: Any) -> list[str]:
return PRESETS_HW_ACCEL_ENCODE.get(arg, PRESETS_HW_ACCEL_ENCODE["default"]) return PRESETS_HW_ACCEL_ENCODE.get(arg, PRESETS_HW_ACCEL_ENCODE["default"])
def parse_preset_hardware_acceleration_go2rtc_engine(arg: Any) -> list[str]:
"""Return the correct engine for the preset otherwise returns None."""
if not isinstance(arg, str):
return None
return PRESETS_HW_ACCEL_GO2RTC_ENGINE.get(arg)
PRESETS_INPUT = { PRESETS_INPUT = {
"preset-http-jpeg-generic": _user_agent_args "preset-http-jpeg-generic": _user_agent_args
+ [ + [

View File

@ -3,18 +3,33 @@
import logging import logging
import requests import requests
from frigate.util import escape_special_characters
from frigate.config import FrigateConfig from typing import Optional
from frigate.config import FrigateConfig, RestreamCodecEnum
from frigate.const import BIRDSEYE_PIPE from frigate.const import BIRDSEYE_PIPE
from frigate.ffmpeg_presets import parse_preset_hardware_acceleration_encode from frigate.ffmpeg_presets import (
parse_preset_hardware_acceleration_encode,
parse_preset_hardware_acceleration_go2rtc_engine,
)
from frigate.util import escape_special_characters
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def get_manual_go2rtc_stream(camera_url: str) -> str: def get_manual_go2rtc_stream(
camera_url: str, codec: RestreamCodecEnum, engine: Optional[str]
) -> str:
"""Get a manual stream for go2rtc.""" """Get a manual stream for go2rtc."""
return f"ffmpeg:{camera_url}#video=copy#audio=aac#audio=opus" if codec == RestreamCodecEnum.copy:
return f"ffmpeg:{camera_url}#video=copy#audio=aac#audio=opus"
if engine:
return (
f"ffmpeg:{camera_url}#video={codec}#hardware={engine}#audio=aac#audio=opus"
)
return f"ffmpeg:{camera_url}#video={codec}#audio=aac#audio=opus"
class RestreamApi: class RestreamApi:
@ -41,7 +56,11 @@ class RestreamApi:
else: else:
# go2rtc only supports rtsp for direct relay, otherwise ffmpeg is used # go2rtc only supports rtsp for direct relay, otherwise ffmpeg is used
self.relays[cam_name] = get_manual_go2rtc_stream( self.relays[cam_name] = get_manual_go2rtc_stream(
escape_special_characters(input.path) escape_special_characters(input.path),
camera.restream.video_encoding,
parse_preset_hardware_acceleration_go2rtc_engine(
self.config.ffmpeg.hwaccel_args
),
) )
if self.config.restream.birdseye: if self.config.restream.birdseye:

View File

@ -45,7 +45,9 @@ class TestRestream(TestCase):
} }
@patch("frigate.restream.requests") @patch("frigate.restream.requests")
def test_rtsp_stream(self, mock_requests) -> None: def test_rtsp_stream(
self, mock_request
) -> None: # need to ensure restream doesn't try to call API
"""Test that the normal rtsp stream is sent plainly.""" """Test that the normal rtsp stream is sent plainly."""
frigate_config = FrigateConfig(**self.config) frigate_config = FrigateConfig(**self.config)
restream = RestreamApi(frigate_config) restream = RestreamApi(frigate_config)
@ -53,13 +55,28 @@ class TestRestream(TestCase):
assert restream.relays["back"].startswith("rtsp") assert restream.relays["back"].startswith("rtsp")
@patch("frigate.restream.requests") @patch("frigate.restream.requests")
def test_http_stream(self, mock_requests) -> None: def test_http_stream(
self, mock_request
) -> None: # need to ensure restream doesn't try to call API
"""Test that the http stream is sent via ffmpeg.""" """Test that the http stream is sent via ffmpeg."""
frigate_config = FrigateConfig(**self.config) frigate_config = FrigateConfig(**self.config)
restream = RestreamApi(frigate_config) restream = RestreamApi(frigate_config)
restream.add_cameras() restream.add_cameras()
assert not restream.relays["front"].startswith("rtsp") assert not restream.relays["front"].startswith("rtsp")
@patch("frigate.restream.requests")
def test_restream_codec_change(
self, mock_request
) -> None: # need to ensure restream doesn't try to call API
"""Test that the http stream is sent via ffmpeg."""
self.config["cameras"]["front"]["restream"]["video_encoding"] = "h265"
self.config["ffmpeg"] = {"hwaccel_args": "preset-nvidia-h264"}
frigate_config = FrigateConfig(**self.config)
restream = RestreamApi(frigate_config)
restream.add_cameras()
assert "#hardware=cuda" in restream.relays["front"]
assert "#video=h265" in restream.relays["front"]
if __name__ == "__main__": if __name__ == "__main__":
main(verbosity=2) main(verbosity=2)