Skip to content

Adaptive transcoding

Adaptive transcoding is a Fregata-exclusive feature — it does not exist in upstream Frigate. It gives both recordings and live camera streams a ladder of quality rungs, transcoded on the Media Engine, so a stream that is too heavy for the connection has something lighter to fall back to.

The two tiers choose a rung differently, and the difference is deliberate:

  • Recordings are served as an HTTP Live Streaming (HLS) adaptive bitrate ladder, and the player selects automatically — it takes the highest-quality rung the viewer’s connection can sustain without buffering, dropping and recovering on its own as the connection changes.
  • Live is not HLS and does not switch on its own. Each rung is offered in the stream dropdown and the viewer picks one. See Live transcoding below for why.

The primary use case is remote and mobile viewing. A 4K or even a typical 1080p high-bitrate recording stream is completely unwatchable on a weak LTE or 5G signal — the stream simply won’t load at all, or buffers every few seconds. Watching a recording, Fregata automatically drops to a lower quality, which loads cleanly even on one bar of cellular, and steps back up to full quality once the connection improves. Watching live, the same rungs are there to switch to by hand.

All transcoding runs entirely on the Apple Silicon dedicated Media Engine via VideoToolbox — zero CPU. Everything is off by default and must be enabled in Settings or in your config file.

Turn on adaptive transcoding in Settings, or add the following to config.yaml, and restart Fregata.

  1. In the web UI, go to Settings → Fregata (macOS) → Global configuration → Adaptive transcoding.
  2. Turn on Enable adaptive transcoding (recordings).
  3. Turn on Enable live-view transcoding if you also want live-view rungs.
  4. Set Starting rung height to 720 to start at 720p instead of the lowest rung.
  5. Leave Rungs and LAN networks at their defaults, or edit them (see LAN bypass).
  6. Click Save, then Restart when the page asks.

When adaptive transcoding is enabled:

  1. ffprobe inspects the camera’s stream once at startup and caches the result — dimensions, codec, bitrate.
  2. The ABR engine builds an HLS segment cache on the RAM disk for each active rung.
  3. The top rung is always served as original quality if bandwidth allows — free, no encode cycles at all.
  4. Lower rungs are transcoded on the Media Engine: VideoToolbox hardware decode and encode.
  5. By default streams of recordings always start on the lowest rung. This can be overridden by starting_rung_height.
  6. The hls.js player in the web UI reads the HLS master playlist and switches rungs in real time as it measures throughput.
  7. When adaptive transcoding is active, you will see the currently playing quality as a small bubble in the lower left corner of the video stream. If you do not see this, original quality is being played.

Fregata recordings player showing the quality-rung bubble in the lower-left corner of the video while adaptive transcoding is active.

Clients whose IP falls within lan_networks (default: all private, loopback, and link-local ranges) receive the original source stream, bypassing the ABR ladder entirely. On a local network you have more than enough bandwidth; transcoding would not add any benefit.

Set lan_networks: [] (an empty LAN networks list in Settings) to disable the bypass and force the ABR ladder for all clients.

Setting live: true under adaptive_transcoding also enables adaptive rungs for the live view.

The stream dropdown in the live view lists each rung Fregata created for that camera as a selectable option (e.g. “720p 1500 kbps (Fregata Transcode)”). For live viewing the stream does NOT currently switch qualities automatically, the selection is manual. This is due to the fact that live streams are, well, live. There is no buffer to determine if we are draining the buffer faster than we can replenish it to signal that we need to drop the quality.

Live transcoding is a separate opt-in from recordings ABR (live: true): live rungs are created as go2rtc streams at startup. They consume no resources until played. Requires the camera to be set up to be restreamed via go2rtc (default).

Fregata live view showing the stream quality dropdown with adaptive transcoding rungs listed as selectable options.

At startup Fregata reads the codec of each camera’s main stream and creates live rungs only for these codecs:

Main stream codec Live rungs
H.264 Every configured rung below the main stream’s height
H.265 (HEVC) Every configured rung below the main stream’s height
Anything else (MJPEG, MPEG-4, AV1, VP9) None

Every rung is H.264, whatever the camera sends, so a rung plays in every browser and in Fregata Mobile even when the camera’s own H.265 stream does not. The main stream stays the top option in the dropdown, so Fregata never creates a rung at or above its height: with the default rungs, a 4K camera gets 1080p, 720p and 360p, a 1080p camera gets 720p and 360p, and a 720p camera gets 360p only.

Other codecs get no rungs because the Media Engine has to decode the camera’s stream before it can scale it, and it does not decode MJPEG at all. AV1 hardware decoding exists only on M3 and later chips.

A camera that gets no rungs leaves this line in the Frigate log, naming the codec it found:

adaptive live: camera <name> produced no rungs (main stream codec [mjpeg] is not H.264 or H.265, or the source is at or below every rung); skipping

A camera’s main stream may only exist inside go2rtc: its sources are exec: or ffmpeg: commands, or it pulls another go2rtc stream through go2rtc’s own RTSP port. Fregata can only read such a stream through go2rtc, so the first start with one of these cameras can take up to 60 seconds longer while Fregata asks. Later starts use the saved answer and are not slowed, until you change the stream’s sources or a stream it pulls.

A camera that doesn’t answer in that time, because it is offline or very slow to connect, has no rungs and leaves this line in the Frigate log:

adaptive live: camera <name> gets main stream '<stream>' through go2rtc and has not answered yet, so it has no live rungs this run. Fregata keeps trying; once the camera answers, its rungs appear at the next restart

Each start gives such a camera up to 15 more seconds, so once it is back online, restart Frigate. While Frigate runs, Fregata also keeps asking in the background, less and less often, down to once an hour. A camera that is turned off in the config isn’t asked.

If such a camera’s stream format changes later, Frigate logs a warning, and the next restart uses the new format.

Minimal global AND per-camera config:

For every camera

  1. Go to Settings → Fregata (macOS) → Global configuration → Adaptive transcoding.
  2. Turn on Enable adaptive transcoding, and Enable live-view transcoding if you want live-view rungs.
  3. Click Save, then Restart when the page asks.

For one camera (opt in, or opt out of a global setting)

  1. Go to Settings → Fregata (macOS) → Camera configuration → Adaptive transcoding and pick the camera in the selector at the top.
  2. Set Enable adaptive transcoding (and Enable live-view transcoding) for that camera.
  3. Click Save, then Restart when the page asks.

These fields can be set at the top level (adaptive_transcoding:) to apply a default to every camera, or per-camera (cameras.<name>.adaptive_transcoding:) to override.

Field Settings label Type Default Description
enabled Enable adaptive transcoding bool false Enable recordings ABR for this camera.
live Enable live-view transcoding bool false Enable live-view rungs. Does not follow enabled — must be set explicitly.
starting_rung_height Starting rung height int or null null Start playback on the highest configured rung whose height is ≤ this value. null starts at the lowest rung — safest for variable connections.
rungs Rungs list See below The ABR ladder, highest quality first. Each entry: height (px), bitrate (kbps). The engine will never upscale, if you have a 1080p rung listed by have a single camera that maxes out at 720p, the 1080p rung will be disabled for that camera.

Default rung ladder:

Height Bitrate
1080p 4000 kbps
720p 1500 kbps
360p 600 kbps

Original quality is always served if bandwidth allows with no transcoding at all.

These fields are honored only at the top-level adaptive_transcoding: block, not per-camera.

Field Settings label Type Default Description
lan_networks LAN networks list of CIDRs Private/loopback/link-local Clients in these ranges receive the original source stream. See LAN bypass above.
max_concurrent_transcodes none (config.yaml only) integer By chip: 4 base, 6 Pro, 8 Max, 12 Ultra How many hardware conversions may run at once, shared by recordings, live view and Apple Home.

Every field in the example is on one of two pages. The top-level adaptive_transcoding: block is Settings → Fregata (macOS) → Global configuration → Adaptive transcoding. Each camera’s adaptive_transcoding: block is Settings → Fregata (macOS) → Camera configuration → Adaptive transcoding, with the camera chosen in the selector at the top. LAN networks exists only on the global page.

Adaptive transcoding runs exclusively on the Apple Silicon Media Engine via VideoToolbox. It is macOS-only and is not available in upstream Frigate-on-Docker. Any M-series chip (M1 or later) supports it; no special hardware beyond Fregata’s system requirements is needed.

Set FREGATA_ABR_DEBUG=1 (Tray → Settings → Environment Variables, then restart) to enable verbose ABR diagnostics in the Frigate log. The player logs rung switches, bandwidth estimates, and segment timing.

For recordings, when adaptive transcoding is active, you will see the current quality in a small bubble in the bottom left corner of the video stream (see screenshots above). If you do not see this bubble the original quality video is being played.

Live stream transcode streams must be selected manually from the stream dropdown for that camera but they also show a bubble in the bottom left of the UI when a non-original quality stream is being played.

To further verify adaptive transcoding is active, open the recordings view for an enabled camera and watch the browser DevTools Network tab. You should see requests to:

/api/<camera>/start/<s>/end/<e>/adaptive/master.m3u8
/api/<camera>/start/<s>/end/<e>/adaptive/rung/<height>.m3u8
/api/<camera>/start/<s>/end/<e>/adaptive/rung/<height>/<seq>.ts

If you see /vod/ requests instead, adaptive transcoding is not active for that camera — check that enabled: true is set and that Fregata was restarted after the config change.