# How to stream video with ultra-low latency

**URL:** <https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038>\
**Category:** Share Your Projects\
**Created:** [1 November 2026 14:26 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038 "2026-11-01T14:26:59Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![krambriw](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/krambriw/32/5429_2.png) [@krambriw](https://discourse.nodered.org/u/krambriw)\
**Post date:** [1 November 2026 14:26 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/1 "2026-11-01T14:26:59Z")

</div>

Since long many of us have been searching, trying, helping & contributing in our efforts to stream & view live video from networked cameras. With minimum latency!

Many contributers have spent so many, many hours helping the rest of us; Like @kevinGodell, @BartButenaers and many others, not mentioned but never to be forgotten

So far, what I have experienced myself, we do have useful & working solutions even if they are a bit tricky to setup & configure, requiering several nodes of various kind and many times even advanced debugging

They all also tend to introduce some latency, i.e. the presented video is some seconds or more delayed relative the real life

Many tricks and tuning attempts have been tested to overcome. Sometimes sacrificing quality but even so, some delays are still noticable and present

The other day, for some unknown reason, I just stumbled over an interesting tool that looked promising; mediamtx

> **[Introduction | MediaMTX](https://mediamtx.org/docs/kickoff/introduction)**
>
> Ready-to-use, open source, live media router. Publish, read, proxy, forward, record and playback real-time video and audio streams with a wide variety of protocols and codecs, plus many other feature.

A fantastic light weight live media server!!

I installed it on a simple old RPi3 and configured it to connect to a TAPO netcam via RTSP. The server then provides the stream via various protocols and formats, the one I selected in this project is WebRTC since it is perfect for streaming with Ultra-Low Latency (Sub-500ms)

The CPU load on the RPi3 showed that it was not too much to worry about, only around 7%

Next on the table was to show the video and some controls in the FlowFuse Dashboard. This was not that difficult, just used the template node and added an iframe. See the image below

Another good thing is that you just consume one RTSP connection from camera (they normally have some limited numbers available) but the mediamtx can share the single stream with many users

In my case I have a number of locations where I have installed network (RTSP) cameras and a local RPi3. They are all members of my Tailscale network so I can reach them basically from ”anywhere”

If you feel you wanna try you can use the simple flow below as a starting example, just install the version of mediamtx that fits your environment, configure it and add one single template node in the flow!

 ![Skärmavbild 2026-11-01 kl. 15.21.44](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/9/5/958093c877b7d169384e370101b4ab335d02877e.png)

 ![Skärmavbild 2026-11-01 kl. 15.24.21](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/6/3/63e2ad9fb378c157dc6042b691d13d9754fcf31c.png)

```auto
[
    {
        "id": "622a7d44cf807c92",
        "type": "ui-template",
        "z": "3a2119e9c11365e2",
        "group": "e09ee07d132abea6",
        "page": "",
        "ui": "",
        "name": "",
        "order": 0,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "<template>\n <div>\n <!-- Rubrik med dynamisk statusindikator -->\n <div class=\"header-container\">\n <h2>Camera WebRTC stream: Tapo</h2>\n <span :class=\"['status-dot', isPlaying ? 'status-online' : 'status-offline']\"></span>\n </div>\n\n <!-- WebRTC Video via inbyggd iFrame -->\n <div class=\"video-container\">\n <iframe \n v-if=\"isPlaying\"\n src=\"http://ip-to-your-rpi:8889/tapo/\" \n scrolling=\"no\" \n allow=\"autoplay; encrypted-media; picture-in-picture\"\n allowfullscreen>\n </iframe>\n <!-- Meddelande när strömmen är stoppad -->\n <div v-else class=\"video-placeholder\">\n <p>Stream is stopped</p>\n </div>\n </div>\n\n <!-- Kontrollknappar -->\n <div class=\"button-container\">\n <v-btn color=\"success\" :disabled=\"isPlaying\" @click=\"startStream()\">Start</v-btn>\n <v-btn color=\"error\" :disabled=\"!isPlaying\" @click=\"stopStream()\">Stop</v-btn>\n </div>\n </div>\n</template>\n\n<script>\nexport default {\n data() {\n return {\n isPlaying: true\n }\n },\n methods: {\n startStream() {\n this.isPlaying = true;\n },\n stopStream() {\n this.isPlaying = false;\n }\n }\n}\n</script>\n\n<style>\n/* Layout för rubrik och indikator */\n.header-container {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 10px;\n}\n\n/* Grundstil för statuspricken */\n.status-dot {\n width: 12px;\n height: 12px;\n border-radius: 50%;\n display: inline-block;\n transition: background-color 0.3s ease;\n}\n\n/* Grön pulserande prick (Igång) */\n.status-online {\n background-color: #4caf50;\n box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7);\n animation: pulse 2s infinite;\n}\n\n/* Röd fast prick (Stoppad) */\n.status-offline {\n background-color: #f44336;\n box-shadow: none;\n animation: none;\n}\n\n/* Animation för pulserande effekt */\n@keyframes pulse {\n 0% {\n transform: scale(0.95);\n box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7);\n }\n 70% {\n transform: scale(1);\n box-shadow: 0 0 0 8px rgba(76, 175, 80, 0);\n }\n 100% {\n transform: scale(0.95);\n box-shadow: 0 0 0 0 rgba(76, 175, 80, 0);\n }\n}\n\n/* Video- och iframe-styling */\n.video-container {\n width: 100%;\n max-width: 640px;\n aspect-ratio: 16 / 9;\n margin-bottom: 15px;\n background: #000;\n border-radius: 8px;\n overflow: hidden;\n box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n position: relative;\n}\n\n.video-container iframe {\n width: 100%;\n height: 100%;\n border: none;\n display: block;\n}\n\n.video-placeholder {\n width: 100%;\n height: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n color: #aaa;\n font-size: 1.1em;\n font-family: sans-serif;\n}\n\n.button-container {\n display: flex;\n gap: 10px;\n margin-bottom: 20px;\n}\n</style>\n",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 710,
        "y": 230,
        "wires": [
            []
        ]
    },
    {
        "id": "e09ee07d132abea6",
        "type": "ui-group",
        "name": "Group 4",
        "page": "f85cf0eee9119058",
        "width": "4",
        "height": "1",
        "order": 1,
        "showTitle": false,
        "className": "",
        "visible": "true",
        "disabled": "false",
        "groupType": "default"
    },
    {
        "id": "f85cf0eee9119058",
        "type": "ui-page",
        "name": "Home",
        "ui": "72c1e5a9ec204878",
        "path": "/home",
        "icon": "home",
        "layout": "grid",
        "theme": "0d92c765bfad87e6",
        "breakpoints": [
            {
                "name": "Default",
                "px": "0",
                "cols": "3"
            },
            {
                "name": "Tablet",
                "px": "576",
                "cols": "6"
            },
            {
                "name": "Small Desktop",
                "px": "768",
                "cols": "9"
            },
            {
                "name": "Desktop",
                "px": "1024",
                "cols": "12"
            }
        ],
        "order": 2,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "72c1e5a9ec204878",
        "type": "ui-base",
        "name": "My Dashboard",
        "path": "/dashboard",
        "appIcon": "",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control",
            "ui-chart"
        ],
        "showPathInSidebar": false,
        "headerContent": "page",
        "titleBarStyle": "default",
        "showReconnectNotification": true,
        "notificationDisplayTime": 5,
        "showDisconnectNotification": true,
        "allowInstall": true
    },
    {
        "id": "0d92c765bfad87e6",
        "type": "ui-theme",
        "name": "Basic Blue Theme",
        "colors": {
            "surface": "#4d58ff",
            "primary": "#0094ce",
            "bgPage": "#eeeeee",
            "groupBg": "#ffffff",
            "groupOutline": "#cccccc"
        },
        "sizes": {
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "4px",
            "widgetGap": "12px",
            "density": "default"
        }
    },
    {
        "id": "893270d9dfd7315d",
        "type": "global-config",
        "env": [],
        "modules": {
            "@flowfuse/node-red-dashboard": "1.30.2"
        }
    }
]

```

---

<div class="post-metadata">

**Author:** ![kevinGodell](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/kevingodell/32/27040_2.png) [@kevinGodell](https://discourse.nodered.org/u/kevinGodell)\
**Post date:** [1 November 2026 16:58 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/2 "2026-11-01T16:58:29Z")

</div>

Yes, mediamtx is a good tool. I came across it a few months ago when I was stress testing my current cctv server project and didn't want to put that burden directly on my cameras. You know how flaky our cameras are when you have multiple or repeated connections.

> [@krambriw](#):
>
> With minimum latency!

It's good to hear that you are still searching for the holy grail of latency. In my new system under development, I explored webrtc, but decided to take a different path. One of my ai agents informed me of [webcodecs api](https://developer.mozilla.org/en-US/docs/Web/API/WebCodecs_API) in the browser (which has been around for some years but I had no idea it existed). It allows for a comparable latency to webrtc without all of the extra stun/ice stuff, except I have to manage the transport separately, which led me to regular websockets since node does not fully support http3 webtransport yet (but i see deno does, might give that a try since my code works there too).

My 2nd place option for latency is using LL-HLS with hls.js. The trick is to deliver 250ms fragments to the browser via websockets and have a web worker handle them, create a dynamic playlist there on the client side, and feed it to the hls.js by overriding its loaders. Works great, less latency than a regular HLS, but not as fast as the webcodecs implementation. Smoother playback and seems to work better in firefox than the webcodecs, while chromium based browsers handle both streaming methods efficiently.

---

<div class="post-metadata">

**Author:** ![krambriw](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/krambriw/32/5429_2.png) [@krambriw](https://discourse.nodered.org/u/krambriw)\
**Post date:** [1 November 2026 18:00 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/3 "2026-11-01T18:00:39Z")

</div>

Great to see & hear from you again!!

Yeah, you know, the thing puzzled my mind a bit. I understand what you say, cool stuff you are upto!! For me the late wake up with mediamtx came the other day, but better late than never

I also tried the HLS stream, it worked also fine I think (didn't try the LL- variant yet). I think the RTSP from it also works pretty good, I do have rather low latency on that one as well, at least compared to using ffmpeg as we did before. I have some further idea to use WHEP for the WebRTC instead of having an iframe but I do not know if that would give any further advantages

The webcodec looks very powerful I must say, maybe....but for now I'm rather happy with the mediamtx, at least for a while 😉

Take care, nice talking to you again, Walter

---

<div class="post-metadata">

**Author:** ![VisuDock](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/visudock/32/108796_2.png) [@VisuDock](https://discourse.nodered.org/u/VisuDock)\
**Post date:** [2 November 2026 03:34 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/4 "2026-11-02T03:34:21Z")

</div>

Thanks for the write-up. The "one connection to the camera" point has a second use: other readers can pull from mediamtx too, not only browsers. A snapshot grabber or an OCR step can read the mediamtx RTSP address (default port 8554, so rtsp://PI\_IP:8554/tapo for your path) instead of opening another session on the camera. That matters with cheap cameras that only allow two or three connections.

If you want a light preview for phones, you can add a second path that pulls the camera's sub stream, and keep the main stream path for recording or analysis.

Have you tried adding more viewers on the RPi3 to see where the CPU starts to climb?

---

<div class="post-metadata">

**Author:** ![krambriw](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/krambriw/32/5429_2.png) [@krambriw](https://discourse.nodered.org/u/krambriw)\
**Post date:** [2 November 2026 07:09 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/5 "2026-11-02T07:09:43Z")

</div>

> [@VisuDock](#):
>
> Have you tried adding more viewers on the RPi3 to see where the CPU starts to climb?

Thanks,

I just did a simple test opening the same several times, at most 5 on my mac in Safari & Chrome as well as another 2 on a Windows PC in Chrome and Edge (it worked fine in all)

The CPU load increase on the RPi3 was pretty linear I think, it increased around 2% for each "viewer". I assume the load would also be dependent on the FPS, resolution and frame size. The TAPO is a super-cheap C200 set to give 1080p, frame size set to 640x360 and FPS around 14

0 1.7-2%  
1 5%  
2 8%  
3 10%  
4 12%  
5 14%  
6 16%  
7 18%

 ![Skärmavbild 2026-11-02 kl. 08.08.06](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/2/7/27e25fca5120e8b0ca8a5ad58ab15c54c2680477.jpeg)

The camera is in a different location than from where I'm viewing so the transfer is via internet. The latency is around half to one second for the WebRTC and EDIT: _additional 2 seconds_ for the LL-HLS

 ![Skärmavbild 2026-11-02 kl. 08.16.29](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/c/1/c176c6b30231c7a9d7146de2a2fa42355231e8bd.png)

---

<div class="post-metadata">

**Author:** ![krambriw](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/krambriw/32/5429_2.png) [@krambriw](https://discourse.nodered.org/u/krambriw)\
**Post date:** [2 November 2026 07:20 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/6 "2026-11-02T07:20:28Z")

</div>

Just forgot to share the LL-HLS flow...

```auto
[
    {
        "id": "d472b66a882eda03",
        "type": "ui-template",
        "z": "3a2119e9c11365e2",
        "group": "b7971684f73a027c",
        "page": "",
        "ui": "",
        "name": "",
        "order": 0,
        "width": 0,
        "height": 0,
        "head": "",
        "format": "<template>\n <div>\n <!-- Rubrik med dynamisk statusindikator -->\n <div class=\"header-container\">\n <h2>Camera LL-HLS stream: Tapo</h2>\n <span :class=\"['status-dot', isPlaying ? 'status-online' : 'status-offline']\"></span>\n </div>\n\n <!-- HLS Video via MediaMTX inbyggda HLS-gränssnitt -->\n <div class=\"video-container\">\n <iframe \n v-if=\"isPlaying\"\n src=\"http://your-ip:8888/tapo/\" \n scrolling=\"no\" \n allow=\"autoplay; encrypted-media; picture-in-picture\"\n allowfullscreen>\n </iframe>\n <!-- Meddelande när strömmen är stoppad -->\n <div v-else class=\"video-placeholder\">\n <p>Stream is stopped</p>\n </div>\n </div>\n\n <!-- Kontrollknappar -->\n <div class=\"button-container\">\n <v-btn color=\"success\" :disabled=\"isPlaying\" @click=\"startStream()\">Start</v-btn>\n <v-btn color=\"error\" :disabled=\"!isPlaying\" @click=\"stopStream()\">Stop</v-btn>\n </div>\n </div>\n</template>\n\n<script>\nexport default {\n data() {\n return {\n isPlaying: true\n }\n },\n methods: {\n startStream() {\n this.isPlaying = true;\n },\n stopStream() {\n this.isPlaying = false;\n }\n }\n}\n</script>\n\n<style>\n/* CSS är identisk med den övre komponenten */\n.header-container { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }\n.status-dot { width: 12px; height: 12px; border-radius: 50%; display: inline-block; transition: background-color 0.3s ease; }\n.status-online { background-color: #4caf50; box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7); animation: pulse 2s infinite; }\n.status-offline { background-color: #f44336; box-shadow: none; animation: none; }\n@keyframes pulse { 0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(76, 175, 80, 0.7); } 70% { transform: scale(1); box-shadow: 0 0 0 8px rgba(76, 175, 80, 0); } 100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(76, 175, 80, 0); } }\n.video-container { width: 100%; max-width: 640px; aspect-ratio: 16 / 9; margin-bottom: 15px; background: #000; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); position: relative; }\n.video-container iframe { width: 100%; height: 100%; border: none; display: block; }\n.video-placeholder { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; color: #aaa; font-size: 1.1em; font-family: sans-serif; }\n.button-container { display: flex; gap: 10px; margin-bottom: 20px; }\n</style>\n",
        "storeOutMessages": true,
        "passthru": true,
        "resendOnRefresh": true,
        "templateScope": "local",
        "className": "",
        "x": 610,
        "y": 320,
        "wires": [
            []
        ]
    },
    {
        "id": "b7971684f73a027c",
        "type": "ui-group",
        "name": "Group 5",
        "page": "f85cf0eee9119058",
        "width": "4",
        "height": "1",
        "order": 2,
        "showTitle": false,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "f85cf0eee9119058",
        "type": "ui-page",
        "name": "Home",
        "ui": "72c1e5a9ec204878",
        "path": "/home",
        "icon": "home",
        "layout": "grid",
        "theme": "0d92c765bfad87e6",
        "breakpoints": [
            {
                "name": "Default",
                "px": "0",
                "cols": "3"
            },
            {
                "name": "Tablet",
                "px": "576",
                "cols": "6"
            },
            {
                "name": "Small Desktop",
                "px": "768",
                "cols": "9"
            },
            {
                "name": "Desktop",
                "px": "1024",
                "cols": "12"
            }
        ],
        "order": 2,
        "className": "",
        "visible": "true",
        "disabled": "false"
    },
    {
        "id": "72c1e5a9ec204878",
        "type": "ui-base",
        "name": "My Dashboard",
        "path": "/dashboard",
        "appIcon": "",
        "includeClientData": true,
        "acceptsClientConfig": [
            "ui-notification",
            "ui-control",
            "ui-chart"
        ],
        "showPathInSidebar": false,
        "headerContent": "page",
        "titleBarStyle": "default",
        "showReconnectNotification": true,
        "notificationDisplayTime": 5,
        "showDisconnectNotification": true,
        "allowInstall": true
    },
    {
        "id": "0d92c765bfad87e6",
        "type": "ui-theme",
        "name": "Basic Blue Theme",
        "colors": {
            "surface": "#4d58ff",
            "primary": "#0094ce",
            "bgPage": "#eeeeee",
            "groupBg": "#ffffff",
            "groupOutline": "#cccccc"
        },
        "sizes": {
            "pagePadding": "12px",
            "groupGap": "12px",
            "groupBorderRadius": "4px",
            "widgetGap": "12px",
            "density": "default"
        }
    },
    {
        "id": "ba0c4b44b8ea7a0f",
        "type": "global-config",
        "env": [],
        "modules": {
            "@flowfuse/node-red-dashboard": "1.30.2"
        }
    }
]

```

---

<div class="post-metadata">

**Author:** ![VisuDock](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/visudock/32/108796_2.png) [@VisuDock](https://discourse.nodered.org/u/VisuDock)\
**Post date:** [3 November 2026 06:16 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/7 "2026-11-03T06:16:25Z")

</div>

Thanks for the numbers, the roughly 2% per viewer is handy to know. On the extra two seconds for LL-HLS: part of that is often the camera's keyframe interval, because HLS can only start a new segment on a keyframe. Many cheap cameras default to a keyframe every 2 to 4 seconds. If the camera lets you set it to about 1 second, the HLS delay often drops.

The mediamtx HLS segment and part length settings are also worth a look, but I would check the camera setting first.

Does the Tapo C200 let you change the keyframe (I-frame) interval, or is it fixed?

---

<div class="post-metadata">

**Author:** ![krambriw](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/krambriw/32/5429_2.png) [@krambriw](https://discourse.nodered.org/u/krambriw)\
**Post date:** [3 November 2026 06:40 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/8 "2026-11-03T06:40:31Z")

</div>

> [@VisuDock](#):
>
> Does the Tapo C200 let you change the keyframe

Not through the settings provided by the app and I do not know if the camera does support ONVIF and if it would be possible that way. Right know I'm happy using WebRTC

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [3 November 2026 12:27 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/9 "2026-11-03T12:27:13Z")

</div>

How does MediaMTX compare to this?

> **[GitHub - AlexxIT/go2rtc: Ultimate camera streaming application](https://github.com/AlexxIT/go2rtc)**
>
> Ultimate camera streaming application

---

<div class="post-metadata">

**Author:** ![krambriw](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/krambriw/32/5429_2.png) [@krambriw](https://discourse.nodered.org/u/krambriw)\
**Post date:** [3 November 2026 15:10 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/10 "2026-11-03T15:10:00Z")

</div>

Seen that also as a possible server, haven't tested it though and haven't checked any differencies in functionlity details either but they do like very similar in that respect. Might be that some features provided by go2rtc could be very useful when building a video gui (like the digital zoom feature) but I haven't investigated this further  
Best regards, Walter

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [3 November 2026 15:12 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/11 "2026-11-03T15:12:33Z")

</div>

If you do ever get to testing it, I'd be interested in the relative resource usage of each as my home server is already getting somewhat resource constrained.

---

<div class="post-metadata">

**Author:** ![krambriw](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/krambriw/32/5429_2.png) [@krambriw](https://discourse.nodered.org/u/krambriw)\
**Post date:** [3 November 2026 15:43 UTC](https://discourse.nodered.org/t/how-to-stream-video-with-ultra-low-latency/102038/12 "2026-11-03T15:43:47Z")

</div>

Today I fine-tuned an instance of MediaMTX installed & running on an RPi4, located in our country house where I just have a 4G Teltonika router for the internet connection. I did'nt want the streaming to consume too much of my monthly quota so I reduced the FPS to only 1 frame per second (camera setting did not allow lower than 15 FPS). This change gave a latency around a few seconds (good enough for a quick overview, if I need better I can always use the TAPO app)

 ![Skärmavbild 2026-11-03 kl. 16.19.11](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/9/8/98cb40fd9cbae992e5a5ad3602b9595368624e9e.png)

To achieve this I just added some transcoding to the MediaMTX configuration, using the GPU in the RPi and, wow, the load went down to almost nothing, 3-4% for mediamtx and 2% for ffmpeg used for the transcoding. See image below

The key thing was transcoding, adding the "tapo\_low\_fps" in the config. In this way we use the GPU for both reading and outputting the stream, The RPi runs very cool, Trixie 64-bit:

```auto
  tapo:
    source: rtsp://................etc
    sourceProtocol: tcp
    sourceOnDemand: yes

  tapo_low_fps:
    runOnInit: ffmpeg -rtsp_transport tcp -c:v h264_v4l2m2m -i rtsp://localhost:8554/tapo -vf "fps=1,format=yuv420p" -c:v h264_v4l2m2m -b:v 1000k -an -f rtsp rtsp://localhost:8554/tapo_low_fps
    runOnInitRestart: yes

```

 ![Skärmavbild 2026-11-03 kl. 16.25.58](https://us1.discourse-cdn.com/flex026/uploads/nodered/original/3X/f/7/f717a8020774b6f498bfefd7a5ebfa89ff585113.png)
