A simple distributed video transcoding service built with Node.js, Express, Redis, Multer, and FFmpeg.
The API accepts uploaded videos, pushes transcoding jobs into a Redis queue, and a worker consumes those jobs and generates multiple video resolutions such as 72p, 144p, and 240p using FFmpeg.
Client
│
│ POST /transcode
▼
Express API
│
│ Upload video
▼
Multer
│
│ Push file path
▼
Redis Queue
│
│ BRPOP
▼
Transcoding Worker
│
│ FFmpeg
├──────────► 72p.mp4
├──────────► 144p.mp4
└──────────► 240p.mp4
- Video upload API
- Redis-backed job queue
- Asynchronous video processing
- FFmpeg-based transcoding
- Multiple output resolutions
- Concurrent transcoding jobs
- Static serving of generated videos
- CORS support
- Separate API and worker processes
- Node.js
- TypeScript
- Express
- Redis
- Multer
- FFmpeg
- child_process
- HTTP/REST API
video-transcoder/
├── api/
│ └── index.ts
├── worker/
│ └── index.ts
├── uploads/
├── outputs/
├── package.json
└── README.md
Send a POST request to:
POST /transcode
with the video as multipart form data:
video: <video-file>
Example using cURL:
curl -X POST http://localhost:8000/transcode \
-F "video=@video.mp4"Successful response:
{
"response": "success",
"file": "generated-file-name"
}The API stores the uploaded video's path in the Redis list:
video-queue
await client.lPush("video-queue", req.file.path);This separates uploading from video processing, so the API doesn't have to wait for FFmpeg.
The worker continuously waits for jobs:
const job = await client.brPop("video-queue", 0);BRPOP blocks until a new video becomes available.
Each uploaded video is processed into multiple resolutions:
72p
144p
240p
The worker runs FFmpeg with:
ffmpeg -i input.mp4 \
-vf scale=-2:240 \
-c:v libx264 \
-preset fast \
-c:a aac \
output.mp4The -2 width keeps the video's aspect ratio while ensuring a compatible even width.
Install:
- Node.js
- Redis
- FFmpeg
Verify FFmpeg:
ffmpeg -versionVerify Redis:
redis-cli pingExpected:
PONG
npm installMake sure your Redis server is running.
For Docker:
docker run -d \
--name redis \
-p 6379:6379 \
redisnpm run devThe API runs on:
http://localhost:8000
In another terminal:
npm run workerThe worker will continuously listen to the Redis queue.
After processing, videos are generated under:
outputs/
For example:
outputs/
└── video-id/
├── 72p.mp4
├── 144p.mp4
└── 240p.mp4
Generated videos can be served through the API's /public route.
Uploads a video and adds it to the transcoding queue.
Request
Content-Type: multipart/form-data
Field:
video
Success
{
"response": "success",
"file": "filename"
}Failure
{
"response": "error"
}Redis acts as a lightweight message broker:
Producer Consumer
│ │
│ LPUSH │ BRPOP
▼ ▼
┌─────────────────────────────────┐
│ video-queue │
└─────────────────────────────────┘
This architecture makes it possible to run multiple workers independently of the API server.
For example:
┌── Worker 1
│
API ──► Redis├── Worker 2
│
└── Worker 3
More workers can be added when transcoding workload increases.
- Add job IDs and job status tracking
- Add progress tracking from FFmpeg
- Support more resolutions and codecs
- Add authentication
- Add persistent job metadata
- Use Docker for API, workers, Redis, and FFmpeg
- Add retry handling for failed jobs
- Add dead-letter queues
- Add S3/object-storage support
- Add automatic cleanup of uploaded files
- Add HLS/DASH streaming output
- Add horizontal worker scaling
- Add monitoring and metrics
Project activity:
This project is intended for learning and experimentation with video processing, FFmpeg, Redis queues, and distributed worker architectures.