PAD Forge

L’encodage vidéo, distribué sur vos machines Distributed video transcoding, on your own machines

PAD Forge est un système d’encodage vidéo distribué : un orchestrateur central distribue des travaux d’encodage à une flotte de machines de post-production, en fonction de ce que chaque machine sait réellement faire. PAD Forge is a distributed video transcoding system: a central orchestrator dispatches encode work to a fleet of post-production machines, according to what each machine can actually do.

Un orchestrateur, une flotte de postes, et un routage qui sait ce que chaque machine peut réellement faire. One orchestrator, a fleet of workstations, and routing that knows what each machine can actually do.

Vue d’ensembleOverview

Ce que Forge fait What Forge does

Dans un studio, l’encodage occupe une machine à la fois pendant que les autres dorment. Forge s’installe sur celles que vous avez déjà et répartit le travail entre elles. In a facility, encoding ties up one machine at a time while the rest sit idle. Forge installs on the ones you already have and spreads the work between them.

  • N’importe quelle machine devient un agentAny machine becomes an agent

    Un Mac de montage, une tour inutilisée, un poste libre le soir : on y installe un agent, il se signale au serveur, il reçoit du travail. Rien à reconfigurer quand la flotte change.An edit Mac, an idle tower, a workstation free for the evening: install an agent on it, it announces itself to the server, it gets work. Nothing to reconfigure when the fleet changes.

  • Un export, plusieurs machinesOne export, several machines

    Forge découpe le travail en tâches et les distribue. Deux clips à encoder partent sur deux machines en même temps, puis Forge assemble le résultat.Forge breaks the work into tasks and hands them out. Two clips to encode go to two machines at once, then Forge assembles the result.

  • Chaque machine ne reçoit que ce qu’elle sait faireEach machine only gets what it can handle

    Au démarrage, l’agent sonde sa propre machine et déclare les codecs et les formats qu’elle peut réellement traiter. Une machine sans décodeur RED n’en recevra jamais.On starting up, the agent probes its own machine and declares the codecs and formats it can actually handle. A machine with no RED decoder never receives any.

  • Les vignettes arrivent avant la finThumbnails arrive before the end

    Les images fixes sont prises pendant l’encodage, pas après. Sur un rush RED de 41 minutes, la fiche a ses vignettes dans la première minute.Stills are taken while the encode runs, not after it. On a 41-minute RED rush, the item page has its thumbnails within the first minute.

  • Une machine qui s’éteint ne fait rien perdreA machine that shuts down loses nothing

    Un poste éteint ou redémarré en plein travail rend sa tâche à la file, et elle repart sur une autre machine. Aucun fichier tronqué n’est laissé à la place du livrable.A workstation switched off or restarted mid-job returns its task to the queue, and it goes back out to another machine. No truncated file is left where the deliverable should be.

  • Les médias ne passent pas par le serveurMedia does not pass through the server

    Les agents lisent et écrivent directement sur le NAS. Le serveur distribue le travail et suit son avancement ; il ne fait transiter aucun média.Agents read and write the NAS directly. The server hands out the work and tracks its progress; no media passes through it.

La suite entre dans le détail : comment le travail est routé, ce que l’API attend, et ce qui ne marche pas encore. What follows goes into the detail: how work is routed, what the API expects, and what does not work yet.

RoutageRouting

Une ferme d’encodage n’est pas une file d’attente An encode farm is not a queue

Une déclaration de capacité est un résultat de sonde, jamais une promesse. A capability declaration is a probe result, never a promise.

Un agent ne promet rien. En se connectant, il sonde son propre hôte et déclare ce qu’il a observé : les codecs que son ffmpeg sait produire, les formats qu’il sait ouvrir, les opérations qu’il sait exécuter, les stockages qu’il atteint réellement. An agent promises nothing. On connecting it probes its own host and declares what it observed: the codecs its ffmpeg can produce, the formats it can open, the operations it can perform, the storages it actually reaches.

Les jokers (*) sont refusés à la connexion. Un axe manquant est refusé lui aussi. Wildcards (*) are refused at connection. A missing axis is refused too.

Vous n’écrivez jamais « envoie ça sur le Mac de montage ». Vous décrivez le travail, et il part là où il peut être fait. You never write “send that to the edit Mac”. You describe the work, and it goes where it can be done.

Un stockage qui n’est pas monté fait disparaître son identifiant à la connexion, et l’agent n’est plus servi dessus. L’erreur apparaît au démarrage, pas au milieu d’un encodage. A storage that is not mounted makes its identifier disappear at connection, and the agent is never served on it again. The error surfaces at startup, not halfway through an encode.

OpérationsOperations

Cinq opérations Five operations

transcode Encoder un clip : codec, résolution, débit, cadence, désentrelacement. Encode a clip: codec, resolution, bitrate, frame rate, deinterlacing.
concat Mettre des clips bout à bout, piste par piste. Join clips end to end, track by track.
mux Appliquer des pistes audio de niveau reel sur l’assemblage. Apply reel-level audio tracks onto the assembly.
extract Extraire des flux d’un fichier source. Extract streams from a source file.
stills Prendre des images fixes sur la source, pendant l’encodage. Take stills from the source while the encode runs.

Les vignettes sont produites pendant l’encodage, pas après. Sur un RED de 41 minutes, la fiche reçoit ses images dans la première minute. Stills are produced while the encode runs, not after it. On a 41-minute RED clip, the item page has its thumbnails within the first minute.

Un job porte de 1 à 10 jeux d’images : placement par every_seconds, count ou times explicites ; cadrage contain, letterbox, crop ou stretch ; sortie jpeg ou png, en fichiers séparés ou en planche contact. A job carries 1 to 10 still sets: placement by every_seconds, count or explicit times; framing contain, letterbox, crop or stretch; jpeg or png output, as separate files or as one contact-sheet grid.

Modèle de soumissionSubmission model

Vous dites quel flux alimente quelle piste You say which stream feeds which track

Vous déclarez chaque fichier une fois, puis vous dites quel flux de quel fichier alimente quelle piste de sortie. Comme les -map de ffmpeg. You declare each file once, then say which stream of which file feeds which output track. Like ffmpeg’s -map.

Il n’y a aucune règle implicite : le premier fichier n’est pas « la vidéo ». Rien n’est deviné. There is no implicit rule such as “the first file is the video”. Nothing is guessed.

Une requête, quatre tâches, trois steps Au step 10, deux tâches transcode tournent en parallèle sur deux machines, une par clip. Au step 20, un seul concat les met bout à bout. Au step 30, un mux applique le mix de niveau reel. La sortie est out/day1.mp4, avec une piste vidéo et trois pistes audio. step 10 step 20 step 30 transcode (clip 1) transcode (clip 2) concat l’assemblage the assembly mux le mix de niveau reel the reel-level mix deux machines, en parallèle two machines, in parallel out/day1.mp4 1 piste vidéo · 3 pistes audio 1 video track · 3 audio tracks Une requête, quatre tâches, trois steps Au step 10, deux tâches transcode tournent en parallèle sur deux machines, une par clip. Au step 20, un seul concat les met bout à bout. Au step 30, un mux applique le mix de niveau reel. La sortie est out/day1.mp4, avec une piste vidéo et trois pistes audio. step 10 deux machines, en parallèle two machines, in parallel transcode (clip 1) transcode (clip 2) step 20 l’assemblage the assembly concat step 30 le mix de niveau reel the reel-level mix mux out/day1.mp4 1 piste vidéo · 3 pistes audio 1 video track · 3 audio tracks
Deux clips, chacun avec ses deux pistes son, mis bout à bout, plus une piste de mix sur l’ensemble. Quatre tâches, une seule requête. Two clips, each with its own two sound tracks, joined end to end, plus a mix track over the whole. Four tasks, one request.

Un job est une chaîne d’opérations ordonnée par un entier — le step — et non un graphe. Les steps 10 / 20 / 30 sont le cas courant. La requête complète est plus bas. A job is a chain of operations ordered by an integer — the step — not a graph. Steps 10 / 20 / 30 are the common case. The full request is further down.

ArchitectureArchitecture

Le média ne passe jamais par le serveur Media never passes through the server

Le média ne passe jamais par le serveur. L’orchestrateur tient l’état et distribue des références ; les agents lisent et écrivent le stockage directement. Media never passes through the server. The orchestrator holds state and dispatches references; agents read and write shared storage directly.

Architecture : le plan de contrôle et le plan de données Le navigateur et les agents ouvrent chacun une connexion vers l’orchestrateur, qui tient l’état, la file et le routage et ne touche jamais un média. Les agents ffmpeg lisent et écrivent le stockage partagé directement. Il n’y a aucune flèche de l’orchestrateur vers le stockage. L’agent Adobe Media Encoder est dessiné en trait discontinu : il est à venir, il ne se connecte pas aujourd’hui. navigateur (dashboard) browser (dashboard) agent ffmpeg (Mac) agent ffmpeg (Mac) agent AME (à venir) AME agent (coming) Orchestrateur Orchestrator plan de contrôle control plane état, file, routage state, queue, routing API REST + WebSocket ne touche jamais un média never touches media il n’y a pas de flèche ici there is no arrow here le serveur ne touche pas le média the server does not touch media NAS / stockage partagé NAS / shared storage les agents lisent et écrivent directement agents read and write directly Architecture : le plan de contrôle et le plan de données Le navigateur et les agents ouvrent chacun une connexion vers l’orchestrateur, qui tient l’état, la file et le routage et ne touche jamais un média. Les agents ffmpeg lisent et écrivent le stockage partagé directement, par un trajet qui longe l’orchestrateur sans y entrer. Il n’y a aucune flèche de l’orchestrateur vers le stockage. L’agent Adobe Media Encoder est dessiné en trait discontinu : il est à venir, il ne se connecte pas aujourd’hui. navigateur (dashboard) browser (dashboard) agent ffmpeg (Mac) agent ffmpeg (Mac) agent AME (à venir) AME agent (coming) Orchestrateur Orchestrator plan de contrôle control plane état, file, routage state, queue, routing API REST + WebSocket ne touche jamais un média never touches media il n’y a pas de flèche ici there is no arrow here le serveur ne touche pas le média the server does not touch media NAS / stockage partagé NAS / shared storage lecture et écriture directes read and written directly
Les dépendances pointent vers l’intérieur. Aucun agent ne sait qu’un autre agent existe, et l’orchestrateur ne contacte jamais un agent autrement que par la connexion que l’agent a ouverte. La flèche du serveur vers le stockage n’existe pas : son absence est le sujet. Dependencies point inward only. No agent knows another agent exists, and the orchestrator never contacts an agent except over the connection the agent opened. The server-to-storage arrow does not exist: its absence is the point.
  1. Un média est adressé par identifiant de stockage et chemin relatif, jamais par un chemin absolu. C’est ce qui permet à des agents macOS, Linux et Windows de partager une même file. Media is addressed by storage identifier plus relative path, never an absolute path. That is what lets macOS, Linux and Windows agents share one queue.
  2. Une déclaration de capacité est un résultat de sonde, jamais une promesse. A capability declaration is a probe result, never a promise.
  3. Une tâche par agent à la fois. One task per agent at a time.
  4. La politique de sélection tient derrière une seule couture : pour un transcode, la déclaration la plus étroite gagne — le spécialiste ; pour toute autre opération, la plus large gagne, sinon une simple copie de flux monopoliserait la seule machine capable de décoder du RED. Selection policy sits behind a single seam: for a transcode the narrowest declaration wins — the specialist; for any other operation the broadest wins, otherwise a plain stream copy would monopolise the only machine that can decode RED.

Une ligne pour laquelle aucun agent capable n’est connecté est garéeWAITING, parked_reason: no_capable_agent — et re-proposée dès qu’un agent capable se connecte. Une tâche garée n’est pas une tâche en échec. A line with no capable agent connected is parkedWAITING, parked_reason: no_capable_agent — and re-offered as soon as a capable agent connects. A parked task is not a failed task.

Le décodage RED, et pourquoi il vit dans son propre processus RED decoding, and why it lives in its own process

ffmpeg annonce un démuxeur r3d mais ne porte aucun décodeur REDCODE. Le SDK de RED est une bibliothèque statique propriétaire : la lier dans un ffmpeg GPL/LGPL produirait un binaire non redistribuable. Forge met donc le décodage dans son propre processus, r3ddump, et garde un ffmpeg d’origine. La frontière de licence est la raison d’être de ce composant. ffmpeg lists an r3d demuxer but carries no REDCODE decoder. RED’s SDK is a proprietary static library: linking it into a GPL/LGPL ffmpeg would produce a non-redistributable binary. Forge therefore puts decoding in its own process, r3ddump, and keeps a stock ffmpeg. The licence boundary is the reason this component exists.

Le SDK RED n’est pas dans le dépôt et n’y sera jamais : la licence de RED interdit d’en redistribuer les en-têtes et les bibliothèques. Un intégrateur doit en obtenir une copie auprès de RED et pointer la compilation dessus. The RED SDK is not in the repository and never will be: RED’s licence forbids redistributing its headers and libraries. An integrator must obtain a copy from RED and point the build at it.

MesuresNumbers

Mesuré, pas estimé Measured, not estimated

Les chiffres viennent de travaux réels, datés, sur du matériel nommé. Sur un même clip, le décodage RED passe de sept minutes à quarante-deux secondes dès que Metal prend la main. These figures come from real jobs, dated, on named hardware. On the same clip, RED decoding goes from seven minutes to forty-two seconds once Metal takes over.

Les deux lignes RED sont le même travail de bout en bout, sur le même clip. Les mesures viennent des agents de la flotte, des Macs sous macOS ; le décodage Metal suppose une puce Apple Silicon. The two RED rows are the same end-to-end job on the same clip. Measurements come from the fleet’s agents, Macs running macOS; Metal decoding assumes Apple Silicon.
Ce qui est mesuréWhat ChiffreFigure DateWhen
Décodage RED, multi-processus (r3ddump --workers)RED decode, multi-process (r3ddump --workers) ~7 min 2026-09-15
Décodage RED, Metal (Apple Silicon)RED decode, Metal (Apple Silicon) ~42 s 2026-09-15
H.264, accélération matérielle sur 4:2:0 (rush DJI réel)H.264, hardware acceleration on 4:2:0 (real DJI rush) 254 s → 100 s 2026-09-17
Planche contact, décodage par densitéContact sheet, density-based decoding 32,8 s → 3,8 s32.8 s → 3.8 s 2026-09-19
Images fixes sur de la 4KStills on 4K 50 images en 5–7 s50 stills in 5–7 s 2026-09-19

Aucun chiffre ne sort d’ici sans sa date et ses conditions. C’est la seule liste publiée : le projet mesure au lieu de supposer, et cinq lignes suffisent à le montrer. No figure leaves this table without its date and its conditions. It is the only published list: the project measures instead of assuming, and five rows are enough to show it.

Tolérance aux pannesFault tolerance

Une machine qui tombe ne coûte rien A machine that dies costs nothing

Le travail est tenu sous bail, pas assigné. Une machine qui s’éteint rend sa tâche à la file toute seule — et aucun fichier incomplet n’apparaît jamais au chemin final. Work is held under a lease, not assigned. A machine that goes down returns its task to the queue on its own — and no partial file ever appears at the final output path.

  1. Le bail expire. Une tâche n’est jamais la propriété d’une machine : elle lui est prêtée pour un temps. The lease expires. A task is never a machine’s property: it is lent to it for a while.
  2. La tâche retourne à la file d’elle-même, sans intervention et sans qu’un opérateur ait à constater la panne. The task returns to the queue on its own, with no intervention and with no operator having to notice the failure.
  3. Rien n’est écrit au chemin final avant d’être complet : un encodage interrompu ne laisse pas un prores tronqué là où un monteur attend un livrable. Nothing is written to the final path before it is complete: an interrupted encode does not leave a truncated prores where an editor expects a deliverable.

IntégrationIntegrating

Une requête HTTP One HTTP request

Soumettre un travail, c’est un POST et un corps JSON. Vous déclarez les fichiers, vous dites quel flux alimente quelle piste, et la file s’occupe du reste. Handing work to Forge is one POST and a JSON body. You declare the files, you say which stream feeds which track, and the queue takes it from there.

Le cas simple — un proxy h264 The simple case — an h264 proxy
curl -X POST http://<hôte>:8000/api/tasks/ \
  -H "Authorization: Bearer $PAD_FORGE_CLIENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "inputs": {"interview": {"storage": "rushes", "path": "footage/interview.mov"}},
    "video": [{"v": "interview:v:0", "a": ["interview:a:0"]}],
    "output_settings": {
      "storage": "proxies",
      "path": "interview_1080p.mp4",
      "codec": "h264",
      "container": "mp4",
      "resolution": "1920x1080",
      "bitrate": 5000
    }
  }'
Le cas qui montre le modèle — un bout-à-bout monté The case that shows the model — a cut reel
{
  "inputs": {
    "v1":  {"storage": "rushes", "path": "day1/clip1.mov"},
    "a11": {"storage": "rushes", "path": "day1/clip1_ch1.wav"},
    "a12": {"storage": "rushes", "path": "day1/clip1_ch2.wav"},
    "v2":  {"storage": "rushes", "path": "day1/clip2.mov"},
    "a21": {"storage": "rushes", "path": "day1/clip2_ch1.wav"},
    "a22": {"storage": "rushes", "path": "day1/clip2_ch2.wav"},
    "mix": {"storage": "rushes", "path": "sound/mix.wav"}
  },
  "video": [
    {"v": "v1:v:0", "a": ["a11:a:0", "a12:a:0"]},
    {"v": "v2:v:0", "a": ["a21:a:0", "a22:a:0"]}
  ],
  "audio": [["mix:a:0"]],
  "output_settings": {
    "storage": "proxies", "path": "out/day1.mp4",
    "codec": "h264", "container": "mp4", "resolution": "1920x1080"
  }
}

Les hôtes s’écrivent <hôte> ou localhost : aucune adresse réelle n’apparaît ici. Hosts are written <hôte> or localhost: no real address appears here.

  • L’authentification est un jeton partagé porté par Authorization: Bearer — un jeton d’agent pour la connexion WebSocket, un jeton de client pour les écritures via l’API. Une connexion WebSocket sans jeton est refusée avant qu’un état d’agent n’existe. Authentication is a shared token carried as Authorization: Bearer — an agent token for the WebSocket connection, a client token for writes through the API. A WebSocket connection with no token is refused before any agent state exists.
  • client_ref est la référence du système appelant, stockée telle quelle sur chaque ligne du job. C’est la clé de relecture et d’idempotence côté intégrateur. client_ref is the calling system’s own reference, stored verbatim on every row of the job. It is the read-back and idempotency key on the integrator’s side.
  • Ni bitrate ni crf n’a de valeur par défaut : sans l’un des deux, c’est le défaut de l’encodeur qui s’applique. C’est une décision, pas un oubli. Neither bitrate nor crf has a default: without one of the two, the encoder’s own default runs. That is a decision, not an oversight.
  • Le contrat complet est docs/client-protocol.md, en français, environ 1 700 lignes, vérifié contre le code — et son client de référence est exécuté à chaque make test. The full contract is docs/client-protocol.md, in French, around 1,700 lines, verified against the code — and its reference client is executed on every make test.
  • Forge est intégré à l’écosystème de PAD par un plugin côté MAM : Cantemo, édité par Codemill. C’est un fait côté PAD, pas une fonctionnalité supportée de Forge. Forge is integrated into PAD’s ecosystem through a plugin on the MAM side: Cantemo, published by Codemill. That is a fact on PAD’s side, not a supported Forge feature.

DéploiementDeploying

Un git push pour le serveur, un launchd par machine One git push for the server, one launchd per machine

CoucheLayer TechnologieTechnology
Plan de contrôleControl plane Python 3.11, FastAPI, SQLAlchemy 2, Alembic, PostgreSQL, WebSocket
Dashboard React 18, TypeScript, MUI v7, Tailwind, Vite, react-query
Agent d’encodageEncode agent Python + ffmpeg, sonde de capacité au démarrage, reprise sur checkpointPython + ffmpeg, capability probe at startup, checkpoint resume
Décodage REDRED decoding r3ddump, un binaire C++ dédié lié au SDK R3D 9.x, décodage Metal sur Apple Siliconr3ddump, a dedicated C++ binary linked against the R3D 9.x SDK, Metal decoding on Apple Silicon
Agent AdobeAdobe agent Panneau CEP pour Adobe Media Encoder 2025, via un pont ExtendScript — à venir, voir Où en est ForgeCEP panel for Adobe Media Encoder 2025, over an ExtendScript bridge — coming, see Project status
Déploiement serveurServer deployment Dokku : une app, une image, un git push. L’API et le dashboard compilé sont servis depuis la même origine, donc il n’y a aucun CORS à configurerDokku: one app, one image, one git push. API and compiled dashboard are served from the same origin, so there is no CORS to configure
Déploiement agentAgent deployment macOS natif, service launchd à l’ouverture de session, sans conteneur, lisant le NAS directementNative macOS, launchd service at login, no container, reading the NAS directly
Tests Deux suites pytest (make test), plus vitest sur le frontendTwo pytest suites (make test), plus vitest on the frontend

Les agents de production ne tournent pas dans Docker. Docker Compose est le chemin de développement : il lève l’orchestrateur, le dashboard et trois agents d’un coup. En production, l’agent est un LaunchAgent natif, parce qu’un conteneur entre l’encodeur et le NAS coûte du débit. Production agents do not run in Docker. Docker Compose is the development path: it brings up the orchestrator, the dashboard and three agents at once. In production the agent is a native LaunchAgent, because a container between the encoder and the NAS costs throughput.

État du projetProject status

Où en est Forge Project status

En production Running in production

  • L’orchestrateur, déployé sur Dokku.The orchestrator, deployed on Dokku.
  • Les agents ffmpeg sur les Macs de production, en services launchd.ffmpeg agents on the production Macs, as launchd services.
  • Le dashboard, servi par l’orchestrateur.The dashboard, served by the orchestrator.
  • Le décodage RED avec accélération Metal.RED decoding with Metal acceleration.
  • Les images fixes, l’assemblage multi-pistes, l’encodage segmenté.Stills, multi-track assembly, segmented encoding.

Pas là aujourd’hui Not there today

  • L’agent Adobe Media Encoder ne se connecte pas. Son panneau CEP existe et fonctionne, mais sa trame de connexion est antérieure au contrôle de capacité : elle porte des jokers et il lui manque des axes, donc le serveur la refuse. Un travail est en cours pour la rendre conforme. Aujourd’hui, le prores et le RED sont routés vers les agents ffmpeg.The Adobe Media Encoder agent does not connect. Its CEP panel exists and works, but its connection frame predates capability checking: it carries wildcards and is missing axes, so the server refuses it. A conformance effort covers this. Today, prores and RED work is routed to ffmpeg agents.
  • Une seule route au dashboard, la vue Jobs. La page Agents n’existe pas encore ; le rail déplie le panneau latéral.One route in the dashboard, the Jobs view. The Agents page does not exist yet; the rail unfolds the side panel.
  • Desktop uniquement, à partir de 1280 px. Il n’y a pas de version mobile de l’outil.Desktop only, from 1280px up. There is no mobile version of the tool.
  • macOS est la seule plateforme d’agent validée. L’architecture protège la portabilité — chemins relatifs, catalogue de stockages — mais rien n’est validé sur Windows ni sur Linux.macOS is the only validated agent platform. The architecture protects portability — relative paths, storage catalogue — but nothing is validated on Windows or Linux.
  • Le frontend et le panneau AME n’ont pas encore de tests automatisés complets ; les deux paquets Python en ont.The frontend and the AME panel have no complete automated tests yet; both Python packages do.
La vue Jobs du dashboard PAD Forge : une liste de travaux terminés avec leur progression, leur agent, leur fichier d’entrée et leur sortie, et un panneau latéral listant deux agents ffmpeg en ligne avec les codecs qu’ils déclarent.
Capture de la vue Jobs en production, le 22 septembre 2026. Deux agents ffmpeg en ligne ; aucun agent AME, conformément à ce qui est dit ci-dessus. Recadrée, jamais retouchée. Capture of the Jobs view in production, 22 September 2026. Two ffmpeg agents online; no AME agent, exactly as stated above. Cropped, never retouched.
Emplacement réservé — la fenêtre de création de tâche Reserved slot — the task-creation window
Emplacement réservé — un job en cours, ses tâches réparties sur les agents Reserved slot — a job in progress, its tasks spread across agents

Ouvrir le code est envisagé. Rien n’est arrêté : ni les modalités, ni le calendrier. Opening the code up is being considered. Nothing is settled: neither the terms nor the timing.