<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Seymour Birkhoff</title>
    <description>The latest articles on DEV Community by Seymour Birkhoff (@seymour_birkhoff).</description>
    <link>https://dev.to/seymour_birkhoff</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3810468%2Fd53ceb96-ed3b-4ec0-bc99-81d5bd513ad6.jpg</url>
      <title>DEV Community: Seymour Birkhoff</title>
      <link>https://dev.to/seymour_birkhoff</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/seymour_birkhoff"/>
    <language>en</language>
    <item>
      <title>How We Convert RTSP Camera Streams to Browser-Playable HLS for Kazakhstan's Government Portal</title>
      <dc:creator>Seymour Birkhoff</dc:creator>
      <pubDate>Fri, 06 Mar 2026 19:18:29 +0000</pubDate>
      <link>https://dev.to/seymour_birkhoff/how-we-convert-rtsp-camera-streams-to-browser-playable-hls-for-kazakhstans-government-portal-27lc</link>
      <guid>https://dev.to/seymour_birkhoff/how-we-convert-rtsp-camera-streams-to-browser-playable-hls-for-kazakhstans-government-portal-27lc</guid>
      <description>&lt;p&gt;Since January 1, 2026, manufacturers in Kazakhstan must provide &lt;strong&gt;live online video surveillance&lt;/strong&gt; at their production facilities to be included in the &lt;strong&gt;Registry of Manufacturers&lt;/strong&gt; and obtain the &lt;strong&gt;ST-KZ certificate&lt;/strong&gt;. The government portal &lt;a href="https://e-ondiris.gov.kz" rel="noopener noreferrer"&gt;e-ondiris.gov.kz&lt;/a&gt; requires a &lt;strong&gt;web link&lt;/strong&gt; to the camera stream that opens in a standard browser.&lt;/p&gt;

&lt;p&gt;Sounds simple. In practice — not so much.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;IP surveillance cameras (Hikvision, Dahua, Uniview, etc.) stream video via &lt;strong&gt;RTSP&lt;/strong&gt; (Real Time Streaming Protocol) — a binary protocol that browsers don't support. You can't just paste &lt;code&gt;rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101&lt;/code&gt; into a government form and expect it to work.&lt;/p&gt;

&lt;p&gt;On top of that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;~70% of enterprises&lt;/strong&gt; in Kazakhstan lack a static IP address&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Double NAT&lt;/strong&gt; — camera behind a router, behind another ISP router&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No IT staff&lt;/strong&gt; — factory directors don't configure nginx and ffmpeg&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We built &lt;a href="https://rtsp.kz" rel="noopener noreferrer"&gt;RTSP.KZ&lt;/a&gt; to solve this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;We support three connection methods:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Pull (RTSP)
&lt;/h3&gt;

&lt;p&gt;If the enterprise has a static IP with port 554 forwarded, our server pulls the RTSP stream directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Camera (RTSP:554) → Internet → RTSP.KZ server → HLS → Browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Push (RTMP)
&lt;/h3&gt;

&lt;p&gt;The camera or NVR pushes the stream to our server via RTMP.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Camera → NVR (RTMP push) → RTSP.KZ server → HLS → Browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. VPN Tunnel (WireGuard)
&lt;/h3&gt;

&lt;p&gt;For cameras without a static IP. A WireGuard client and lightweight MediaMTX media server run on the enterprise's PC.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Camera (LAN) → PC with MediaMTX → WireGuard VPN → RTSP.KZ server → HLS → Browser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MediaMTX&lt;/strong&gt; — media server for receiving RTSP/RTMP streams and converting to HLS (written in Go, minimal resources)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FastAPI&lt;/strong&gt; (Python) — backend API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;React 18 + TypeScript&lt;/strong&gt; — frontend SPA + static landing for SEO&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PostgreSQL&lt;/strong&gt; — primary database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt; — caching, queues&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nginx&lt;/strong&gt; — reverse proxy, SSL termination&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WireGuard&lt;/strong&gt; — VPN for cameras behind NAT&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docker Compose&lt;/strong&gt; — orchestration&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  RTSP → HLS Conversion
&lt;/h2&gt;

&lt;p&gt;The key process is &lt;strong&gt;remuxing&lt;/strong&gt; RTSP into HLS (HTTP Live Streaming). HLS splits the video stream into short &lt;code&gt;.ts&lt;/code&gt; segments described in a &lt;code&gt;.m3u8&lt;/code&gt; playlist. Every modern browser plays HLS natively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;RTSP (H.264/H.265) → MediaMTX → HLS (.m3u8 + .ts segments)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We do &lt;strong&gt;no transcoding&lt;/strong&gt; — just repackaging from RTSP container to MPEG-TS. This allows handling dozens of streams on a single server without a GPU.&lt;/p&gt;

&lt;p&gt;The result is a URL like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://stream.rtsp.kz/{camera_id}/index.m3u8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This link goes directly into the e-ondiris.gov.kz application form.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solving NAT with WireGuard
&lt;/h2&gt;

&lt;p&gt;The most common case — no static IP. In Kazakhstan, that's ~70% of enterprises.&lt;/p&gt;

&lt;p&gt;Our solution:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate a WireGuard keypair on the server&lt;/li&gt;
&lt;li&gt;Allocate a subnet from the pool (10.13.x.0/24)&lt;/li&gt;
&lt;li&gt;Send the client a WireGuard config&lt;/li&gt;
&lt;li&gt;On the client's PC, run MediaMTX configured to pull from the local camera and relay through VPN
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# mediamtx.yml on client PC&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;camera1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rtsp://admin:password@192.168.1.64:554/Streaming/Channels/101&lt;/span&gt;
    &lt;span class="na"&gt;sourceProtocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tcp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;WireGuard was chosen for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Minimal overhead (~3% bandwidth)&lt;/li&gt;
&lt;li&gt;Works through any NAT, including mobile CGNAT&lt;/li&gt;
&lt;li&gt;Simple setup (single config file)&lt;/li&gt;
&lt;li&gt;Stability during network switches&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Camera Monitoring
&lt;/h2&gt;

&lt;p&gt;For each camera, we run a TCP ping every 30 seconds and display status in the dashboard:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🟢 &lt;strong&gt;Online&lt;/strong&gt; — camera reachable, stream active&lt;/li&gt;
&lt;li&gt;🔴 &lt;strong&gt;Offline&lt;/strong&gt; — no connection&lt;/li&gt;
&lt;li&gt;📊 &lt;strong&gt;Latency&lt;/strong&gt; — 24-hour ping graph&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Average camera setup time: &lt;strong&gt;5-10 minutes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Stream uptime: &lt;strong&gt;99.5%+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Latency: &lt;strong&gt;under 3 seconds&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Compatible with: Hikvision, Dahua, Trassir, Uniview, TP-Link, Axis, Hanwha, and NVR/DVR devices&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;MediaMTX&lt;/strong&gt; is an excellent lightweight alternative to FFmpeg for simple remuxing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WireGuard&lt;/strong&gt; solves NAT better than any STUN/TURN for surveillance cameras&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HLS&lt;/strong&gt; remains the most reliable format for browser-based streaming&lt;/li&gt;
&lt;li&gt;Sometimes a niche government regulation creates a real technical challenge worth solving&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Check out &lt;a href="https://rtsp.kz" rel="noopener noreferrer"&gt;RTSP.KZ&lt;/a&gt; | &lt;a href="https://rtsp.kz/help/cameras" rel="noopener noreferrer"&gt;Camera setup guides&lt;/a&gt; | &lt;a href="https://rtsp.kz/pricing" rel="noopener noreferrer"&gt;Pricing&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>devops</category>
      <category>networking</category>
    </item>
  </channel>
</rss>
