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:
 | 
					restream:
 | 
				
			||||||
  # Optional: Enable the restream (default: True)
 | 
					  # Optional: Enable the restream (default: True)
 | 
				
			||||||
  enabled: True
 | 
					  enabled: True
 | 
				
			||||||
  # Optional: Force audio compatibility with browsers (default: shown below)
 | 
					  # Optional: Set the audio codecs to restream with
 | 
				
			||||||
  force_audio: True
 | 
					  # 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
 | 
					  # 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
 | 
					  # it can be switched to another or an MJPEG stream can be encoded and restreamed
 | 
				
			||||||
  # as h264 (default: shown below)
 | 
					  # 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.
 | 
					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
 | 
					#### Birdseye Restream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -524,16 +524,26 @@ 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):
 | 
					class RestreamVideoCodecEnum(str, Enum):
 | 
				
			||||||
    copy = "copy"
 | 
					    copy = "copy"
 | 
				
			||||||
    h264 = "h264"
 | 
					    h264 = "h264"
 | 
				
			||||||
    h265 = "h265"
 | 
					    h265 = "h265"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class RestreamAudioCodecEnum(str, Enum):
 | 
				
			||||||
 | 
					    aac = "aac"
 | 
				
			||||||
 | 
					    copy = "copy"
 | 
				
			||||||
 | 
					    opus = "opus"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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(
 | 
					    audio_encoding: list[RestreamAudioCodecEnum] = Field(
 | 
				
			||||||
        default=RestreamCodecEnum.copy, title="Method for encoding the restream."
 | 
					        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(
 | 
					    force_audio: bool = Field(
 | 
				
			||||||
        default=True, title="Force audio compatibility with the browser."
 | 
					        default=True, title="Force audio compatibility with the browser."
 | 
				
			||||||
 | 
				
			|||||||
@ -6,7 +6,7 @@ import requests
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from typing import Optional
 | 
					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.const import BIRDSEYE_PIPE
 | 
				
			||||||
from frigate.ffmpeg_presets import (
 | 
					from frigate.ffmpeg_presets import (
 | 
				
			||||||
    parse_preset_hardware_acceleration_encode,
 | 
					    parse_preset_hardware_acceleration_encode,
 | 
				
			||||||
@ -18,18 +18,26 @@ logger = logging.getLogger(__name__)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def get_manual_go2rtc_stream(
 | 
					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:
 | 
					) -> str:
 | 
				
			||||||
    """Get a manual stream for go2rtc."""
 | 
					    """Get a manual stream for go2rtc."""
 | 
				
			||||||
    if codec == RestreamCodecEnum.copy:
 | 
					    stream = f"ffmpeg:{camera_url}"
 | 
				
			||||||
        return f"ffmpeg:{camera_url}#video=copy#audio=aac#audio=opus"
 | 
					
 | 
				
			||||||
 | 
					    for aCodec in aCodecs:
 | 
				
			||||||
 | 
					        stream += f"#audio={aCodec}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if vCodec == RestreamVideoCodecEnum.copy:
 | 
				
			||||||
 | 
					        stream += "#video=copy"
 | 
				
			||||||
 | 
					    else:
 | 
				
			||||||
 | 
					        stream += f"#video={vCodec}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if engine:
 | 
					        if engine:
 | 
				
			||||||
        return (
 | 
					            stream += f"#hardware={engine}"
 | 
				
			||||||
            f"ffmpeg:{camera_url}#video={codec}#hardware={engine}#audio=aac#audio=opus"
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return f"ffmpeg:{camera_url}#video={codec}#audio=aac#audio=opus"
 | 
					    return stream
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class RestreamApi:
 | 
					class RestreamApi:
 | 
				
			||||||
@ -50,7 +58,10 @@ class RestreamApi:
 | 
				
			|||||||
                if "restream" in input.roles:
 | 
					                if "restream" in input.roles:
 | 
				
			||||||
                    if (
 | 
					                    if (
 | 
				
			||||||
                        input.path.startswith("rtsp")
 | 
					                        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[
 | 
					                        self.relays[
 | 
				
			||||||
                            cam_name
 | 
					                            cam_name
 | 
				
			||||||
@ -59,6 +70,7 @@ class RestreamApi:
 | 
				
			|||||||
                        # 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.audio_encoding,
 | 
				
			||||||
                            camera.restream.video_encoding,
 | 
					                            camera.restream.video_encoding,
 | 
				
			||||||
                            parse_preset_hardware_acceleration_go2rtc_engine(
 | 
					                            parse_preset_hardware_acceleration_go2rtc_engine(
 | 
				
			||||||
                                self.config.ffmpeg.hwaccel_args
 | 
					                                self.config.ffmpeg.hwaccel_args
 | 
				
			||||||
 | 
				
			|||||||
@ -25,7 +25,7 @@ class TestRestream(TestCase):
 | 
				
			|||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    "restream": {
 | 
					                    "restream": {
 | 
				
			||||||
                        "enabled": True,
 | 
					                        "enabled": True,
 | 
				
			||||||
                        "force_audio": False,
 | 
					                        "audio_encoding": ["copy"],
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                "front": {
 | 
					                "front": {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user