diff --git a/docs/docs/frigate/installation.md b/docs/docs/frigate/installation.md index f78db5f14..26993700f 100644 --- a/docs/docs/frigate/installation.md +++ b/docs/docs/frigate/installation.md @@ -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 Add-on](https://www.home-assistant.io/addons/). Note that the Home Assistant Add-on 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 Add-on. :::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 and -$ python -c 'print("{:.2f}MB".format(( * * 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 -``` + The shm size cannot be set per container for Home Assistant add-ons. 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. diff --git a/docs/src/components/ShmCalculator/index.jsx b/docs/src/components/ShmCalculator/index.jsx new file mode 100644 index 000000000..11e64f17a --- /dev/null +++ b/docs/src/components/ShmCalculator/index.jsx @@ -0,0 +1,191 @@ +import React, { useState, useEffect } from "react"; +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]}{match[2]} + + ); + } + return value; + }; + + const applyPreset = (w, h, count) => { + setWidth(w); + setHeight(h); + setCameraCount(count); + calculate(); + }; + + useEffect(() => { + calculate(); + }, [width, height, cameraCount]); + + return ( +
+
+

SHM Calculator

+

+ Calculate required shared memory (SHM) based on camera resolution and + count +

+ +
+
+ + setWidth(Number(e.target.value))} + /> +
+ +
+ + setHeight(Number(e.target.value))} + /> +
+
+ +
+ + setCameraCount(Number(e.target.value))} + /> +
+ +
+

Calculation Result

+
+ {formatWithUnit(result)} +
+
+

+ Single Camera: {formatWithUnit(singleCameraShm)} +

+

+ Formula: (width × height × 1.5 × 20 + 270480) ÷ + 1048576 +

+ {cameraCount > 1 && ( +

+ Total ({cameraCount} cameras): {formatWithUnit(totalShm)} +

+ )} +

+ With Logs: + 40mb +

+
+
+ +
+

Common Presets

+
+ + + + + + +
+
+
+
+ ); +}; + +export default ShmCalculator; diff --git a/docs/src/components/ShmCalculator/styles.module.css b/docs/src/components/ShmCalculator/styles.module.css new file mode 100644 index 000000000..2afbbdceb --- /dev/null +++ b/docs/src/components/ShmCalculator/styles.module.css @@ -0,0 +1,173 @@ +.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: 0.5rem; +} + +.row { + display: flex; + gap: 1rem; + margin-bottom: 0.5rem; +} + +.row .formGroup { + flex: 1; +} + +.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; +} + +.presetButton { + padding: 0.5rem 1rem; + background: var(--ifm-background-surface-color); + border: 1px solid var(--ifm-border-color); + border-radius: 6px; + color: var(--ifm-font-color-base); + cursor: pointer; + font-size: 0.85rem; + font-weight: var(--ifm-font-weight-normal); + transition: all 0.2s; +} + +[data-theme='light'] .presetButton { + background: #fff; + border: 1px solid #d0d7de; +} + +[data-theme='dark'] .presetButton { + background: #24292f; + border: 1px solid #3b434b; +} + +.presetButton:hover { + background: var(--ifm-color-primary); + color: #fff; + border-color: var(--ifm-color-primary); +} + +.presetButton:active { + transform: translateY(1px); +}