Lately, I’ve been working on a project that involved video streaming. The app was getting a lot of traffic, and I needed to figure out how to stream videos efficiently without making things slow or clunky. That got me deep into how video streaming actually works—and honestly, I learned a lot and I thought it would be a good idea to share my findings via this blog post.
If you’ve ever asked yourself why platforms like YouTube or Netflix feel so smooth, but playing a regular MP4 from a server sometimes feels… laggy—this post is for you.
In video streaming, you've probably heard terms like HLS, DASH, and RTMP thrown around. These are protocols that power the smooth video experiences we take for granted. Each has its own strengths and use cases, but today we'll focus on the most important ones.
HLS and DASH are widely used protocols for streaming videos over the internet.
But before we dive into these protocols, let's first start from very basics.
So you upload a 1 GB MP4 video to your S3 bucket, and it plays just fine. Maybe it takes a couple of seconds to start, but once it does - it plays smoothly. So why are these fancy terms like HLS and DASH are thrown around all the time when it comes to video streaming?
Let's break it down - step by step, without the jargon.
🧐 Wait, what's wrong with a normal MP4 video?
When you use an ordinary .mp4 video file and embed it in a webpage with HTML like this:
<video src="https://your-bucket.s3.amazonaws.com/video.mp4" controls></video>
Here’s what actually happens:
- The browser sends a regular HTTP GET request to fetch the video file.
- The server starts sending the video from the beginning.
- Playback begins after a portion is downloaded (this is called progressive download).
- The
<video>
element starts playing, while buffering more in the background.
There are some issues like:
- It plays smoothly if the network is stable.
- On poor connection? It pauses or buffers frequently.
- Want to skip to the end? It might hang while it loads.
So yes - it works. But it doesn't adapt. And that's the problem.
Q) If I skip to the 50th minute of a 60-minute video, does the browser wait until 50 minutes worth of video has downloaded?
No, not necessarily.
In progressive download:
- When you skip to 50 minutes, the browser doesn't need to download everything before it.
- Instead, it sends a Range request for the part of the MP4 that contains the 50-minute mark.
Range: bytes=300000000-400000000
This tells the server: “Hey, send me only from byte X to byte Y.”
BUT this only works properly if the MP4 is “streamable.”
🔍 What makes an MP4 “streamable”?
- The moov atom (metadata) needs to be at the start of the file. Otherwise, the browser can't know where to seek, and will try to download the whole file first to find that info.
So if:
- Your MP4 has moov at the start → It can seek and play instantly from the 50-minute mark.
- Your MP4 has moov at the end → It may need to download the full file first to get the metadata.
🤔 What if the user’s network is slow?
Here’s the catch:
- A 1 GB video encoded in 1080p is always 1 GB, no matter what.
- If someone on a 3G connection tries to watch it, they still have to download the same big file.
- That user will likely give up and close the app.
Wouldn’t it be better if:
- The app sent a lighter version of the video for slower networks?
- And a higher-quality version when the internet is fast?
That’s exactly what HLS and DASH do.
Enter HLS and DASH
Both are protocols for adaptive streaming. That means they break your video into:
- Multiple resolutions/bitrates (e.g., 1080p, 720p, 480p)
- Tiny segments (e.g., 4-6 seconds each)
- A playlist/manifest file that tells the player where to find those segments
HLS (HTTP Live Streaming)
- Developed by Apple
- Uses
.m3u8
playlist files - Segments are usually
.ts
files (MPEG-TS format)
DASH (Dynamic Adaptive Streaming over HTTP)
- Developed by the MPEG group (open standard)
- Uses
.mpd
manifest files - Segments are usually
.mp4
(ISO BMFF format)
How Does HLS/DASH Work?
Let’s say you have a 10-minute video.
Instead of uploading it as a single .mp4, you do this:
Original Video
├── 1080p: chunk1.ts, chunk2.ts, ...
├── 720p: chunk1.ts, chunk2.ts, ...
├── 480p: chunk1.ts, chunk2.ts, ...
└── playlist.m3u8 / manifest.mpd
When a user starts watching:
- The player downloads the playlist.
- It detects network speed.
- It starts streaming chunks from the best quality stream.
- If the network slows down, it switches to lower resolution.
- If the network improves, it switches up again.
- All of this happens seamlessly — every few seconds.
Core Difference: Progressive vs Adaptive Streaming
Feature | Progressive Download (MP4) | Adaptive Streaming (HLS/DASH) |
---|---|---|
Type | Single file | Multiple files (segments) |
File Format | Single .mp4 file | Playlist (.m3u8 / .mpd) + segments |
Quality Switching | ❌ Fixed bitrate | ✅ Auto-switches quality dynamically |
Buffering Behavior | Linear (starts from beginning) | Chunk-based buffering (can jump ahead) |
Seeking | Byte-range seek (if moov at start) | Segment-based seek |
Server Load | Simple HTTP file serving | Requires playlist and segment logic |
TL;DR
Basic MP4 works great for simple use cases—it's quick to implement and straightforward. But it doesn't scale well across varying networks and devices, which is crucial in today's diverse digital landscape.
If you're building something user-facing, adaptive streaming protocols like HLS or DASH can dramatically improve your users' experience. They make videos start faster, play smoother, and adapt intelligently to network conditions.
For my recent project, I implemented this using Cloudflare Stream, which handles all the heavy lifting—encoding, storage, and global delivery. You simply upload your video, get back a videoId
along with HLS and DASH URLs, then embed them using Cloudflare's iframe or your own video player.
The future of video is adaptive, and your users will thank you for the smoother experience :)