Les chaînes FAST génèrent des revenus grâce aux coupures publicitaires. L'ensemble du modèle économique repose sur une seule hypothèse : lorsque la chaîne dit « passer à la publicité », chaque système aval à l'écoute — le serveur publicitaire, le splicer SSAI, le lecteur de smart TV — l'entend exactement au même moment, avec la même durée exacte. SCTE-35 est le protocole standard qui véhicule ce message. Une erreur silencieuse — et le silence est le mode de défaillance par défaut — entraîne soit l'absence d'affichage des publicités, soit leur déclenchement à la mauvaise image, soit une durée incorrecte. Le spectateur voit un écran noir. Les revenus ne sont tout simplement pas au rendez-vous.
Ceci est le récit technique de la façon dont les chaînes FAST de mStudio émettent des signaux SCTE-35 conformes aux normes à partir d'entrées conviviales pour l'opérateur, et pourquoi nous l'avons construit comme trois signaux par coupure au lieu d'un seul.
Aperçu rapide
| Aspect | Détail |
|---|---|
| Domaine | Signalisation de coupure publicitaire SCTE-35 pour les chaînes FAST AWS MediaLive |
| Flux de travail de l'opérateur | Éditeur de coupures publicitaires par programme — position en secondes, durée en secondes |
| Signaux émis par coupure | Trois — début de publicité TimeSignal, SpliceInsert, fin de publicité TimeSignal |
| Contraintes de durée | 10 à 120 secondes, validées au niveau de la couche de données |
| Consommateurs en aval | Les lecteurs, les serveurs publicitaires et AWS MediaTailor (conçu mais pas encore intégré) en sont des exemples. |
| Statut | Émission de signaux SCTE-35 en production ; intégration SSAI en cours de conception |
Le Problème Métier
Chaque coupure publicitaire sur une chaîne FAST est un événement générateur de revenus. La chaîne émet un marqueur qui dit « une publicité sera insérée ici, elle durera 30 secondes, veuillez vous préparer. » Les systèmes aval — Google Ad Manager, AWS MediaTailor, les serveurs publicitaires régionaux, les SDK de lecteur de smart TV — lisent ce marqueur et décident quoi insérer.
Lorsque le marqueur est correct, la coupure publicitaire se déroule sans accroc, l'impression est comptabilisée et l'opérateur est payé. Lorsque le marqueur est erroné — durée incorrecte, forme incorrecte, format incorrect — le système publicitaire le rejette (aucune publicité diffusée) ou l'accepte incorrectement (publicité de mauvaise durée, ou qui déborde sur le programme suivant). Les deux résultats coûtent de l'argent réel, et les deux échouent silencieusement. La chaîne continue de diffuser. Le spectateur voit un écran noir ou une coupe maladroite. Le pipeline de monétisation produit simplement des chiffres plus bas et aucun message d'erreur à suivre.
L'objectif d'ingénierie n'est pas de « prendre en charge les publicités ». C'est : chaque coupure publicitaire définie par l'opérateur doit parvenir à chaque consommateur aval comme un signal SCTE-35 conforme aux normes, à l'image exacte choisie par l'opérateur, à chaque fois.
Ce qu'est réellement SCTE-35 (la version en 90 secondes)
SCTE-35 est une norme pour l'insertion de messages de signalisation dans un flux vidéo. Les signaux ne contiennent pas de contenu publicitaire ; ils contiennent des signaux — « la coupure publicitaire commence ici », « la coupure publicitaire se termine ici », « ce segment dure N secondes. » Le système aval lit ces signaux et agit en conséquence : une couche SSAI insère un véritable élément publicitaire dans le manifeste HLS, un lecteur de smart TV déclenche une superposition, un serveur publicitaire enregistre une opportunité d'emplacement.
Deux formes de signaux sont importantes pour notre cas d'utilisation :
- SpliceInsert — le signal SCTE-35 original. Contient un SpliceEventId, une durée en secondes et un drapeau « hors réseau ». La plupart des serveurs publicitaires classiques lisent ce signal.
- TimeSignal — le signal plus récent et plus expressif. Contient un SegmentationDescriptor avec un SegmentationTypeId (52 = Début de publicité du fournisseur, 53 = Fin de publicité du fournisseur) et une SegmentationDuration en unités de 90 000 ticks. Les systèmes SSAI et les lecteurs modernes préfèrent cette forme car elle s'associe proprement entre le début et la fin.
Les deux formes sont des SCTE-35 correctes. Différents consommateurs en aval préfèrent différentes formes. N'émettre qu'une seule forme laisse de l'argent sur la table.
Pourquoi c'est important. Une implémentation naïve émet un seul SpliceInsert par coupure publicitaire et considère que c'est fait. La coupure publicitaire fonctionne sur les lecteurs hérités et échoue silencieusement sur SSAI. La moitié de votre inventaire publicitaire est monétisée ; l'autre moitié ne l'est pas. Vous ne saurez pas quelle est quelle avant que vos chiffres de revenus ne soient bas.
Pourquoi les Approches Naïves Échouent
Les raccourcis sont tentants car ils fonctionnent tous presque.
- « Au moment de l'encodage, insérer des publicités dans la vidéo originale. » Il n'y a pas de flexibilité. L'opérateur ne peut pas tester le placement en A/B, ne peut pas modifier les publicités après le déploiement, ne peut pas diffuser des publicités régionales ou ciblées par audience. La raison même de l'existence des chaînes FAST est de monétiser le même contenu de multiples façons — l'incrustation des publicités au moment de l'encodage ferme cette possibilité.
- « Utiliser l'insertion de publicité côté client. » Le blocage de publicités est simple. Chemin de code différent par appareil. Pas de personnalisation côté serveur. Et cela contourne entièrement SSAI, ce qui signifie des CPM plus bas.
- « N'émettre qu'un seul SpliceInsert par coupure. » L'erreur de production la plus courante. Fonctionne sur certains lecteurs, ignoré silencieusement par les systèmes SSAI qui souhaitent des signaux TimeSignal pour apparier début/fin. Monétisation partielle, sans erreur à investiguer.
- « Tout exprimer en ticks de 90 kHz parce que la spécification les mentionne. » La spécification est plus contraignante que cela. SpliceInsert.Duration est en secondes. SegmentationDuration est en ticks de 90 kHz. Les mélanger produit des signaux qui passent la validation du schéma et sont expédiés, puis échouent au niveau du lecteur en tant que durées mal formées. L'encodeur ne le détectera pas. Le lecteur saute simplement la coupure.
- « Permettre à MediaLive de confirmer. » Il ne le fera pas. MediaLive accepte les coupures qui se chevauchent, les coupures de durée nulle et les durées de segmentation impossibles sans se plaindre. La confirmation doit avoir lieu avant que MediaLive ne voie l'action.
Le levier dont nous disposions était la limite entre l'interface utilisateur de l'opérateur — où les coupures publicitaires ne sont que des paires {position, duration} — et le calendrier MediaLive, où chaque signal doit être parfait.
Notre Solution
Traiter la coupure publicitaire comme un objet de données de première classe de bout en bout. L'opérateur le définit dans les termes les plus simples possibles. Le backend le valide une fois au niveau de la couche de schéma. La Lambda le traduit en une pile de trois signaux au moment du déploiement, chaque signal étant exprimé dans la base de temps exigée par sa propre spécification. Rien d'autre dans la pile n'a besoin de connaître SCTE-35 — la traduction réside dans une seule fonction, dans la même Lambda qui gère toutes les autres actions de planification.
Diagramme 1 · Pipeline de Marqueurs Publicitaires de Bout en Bout

Architecture
- Éditeur de coupures publicitaires frontend — les opérateurs ajoutent des coupures publicitaires par programme en spécifiant un décalage de position (secondes depuis le début du programme) et une durée (secondes). Les emplacements de bumpers font partie du modèle de données et sont prêts pour la couche de diffusion.
- Collection AdMarker (MongoDB) — un document par programme avec des coupures publicitaires, faisant référence à la vidéo. L'array adBreaks[] stocke {position, duration} avec Mongoose appliquant 10 ≤ duration ≤ 120 au moment de la sauvegarde. Les Bumpers portent une référence adBreakId pour l'appariement en aval.
- Backend NestJS (schedule.service.ts) — au moment du déploiement, joint chaque calendrier à son AdMarker et renomme les champs selon le contrat de la Lambda (position → offsetSeconds, duration → durationSeconds). Une récupération en masse, pas de N+1.
- Orchestrateur Lambda (fastChannel-lambda-fun/index.js) — gère la traduction SCTE-35. Pour chaque coupure publicitaire, émet la pile de trois signaux dans la même BatchUpdateScheduleCommand qui contient les commutations d'entrée et les filigranes. Les noms d'action sont versionnés par programme afin que le balayage des orphelins puisse les apparier après un déploiement partiel.
- AWS MediaLive — reçoit les actions, émet les marqueurs #EXT-SCTE35 dans le manifeste HLS à la seconde choisie par l'opérateur.
Décisions d'Ingénierie Clés
1. Trois signaux par coupure publicitaire, pas un seul
Chaque coupure publicitaire émet trois actions distinctes pour le même moment logique dans le programme.
Diagramme 2 · Cycle de Vie d'une Coupure Publicitaire Unique

Les signaux TimeSignal de début et de fin partagent un SegmentationUpid afin que les systèmes aval puissent les apparier de manière déterministe. Le SpliceInsert contient un SpliceEventId unique pour la déduplication au niveau du lecteur. Différents consommateurs lisent différents signaux. L'émission des trois couvre tous les contrats qui nous intéressent aujourd'hui et tous les contrats plausibles de demain.
Erreur de production courante. Émettre un seul SpliceInsert et supposer que le reste de l'écosystème s'en sortira. Les systèmes SSAI suppriment discrètement les coupures qui ne contiennent pas de signaux TimeSignal correspondants ; les serveurs publicitaires classiques ignorent les signaux uniquement TimeSignal. Sans les trois, chaque coupure publicitaire est monétisée par une partie de votre pile en aval et manquée par le reste — et vous le découvrez à partir du rapport de revenus, pas des journaux.
2. Deux bases de temps, réconciliées dans une seule fonction
SpliceInsert.Duration est en secondes. SegmentationDuration à l'intérieur d'un descripteur TimeSignal est en unités de 90 000 ticks. Même durée logique, deux encodages — et l'erreur SCTE-35 la plus courante est de les mélanger.
Diagramme 3 · Logique de Conversion du Temps

La conversion réside dans buildProgramActions et uniquement là . Il y a un seul endroit canonique où chercher lorsqu'une durée de signal est incorrecte, et un seul endroit à modifier si la spécification évolue.
Compromis que nous avons délibérément fait. Nous aurions pu stocker les deux bases de temps sur le document AdMarker et laisser la Lambda les copier. Nous ne l'avons délibérément pas fait : ne stocker que la valeur en secondes signifie qu'il n'y a qu'un seul nombre à définir pour un opérateur, un seul nombre à valider, et un seul nombre qui peut être erroné. La valeur en 90 kHz est dérivée, jamais stockée. Les données dérivées ne peuvent pas dériver.
3. La position est relative, le temps de déclenchement est absolu
L'opérateur dit « coupure publicitaire à 720 secondes du programme. » La Lambda calcule l'heure de déclenchement UTC réelle comme programStartTime + offsetSeconds × 1000 et l'applique à l'action. Toute autre action de planification — commutation d'entrée, activation de filigrane, fin de programme — utilise le même pipeline de décalage vers horodatage. Les signaux publicitaires atterrissent sur les mêmes limites d'image que tout le reste, sans ambiguïté quant à l'horloge qui gère la chronologie.
4. Validation au niveau de la couche de données, pas sur le flux de données
Le schéma AdMarker impose duration ∈ [10, 120] secondes au moment de la sauvegarde de Mongoose. Une coupure hors limites n'atteint jamais la Lambda. Les coupures publicitaires qui s'étendraient au-delà de la fin du programme sont tronquées au moment de la construction plutôt que rejetées — les opérateurs ne perdent pas leur travail à cause d'une seule mauvaise coupure. Les catégories de bogues qui ne peuvent pas se produire sont plus intéressantes que celles qui le peuvent.
5. Émission de signaux découplée de SSAI
Ce que nous livrons aujourd'hui est la couche de signalisation. L'insertion de publicités côté serveur via AWS MediaTailor — qui consommerait ces signaux pour insérer de véritables éléments publicitaires dans le manifeste HLS — est entièrement conçue mais pas encore intégrée. L'ordre délibéré est le suivant : obtenir d'abord la couche de signalisation correcte, en production, utilisée par de vraies chaînes, avant d'activer SSAI. Lorsque l'intégration devient opérationnelle, les signaux sont déjà présents. Le système en aval reçoit un contrat clair à consommer dès le premier jour.
Pourquoi cet ordre est important. Le débogage SSAI est brutal lorsque vos signaux sont incorrects, car chaque échec ressemble à un échec SSAI même lorsque les signaux sont en cause. En livrant d'abord la couche de signalisation de manière isolée et en la validant avec de vrais lecteurs et serveurs publicitaires, nous avons éliminé une catégorie entière de confusion d'intégration avant qu'elle ne puisse se produire.
Résultats
- Chaque coupure publicitaire définie par un opérateur émet une pile SCTE-35 de trois signaux conforme aux normes dans le manifeste HLS de la chaîne — à la bonne seconde, par rapport à la bonne base de temps.
- La couche de traduction réside dans une seule fonction. Les modifications de spécifications ou les nouveaux consommateurs en aval sont un changement de fichier unique, pas une recherche à travers la base de code.
- La validation de la durée s'exécute au niveau de la couche de données, avant qu'aucun signal n'atteigne l'encodeur. Les erreurs de l'opérateur apparaissent dans l'interface utilisateur, pas silencieusement à l'antenne — les signaux mal formés ne peuvent pas atteindre MediaLive.
- La génération de signaux est déterministe : une entrée opérateur identique produit des actions SCTE-35 identiques, de sorte que la même coupure se comporte de la même manière à chaque déploiement et sur chaque chaîne.
- La couche de signalisation est livrée et en direct. Le consommateur SSAI (MediaTailor) est conçu et prêt à être intégré — et lorsqu'il le sera, les signaux de chaque chaîne existante l'attendront déjà .
Pile Technologique : AWS MediaLive · AWS Lambda · NestJS · MongoDB · TypeScript · Node 18 · SCTE-35 (Scte35TimeSignalSettings, Scte35SpliceInsertSettings) · AWS SDK v3

