Menu
Products
Products
Video Hosting
Upload and manage your videos in a centralized video library.
Image Hosting
Upload and manage all your images in a centralized library.
Galleries
Choose from 100+templates to showcase your media in style.
Video Messaging
Record, and send personalized video messages.
CincoTube
Create your own community video hub your team, students or fans.
Pages
Create dedicated webpages to share your videos and images.
Live
Create dedicated webpages to share your videos and images.
For Developers
Video API
Build a unique video experience.
DeepUploader
Collect and store user content from anywhere with our file uploader.
Solutions
Solutions
Enterprise
Supercharge your business with secure, internal communication.
Townhall
Webinars
Team Collaboration
Learning & Development
Creative Professionals
Get creative with a built in-suite of editing and marketing tools.
eCommerce
Boost sales with interactive video and easy-embedding.
Townhall
Webinars
Team Collaboration
Learning & Development
eLearning & Training
Host and share course materials in a centralized portal.
Sales & Marketing
Attract, engage and convert with interactive tools and analytics.
"Cincopa helped my Enterprise organization collaborate better through video."
Book a Demo
Resources
Resources
Blog
Learn about the latest industry trends, tips & tricks.
Help Centre
Get access to help articles FAQs, and all things Cincopa.
Partners
Check out our valued list of partners.
Product Updates
Stay up-to-date with our latest greatest features.
Ebooks, Guides & More
Customer Stories
Hear how we've helped businesses succeed.
Boost Campaign Performance Through Video
Discover how to boost your next campaign by using video.
Download Now
Pricing
Log in
Get a demo
Get Started
Building a custom RTMP-to-HLS converter starts with understanding how live video moves from a contribution protocol to a delivery protocol. RTMP is commonly used to push live video from an encoder to a server with relatively low latency, while most modern browsers and mobile devices expect HLS for playback. Your converter’s job is to ingest an RTMP stream, transcode or repackage it, and produce HLS playlists and segments that can be served over HTTP(S) to viewers. This conversion is important because mismatched protocols and codecs often cause playback failures, unnecessary latency, or limited device compatibility. A custom converter lets you tune encoding settings, resource usage, error handling, and security to fit your workload instead of relying on generic tools. Prerequisites To build your own converter, have these on your computer: A programming environment such as Node.js or Python, or any language that can spawn child processes. FFmpeg is installed and available on the system PATH. An RTMP live stream URL for testing (for example, from OBS Studio or another RTMP encoder). A directory for HLS output (playlist and segment files). Basic familiarity with scripting, processes, and the command line. RTMP, HLS, and Workflow Basics RTMP (Real-Time Messaging Protocol) is typically used to send live video from an encoder to a server because it maintains a persistent connection and supports relatively low-latency contribution. HLS (HTTP Live Streaming) packages the live stream into short media segments (commonly MPEG-TS or fragmented MP4) and an index playlist (.m3u8) that players fetch over HTTP, enabling adaptive bitrate, caching, and broad compatibility. A simple RTMP-to-HLS workflow looks like this: Encoder (e.g., OBS) → RTMP stream. Converter (your custom process calling FFmpeg) → HLS segments and playlists. HTTP(S) server → serves .m3u8 and segment files to web or app players. Core FFmpeg Command for RTMP → HLS The core of your converter is a wrapper around FFmpeg that pulls from an RTMP URL and writes HLS output. In Node.js, this typically uses the child process APIs to start and manage FFmpeg. Example : ffmpeg -i rtmp://example.com/live/stream -c:v libx264 -preset veryfast -crf 23 -g 60 -sc_threshold 0 -c:a aac -b:a 128k -f hls -hls_time 4 -hls_list_size 5 -hls_flags delete_segments+append_list ./hls_output/index.m3u8 Key Points : -c:v libx264, -preset, -crf control video codec, speed, and quality. -g aligns the GOP (keyframe interval) with segment duration (for 30 fps, g=60 for ~2s, g=120 for ~4s). -sc_threshold 0 reduces random keyframes for more predictable segment boundaries. -f hls, -hls_time, -hls_list_size, -hls_flags control HLS segmenting and playlist behavior. If your RTMP input already uses H.264/AAC and is HLS-compatible, you can avoid transcoding: ffmpeg -i rtmp://example.com/live/stream -c:v copy -c:a copy -f hls -hls_time 4 -hls_list_size 5 -hls_flags delete_segments+append_list ./hls_output/index.m3u8 Use this “copy” mode when you only need protocol conversion and your input codecs are already suitable. Node.js Wrapper: Running FFmpeg from Code For a custom converter, wrap FFmpeg in a Node.js script, so your app can start, monitor, and stop conversions programmatically. Example Node.js Module : const { spawn } = require('child_process'); const path = require('path'); function createHlsArgs({ rtmpUrl, outputDir, options = {} }) { const { transcode = true, videoBitrate = '2000k', audioBitrate = '128k', segmentDuration = 4, playlistSize = 5, gopSeconds = 4, } = options; const gopSize = gopSeconds * 30; // assuming ~30 fps; adjust if needed const args = ['-i', rtmpUrl]; if (transcode) { args.push( '-c:v', 'libx264', '-preset', 'veryfast', '-crf', '23', '-b:v', videoBitrate, '-g', gopSize.toString(), '-sc_threshold', '0', '-c:a', 'aac', '-b:a', audioBitrate ); } else { args.push( '-c:v', 'copy', '-c:a', 'copy' ); } args.push( '-f', 'hls', '-hls_time', segmentDuration.toString(), '-hls_list_size', playlistSize.toString(), '-hls_flags', 'delete_segments+append_list', path.join(outputDir, 'index.m3u8') ); return args; } function startRtmpToHls(rtmpUrl, outputDir, options = {}) { const ffmpegArgs = createHlsArgs({ rtmpUrl, outputDir, options }); const ffmpeg = spawn('ffmpeg', ffmpegArgs); ffmpeg.stdout.on('data', data => { console.log([FFmpeg stdout]: ${data}); }); ffmpeg.stderr.on('data', data => { console.log([FFmpeg stderr]: ${data}); }); ffmpeg.on('close', code => { console.log(FFmpeg exited with code ${code}); }); return ffmpeg; } module.exports = { startRtmpToHls }; Usage example: const { startRtmpToHls } = require('./converter'); const ffmpegProcess = startRtmpToHls( 'rtmp://example.com/live/stream', './hls_output', { transcode: true, videoBitrate: '2500k', audioBitrate: '128k', segmentDuration: 4, playlistSize: 5, gopSeconds: 4, } ); // Later, to stop: // ffmpegProcess.kill('SIGTERM'); This structure keeps arguments configurable and gives you a handle on the FFmpeg process for management and error handling. Serving HLS Over HTTP(S) FFmpeg writes .m3u8 and segment files to disk, but players must access them through HTTP/HTTPS. A simple Node/Express static server is often enough for development: const express = require('express'); const path = require('path'); const app = express(); const HLS_DIR = path.join(__dirname, 'hls_output'); app.use('/hls', express.static(HLS_DIR, { setHeaders: (res, filePath) => { if (filePath.endsWith('.m3u8')) { res.setHeader('Cache-Control', 'no-cache'); } else if (filePath.endsWith('.ts')) { res.setHeader('Cache-Control', 'public, max-age=5'); } } })); app.listen(8000, () => { console.log('HLS server running at http://localhost:8000/hls/index.m3u8'); }); In production, you might instead: Serve the HLS directory with Nginx/Apache. Place a CDN in front of the origin. Configure HTTPS, CORS, and appropriate cache headers. Basic Browser Playback Example Delivering HLS streams to browsers requires compatibility handling, as not all browsers support HLS. This example demonstrates how to ensure smooth playback across environments by using conditional logic to select native playback or JavaScript-based players. This provides an uninterrupted viewing experience regardless of browser limitations.
HLS Test
This shows the end-to-end path: RTMP in, HLS out, and playback in a browser. Multi-Bitrate (Adaptive) HLS To support adaptive bitrate, generate multiple renditions and a master playlist. One simple approach is to run FFmpeg multiple times with different resolutions/bitrates: ffmpeg -i rtmp://example.com/live/stream -c:v libx264 -preset veryfast -crf 23 -b:v 3000k -s 1280x720 -g 120 -sc_threshold 0 -c:a aac -b:a 128k -f hls -hls_time 4 -hls_list_size 5 -hls_flags delete_segments+append_list ./hls_output/720p.m3u8 ffmpeg -i rtmp://example.com/live/stream -c:v libx264 -preset veryfast -crf 23 -b:v 1500k -s 854x480 -g 120 -sc_threshold 0 -c:a aac -b:a 96k -f hls -hls_time 4 -hls_list_size 5 -hls_flags delete_segments+append_list ./hls_output/480p.m3u8 Then create a master playlist (master.m3u8) in the same directory: #EXTM3U #EXT-X-VERSION:3 #EXT-X-STREAM-INF:BANDWIDTH=3500000,RESOLUTION=1280x720 720p.m3u8 #EXT-X-STREAM-INF:BANDWIDTH=1800000,RESOLUTION=854x480 480p.m3u8 Players load master.m3u8 and switch between renditions based on network conditions. For a larger scale, you can also use a single FFmpeg process with var_stream_map to generate all renditions and the master playlist in one go. Latency and GOP/Segment Tuning HLS generally has higher latency than RTMP because players buffer several segments. To reduce latency: Use shorter segments, such as 2–4 seconds via -hls_time. Keep playlist size small (for example, 3–5 segments). Ensure GOP size roughly matches segment length (e.g., 60 frames at 30 fps for 2 s). These changes lower latency but increase server and network overhead. Test various configurations with real viewers and connection types. Resource Management and Hardware Acceleration Transcoding multiple live streams is CPU-intensive. To optimize, you must use copy mode when codecs are already compatible: -c:v copy -c:a copy , enable hardware encoders when available (e.g.: -c:v h264_nvenc on compatible NVIDIA GPUs), and adjust -preset and -crf to match available CPU/GPU resources and quality targets. Monitor CPU, memory, and disk usage under real load, and limit the number of concurrent transcodes per machine accordingly. Error Handling and Resilience in Code Live inputs can drop due to network issues, encoder crashes, or configuration changes. Add resiliency around the FFmpeg process in your Node code. Example Restart Logic (Simplified) : function startManagedRtmpToHls(rtmpUrl, outputDir, options = {}) { let retries = 0; const maxRetries = options.maxRetries || 5; const retryDelayMs = options.retryDelayMs || 5000; let currentProc = null; function start() { console.log('Starting FFmpeg for', rtmpUrl); currentProc = startRtmpToHls(rtmpUrl, outputDir, options); currentProc.on('close', code => { console.log(`FFmpeg exited with code ${code}`); if (code !== 0 && retries < maxRetries) { retries += 1; console.log(`Retrying in ${retryDelayMs} ms (attempt ${retries}/${maxRetries})`); setTimeout(start, retryDelayMs); } else { console.log('No more retries, stream stopped.'); } }); } start(); return { stop: () => { if (currentProc) { currentProc.kill('SIGTERM'); } } }; } This pattern makes your converter more robust against transient failures. Security and Access Control Protecting your video streams involves controlling who can access content and preventing unauthorized distribution. Effective security measures encompass using HTTPS for transport, implementing token-based authentication, or signed URLs. It restricts origin access via CDNs and disables directory listing. To secure your HLS delivery, you must follow the steps below: Step 1 : Serve HLS over HTTPS to avoid mixed-content issues and to protect against tampering. Step 2 : Use signed URLs or tokens to control who can access playlists and segments. Step 3 : Hide your origin behind a CDN where possible, with origin access restricted to the CDN. Step 4 : Disable directory listing on your HLS paths and avoid exposing internal file structure. These steps help prevent unauthorized viewing or scraping of your streams. Testing and Iterative Improvement Continuous testing under real-world conditions is paramount to ensure stream stability, quality, and user satisfaction. Iterative improvements based on playback feedback, error monitoring, latency measurements, and adaptive bitrate adjustments help maintain a streaming pipeline that meets diverse device and network requirements while optimizing resource usage. Step 1 : Push a test RTMP stream from OBS or another encoder. Step 2 : Confirm FFmpeg generates HLS files and that your HTTP server exposes them correctly. Step 3 : Play the HLS URL in VLC, Safari (native HLS), and a browser using the hls.js example above. Step 4 : Adjust segment duration, playlist size, GOP size, and bitrate ladder based on observed latency, stability, and quality. Step 5 : Add logging and metrics (e.g., per-stream status, error counts, restart counts) to monitor the system over time. With these code examples and practices, the article now gives both conceptual understanding and concrete implementation guidance, making it a strong, production-ready introduction to building a custom RTMP-to-HLS converter.