FAST-kanaler tjener penge på reklamepauser. Hele forretningsmodellen hviler på én enkelt antagelse: når kanalen siger "skift til reklame," hører alle lyttende efterfølgende systemer – ad serveren, SSAI-spliceren, smart-TV-afspilleren – det på præcis samme tidspunkt og med præcis samme varighed. SCTE-35 er standardprotokollen, der bærer den besked. Går det galt i det skjulte – og stilhed er standardfejltilstanden – vil annoncer enten ikke blive vist, udløses ved den forkerte ramme eller have den forkerte længde. Seeren ser et blankt skærmbillede. Indtægten udebliver simpelthen.
Dette er ingeniørhistorien om, hvordan mStudios FAST-kanaler udsender SCTE-35-signaler, der er i overensstemmelse med standarder, fra operatørvenlige input, og hvorfor vi byggede det som tre signaler pr. pause i stedet for ét.
Kort overblik
| Aspekt | Detalje |
|---|---|
| Domæne | SCTE-35 reklamepausesignalering for AWS MediaLive FAST-kanaler |
| Operatør workflow | Redigeringsværktøj for reklamepauser pr. program — position i sekunder, varighed i sekunder |
| Signaler udsendt pr. pause | Tre — reklame-start TimeSignal, SpliceInsert, reklame-slut TimeSignal |
| Varighedsbegrænsninger | 10–120 sekunder, valideret på datalaget |
| Efterfølgende forbrugere | Afspillere, ad servers og AWS MediaTailor (designet men endnu ikke implementeret) er eksempler. |
| Status | SCTE-35 signaludsendelse i produktion; SSAI-integration under design |
Forretningsproblemet
Hver reklamepause på en FAST-kanal er en indtægtsgivende begivenhed. Kanalen udsender en markør, der siger "en reklame vil blive indsat her, den vil vare 30 sekunder, vær venlig at forberede." Efterfølgende systemer – Google Ad Manager, AWS MediaTailor, regionale ad servers, smart-TV player SDK'er – læser den markør og beslutter, hvad de skal indsætte.
Når markøren er korrekt, kører reklamepausen rent, impression tælles, og operatøren får betaling. Når markøren er forkert – forkert varighed, forkert form, forkert format – afviser annoncesystemet det enten (ingen reklame vist) eller accepterer det forkert (reklame af forkert længde, eller en der løber over i det næste program). Begge udfald koster rigtige penge, og begge fejler i stilhed. Kanalen fortsætter med at streame. Seeren ser et blankt skærmbillede eller en akavet klipning. Moneteringspipelinen producerer blot lavere tal og ingen fejlmeddelelse at spore.
Målet med ingeniørarbejdet er ikke "at understøtte reklamer." Det er: hver reklamepause, som operatøren definerer, skal ankomme til hver downstream-forbruger som et standardkonformt SCTE-35-signal, ved den nøjagtige ramme, operatøren valgte, hver gang.
Hvad SCTE-35 faktisk er (90-sekunders versionen)
SCTE-35 er en standard for indsættelse af signalbeskeder i en videostream. Signalerne bærer ikke reklameindhold; de bærer signaler — "reklamepause starter her," "reklamepause slutter her," "dette segment er N sekunder langt." Det efterfølgende system læser disse signaler og handler ud fra dem: et SSAI-lag splejser et rigtigt reklamekreativ ind i HLS-manifestet, en smart-TV-afspiller udløser et overlay, en ad server logger en slot-mulighed.
To signalformer er relevante for vores brugsscenarie:
- SpliceInsert — det originale SCTE-35-signal. Bærer et SpliceEventId, en varighed i sekunder og et "out of network"-flag. De fleste klassiske ad servers læser dette.
- TimeSignal — det nyere, mere udtryksfulde signal. Bærer en SegmentationDescriptor med et SegmentationTypeId (52 = Provider Ad Start, 53 = Provider Ad End) og en SegmentationDuration i 90.000-tick enheder. SSAI-systemer og moderne afspillere foretrækker denne form, da den parrer rent på tværs af start og slut.
Begge former er korrekt SCTE-35. Forskellige efterfølgende forbrugere foretrækker forskellige. Kun at udsende én form efterlader penge på bordet.
Hvorfor dette er vigtigt. En naiv implementering udsender ét SpliceInsert pr. reklamepause og anser det for færdigt. Reklamepausen fungerer på ældre afspillere og fejler i stilhed på SSAI. Halvdelen af din reklamebeholdning monetariseres; halvdelen gør det ikke. Du vil ikke vide, hvad der er hvad, før dine indtægtstal er lave.
Hvorfor naive tilgange fejler
Genvejene er fristende, fordi de alle næsten fungerer.
- "Indsæt reklamer i den originale video under kodning." Der er ingen plads til fleksibilitet. Operatøren kan ikke A/B-teste placering, kan ikke ændre reklamer efter udrulning, kan ikke køre regionale eller målgruppe-målrettede reklamer. Hele grunden til, at FAST-kanaler eksisterer, er at monetarisere det samme indhold på flere måder – at brænde reklamer ind under kodning udelukker dette.
- "Benyt client-side annonceindsættelse." Annonceblokering er enkelt. Forskellig kodesti pr. enhed. Ingen server-side personalisering. Og det omgår SSAI fuldstændigt, hvilket betyder lavere CPM'er.
- "Udsend blot ét SpliceInsert pr. pause." Den mest almindelige produktionsfejl. Fungerer på nogle afspillere, ignoreres i stilhed af SSAI-systemer, der ønsker TimeSignal-signaler til at parre start/slut. Delvis monetarisering, uden fejl at undersøge.
- "Udtryk alt i 90 kHz ticks, fordi specifikationen nævner dem." Specifikationen er mere irriterende end det. SpliceInsert.Duration er i sekunder. SegmentationDuration er i 90 kHz ticks. At blande dem sammen producerer signaler, der består skemavalidering og sendes ud, men derefter fejler ved afspilleren som fejlformede varigheder. Encoderen fanger det ikke. Afspilleren springer blot pausen over.
- "Tillad MediaLive at bekræfte." Det vil den ikke. MediaLive accepterer overlappende pauser, pauser med nul-varighed og umulige segmenteringsvarigheder uden klage. Bekræftelsen skal ske, før MediaLive ser handlingen.
Det greb, vi havde, var grænsefladen mellem operatør-UI'en – hvor reklamepauser blot er {position, duration}-par – og MediaLive-planen, hvor hvert signal skal være perfekt.
Vores løsning
Behandl reklamepausen som et førsteklasses dataobjekt ende-til-ende. Operatøren definerer det på de enklest mulige vilkår. Backend validerer det én gang på skema-laget. Lambda'en oversætter det til en tre-signal-stak ved udrulningstidspunktet, med hvert signal udtrykt i den tidsbase, dens egen specifikation kræver. Intet andet i stakken behøver at kende til SCTE-35 – oversættelsen lever i én funktion, i den samme Lambda, der ejer alle andre planlægningshandlinger.
Diagram 1 · Ende-til-ende Annoncemarkør-pipeline

Arkitektur
- Frontend reklamepause-redigeringsværktøj — operatører tilføjer reklamepauser pr. program ved at angive en positionsforskydning (sekunder fra programstart) og en varighed (sekunder). Bumper-slots er en del af datamodellen og klar til playout-laget.
- AdMarker samling (MongoDB) — ét dokument pr. program med reklamepauser, der refererer til videoen. adBreaks[]-arrayet gemmer {position, duration} med Mongoose, der håndhæver 10 ≤ duration ≤ 120 ved lagring. Bumpere bærer en adBreakId-reference til efterfølgende parring.
- NestJS backend (schedule.service.ts) — ved udrulningstidspunktet kobler hver tidsplan til sin AdMarker og omdøber felter til Lambda'ens kontrakt (position → offsetSeconds, duration → durationSeconds). Én bulk-hentning, ingen N+1.
- Lambda orkestrator (fastChannel-lambda-fun/index.js) — ejer SCTE-35 oversættelsen. For hver reklamepause udsender den tre-signal-stakken ind i den samme BatchUpdateScheduleCommand, der bærer input-switches og vandmærker. Handlingsnavne er versioneret pr. program, så "orphan sweep" kan parre dem efter en delvis udrulning.
- AWS MediaLive — modtager handlingerne, udsender #EXT-SCTE35-markører i HLS-manifestet ved operatørens valgte sekund.
Vigtige ingeniørbeslutninger
1. Tre signaler pr. reklamepause, ikke ét
Hver reklamepause udsender tre diskrete handlinger mod det samme logiske øjeblik i programmet.
Diagram 2 · Livscyklus for en enkelt reklamepause

Start- og slut-TimeSignal-signaler deler et SegmentationUpid, så efterfølgende systemer kan parre dem deterministisk. SpliceInsert bærer et unikt SpliceEventId til deduplikering på afspillerniveau. Forskellige forbrugere læser forskellige signaler. Udsendelse af alle tre dækker enhver kontrakt, vi bekymrer os om i dag, og enhver plausibel en i morgen.
Almindelig produktionsfejl. Udsend ét SpliceInsert og antag, at resten af økosystemet finder ud af det. SSAI-systemer dropper stille og roligt pauser, der mangler matchende TimeSignal-signaler; klassiske ad servers ignorerer TimeSignal-kun signaler. Uden alle tre monetariseres hver reklamepause af nogle dele af din downstream-stak og misses af resten — og du finder ud af det fra indtægtsrapporten, ikke logfilerne.
2. To tidsbaser, afstemt i én funktion
SpliceInsert.Duration er i sekunder. SegmentationDuration inde i en TimeSignal-deskriptor er i 90.000-tick-enheder. Samme logiske varighed, to kodninger — og den mest almindelige SCTE-35-fejl derude er at blande dem sammen.
Diagram 3 · Tidsomregningslogik

Konverteringen lever i buildProgramActions og kun der. Der er ét kanonisk sted at kigge, når en signalvarighed nogensinde er forkert, og ét sted at ændre, hvis specifikationen nogensinde udvikler sig.
Kompromis vi bevidst har truffet. Vi kunne have gemt begge tidsbaser på AdMarker-dokumentet og ladet Lambda'en kopiere dem igennem. Vi gjorde det bevidst ikke: at gemme kun sekundværdien betyder, at der kun er ét tal for en operatør at indstille, kun ét tal at validere, og kun ét tal, der kan være forkert. 90 kHz-værdien er udledt, aldrig gemt. Udledte data kan ikke drive.
3. Position er relativ, udløsningstid er absolut
Operatøren siger "reklamepause 720 sekunder inde i programmet." Lambda'en beregner den faktiske UTC-udløsningstid som programStartTime + offsetSeconds × 1000 og stempler det på handlingen. Hver anden planlagte handling – input-switch, vandmærke til, programslut – bruger den samme offset-til-tidsstempel-pipeline. Reklamesignaler lander på de samme rammegrænser som alt andet, uden tvetydighed om, hvilket ur der ejer tidslinjen.
4. Validering på datalaget, ikke på wiren
AdMarker-skemaet håndhæver duration ∈ [10, 120] sekunder ved Mongoose-lagring. En pause uden for intervallet når aldrig Lambda'en. Reklamepauser, der ville strække sig ud over programslut, klippes ved build-tidspunktet i stedet for at blive afvist – operatører mister ikke deres arbejde på grund af en enkelt dårlig pause. De typer af fejl, der ikke kan opstå, er mere interessante end dem, der kan.
5. Signaludsendelse afkoblet fra SSAI
Hvad vi leverer i dag er signaleringslaget. Server-side annonceindsættelse via AWS MediaTailor – som ville forbruge disse signaler til at splejse rigtige reklamekreativer ind i HLS-manifestet – er fuldt designet og endnu ikke implementeret. Den bevidste rækkefølge: få signallaget korrekt først, i produktion, brugt af rigtige kanaler, før SSAI aktiveres. Når integrationen går live, er signaler allerede der. Det efterfølgende system får en ren kontrakt at forbruge fra dag ét.
Hvorfor denne rækkefølge er vigtig. SSAI-debugging er brutal, når dine signaler er forkerte, fordi hver fejl ligner en SSAI-fejl, selv når signalerne er skyldige. Ved at levere signallaget isoleret først og validere det mod rigtige afspillere og ad servers, fjernede vi en hel kategori af integrationsforvirring, før den kunne opstå.
Resultater
- Hver reklamepause, en operatør definerer, udsender en standardkonform tre-signal SCTE-35-stak i kanalens HLS-manifest – på det rigtige sekund, mod den rigtige tidsbase.
- Oversættelseslaget lever i én funktion. Specifikationsændringer eller nye efterfølgende forbrugere er en enkeltfilsændring, ikke en jagt gennem kodebasen.
- Varighedsvalidering kører på datalaget, før noget signal når encoderen. Operatørfejl dukker op i UI'en, ikke i stilhed ved sendetidspunktet — fejlformede signaler kan ikke nå MediaLive.
- Signalgenerering er deterministisk: identisk operatørinput producerer identiske SCTE-35-handlinger, så den samme pause opfører sig på samme måde på tværs af hver udrulning og hver kanal.
- Signaleringslaget er leveret og live. SSAI-forbrugeren (MediaTailor) er designet og klar til at blive implementeret – og når det sker, vil alle eksisterende kanalers signaler allerede være der og vente på det.
Teknologistak: AWS MediaLive · AWS Lambda · NestJS · MongoDB · TypeScript · Node 18 · SCTE-35 (Scte35TimeSignalSettings, Scte35SpliceInsertSettings) · AWS SDK v3

