How it works
What happens between dropping a file on the master and getting a finished encode back. Read this before you scale past one machine.
Everything below happens automatically. You do not schedule chunks or manage workers by hand. It is documented because when something goes wrong, the failure will name one of these stages, and because the boundaries described here are what determine whether output is deliverable.
Two programs#
TRNSCODE is a master and any number of workers.
The master owns everything that must be true exactly once: the job queue, the database, the web interface, the chunk plan, and the final assembly. There is one master. It is the machine you point your browser at.
A worker owns nothing. It connects to the master, says what it is capable of, receives chunks, runs FFmpeg, and reports back. Workers are disposable by design — you can add one mid-job, and you can lose one mid-job without losing the job.
The master can also be a worker. On a single machine you run both, and you still get parallel encoding across your cores. Adding a second machine later changes nothing about how you use it.
The pipeline#
A job moves through five stages. The queue shows you which one it is in.
The master runs ffprobe against your source and reads the container, duration, resolution, frame rate, and audio layout. This is what the review screen shows you before you commit, and it is what the chunk planner works from.
The source is divided into segments. The default target is 12 seconds per chunk, adjusted so boundaries land on keyframes rather than arbitrary timestamps.
The scheduler matches each chunk to a worker that can actually encode it — right codec, right accelerator, right FFmpeg build. Workers pull work as they free up, so a fast machine takes more chunks than a slow one without you configuring anything.
Each worker runs an ordinary FFmpeg command you can read and reproduce. Progress streams back to the master frame by frame, which is what drives the live chunk map.
When every chunk is done the master concatenates them, muxes the audio back in, and verifies the result before calling the job complete.
Why chunk boundaries matter#
This is the part that separates a distributed encoder from a broken one, and it is worth understanding even if you never change a setting.
You cannot cut a compressed video at an arbitrary frame. Inter-frame codecs describe most frames as differences from other frames, so a cut in the middle of a group of pictures leaves frames whose references are in a different chunk. Encode those independently and you get visible seams at every boundary.
TRNSCODE cuts on keyframes. A scene-detection pass finds real cut points in the picture, then adjacent segments are merged toward the target duration. The boundary lands where the encoder was going to place a keyframe anyway, so joining the chunks afterwards is a stream copy, not a re-encode.
Chunk size is a trade. Shorter chunks spread work more evenly across a fleet but force more keyframes, which costs bitrate at a given quality. Longer chunks are more efficient but leave fast workers idle at the end of a job waiting on one slow chunk. The 12-second default is a middle setting, not a physical constant.
Codec parity across the fleet#
Every chunk of a job must be encoded the same way, or the finished file is inconsistent in ways that are difficult to see and impossible to fix after the fact. Two different FFmpeg builds can produce measurably different output from identical arguments.
The scheduler therefore treats a worker's reported capability set as a matching constraint, not a hint. A worker that cannot produce the required codec does not receive those chunks. This is why every bundle ships its own tested FFmpeg in bin/ rather than using whatever happens to be installed on each machine.
If you deliberately point a worker at a host-installed FFmpeg with TRNSCODE_WORKER_FFMPEG_BINARY, you own the consequences of that build differing from the rest of the fleet.
How source and output move#
Workers need the source to encode from, and the master needs the results back. There are two ways this happens, and TRNSCODE picks per job without being told.
| Situation | What happens |
|---|---|
| Worker can already read the path — same machine, or a shared mount everyone sees at the same path | The worker opens the file directly. Nothing is copied across the network. |
| Worker cannot read the path — a laptop on Wi-Fi with no share | The master serves the bytes each chunk needs and accepts the encoded result back over HTTP, authorised by a signed, expiring token. |
The practical consequence: a shared filesystem is faster, because the source is never copied. It is not a requirement. A machine with nothing but a network route to the master can still contribute.
What happens when a worker dies#
Chunks are independent units of work, which makes failure cheap. If a worker stops responding mid-chunk, that chunk is reassigned to another worker. Chunks that already finished stay finished. The job loses the time spent on the one incomplete chunk, not the whole encode.
This is also why you can shut a worker down deliberately during a job — drain it, let in-flight chunks finish, and the fleet absorbs the loss.
The verification step#
Assembly is not the last stage. Once the chunks are joined and the audio is muxed back in, the master checks the result against what it expected: the duration must match the source within a one-frame tolerance, and if the source had audio, the output must have audio.
A job that fails verification is reported as failed rather than delivered. This matters more than it sounds — the failure mode this catches is the one where FFmpeg exits 0 and produces a file that is quietly wrong.