<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Mhasan502 Blog</title>
        <link>https://mhasan502.github.io/series</link>
        <description>Mhasan502 Blog</description>
        <lastBuildDate>Fri, 09 Oct 2026 05:32:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Stream MP4 Videos as Virtual RTSP Cameras]]></title>
            <link>https://mhasan502.github.io/series/mp4-to-rtsp</link>
            <guid>https://mhasan502.github.io/series/mp4-to-rtsp</guid>
            <pubDate>Fri, 09 Oct 2026 05:32:25 GMT</pubDate>
            <description><![CDATA[Learn how to loop MP4 videos as live RTSP streams using FFmpeg and MediaMTX, and simulate multiple CCTV cameras for computer vision testing.]]></description>
            <content:encoded><![CDATA[<p>Say you're building a computer vision application that processes live CCTV footage. You connect a couple of cameras,
test the application, and everything works fine. Then you need to find out how it handles 50 or 100 cameras at once.</p>
<p>I'd rather not buy 100 CCTV cameras just to answer that question.</p>
<p>That got me thinking about how to simulate multiple RTSP streams without physical cameras or unreliable public RTSP
links. Could a few MP4 files behave like live camera feeds?</p>
<p>With FFmpeg and MediaMTX, we can loop those MP4 videos and give each one its own RTSP URL. Let's start with one video,
then use a Bash script to run a whole folder of them as virtual CCTV cameras.</p>
<p><img decoding="async" loading="lazy" alt="MP4 videos published through FFmpeg and MediaMTX to RTSP clients" src="https://mhasan502.github.io/assets/images/stream-mp4-videos-as-virtual-rtsp-cameras-a0fed14d77b850edafc42b7a59fbabef.png" width="1731" height="909" class="img_ev3q"></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="goal">Goal<a href="https://mhasan502.github.io/series/mp4-to-rtsp#goal" class="hash-link" aria-label="Direct link to Goal" title="Direct link to Goal" translate="no">​</a></h2>
<p>Our goal is to take a collection of video files and make them accessible as RTSP camera streams. Any RTSP-compatible
client can connect to these URLs and receive video frames.</p>
<p>For example, consider we have three videos:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token plain">videos/</span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">├── entrance.mp4</span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">├── parking.mp4</span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">└── traffic.mp4</span><br></div></code></pre></div></div>
<p>We want to expose them as:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token plain">rtsp://localhost:8554/entrance</span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">rtsp://localhost:8554/parking</span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">rtsp://localhost:8554/traffic</span><br></div></code></pre></div></div>
<p>MP4 videos eventually end but CCTV cameras are expected to keep streaming. We can loop each video indefinitely so that
our virtual cameras can stream continuously.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="prerequisites">Prerequisites<a href="https://mhasan502.github.io/series/mp4-to-rtsp#prerequisites" class="hash-link" aria-label="Direct link to Prerequisites" title="Direct link to Prerequisites" translate="no">​</a></h2>
<p>We'll use two tools:</p>
<ul>
<li class=""><a href="https://ffmpeg.org/download.html" target="_blank" rel="noopener noreferrer" class="">FFmpeg</a> reads each video file, loops it, and publishes it as an RTSP stream.</li>
<li class=""><a href="https://mediamtx.org/" target="_blank" rel="noopener noreferrer" class="">MediaMTX</a> is the RTSP server that clients connect to.</li>
</ul>
<p>We'll run MediaMTX in Docker to keep the setup simple.</p>
<p>The processing pipeline looks like this:</p>
<p><code>MP4 Video → FFmpeg → MediaMTX → RTSP Client</code></p>
<p>Before getting started, make sure you have:</p>
<ul>
<li class="">Docker installed and running</li>
<li class="">FFmpeg installed on your host machine</li>
<li class="">At least one MP4 video, preferably encoded with H.264</li>
<li class="">VLC or FFplay for testing</li>
</ul>
<p>You can verify FFmpeg and Docker using:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token plain">ffmpeg </span><span class="token parameter variable" style="color:#A9B7C6">-version</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain"></span><span class="token function" style="color:#FFC66D">docker</span><span class="token plain"> </span><span class="token parameter variable" style="color:#A9B7C6">--version</span><br></div></code></pre></div></div>
<p>This article uses a Linux environment. The RTSP server will run in Docker, while FFmpeg will run on the host machine.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setting-up-the-rtsp-server">Setting up the RTSP server<a href="https://mhasan502.github.io/series/mp4-to-rtsp#setting-up-the-rtsp-server" class="hash-link" aria-label="Direct link to Setting up the RTSP server" title="Direct link to Setting up the RTSP server" translate="no">​</a></h2>
<p>We'll use MediaMTX, a media server that supports RTSP and several other streaming protocols.</p>
<p>Start it using Docker:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token function" style="color:#FFC66D">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:#A9B7C6">-d</span><span class="token plain"> </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">  </span><span class="token parameter variable" style="color:#A9B7C6">--name</span><span class="token plain"> rtsp-server </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">  </span><span class="token parameter variable" style="color:#A9B7C6">--restart</span><span class="token plain"> unless-stopped </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">  </span><span class="token parameter variable" style="color:#A9B7C6">-e</span><span class="token plain"> </span><span class="token assign-left variable" style="color:#A9B7C6">MTX_RTSPTRANSPORTS</span><span class="token operator" style="color:#A9B7C6">=</span><span class="token plain">tcp </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">  </span><span class="token parameter variable" style="color:#A9B7C6">-p</span><span class="token plain"> </span><span class="token number" style="color:#6897BB">8554</span><span class="token plain">:8554 </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">  bluenviron/mediamtx:latest</span><br></div></code></pre></div></div>
<p>This command starts MediaMTX in the background and exposes port 8554 for RTSP connections.</p>
<p>Check that the container is running:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token function" style="color:#FFC66D">docker</span><span class="token plain"> </span><span class="token function" style="color:#FFC66D">ps</span><span class="token plain"> </span><span class="token parameter variable" style="color:#A9B7C6">-a</span><span class="token plain"> </span><span class="token parameter variable" style="color:#A9B7C6">--filter</span><span class="token plain"> </span><span class="token string" style="color:#6A8759">"name=rtsp-server"</span><br></div></code></pre></div></div>
<p>MediaMTX is now ready to receive streams. We'll use FFmpeg to publish our videos to it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="streaming-our-first-video">Streaming our first video<a href="https://mhasan502.github.io/series/mp4-to-rtsp#streaming-our-first-video" class="hash-link" aria-label="Direct link to Streaming our first video" title="Direct link to Streaming our first video" translate="no">​</a></h2>
<p>Let's start with a single MP4 file named <code>test.mp4</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token plain">ffmpeg </span><span class="token parameter variable" style="color:#A9B7C6">-re</span><span class="token plain"> </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">  </span><span class="token parameter variable" style="color:#A9B7C6">-stream_loop</span><span class="token plain"> </span><span class="token parameter variable" style="color:#A9B7C6">-1</span><span class="token plain"> </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">  </span><span class="token parameter variable" style="color:#A9B7C6">-i</span><span class="token plain"> test.mp4 </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">  </span><span class="token parameter variable" style="color:#A9B7C6">-an</span><span class="token plain"> </span><span class="token parameter variable" style="color:#A9B7C6">-c:v</span><span class="token plain"> copy </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">  </span><span class="token parameter variable" style="color:#A9B7C6">-f</span><span class="token plain"> rtsp </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">  </span><span class="token parameter variable" style="color:#A9B7C6">-rtsp_transport</span><span class="token plain"> tcp </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">  rtsp://localhost:8554/cam_1</span><br></div></code></pre></div></div>
<p>The options mean:</p>





































<table><thead><tr><th>Option</th><th>Purpose</th></tr></thead><tbody><tr><td><code>-re</code></td><td>Read the file at its native playback speed</td></tr><tr><td><code>-stream_loop -1</code></td><td>Repeat the video indefinitely</td></tr><tr><td><code>-i test.mp4</code></td><td>Input video file</td></tr><tr><td><code>-an</code></td><td>Disable audio</td></tr><tr><td><code>-c:v copy</code></td><td>Copy the existing video stream without re-encoding</td></tr><tr><td><code>-f rtsp</code></td><td>Publish using RTSP</td></tr><tr><td><code>-rtsp_transport tcp</code></td><td>Use TCP for the RTSP media transport</td></tr></tbody></table>
<p>The <code>-c:v copy</code> option avoids re-encoding, keeping CPU usage low. However, it requires a video codec that MediaMTX and
the RTSP client support. H.264 is a reliable starting point.</p>
<p>Our MP4 video is now being published as an RTSP stream:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token plain">rtsp://localhost:8554/cam_1</span><br></div></code></pre></div></div>
<p>FFmpeg restarts the video whenever it reaches the end. Leave it running while you test the stream.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="testing-the-rtsp-stream">Testing the RTSP stream<a href="https://mhasan502.github.io/series/mp4-to-rtsp#testing-the-rtsp-stream" class="hash-link" aria-label="Direct link to Testing the RTSP stream" title="Direct link to Testing the RTSP stream" translate="no">​</a></h2>
<p>Open another terminal and connect to the camera:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token plain">ffplay </span><span class="token parameter variable" style="color:#A9B7C6">-rtsp_transport</span><span class="token plain"> tcp rtsp://localhost:8554/cam_1</span><br></div></code></pre></div></div>
<p>Alternatively, open VLC Media Player, select <strong>Media → Open Network Stream</strong>, and enter:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token plain">rtsp://localhost:8554/cam_1</span><br></div></code></pre></div></div>
<p>If the setup is working, you'll see the MP4 video playing through RTSP in FFplay or VLC.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="simulating-multiple-cctv-cameras">Simulating multiple CCTV cameras<a href="https://mhasan502.github.io/series/mp4-to-rtsp#simulating-multiple-cctv-cameras" class="hash-link" aria-label="Direct link to Simulating multiple CCTV cameras" title="Direct link to Simulating multiple CCTV cameras" translate="no">​</a></h2>
<p>Now that one camera is working, we can give each video in our folder its own stream. For 100 cameras, I'd rather use a
small Bash script than open 100 terminals and run FFmpeg manually.</p>
<p>First, organize our video files:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token plain">virtual-cctv/</span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">├── videos/</span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">│   ├── entrance.mp4</span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">│   ├── parking.mp4</span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">│   ├── traffic.mp4</span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">│   └── warehouse.mp4</span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">└── start_cameras.sh</span><br></div></code></pre></div></div>
<p>Create a file named <code>start_cameras.sh</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token shebang important" style="color:#FF0000">#!/bin/bash</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain"></span><span class="token keyword" style="color:#CC7832">for</span><span class="token plain"> </span><span class="token for-or-select variable" style="color:#A9B7C6">video</span><span class="token plain"> </span><span class="token keyword" style="color:#CC7832">in</span><span class="token plain"> videos/*.mp4</span><span class="token punctuation" style="color:#A9B7C6">;</span><span class="token plain"> </span><span class="token keyword" style="color:#CC7832">do</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">    </span><span class="token punctuation" style="color:#A9B7C6">[</span><span class="token plain"> </span><span class="token parameter variable" style="color:#A9B7C6">-f</span><span class="token plain"> </span><span class="token string" style="color:#6A8759">"</span><span class="token string variable" style="color:#A9B7C6">$video</span><span class="token string" style="color:#6A8759">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#A9B7C6">]</span><span class="token plain"> </span><span class="token operator" style="color:#A9B7C6">||</span><span class="token plain"> </span><span class="token builtin class-name" style="color:#A9B7C6">continue</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">    </span><span class="token assign-left variable" style="color:#A9B7C6">name</span><span class="token operator" style="color:#A9B7C6">=</span><span class="token variable" style="color:#A9B7C6">$(</span><span class="token variable function" style="color:#FFC66D">basename</span><span class="token variable" style="color:#A9B7C6"> </span><span class="token variable string" style="color:#6A8759">"</span><span class="token variable string variable" style="color:#A9B7C6">$video</span><span class="token variable string" style="color:#6A8759">"</span><span class="token variable" style="color:#A9B7C6"> .mp4</span><span class="token variable" style="color:#A9B7C6">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">    ffmpeg </span><span class="token parameter variable" style="color:#A9B7C6">-nostdin</span><span class="token plain"> </span><span class="token parameter variable" style="color:#A9B7C6">-re</span><span class="token plain"> </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">      </span><span class="token parameter variable" style="color:#A9B7C6">-stream_loop</span><span class="token plain"> </span><span class="token parameter variable" style="color:#A9B7C6">-1</span><span class="token plain"> </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">      </span><span class="token parameter variable" style="color:#A9B7C6">-i</span><span class="token plain"> </span><span class="token string" style="color:#6A8759">"</span><span class="token string variable" style="color:#A9B7C6">$video</span><span class="token string" style="color:#6A8759">"</span><span class="token plain"> </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">      </span><span class="token parameter variable" style="color:#A9B7C6">-an</span><span class="token plain"> </span><span class="token parameter variable" style="color:#A9B7C6">-c:v</span><span class="token plain"> copy </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">      </span><span class="token parameter variable" style="color:#A9B7C6">-f</span><span class="token plain"> rtsp </span><span class="token parameter variable" style="color:#A9B7C6">-rtsp_transport</span><span class="token plain"> tcp </span><span class="token punctuation" style="color:#A9B7C6">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">      </span><span class="token string" style="color:#6A8759">"rtsp://localhost:8554/</span><span class="token string variable" style="color:#A9B7C6">$name</span><span class="token string" style="color:#6A8759">"</span><span class="token plain"> </span><span class="token operator" style="color:#A9B7C6">&amp;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain">    </span><span class="token builtin class-name" style="color:#A9B7C6">echo</span><span class="token plain"> </span><span class="token string" style="color:#6A8759">"Starting camera: </span><span class="token string variable" style="color:#A9B7C6">$name</span><span class="token string" style="color:#6A8759">"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain"></span><span class="token keyword" style="color:#CC7832">done</span><span class="token plain"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#A9B7C6"><span class="token plain"></span><span class="token function" style="color:#FFC66D">wait</span><br></div></code></pre></div></div>
<p>Run the script:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#A9B7C6;--prism-background-color:#191a1c"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#A9B7C6;background-color:#191a1c"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#A9B7C6"><span class="token function" style="color:#FFC66D">bash</span><span class="token plain"> start_cameras.sh</span><br></div></code></pre></div></div>
<p>The script finds every MP4 file inside the <code>videos/</code> directory and launches a separate FFmpeg process for each video.
The streams run simultaneously, each using the video's filename as its camera name.</p>
<p>For example:</p>

























<table><thead><tr><th>Video file</th><th>RTSP URL</th></tr></thead><tbody><tr><td>entrance.mp4</td><td><code>rtsp://localhost:8554/entrance</code></td></tr><tr><td>parking.mp4</td><td><code>rtsp://localhost:8554/parking</code></td></tr><tr><td>traffic.mp4</td><td><code>rtsp://localhost:8554/traffic</code></td></tr><tr><td>warehouse.mp4</td><td><code>rtsp://localhost:8554/warehouse</code></td></tr></tbody></table>
<p>The same approach works with 100 videos, assuming the system has sufficient resources.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="performance-considerations">Performance considerations<a href="https://mhasan502.github.io/series/mp4-to-rtsp#performance-considerations" class="hash-link" aria-label="Direct link to Performance considerations" title="Direct link to Performance considerations" translate="no">​</a></h2>
<p>At 100 streams, your machine has to read all those files and serve the viewers. Stream copying keeps CPU usage down
because FFmpeg doesn't need to decode and re-encode every frame, but disk reads, network traffic, and the resources
each process uses still add up.</p>
<p><strong>Video codec:</strong> H.264 is a good starting point because it is widely supported by RTSP clients.</p>
<p><strong>Storage:</strong> Streaming 100 videos means reading 100 files concurrently. SSD storage can help keep disk performance
consistent.</p>
<p><strong>Network bandwidth:</strong> If each stream uses 2 Mbps, 100 streams generate approximately 200 Mbps of publishing traffic.
Since FFmpeg and MediaMTX run on the same machine, this traffic stays local. Each remote viewer adds outgoing bandwidth
requirements.</p>
<p><strong>Client connections:</strong> The number of connected viewers also affects how many viewers the server can support, since each
connection consumes additional server bandwidth.</p>
<p><strong>CPU and memory:</strong> Even without re-encoding, running 100 separate FFmpeg processes consumes CPU and memory. Measure
performance with a few streams before scaling up.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://mhasan502.github.io/series/mp4-to-rtsp#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>We can now test with multiple camera feeds without buying a room full of cameras. Each feed comes from an MP4 file, so
we can use this setup to test RTSP integrations, run performance benchmarks, or experiment with video analytics.</p>
<p>There are plenty of things we could improve, such as automatically restarting failed streams, adding timestamps, or
dynamically managing cameras. But for now, this should be more than enough to get started.</p>]]></content:encoded>
        </item>
    </channel>
</rss>