# DESIGN — Bus PROGRAMME (régie : commuter la source diffusée)

**Auteur** : Dr Hamid MADANI <drmdh@msn.com>
**Date** : 2026-06-07 · **Module** : `@mostajs/media-mcu` v0.3.0
**Liés** : `docs/DESIGN-ROLES-DIRECTION.md`, `docs/02-AUDIT-BUGS-MEDIA-MCU.md`, `README.md`, `llms.txt`

> Capacité régie (type **Studio LIVE TALK**) : l'écran diffusé montre **une** source à
> la fois ; le régisseur **commute** la source diffusée (TAKE) ; **seul le PROGRAM
> émet l'audio**.

## 1. Besoin
Une régie de course (RaceVision) veut basculer en direct entre des sources
(moto / voiture / ambulance / podium / plateau LIVE TALK). L'écran de diffusion
montre la source choisie **plein écran**, et **l'audio suit le programme** : quand
on met une source à l'antenne, c'est **son** audio qui passe, les autres se taisent.

## 2. Décision clé — l'audio est géré par le MCU (audio-follows-program)
**L'absence d'audio des sources non‑programme vient du MCU, pas d'un mute manuel.**
- En mode `program`, la sortie ffmpeg **ne mappe que l'audio de la source PROGRAM**
  (`-map 0:a:<audioIndex>`). L'audio des autres sources **n'est pas inclus** dans la
  sortie — **aucun `amix`**.
- Les autres sources **restent ingérées** (RTP en cours) → leur **vidéo** est dispo
  pour le multiview/preview ; seul leur **audio** est exclu du programme.
- Le basculement est **automatique** au `setProgram(inputId)` : le régisseur
  **choisit la source principale**, il ne coupe pas chaque source à la main.

> Alternative écartée (pour l'instant) : table de mixage multi‑audio indépendante
> (amix + gains par source pilotés par le régisseur). Le bus programme « audio solo »
> couvre le besoin régie standard ; le mixage fin pourra être une évolution.

## 3. Implémentation
- **`McuSession.setProgram(inputId | null)`** : fixe `programInputId`, passe le layout
  à `'program'`, reconstruit le pipeline ffmpeg si l'enregistrement tourne ; `null`
  ⇒ retour au layout précédent (`switchLayout` remet aussi `programInputId=null`).
- **Calcul des ordinaux** : les flux ffmpeg sont décrits par le SDP dans l'**ordre des
  producers**. On parcourt `this.producers` ; pour la source dont
  `producer.appData.inputId === programInputId`, on relève l'ordinal **vidéo** (parmi
  les vidéos) et **audio** (parmi les audios) → `program = { videoIndex, audioIndex }`.
- **`buildFilterComplex(layout, vCount, aCount, res, program?)`** (fonction PURE) :
  - vidéo : `[0:v:<videoIndex>]scale=W:H[vout]` (plein écran, ré‑encodage),
  - audio : `-map 0:a:<audioIndex>` (**solo**, pas de filtre, pas d'amix).
- **API** : `programSet` → `POST /mcu/sessions/{id}/program { inputId }` (rôle régie/write).

## 4. Flux régie (RaceVision)
```
sources (moto/voiture/ambulance/podium/studio) --WHIP ingest--> entrées MCU (mcuInputId)
régisseur clique une source  ->  setProgram(inputId)
   -> vidéo de la source plein écran + SON audio à l'antenne (les autres muettes)
sortie programme (1 vidéo + 1 audio)  ->  HLS « C » (grand public)
```
Le `/regie/program/take` de geo‑enroll appelle `programSet` sur le MCU
(`source.mcuInputId`) → la commutation console pilote le programme **live**.

## 5. Tests
`test-scripts/mcu-unit.test.mjs` (suite 14/14) — 3 cas program‑bus :
plein écran + **audio solo** (pas d'amix), défaut index 0, commutation vers index 2.
Le runtime mediasoup/ffmpeg (calcul d'ordinaux, restart pipeline) = vérifié par revue
(cohérent avec B6 `startRecording` reconstruit le graphe).

## 5bis. Deux bus audio — ON-AIR (MCU) vs RÉGIE (PFL)

Le régisseur **doit entendre une source AVANT de la passer à l'antenne**. D'où **deux
bus audio distincts** :

| Bus | Qui l'émet | Quelle source | Où |
|---|---|---|---|
| **ON-AIR / PROGRAM** | **le MCU** (serveur) | audio **solo** de la source diffusée (`setProgram`) | sortie programme → HLS « C » (public) |
| **RÉGIE / PFL** (Pre-Fade Listen) | **le navigateur du régisseur** | audio de **n'importe quelle** source en preview (1 à la fois) | casque du régisseur — **n'affecte PAS l'antenne** |

- Le **PFL est côté régie**, pas dans le MCU : dans le **multiview** (flux preview par
  source via `@mostajs/media-sfu`), chaque tuile a son `<audio>` **muté par défaut** ;
  le régisseur clique **🎧** sur une source pour l'**écouter** avant le **TAKE**. Une
  seule écoute à la fois. C'est purement local au navigateur régie.
- Le **multi-audio indépendant** (entendre une source hors antenne) est donc résolu
  **sans** toucher au bus programme : le MCU reste « audio solo on-air », la régie
  monitore librement en preview. *(Un mixage multi-source on-air resterait une
  évolution distincte — non requis ici.)*

> Implémentation console : bouton **🎧** par source (toggle `MONITOR`, (dé)mute
> l'`<audio data-src>` de la tuile). La tuile reçoit le flux preview quand le
> **multiview SFU** est branché (prochaine itération). Sur `web/regie/` (RaceVision).

## 6. Rôles / sécurité
`programSet` réservé au rôle **régie** (scope clé `write`/`program` — cf.
`DESIGN-ROLES-DIRECTION.md`). Les sources n'ont qu'un scope **ingest**.
