mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01:00 
			
		
		
		
	Rework audio encoding for restream (#5092)
* Use memo for recordings timezone * Add audio encoding field and simplify stream creation * Update docs and tests * Fix bad logic
This commit is contained in:
		
							parent
							
								
									daadd206dd
								
							
						
					
					
						commit
						01b9d4d848
					
				@ -356,8 +356,12 @@ rtmp:
 | 
			
		||||
restream:
 | 
			
		||||
  # Optional: Enable the restream (default: True)
 | 
			
		||||
  enabled: True
 | 
			
		||||
  # Optional: Force audio compatibility with browsers (default: shown below)
 | 
			
		||||
  force_audio: True
 | 
			
		||||
  # Optional: Set the audio codecs to restream with
 | 
			
		||||
  # possible values are aac, copy, opus. Set to copy
 | 
			
		||||
  # only to avoid transcoding (default: shown below)
 | 
			
		||||
  audio_encoding:
 | 
			
		||||
    - aac
 | 
			
		||||
    - opus
 | 
			
		||||
  # 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)
 | 
			
		||||
 | 
			
		||||
@ -7,9 +7,9 @@ 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.
 | 
			
		||||
 | 
			
		||||
#### Force Audio
 | 
			
		||||
#### Copy 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.
 | 
			
		||||
Different live view technologies (ex: MSE, WebRTC) support different audio codecs. The `restream -> audio_encoding` field 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 -> audio_encoding` should be set to `copy` only.
 | 
			
		||||
 | 
			
		||||
#### Birdseye Restream
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -524,16 +524,26 @@ class JsmpegStreamConfig(FrigateBaseModel):
 | 
			
		||||
    quality: int = Field(default=8, ge=1, le=31, title="Live camera view quality.")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RestreamCodecEnum(str, Enum):
 | 
			
		||||
class RestreamVideoCodecEnum(str, Enum):
 | 
			
		||||
    copy = "copy"
 | 
			
		||||
    h264 = "h264"
 | 
			
		||||
    h265 = "h265"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RestreamAudioCodecEnum(str, Enum):
 | 
			
		||||
    aac = "aac"
 | 
			
		||||
    copy = "copy"
 | 
			
		||||
    opus = "opus"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RestreamConfig(FrigateBaseModel):
 | 
			
		||||
    enabled: bool = Field(default=True, title="Restreaming enabled.")
 | 
			
		||||
    video_encoding: RestreamCodecEnum = Field(
 | 
			
		||||
        default=RestreamCodecEnum.copy, title="Method for encoding the restream."
 | 
			
		||||
    audio_encoding: list[RestreamAudioCodecEnum] = Field(
 | 
			
		||||
        default=[RestreamAudioCodecEnum.aac, RestreamAudioCodecEnum.opus],
 | 
			
		||||
        title="Codecs to supply for audio.",
 | 
			
		||||
    )
 | 
			
		||||
    video_encoding: RestreamVideoCodecEnum = Field(
 | 
			
		||||
        default=RestreamVideoCodecEnum.copy, title="Method for encoding the restream."
 | 
			
		||||
    )
 | 
			
		||||
    force_audio: bool = Field(
 | 
			
		||||
        default=True, title="Force audio compatibility with the browser."
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,7 @@ import requests
 | 
			
		||||
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
from frigate.config import FrigateConfig, RestreamCodecEnum
 | 
			
		||||
from frigate.config import FrigateConfig, RestreamAudioCodecEnum, RestreamVideoCodecEnum
 | 
			
		||||
from frigate.const import BIRDSEYE_PIPE
 | 
			
		||||
from frigate.ffmpeg_presets import (
 | 
			
		||||
    parse_preset_hardware_acceleration_encode,
 | 
			
		||||
@ -18,18 +18,26 @@ logger = logging.getLogger(__name__)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def get_manual_go2rtc_stream(
 | 
			
		||||
    camera_url: str, codec: RestreamCodecEnum, engine: Optional[str]
 | 
			
		||||
    camera_url: str,
 | 
			
		||||
    aCodecs: list[RestreamAudioCodecEnum],
 | 
			
		||||
    vCodec: RestreamVideoCodecEnum,
 | 
			
		||||
    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"
 | 
			
		||||
    stream = f"ffmpeg:{camera_url}"
 | 
			
		||||
 | 
			
		||||
    if engine:
 | 
			
		||||
        return (
 | 
			
		||||
            f"ffmpeg:{camera_url}#video={codec}#hardware={engine}#audio=aac#audio=opus"
 | 
			
		||||
        )
 | 
			
		||||
    for aCodec in aCodecs:
 | 
			
		||||
        stream += f"#audio={aCodec}"
 | 
			
		||||
 | 
			
		||||
    return f"ffmpeg:{camera_url}#video={codec}#audio=aac#audio=opus"
 | 
			
		||||
    if vCodec == RestreamVideoCodecEnum.copy:
 | 
			
		||||
        stream += "#video=copy"
 | 
			
		||||
    else:
 | 
			
		||||
        stream += f"#video={vCodec}"
 | 
			
		||||
 | 
			
		||||
        if engine:
 | 
			
		||||
            stream += f"#hardware={engine}"
 | 
			
		||||
 | 
			
		||||
    return stream
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RestreamApi:
 | 
			
		||||
@ -50,7 +58,10 @@ class RestreamApi:
 | 
			
		||||
                if "restream" in input.roles:
 | 
			
		||||
                    if (
 | 
			
		||||
                        input.path.startswith("rtsp")
 | 
			
		||||
                        and not camera.restream.force_audio
 | 
			
		||||
                        and camera.restream.video_encoding
 | 
			
		||||
                        == RestreamVideoCodecEnum.copy
 | 
			
		||||
                        and camera.restream.audio_encoding
 | 
			
		||||
                        == [RestreamAudioCodecEnum.copy]
 | 
			
		||||
                    ):
 | 
			
		||||
                        self.relays[
 | 
			
		||||
                            cam_name
 | 
			
		||||
@ -59,6 +70,7 @@ class RestreamApi:
 | 
			
		||||
                        # go2rtc only supports rtsp for direct relay, otherwise ffmpeg is used
 | 
			
		||||
                        self.relays[cam_name] = get_manual_go2rtc_stream(
 | 
			
		||||
                            escape_special_characters(input.path),
 | 
			
		||||
                            camera.restream.audio_encoding,
 | 
			
		||||
                            camera.restream.video_encoding,
 | 
			
		||||
                            parse_preset_hardware_acceleration_go2rtc_engine(
 | 
			
		||||
                                self.config.ffmpeg.hwaccel_args
 | 
			
		||||
 | 
			
		||||
@ -25,7 +25,7 @@ class TestRestream(TestCase):
 | 
			
		||||
                    },
 | 
			
		||||
                    "restream": {
 | 
			
		||||
                        "enabled": True,
 | 
			
		||||
                        "force_audio": False,
 | 
			
		||||
                        "audio_encoding": ["copy"],
 | 
			
		||||
                    },
 | 
			
		||||
                },
 | 
			
		||||
                "front": {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user