Introduction: Video Streaming Architecture
Video streaming has evolved from Flash-based RTMP to HTTP-based adaptive streaming. Modern platforms use RTMP for ingest and HLS/DASH for delivery—combining low-latency capture with scalable HTTP delivery.
Series Context: This is Part 12 of 20 in the Complete Protocols Master series. Streaming protocols operate at the Application Layer, primarily over HTTP for delivery.
1
Part 1: OSI Model & Protocol Foundations
Network layers, encapsulation, TCP/IP model
2
Physical & Data Link Layers
Ethernet, Wi-Fi, VLANs, MAC addressing
3
Network Layer & IP
IPv4, IPv6, ICMP, routing protocols
4
Transport Layer
TCP, UDP, QUIC, ports, sockets
5
Session & Presentation Layers
TLS handshake, encryption, serialization
6
Web Protocols
HTTP/1.1, HTTP/2, HTTP/3, WebSockets
7
API Protocols
REST, GraphQL, gRPC, SOAP
8
DNS Deep Dive
DNS hierarchy, records, DNSSEC
9
Email Protocols
SMTP, IMAP, POP3, SPF/DKIM/DMARC
10
File Transfer Protocols
FTP, SFTP, SCP, rsync
11
Real-Time Protocols
WebRTC, SIP, RTP, VoIP
12
Streaming Protocols
HLS, DASH, RTMP, media delivery
You Are Here
13
IoT Protocols
MQTT, CoAP, Zigbee, LoRaWAN
14
VPN & Tunneling
IPsec, OpenVPN, WireGuard
15
Authentication Protocols
OAuth, SAML, OIDC, Kerberos
16
Network Management
SNMP, NetFlow, Syslog
17
Security Protocols
TLS/SSL, certificates, PKI
18
Cloud Provider Protocols
AWS, Azure, GCP APIs
19
Emerging Protocols
QUIC, HTTP/3, WebTransport
20
Web Security Standards
CORS, CSP, HSTS, SRI
Architecture
Modern Streaming Pipeline
Streaming Pipeline:
ENCODER → INGEST → TRANSCODER → PACKAGER → CDN → PLAYER
1. ENCODER (OBS, Hardware)
• Capture video/audio
• Encode to H.264/H.265
• Send via RTMP/SRT
2. INGEST SERVER
• Receive RTMP stream
• Validate stream key
• Forward to transcoder
3. TRANSCODER
• Create multiple bitrates
• 1080p, 720p, 480p, 360p
• Adaptive streaming variants
4. PACKAGER
• Segment into chunks
• Generate HLS/DASH manifest
• Add encryption (DRM)
5. CDN
• Cache at edge locations
• Deliver to viewers globally
• Handle millions of viewers
6. PLAYER
• Fetch manifest
• Select bitrate (ABR)
• Buffer and play
Comparison
Protocol Comparison
| Protocol | Use Case | Latency | Transport |
| RTMP | Ingest | 1-3s | TCP |
| HLS | Delivery | 10-30s | HTTP |
| LL-HLS | Low-latency delivery | 2-5s | HTTP |
| DASH | Delivery | 10-30s | HTTP |
| LL-DASH | Low-latency delivery | 2-5s | HTTP |
| WebRTC | Ultra-low latency | <1s | UDP/TCP |
| SRT | Reliable ingest | 1-2s | UDP |
RTMP: Real-Time Messaging Protocol
RTMP was created by Adobe for Flash. While Flash is dead, RTMP lives on as the de facto standard for stream ingest—OBS, encoders, and streaming platforms all speak RTMP.
RTMP Basics
RTMP Connection Flow
RTMP Connection:
1. TCP HANDSHAKE
Client → Server: C0 + C1 (version + random)
Server → Client: S0 + S1 + S2
Client → Server: C2
2. CONNECT
Client: connect('rtmp://server/app')
Server: result (success)
3. CREATE STREAM
Client: createStream()
Server: result (stream_id)
4. PUBLISH (for streaming)
Client: publish('stream_key', 'live')
Server: onStatus('NetStream.Publish.Start')
5. SEND DATA
Client: audio/video chunks
(Continues until disconnect)
RTMP URL Format:
rtmp://server.com/app/stream_key
• server.com - Server address
• app - Application name
• stream_key - Unique stream identifier
# RTMP streaming examples
# OBS Settings:
# Server: rtmp://live.twitch.tv/app
# Stream Key: live_xxxxx_yyyyy
# FFmpeg RTMP stream to server
ffmpeg -i input.mp4 \
-c:v libx264 -preset veryfast \
-maxrate 3000k -bufsize 6000k \
-c:a aac -b:a 128k \
-f flv rtmp://server.com/app/stream_key
# Receive RTMP and save to file
ffmpeg -i rtmp://server.com/app/stream_key \
-c copy output.mp4
# RTMP to HLS conversion (live)
ffmpeg -i rtmp://server.com/app/stream_key \
-c:v copy -c:a copy \
-f hls -hls_time 4 -hls_list_size 5 \
/var/www/stream/playlist.m3u8
SRT Alternative
SRT: Secure Reliable Transport
SRT vs RTMP:
SRT Advantages:
• UDP-based (lower latency)
• Built-in encryption (AES)
• Error correction (ARQ)
• Better over unreliable networks
• Open source (Haivision)
When to use SRT:
• Contribution links over internet
• Remote production
• When RTMP has packet loss issues
SRT Example:
ffmpeg -i input.mp4 \
-c:v libx264 -f mpegts \
'srt://server.com:9000?streamid=mystream'
# Receive SRT
ffmpeg -i 'srt://server.com:9000?mode=caller' \
-c copy output.ts
HLS: HTTP Live Streaming
HLS (Apple, 2009) is the most widely supported streaming format. It segments video into small chunks delivered over HTTP, enabling CDN caching and adaptive bitrate switching.
Why HTTP-based? HTTP works through firewalls, caches on CDNs, and scales massively. This is why HLS/DASH won over RTMP for delivery.
HLS Structure
HLS File Organization
HLS Directory Structure:
stream/
├── master.m3u8 # Master playlist (quality selector)
├── 1080p/
│ ├── playlist.m3u8 # Media playlist (segment list)
│ ├── segment000.ts # Video chunk (4-10 seconds)
│ ├── segment001.ts
│ └── segment002.ts
├── 720p/
│ ├── playlist.m3u8
│ └── *.ts
├── 480p/
│ ├── playlist.m3u8
│ └── *.ts
└── audio/
├── playlist.m3u8
└── *.aac
Player Flow:
1. Fetch master.m3u8
2. Select quality based on bandwidth
3. Fetch media playlist for that quality
4. Download and play segments sequentially
5. Switch quality if bandwidth changes
# Master playlist (master.m3u8)
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:BANDWIDTH=800000,RESOLUTION=640x360
360p/playlist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=1400000,RESOLUTION=842x480
480p/playlist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=2800000,RESOLUTION=1280x720
720p/playlist.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=5000000,RESOLUTION=1920x1080
1080p/playlist.m3u8
# Media playlist (720p/playlist.m3u8)
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:6
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:6.000,
segment000.ts
#EXTINF:6.000,
segment001.ts
#EXTINF:6.000,
segment002.ts
#EXTINF:4.500,
segment003.ts
# For VOD, add at end:
#EXT-X-ENDLIST
# For live, playlist updates as new segments added
# Create HLS from video file
ffmpeg -i input.mp4 \
-c:v libx264 -crf 21 -preset veryfast \
-c:a aac -b:a 128k \
-hls_time 6 \
-hls_list_size 0 \
-hls_segment_filename "segment%03d.ts" \
playlist.m3u8
# Multi-bitrate HLS (adaptive)
ffmpeg -i input.mp4 \
-filter_complex "[0:v]split=3[v1][v2][v3];\
[v1]scale=1280:720[v1out];\
[v2]scale=854:480[v2out];\
[v3]scale=640:360[v3out]" \
-map "[v1out]" -c:v:0 libx264 -b:v:0 2800k \
-map "[v2out]" -c:v:1 libx264 -b:v:1 1400k \
-map "[v3out]" -c:v:2 libx264 -b:v:2 800k \
-map 0:a -c:a aac -b:a 128k \
-f hls -hls_time 6 \
-master_pl_name master.m3u8 \
-var_stream_map "v:0,a:0 v:1,a:1 v:2,a:2" \
stream_%v/playlist.m3u8
DASH: Dynamic Adaptive Streaming over HTTP
MPEG-DASH is the international standard for adaptive streaming. Unlike HLS (Apple proprietary), DASH is codec-agnostic and widely adopted on non-Apple platforms.
HLS vs DASH
Protocol Comparison
| Feature | HLS | DASH |
| Creator | Apple | MPEG (ISO Standard) |
| Manifest | .m3u8 (text) | .mpd (XML) |
| Segments | .ts (MPEG-TS) | .m4s (fMP4) |
| iOS Support | ✅ Native | ❌ Requires JS |
| Android Support | ✅ ExoPlayer | ✅ Native |
| DRM | FairPlay | Widevine, PlayReady |
| Codec Flexibility | Limited | Any codec |
<!-- DASH MPD (Media Presentation Description) -->
<?xml version="1.0"?>
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011"
type="static"
mediaPresentationDuration="PT1H30M"
minBufferTime="PT2S">
<Period>
<AdaptationSet mimeType="video/mp4" segmentAlignment="true">
<!-- 1080p -->
<Representation id="1080p" bandwidth="5000000"
width="1920" height="1080">
<SegmentTemplate media="1080p_$Number$.m4s"
initialization="1080p_init.mp4"
duration="6000" timescale="1000"/>
</Representation>
<!-- 720p -->
<Representation id="720p" bandwidth="2800000"
width="1280" height="720">
<SegmentTemplate media="720p_$Number$.m4s"
initialization="720p_init.mp4"
duration="6000" timescale="1000"/>
</Representation>
</AdaptationSet>
<AdaptationSet mimeType="audio/mp4">
<Representation id="audio" bandwidth="128000">
<SegmentTemplate media="audio_$Number$.m4s"
initialization="audio_init.mp4"
duration="6000" timescale="1000"/>
</Representation>
</AdaptationSet>
</Period>
</MPD>
# Create DASH content with FFmpeg
# Single quality
ffmpeg -i input.mp4 \
-c:v libx264 -c:a aac \
-f dash \
-seg_duration 6 \
output.mpd
# Multi-bitrate DASH
ffmpeg -i input.mp4 \
-map 0:v -map 0:v -map 0:v -map 0:a \
-c:v libx264 -c:a aac \
-b:v:0 5000k -s:v:0 1920x1080 \
-b:v:1 2800k -s:v:1 1280x720 \
-b:v:2 1400k -s:v:2 854x480 \
-b:a 128k \
-f dash \
-adaptation_sets "id=0,streams=v id=1,streams=a" \
output.mpd
Adaptive Bitrate Streaming (ABR)
ABR algorithms automatically switch video quality based on network conditions. This ensures smooth playback—high quality when bandwidth allows, lower quality to prevent buffering.
ABR Logic
How ABR Works
ABR Decision Factors:
1. BANDWIDTH ESTIMATION
• Measure download speed of recent segments
• Weighted average (recent segments matter more)
2. BUFFER LEVEL
• How many seconds in buffer?
• Low buffer → safer (lower quality)
• High buffer → can try higher quality
3. QUALITY SWITCHING
• Switch up: Conservative (need consistent bandwidth)
• Switch down: Aggressive (prevent rebuffer)
ABR Strategies:
• Rate-based: Switch based on throughput
• Buffer-based: Switch based on buffer level
• Hybrid: Combine both signals
Example Logic:
if buffer < 5s:
select_lowest_quality()
elif throughput > 1.5 * current_bitrate:
try_higher_quality()
elif throughput < 0.8 * current_bitrate:
switch_lower_quality()
# Simple ABR algorithm simulation
def simple_abr_algorithm():
"""Demonstrate ABR quality selection"""
# Available quality levels
qualities = [
{"name": "360p", "bitrate": 800_000},
{"name": "480p", "bitrate": 1_400_000},
{"name": "720p", "bitrate": 2_800_000},
{"name": "1080p", "bitrate": 5_000_000},
]
def select_quality(throughput_bps, buffer_seconds, current_quality_idx):
"""Select quality based on throughput and buffer"""
# Safety margin (don't use 100% of bandwidth)
safe_throughput = throughput_bps * 0.8
# If buffer is critical, go to lowest
if buffer_seconds < 3:
print(f" ⚠️ Critical buffer ({buffer_seconds}s) - lowest quality")
return 0
# Find highest quality we can sustain
selected = 0
for i, q in enumerate(qualities):
if q["bitrate"] < safe_throughput:
selected = i
# Switching logic
if selected > current_quality_idx:
# Only switch up if buffer healthy
if buffer_seconds > 10:
print(f" ↑ Buffer healthy ({buffer_seconds}s) - upgrading")
return selected
else:
print(f" → Buffer moderate - staying at current")
return current_quality_idx
elif selected < current_quality_idx:
print(f" ↓ Bandwidth dropped - downgrading")
return selected
return current_quality_idx
print("ABR Algorithm Simulation")
print("=" * 50)
# Simulate scenarios
scenarios = [
(5_000_000, 15, 2), # Good bandwidth, healthy buffer
(1_000_000, 8, 2), # Bandwidth dropped
(3_000_000, 2, 1), # Critical buffer
(4_000_000, 20, 1), # Bandwidth recovered
]
for throughput, buffer, current in scenarios:
print(f"\nThroughput: {throughput/1_000_000:.1f} Mbps, "
f"Buffer: {buffer}s, Current: {qualities[current]['name']}")
new_idx = select_quality(throughput, buffer, current)
print(f" Selected: {qualities[new_idx]['name']}")
simple_abr_algorithm()
CDN Delivery
CDNs (Content Delivery Networks) cache streaming content at edge locations worldwide. This reduces latency, handles traffic spikes, and enables global reach.
CDN Architecture
Video CDN Flow
CDN Video Delivery:
ORIGIN → SHIELD → EDGE → VIEWER
1. ORIGIN SERVER
• Source of truth
• Generates HLS/DASH
• Only 1 location
2. SHIELD (Mid-tier)
• Reduces origin load
• First cache layer
• Few locations (1-3)
3. EDGE SERVERS
• Close to viewers
• Final cache layer
• 100+ locations globally
Cache Logic:
1. Viewer requests segment001.ts
2. Edge: Cache miss → ask Shield
3. Shield: Cache miss → ask Origin
4. Origin returns segment
5. Shield caches + returns
6. Edge caches + returns
7. Next viewer request → Edge hit!
CDN Providers for Video:
• CloudFront (AWS)
• Fastly
• Cloudflare Stream
• Akamai
• Azure CDN
# CloudFront + HLS example
# 1. Upload HLS to S3
aws s3 sync ./stream/ s3://my-video-bucket/stream/
# 2. Create CloudFront distribution
# Origin: my-video-bucket.s3.amazonaws.com
# Cache Policy: CachingOptimized
# 3. Access via CDN
# https://d1234567890.cloudfront.net/stream/master.m3u8
# Cache Headers for HLS
# Manifest: Cache-Control: max-age=2 (live) or max-age=31536000 (VOD)
# Segments: Cache-Control: max-age=31536000 (immutable)
# Nginx config for HLS
location /hls/ {
types {
application/vnd.apple.mpegurl m3u8;
video/mp2t ts;
}
root /var/www/stream;
add_header Cache-Control "max-age=31536000";
}
location ~ \.m3u8$ {
add_header Cache-Control "max-age=2"; # Short cache for live manifest
}
FFmpeg Streaming Pipeline
FFmpeg is the Swiss Army knife of video processing. Here are practical commands for building a complete streaming pipeline.
# Complete HLS streaming pipeline
# 1. RTMP Ingest to HLS (live streaming)
ffmpeg -listen 1 -i rtmp://0.0.0.0:1935/live/stream \
-c:v libx264 -preset veryfast -tune zerolatency \
-c:a aac -b:a 128k \
-f hls \
-hls_time 4 \
-hls_list_size 10 \
-hls_flags delete_segments \
/var/www/stream/live.m3u8
# 2. Multi-bitrate encoding (ABR ladder)
ffmpeg -i rtmp://localhost/live/stream \
-filter_complex "[0:v]split=3[v1][v2][v3];\
[v1]scale=1280:720[v720];\
[v2]scale=854:480[v480];\
[v3]scale=640:360[v360]" \
-map "[v720]" -c:v:0 libx264 -b:v:0 2800k -maxrate:v:0 3000k -bufsize:v:0 6000k \
-map "[v480]" -c:v:1 libx264 -b:v:1 1400k -maxrate:v:1 1500k -bufsize:v:1 3000k \
-map "[v360]" -c:v:2 libx264 -b:v:2 800k -maxrate:v:2 900k -bufsize:v:2 1800k \
-map 0:a -c:a aac -b:a 128k -ac 2 \
-f hls -hls_time 4 -hls_list_size 10 \
-master_pl_name master.m3u8 \
-var_stream_map "v:0,a:0,name:720p v:1,a:1,name:480p v:2,a:2,name:360p" \
stream_%v.m3u8
# Python streaming helper
import subprocess
import os
def create_hls_stream(input_file, output_dir, qualities=None):
"""Create multi-bitrate HLS from video file"""
if qualities is None:
qualities = [
{"name": "720p", "scale": "1280:720", "bitrate": "2800k"},
{"name": "480p", "scale": "854:480", "bitrate": "1400k"},
{"name": "360p", "scale": "640:360", "bitrate": "800k"},
]
os.makedirs(output_dir, exist_ok=True)
# Build filter complex
splits = len(qualities)
filter_parts = [f"[0:v]split={splits}" + "".join(f"[v{i}]" for i in range(splits))]
for i, q in enumerate(qualities):
filter_parts.append(f"[v{i}]scale={q['scale']}[v{q['name']}]")
filter_complex = ";".join(filter_parts)
# Build command
cmd = ["ffmpeg", "-i", input_file, "-filter_complex", filter_complex]
# Add outputs
var_stream_map = []
for i, q in enumerate(qualities):
cmd.extend([
"-map", f"[v{q['name']}]",
f"-c:v:{i}", "libx264",
f"-b:v:{i}", q["bitrate"],
])
var_stream_map.append(f"v:{i},a:0,name:{q['name']}")
cmd.extend([
"-map", "0:a", "-c:a", "aac", "-b:a", "128k",
"-f", "hls",
"-hls_time", "6",
"-master_pl_name", "master.m3u8",
"-var_stream_map", " ".join(var_stream_map),
f"{output_dir}/stream_%v.m3u8"
])
print("Generated FFmpeg command:")
print(" ".join(cmd))
return cmd
# Example usage
print("HLS Creation Example")
print("=" * 50)
create_hls_stream("input.mp4", "/var/www/hls")
Summary & Next Steps
Key Takeaways:
- RTMP: Standard for stream ingest (OBS → server)
- HLS: Apple's format, widest device support
- DASH: Open standard, codec-agnostic
- ABR: Automatically adapts quality to bandwidth
- CDN: Essential for global, scalable delivery
- FFmpeg: Swiss Army knife for encoding/packaging
Quiz
Test Your Knowledge
- Why RTMP for ingest but HLS for delivery? (RTMP is low-latency, HLS is cacheable/scalable)
- What's the HLS segment duration trade-off? (Longer = less requests, Shorter = lower latency)
- HLS vs DASH: which for iOS? (HLS - native support)
- What does ABR optimize? (Quality vs buffering balance)
- CDN shield tier purpose? (Reduce origin load)
Next in the Series
In Part 13: IoT Protocols, we'll explore MQTT, CoAP, and protocols designed for constrained devices and sensor networks.
Related Articles
Part 11: Real-Time Protocols
WebSockets, WebRTC for real-time communication.
Read Article
Part 13: IoT Protocols
MQTT, CoAP for IoT devices.
Read Article