docs: add shm calulator (#22103)
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions

* docs: add shm calulator

* feat: update shm calculator tips && style
This commit is contained in:
GuoQing Liu 2026-03-24 21:40:11 +08:00 committed by GitHub
parent 854ef320de
commit de593c8e3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 335 additions and 14 deletions

View File

@ -3,6 +3,8 @@ id: installation
title: Installation
---
import ShmCalculator from '@site/src/components/ShmCalculator'
Frigate is a Docker container that can be run on any Docker host including as a [Home Assistant App](https://www.home-assistant.io/apps/). Note that the Home Assistant App is **not** the same thing as the integration. The [integration](/integrations/home-assistant) is required to integrate Frigate into Home Assistant, whether you are running Frigate as a standalone Docker container or as a Home Assistant App.
:::tip
@ -77,20 +79,7 @@ The default shm size of **128MB** is fine for setups with **2 cameras** detectin
The Frigate container also stores logs in shm, which can take up to **40MB**, so make sure to take this into account in your math as well.
You can calculate the **minimum** shm size for each camera with the following formula using the resolution specified for detect:
```console
# Template for one camera without logs, replace <width> and <height>
$ python -c 'print("{:.2f}MB".format((<width> * <height> * 1.5 * 20 + 270480) / 1048576))'
# Example for 1280x720, including logs
$ python -c 'print("{:.2f}MB".format((1280 * 720 * 1.5 * 20 + 270480) / 1048576 + 40))'
66.63MB
# Example for eight cameras detecting at 1280x720, including logs
$ python -c 'print("{:.2f}MB".format(((1280 * 720 * 1.5 * 20 + 270480) / 1048576) * 8 + 40))'
253MB
```
<ShmCalculator/>
The shm size cannot be set per container for Home Assistant Apps. However, this is probably not required since by default Home Assistant Supervisor allocates `/dev/shm` with half the size of your total memory. If your machine has 8GB of memory, chances are that Frigate will have access to up to 4GB without any additional configuration.

View File

@ -0,0 +1,201 @@
import React, { useState, useEffect } from "react";
import Admonition from "@theme/Admonition";
import styles from "./styles.module.css";
const ShmCalculator = () => {
const [width, setWidth] = useState(1280);
const [height, setHeight] = useState(720);
const [cameraCount, setCameraCount] = useState(1);
const [result, setResult] = useState("26.32MB");
const [singleCameraShm, setSingleCameraShm] = useState("26.32MB");
const [totalShm, setTotalShm] = useState("26.32MB");
const calculate = () => {
if (!width || !height || !cameraCount) {
setResult("Please enter valid values");
setSingleCameraShm("-");
setTotalShm("-");
return;
}
// Single camera base SHM calculation (excluding logs)
// Formula: (width * height * 1.5 * 20 + 270480) / 1048576
const singleCameraBase =
(width * height * 1.5 * 20 + 270480) / 1048576;
setSingleCameraShm(`${singleCameraBase.toFixed(2)}mb`);
// Total SHM calculation (multiple cameras, including logs)
const totalBase = singleCameraBase * cameraCount;
const finalResult = totalBase + 40; // Default includes logs +40mb
setTotalShm(`${(totalBase + 40).toFixed(2)}mb`);
// Format result
if (finalResult < 1) {
setResult(`${(finalResult * 1024).toFixed(2)}kb`);
} else if (finalResult >= 1024) {
setResult(`${(finalResult / 1024).toFixed(2)}gb`);
} else {
setResult(`${finalResult.toFixed(2)}mb`);
}
};
const formatWithUnit = (value) => {
const match = value.match(/^([\d.]+)(mb|kb|gb)$/i);
if (match) {
return (
<>
{match[1]}<span className={styles.unit}>{match[2]}</span>
</>
);
}
return value;
};
const applyPreset = (w, h, count) => {
setWidth(w);
setHeight(h);
setCameraCount(count);
calculate();
};
useEffect(() => {
calculate();
}, [width, height, cameraCount]);
return (
<div className={styles.shmCalculator}>
<div className={styles.card}>
<h3 className={styles.title}>SHM Calculator</h3>
<p className={styles.description}>
Calculate required shared memory (SHM) based on camera resolution and
count
</p>
<Admonition type="note">
The resolution below is the <strong>detect</strong> stream resolution,
not the <strong>record</strong> stream resolution. SHM size is
determined by the detect resolution used for object detection.{" "}
<a href="/frigate/camera_setup#choosing-a-detect-resolution">
Learn more about choosing a detect resolution.
</a>
</Admonition>
{width * height > 1280 * 720 && (
<Admonition type="warning">
Using a detect resolution higher than 720p is not recommended.
Higher resolutions do not improve object detection accuracy and will
consume significantly more resources.
</Admonition>
)}
<div className="row">
<div className="col col--6">
<div className={styles.formGroup}>
<label htmlFor="width" className={styles.label}>
Width:
</label>
<input
id="width"
type="number"
min="1"
placeholder="e.g.: 1280"
className={styles.input}
value={width}
onChange={(e) => setWidth(Number(e.target.value))}
/>
</div>
</div>
<div className="col col--6">
<div className={styles.formGroup}>
<label htmlFor="height" className={styles.label}>
Height:
</label>
<input
id="height"
type="number"
min="1"
placeholder="e.g.: 720"
className={styles.input}
value={height}
onChange={(e) => setHeight(Number(e.target.value))}
/>
</div>
</div>
</div>
<div className={styles.formGroup}>
<label htmlFor="cameraCount" className={styles.label}>
Camera Count:
</label>
<input
id="cameraCount"
type="number"
min="1"
placeholder="e.g.: 8"
className={styles.input}
value={cameraCount}
onChange={(e) => setCameraCount(Number(e.target.value))}
/>
</div>
<div className={styles.resultSection}>
<h4>Calculation Result</h4>
<div className={styles.resultValue}>
<span className={styles.resultNumber}>{formatWithUnit(result)}</span>
</div>
<div className={styles.formulaDisplay}>
<p>
<strong>Single Camera:</strong> {formatWithUnit(singleCameraShm)}
</p>
<p>
<strong>Formula:</strong> (width × height × 1.5 × 20 + 270480) ÷
1048576
</p>
{cameraCount > 1 && (
<p>
<strong>Total ({cameraCount} cameras):</strong> {formatWithUnit(totalShm)}
</p>
)}
<p>
<strong>With Logs:</strong> + 40<span className={styles.unit}>mb</span>
</p>
</div>
</div>
<div className={styles.presets}>
<h4>Common Presets</h4>
<div className={styles.presetButtons}>
<button
className="button button--outline button--primary button--sm"
onClick={() => applyPreset(640, 360, 1)}
>
640x360 × 1
</button>
<button
className="button button--outline button--primary button--sm"
onClick={() => applyPreset(1280, 720, 1)}
>
1280x720 × 1
</button>
<button
className="button button--outline button--primary button--sm"
onClick={() => applyPreset(1280, 720, 4)}
>
1280x720 × 4
</button>
<button
className="button button--outline button--primary button--sm"
onClick={() => applyPreset(1280, 720, 8)}
>
1280x720 × 8
</button>
</div>
</div>
</div>
</div>
);
};
export default ShmCalculator;

View File

@ -0,0 +1,131 @@
.shmCalculator {
margin: 2rem 0;
max-width: 600px;
}
.card {
background: var(--ifm-background-surface-color);
border: 1px solid var(--ifm-border-color);
border-radius: 12px;
padding: 2rem;
box-shadow: var(--ifm-global-shadow-lw);
}
[data-theme='light'] .card {
background: var(--ifm-color-emphasis-100);
border: 1px solid var(--ifm-color-emphasis-300);
}
.title {
margin: 0 0 0.5rem 0;
font-size: 1.5rem;
color: var(--ifm-font-color-base);
font-weight: var(--ifm-font-weight-semibold);
}
.description {
margin: 0 0 1.5rem 0;
color: var(--ifm-font-color-secondary);
font-size: 0.9rem;
}
.formGroup {
margin-bottom: 1rem;
}
.label {
display: block;
margin-bottom: 0.25rem;
color: var(--ifm-font-color-base);
font-weight: var(--ifm-font-weight-semibold);
font-size: 0.9rem;
}
.input {
width: 100%;
padding: 0.5rem 0.75rem;
border: 1px solid var(--ifm-border-color);
border-radius: 6px;
background: var(--ifm-background-color);
color: var(--ifm-font-color-base);
font-size: 0.95rem;
transition: border-color 0.2s, box-shadow 0.2s;
}
[data-theme='light'] .input {
background: #fff;
border: 1px solid #d0d7de;
}
.input:focus {
outline: none;
border-color: var(--ifm-color-primary);
box-shadow: 0 0 0 3px var(--ifm-color-primary-lightest);
}
.resultSection {
margin-top: 1rem;
padding: 1.5rem;
background: var(--ifm-background-color);
border-radius: 8px;
border: 1px solid var(--ifm-border-color);
}
[data-theme='light'] .resultSection {
background: #f6f8fa;
border: 1px solid #d0d7de;
}
.resultSection h4 {
margin: 0 0 1rem 0;
color: var(--ifm-font-color-base);
font-weight: var(--ifm-font-weight-semibold);
}
.resultValue {
text-align: center;
padding: 1rem;
background: var(--ifm-color-primary);
border-radius: 6px;
margin-bottom: 1rem;
}
.resultNumber {
font-size: 2rem;
font-weight: var(--ifm-font-weight-bold);
color: #fff;
}
.formulaDisplay {
font-size: 0.85rem;
color: var(--ifm-font-color-secondary);
line-height: 1.6;
}
.formulaDisplay p {
margin: 0.25rem 0;
}
.formulaDisplay strong {
color: var(--ifm-font-color-base);
}
.unit {
text-transform: uppercase;
}
.presets {
margin-top: 1.5rem;
}
.presets h4 {
margin: 0 0 0.75rem 0;
color: var(--ifm-font-color-base);
font-weight: var(--ifm-font-weight-semibold);
}
.presetButtons {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}