Kumikita ang mga FAST channel mula sa mga commercial break. Ang buong modelo ng negosyo ay nakasalalay sa isang pagpapalagay: kapag sinabi ng channel na "putol sa patalastas," bawat nakikinig na downstream system โ ang ad server, ang SSAI splicer, ang smart-TV player โ ay naririnig ito nang eksakto sa parehong sandali, na may eksaktong parehong tagal. Ang SCTE-35 ay ang karaniwang protocol na nagdadala ng mensaheng iyon. Kung ito ay mali nang tahimik โ at ang tahimik ay ang default na mode ng pagkabigo โ ang mga patalastas ay alinman sa hindi magre-render, magsisimula sa maling frame, o magtatagal nang mali ang haba. Ang manonood ay makakakita ng blangkong screen. Wala lang ang kita.
Ito ang kuwento ng inhinyeriya kung paano naglalabas ang mga FAST channel ng mStudio ng SCTE-35 cues na sumusunod sa pamantayan mula sa mga input na madaling gamitin ng operator, at kung bakit namin ito itinayo bilang tatlong cue bawat break sa halip na isa.
Mabilis na pangkalahatang-ideya
| Aspekto | Detalye |
|---|---|
| Domain | Pag-signal ng SCTE-35 ad-break para sa mga AWS MediaLive FAST channel |
| Daloy ng trabaho ng operator | Editor ng ad break bawat programa โ posisyon sa segundo, tagal sa segundo |
| Mga cue na inilalabas bawat break | Tatlo โ panimula ng ad TimeSignal, SpliceInsert, pagtatapos ng ad TimeSignal |
| Mga limitasyon sa tagal | 10โ120 segundo, balidado sa data layer |
| Downstream consumers | Mga player, ad server, at AWS MediaTailor (idinisenyo ngunit hindi pa nakakonekta) ay mga halimbawa. |
| Status | Paglalabas ng SCTE-35 cue sa produksyon; SSAI integration sa disenyo |
Ang Problema sa Negosyo
Bawat commercial break sa isang FAST channel ay isang kaganapan sa kita. Naglalabas ang channel ng marker na nagsasabing "may patalastas dito, tatagal ito ng 30 segundo, mangyaring maghanda." Ang mga downstream system โ Google Ad Manager, AWS MediaTailor, mga regional ad server, smart-TV player SDKs โ ay nagbabasa ng marker na iyon at nagpapasya kung ano ang ipapasok.
Kapag tama ang marker, malinis na tumatakbo ang ad break, nabibilang ang mga impression, at nababayaran ang operator. Kapag mali ang marker โ maling tagal, maling hugis, maling format โ ang sistema ng ad ay alinman sa tinatanggihan ito (walang patalastas na ipinapakita) o tinatanggap ito nang mali (patalastas na maling haba, o isa na lumampas sa susunod na programa). Parehong nagkakahalaga ng malaking pera ang dalawang resulta, at parehong nabibigo nang tahimik. Patuloy sa pag-stream ang channel. Ang manonood ay makakakita ng blangkong screen o isang hindi magandang putol. Ang pipeline ng pagkakakitaan ay nagbibigay lamang ng mas mababang numero at walang mensahe ng error na susundan.
Ang layunin ng inhinyeriya ay hindi "suportahan ang mga patalastas." Ito ay: bawat ad break na tinukoy ng operator ay dapat makarating sa bawat downstream consumer bilang isang SCTE-35 signal na sumusunod sa pamantayan, sa eksaktong frame na pinili ng operator, sa bawat pagkakataon.
Ano Talaga ang SCTE-35 (ang 90-Segundong Bersyon)
Ang SCTE-35 ay isang pamantayan para sa pagpasok ng mga cue message sa isang video stream. Ang mga cue ay hindi nagdadala ng nilalaman ng ad; nagdadala sila ng mga signal โ "nagsisimula ang ad break dito," "nagtatapos ang ad break dito," "ang segment na ito ay N segundo ang haba." Binabasa ng downstream system ang mga signal na iyon at kumikilos batay sa mga ito: ang isang SSAI layer ay naglalagay ng tunay na ad creative sa HLS manifest, ang isang smart-TV player ay nagti-trigger ng overlay, ang isang ad server ay nagla-log ng pagkakataon sa slot.
Dalawang hugis ng cue ang mahalaga para sa aming use case:
- SpliceInsert โ ang orihinal na SCTE-35 cue. Nagdadala ng SpliceEventId, isang tagal sa segundo, at isang "out of network" na flag. Karamihan sa mga klasikong ad server ay nagbabasa nito.
- TimeSignal โ ang mas bago, mas nagpapahayag na cue. Nagdadala ng SegmentationDescriptor na may SegmentationTypeId (52 = Provider Ad Start, 53 = Provider Ad End) at isang SegmentationDuration sa mga yunit ng 90,000-tick. Mas gusto ng mga SSAI system at modernong player ang hugis na ito dahil malinis itong nagpapares sa simula at dulo.
Parehong tama ang mga hugis na ito sa SCTE-35. Mas gusto ng iba't ibang downstream consumer ang iba't ibang mga ito. Ang paglalabas lamang ng isang hugis ay nag-iiwan ng pera sa mesa.
Bakit ito mahalaga. Ang isang simpleng pagpapatupad ay naglalabas ng isang SpliceInsert bawat ad break at itinuturing itong tapos. Gumagana ang ad break sa mga legacy player at tahimik na nabibigo sa SSAI. Kalahati ng iyong imbentaryo ng ad ay kumikita; kalahati ay hindi. Hindi mo malalaman kung alin ang alin hangga't hindi bumaba ang iyong mga numero ng kita.
Bakit Nabibigo ang mga Simple (Naive) na Pamamaraan
Nakakatukso ang mga shortcut dahil halos lahat sila ay gumagana.
- "Sa oras ng pag-encode, ipasok ang mga patalastas sa orihinal na video." Walang puwang para sa flexibility. Hindi maaaring mag-A/B-test ng placement ang operator, hindi maaaring baguhin ang mga ad pagkatapos ng deploy, hindi maaaring magpatakbo ng mga regional o audience-targeted na ad. Ang buong dahilan kung bakit umiiral ang mga FAST channel ay ang pagkakakitaan ng parehong nilalaman sa maraming paraan โ ang pagpasok ng mga ad sa oras ng pag-encode ay sumasarado sa posibilidad na iyon.
- "Gamitin ang client-side ad insertion." Simple ang ad-blocking. Iba't ibang code path bawat device. Walang server-side personalisation. At ganap nitong nilalampasan ang SSAI, na nangangahulugang mas mababang CPM.
- "Maglabas lang ng isang SpliceInsert bawat break." Ang pinakakaraniwang pagkakamali sa produksyon. Gumagana sa ilang player, tahimik na binabalewala ng mga SSAI system na nangangailangan ng TimeSignal cues para ipares ang simula/dulo. Bahagyang pagkakakitaan, na walang error na iimbestigahan.
- "Ipahayag ang lahat sa 90 kHz tick dahil binabanggit ito ng spec." Mas nakakainis ang spec kaysa roon. Ang SpliceInsert.Duration ay nasa segundo. Ang SegmentationDuration ay nasa mga yunit ng 90 kHz tick. Ang paghahalo sa mga ito ay lumilikha ng mga cue na pumasa sa schema validation at ipinapadala, pagkatapos ay nabibigo sa player bilang malformed durations. Hindi ito mahuhuli ng encoder. Nilalaktawan lang ng player ang break.
- "Pahintulutan ang MediaLive na kumpirmahin." Hindi nito gagawin. Tinatanggap ng MediaLive ang mga magkakapatong na break, zero-duration break, at imposibleng segmentation duration nang walang reklamo. Ang kumpirmasyon ay dapat mangyari bago makita ng MediaLive ang aksyon.
Ang lever na mayroon kami ay ang hangganan sa pagitan ng operator UI โ kung saan ang mga ad break ay simpleng {position, duration} na pares โ at ang iskedyul ng MediaLive, kung saan ang bawat cue ay kailangang perpekto.
Ang Aming Solusyon
Ituring ang ad break bilang isang first-class data object mula simula hanggang dulo. Tinutukoy ito ng operator sa pinakasimpleng posibleng termino. Binabalidado ito ng backend nang isang beses sa schema layer. Isinasalin ito ng Lambda sa isang three-cue stack sa oras ng pag-deploy, kung saan ang bawat cue ay ipinapahayag sa time base na hinihingi ng sarili nitong spec. Wala nang iba pang kailangang malaman sa stack tungkol sa SCTE-35 โ ang pagsasalin ay nasa isang function, sa parehong Lambda na nagmamay-ari ng bawat iba pang aksyon sa iskedyul.
Diagram 1 ยท End-to-End Ad Marker Pipeline

Arkitektura
- Frontend ad-break editor โ nagdaragdag ang mga operator ng ad break bawat programa sa pamamagitan ng pagtukoy ng position offset (segundo mula sa simula ng programa) at duration (segundo). Ang mga bumper slot ay bahagi ng modelo ng data at handa para sa playout layer.
- AdMarker collection (MongoDB) โ isang dokumento bawat programa na may ad breaks, na tumutukoy sa video. Ang adBreaks[] array ay nag-iimbak ng {position, duration} na may Mongoose na nagpapatupad ng 10 โค duration โค 120 sa oras ng pag-save. Ang mga bumper ay nagdadala ng adBreakId reference para sa downstream pairing.
- NestJS backend (schedule.service.ts) โ sa oras ng deploy, sinasama ang bawat iskedyul sa AdMarker nito at pinapalitan ang pangalan ng mga field sa kontrata ng Lambda (position โ offsetSeconds, duration โ durationSeconds). Isang bulk fetch, walang N+1.
- Lambda orchestrator (fastChannel-lambda-fun/index.js) โ nagmamay-ari ng pagsasalin ng SCTE-35. Para sa bawat ad break, inilalabas ang three-cue stack sa parehong BatchUpdateScheduleCommand na nagdadala ng input switches at watermarks. Ang mga pangalan ng aksyon ay naka-bersyon bawat programa upang maipares ng orphan sweep ang mga ito pagkatapos ng partial deploy.
- AWS MediaLive โ tumatanggap ng mga aksyon, naglalabas ng #EXT-SCTE35 markers sa HLS manifest sa napiling segundo ng operator.
Mga Pangunahing Desisyon sa Inhinyeriya
1. Tatlong cue bawat ad break, hindi isa
Bawat ad break ay naglalabas ng tatlong hiwalay na aksyon laban sa parehong lohikal na sandali sa programa.
Diagram 2 ยท Single Ad Break Lifecycle

Ang simula at dulo ng TimeSignal cues ay may parehong SegmentationUpid upang ang mga downstream system ay makapagpares sa kanila nang deterministically. Ang SpliceInsert ay nagdadala ng natatanging SpliceEventId para sa deduplication sa antas ng player. Iba't ibang consumer ang nagbabasa ng iba't ibang cue. Ang paglalabas ng lahat ng tatlo ay sumasaklaw sa bawat kontrata na mahalaga sa amin ngayon at sa bawat posibleng kontrata bukas.
Karaniwang pagkakamali sa produksyon. Maglabas ng isang SpliceInsert at ipagpalagay na malalaman ng iba pang ecosystem. Tahimik na ibinababa ng mga SSAI system ang mga break na walang katugmang TimeSignal cues; binabalewala ng mga klasikong ad server ang mga signal na TimeSignal-only. Kung wala ang lahat ng tatlo, ang bawat ad break ay kinikita ng ilan sa iyong downstream stack at napalampas ng iba โ at malalaman mo ito mula sa ulat ng kita, hindi sa mga log.
2. Dalawang time base, pinagkasundo sa isang function
SpliceInsert.Duration ay nasa segundo. Ang SegmentationDuration sa loob ng isang TimeSignal descriptor ay nasa mga yunit ng 90,000-tick. Parehong lohikal na tagal, dalawang encoding โ at ang pinakakaraniwang SCTE-35 bug sa aktwal na pagpapatupad ay ang paghahalo sa mga ito.
Diagram 3 ยท Time Conversion Logic

Ang conversion ay nasa buildProgramActions at doon lamang. May isang karaniwang lugar upang tingnan kung may maling tagal ng cue, at isang lugar upang baguhin kung magbabago ang spec.
Kompromiso na sinadya naming gawin. Maaari naming iimbak ang parehong time base sa AdMarker document at hayaan ang Lambda na kopyahin ang mga ito. Sinadya naming hindi gawin ito: ang pag-iimbak lamang ng seconds value ay nangangahulugang mayroon lamang isang numero para itakda ng operator, isang numero lamang ang ibabalidado, at isang numero lamang ang maaaring maging mali. Ang 90 kHz value ay derived, hindi kailanman iniimbak. Ang derived data ay hindi maaaring magbago.
3. Relative ang posisyon, absolute ang oras ng pagpapagana
Sinasabi ng operator na "ad break sa 720 segundo ng programa." Kinukwenta ng Lambda ang aktwal na UTC fire time bilang programStartTime + offsetSeconds ร 1000 at inilalagay iyon sa aksyon. Ang bawat iba pang aksyon sa iskedyul โ input switch, watermark on, pagtatapos ng programa โ ay gumagamit ng parehong offset-to-timestamp pipeline. Ang mga ad cue ay lumalapag sa parehong frame boundaries tulad ng lahat ng iba pa, nang walang kalituhan kung aling orasan ang nagmamay-ari ng timeline.
4. Balidasyon sa data layer, hindi sa wire
Ipinapatupad ng AdMarker schema ang duration โ [10, 120] segundo sa oras ng pag-save ng Mongoose. Ang isang out-of-range break ay hindi kailanman nakakarating sa Lambda. Ang mga ad break na lalampas sa pagtatapos ng programa ay pinuputol sa oras ng build sa halip na tanggihan โ hindi nawawala ang trabaho ng mga operator dahil sa isang masamang break. Ang mga klase ng bug na hindi maaaring mangyari ay mas kawili-wili kaysa sa mga maaaring mangyari.
5. Paglalabas ng cue na decoupled mula sa SSAI
Ang ipinapadala namin ngayon ay ang signalling layer. Ang server-side ad insertion sa pamamagitan ng AWS MediaTailor โ na gagamit ng mga cue na ito upang ipasok ang tunay na ad creatives sa HLS manifest โ ay ganap nang idinisenyo at hindi pa nakakonekta. Ang sinadyang pagkakasunud-sunod: ayusin muna ang cue layer nang tama, sa produksyon, ginagamit ng mga tunay na channel, bago i-on ang SSAI. Kapag nag-live ang integration, naroroon na ang mga cue. Ang downstream system ay makakakuha ng malinis na kontrata upang gamitin mula sa unang araw.
Bakit mahalaga ang pagkakasunud-sunod na ito. Mahirap ang SSAI debugging kapag mali ang iyong mga cue, dahil ang bawat pagkabigo ay mukhang SSAI failure kahit na ang mga cue ang may kasalanan. Sa pamamagitan ng pagpapadala muna ng cue layer nang hiwalay at pagba-validate nito laban sa mga tunay na player at ad server, inalis namin ang isang buong kategorya ng pagkalito sa integration bago pa ito mangyari.
Mga Resulta
- Bawat ad break na tinukoy ng operator ay naglalabas ng isang standards-conformant three-cue SCTE-35 stack sa HLS manifest ng channel โ sa tamang segundo, laban sa tamang time base.
- Ang translation layer ay nasa isang function. Ang mga pagbabago sa spec o mga bagong downstream consumer ay isang pagbabago sa iisang file, hindi isang paghahanap sa buong codebase.
- Ang balidasyon ng tagal ay tumatakbo sa data layer, bago pa man makarating ang anumang cue sa encoder. Ang mga pagkakamali ng operator ay lumalabas sa UI, hindi nang tahimik sa oras ng ere โ hindi maaaring makarating sa MediaLive ang mga malformed cue.
- Ang pagbuo ng cue ay deterministic: ang magkaparehong input ng operator ay gumagawa ng magkaparehong SCTE-35 actions, kaya ang parehong break ay kumikilos sa parehong paraan sa bawat deploy at bawat channel.
- Ang signalling layer ay naihatid at live. Ang SSAI consumer (MediaTailor) ay idinisenyo at handa nang ikonekta โ at kapag nangyari iyon, ang mga cue ng bawat umiiral na channel ay naroroon na naghihintay para dito.
Technology Stack: AWS MediaLive ยท AWS Lambda ยท NestJS ยท MongoDB ยท TypeScript ยท Node 18 ยท SCTE-35 (Scte35TimeSignalSettings, Scte35SpliceInsertSettings) ยท AWS SDK v3

