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
# Optional: Force audio compatibility with browsers (default: shown below)
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)
# NOTE: Enabling this will set birdseye to run 24/7 which may increase CPU usage somewhat.
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.
#### 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)
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.")
class RestreamCodecEnum(str, Enum):
copy = "copy"
h264 = "h264"
h265 = "h265"
class RestreamConfig(FrigateBaseModel):
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(
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]:
"""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"])
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 = {
"preset-http-jpeg-generic": _user_agent_args
+ [

View File

@ -3,19 +3,34 @@
import logging
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.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__)
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."""
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:
"""Control go2rtc relay API."""
@ -41,7 +56,11 @@ class RestreamApi:
else:
# go2rtc only supports rtsp for direct relay, otherwise ffmpeg is used
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:

View File

@ -45,7 +45,9 @@ class TestRestream(TestCase):
}
@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."""
frigate_config = FrigateConfig(**self.config)
restream = RestreamApi(frigate_config)
@ -53,13 +55,28 @@ class TestRestream(TestCase):
assert restream.relays["back"].startswith("rtsp")
@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."""
frigate_config = FrigateConfig(**self.config)
restream = RestreamApi(frigate_config)
restream.add_cameras()
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__":
main(verbosity=2)