A production-ready video streaming application with adaptive bitrate streaming using HLS (HTTP Live Streaming) protocol. Built with FFmpeg, Node.js, and HLS.js.
This application allows users to upload videos which are automatically transcoded into multiple quality levels (360p, 480p, 720p) and streamed using HLS protocol with adaptive bitrate switching.
- Multi-quality video transcoding (360p, 480p, 720p)
- Adaptive bitrate streaming with HLS
- Automatic thumbnail generation (FFmpeg-based)
- Manual quality selection
- Playback speed controls (0.5x - 2x)
- Picture-in-Picture mode
- Comprehensive keyboard shortcuts (YouTube-style)
- Real-time streaming metrics (bitrate, resolution, buffer, network speed)
- Rate limiting (5 uploads per 15 min, 100 API requests per min)
- Drag-and-drop file upload
- Responsive modern UI
- Local file serving (no CDN required)
- Node.js & Express.js
- FFmpeg (video transcoding & thumbnail extraction)
- fluent-ffmpeg (Node.js FFmpeg wrapper)
- Multer (file upload handling)
- express-rate-limit (API protection)
- Vanilla JavaScript
- HLS.js (adaptive streaming player)
- HTML5 Video API (PiP, playback speed)
- HTML5 & CSS3
Upload → FFmpeg Transcode → Multi-Quality (360p/480p/720p) → Local Storage → HLS.js Player
Pipeline Flow:
- User uploads video via web interface
- Multer saves video to temporary storage
- FFmpeg transcodes video into 3 quality levels
- HLS manifest (.m3u8) and segments (.ts) generated
- Files stored locally in
backend/uploads/hls/ - HLS.js player streams with adaptive bitrate switching
- Node.js (v18 or higher)
- npm (v9 or higher)
- FFmpeg installed on system
macOS:
brew install ffmpegUbuntu/Debian:
sudo apt update
sudo apt install ffmpegWindows: Download from ffmpeg.org
- Clone repository:
cd "Adaptive Video Streaming Pipeline"- Install backend dependencies:
cd backend
npm install- Create uploads directories:
mkdir -p uploads/temp uploads/hls- Start backend server:
npm startServer runs on http://localhost:3000
- Open frontend:
cd ../frontend
open index.htmlOr use Live Server extension in VS Code
- Open frontend (
http://localhost:5500with Live Server) - Drag and drop a video file or click to select
- Wait for FFmpeg transcoding (shows progress in backend console)
- Video automatically plays with adaptive streaming
- Automatic thumbnail generated at 2 seconds
- Use quality selector dropdown to manually choose quality
- Monitor real-time metrics (resolution, bitrate, buffer, network speed)
Speed Control:
- Click speed button (top-right) to cycle through speeds: 0.5x, 0.75x, 1x, 1.25x, 1.5x, 1.75x, 2x
- Or press
Skeyboard shortcut
Picture-in-Picture:
- Click PiP button or press
Pto float video in small window
Keyboard Shortcuts:
SpaceorK- Play/Pause←/→- Seek ±5 secondsJ/L- Seek ±10 seconds↑/↓- Volume ±10%M- Mute/UnmuteF- FullscreenP- Picture-in-PictureS- Playback speed0orHome- Jump to startEnd- Jump to end
.
├── README.md
├── DEPLOYMENT.md
├── RESUME.md
├── WORKFLOW.md
├── backend/
│ ├── server.js # Express server with rate limiting
│ ├── hls-converter.js # FFmpeg transcoding & thumbnail generation
│ ├── routes/
│ │ └── upload.js # Upload endpoint with rate limiting
│ ├── uploads/
│ │ ├── temp/ # Temporary upload storage
│ │ └── hls/ # HLS output files + thumbnails
│ └── package.json
└── frontend/
├── index.html # Main UI with player controls
├── styles.css # Styling with control buttons
├── app.js # Upload handling
├── player.js # HLS.js player logic
└── player-controls.js # Speed, PiP, keyboard shortcuts
Upload video for transcoding
Request:
- Method: POST
- Content-Type: multipart/form-data
- Body:
videofile (max 100MB)
Response:
{
"success": true,
"message": "Video uploaded and converted to HLS successfully",
"data": {
"fileName": "video.mp4",
"hlsUrl": "http://localhost:3000/videos/video/video_master.m3u8",
"thumbnailUrl": "http://localhost:3000/videos/video/thumbnail.jpg",
"manifestFile": "video_master.m3u8",
"size": 2440227,
"outputDir": "video"
}
}Rate Limits:
- 5 uploads per 15 minutes per IP
- Returns 429 status if exceeded
Serve HLS manifest and segment files
- Port:
3000(default) - CORS: Enabled for all origins
- Upload limit: 100MB
- Supported formats: MP4, MOV, AVI, WebM, MKV
- Video codec: H.264 (libx264)
- Audio codec: AAC
- Segment duration: 10 seconds
- Quality levels:
- 360p: 640x360 @ 800 kbps
- 480p: 854x480 @ 1400 kbps
- 720p: 1280x720 @ 2800 kbps
- Max buffer length: 30 seconds
- Back buffer: 90 seconds
- Auto quality switching enabled
- Manual quality selection available
FFmpeg not found:
which ffmpeg # Verify installation
ffmpeg -version # Check versionPort 3000 already in use:
lsof -ti:3000 | xargs kill -9 # Kill process on port 3000Video not playing:
- Check backend console for FFmpeg errors
- Verify HLS files created in
backend/uploads/hls/ - Check browser console for HLS.js errors
- Ensure video format is supported (MP4 recommended)
Slow transcoding:
- Transcoding time depends on video length and system CPU
- 10-second video: ~15-30 seconds
- 1-minute video: ~2-3 minutes
- Progress shown in backend console
Watch backend logs:
cd backend
npm startTest upload:
curl -X POST http://localhost:3000/api/upload \
-F "[email protected]"Verify HLS output:
ls -la backend/uploads/hls/video-name/
# Should see: *_master.m3u8, *_360p.m3u8, *_480p.m3u8, *_720p.m3u8, *.ts- Transcoding: CPU-intensive, runs sequentially (360p → 480p → 720p)
- Streaming: Efficient, HLS.js handles buffering and quality switching
- Storage: ~3x original file size (3 quality levels)
- Network: Adaptive bitrate adjusts to user bandwidth
- Chrome/Edge: Full support
- Firefox: Full support
- Safari: Native HLS support (HLS.js fallback)
- Mobile: iOS Safari, Chrome Android supported
MIT
Built as a portfolio project demonstrating full-stack video streaming implementation.