Technology

Everything You Ever Wanted to Know About SCTE-35

SCTE-35 is one of the most important standards in broadcast and streaming — and one of the least understood outside of the engineering teams who work with it every day. If you have ever wondered why an ad break fired at the wrong time, why your SSAI platform missed a splice point, or what all those hex values in a cue message actually mean, this guide is for you.

We will cover everything: the history, the binary structure, every splice command type, the complete Segmentation Type ID table, UPID formats, and how real-world systems — from ATSC broadcast to HLS streaming — implement SCTE-35 in practice.

1. What Is SCTE-35?

SCTE-35 is a standard published by the Society of Cable Telecommunications Engineers (SCTE), formally titled Digital Program Insertion Cueing Message for Cable. It defines a binary message format for signalling events — primarily advertising opportunities — within a digital video stream.

The standard was first published in 1999 and has been revised multiple times, with the most widely deployed versions being SCTE-35 2019 and SCTE-35 2022. Despite its name referencing cable, SCTE-35 is now the universal ad signalling standard across cable, satellite, IPTV, OTT, and streaming workflows.

At its core, SCTE-35 answers a simple question: "At this precise moment in the stream, something important is about to happen — what is it, and what should downstream systems do about it?" That "something" is most commonly an ad break, but it can also be a programme boundary, a chapter marker, a blackout event, or a custom content identifier.

2. Where SCTE-35 Lives in the Stack

SCTE-35 messages are carried as a private section within an MPEG-2 Transport Stream (MPEG-2 TS). They occupy their own PID (Packet Identifier), which is declared in the PMT (Program Map Table) of the programme they relate to. The PMT entry for the SCTE-35 PID uses the stream type 0x86.

In HLS and DASH streaming, SCTE-35 cue messages are carried differently:

  • HLS: SCTE-35 data is base64-encoded and inserted as #EXT-X-CUE-OUT, #EXT-X-DATERANGE, or #EXT-X-SCTE35 tags in the manifest. The raw binary is also embedded in the transport stream segments for systems that parse at the TS level.
  • DASH: SCTE-35 events are carried as EventStream elements within the MPD, or as emsg boxes (Event Message boxes, defined in ISO BMFF) within the media segments.

The journey of a SCTE-35 message through a typical broadcast chain involves three distinct layers, each with a clearly defined role. A common misconception is that the traffic system talks directly to the encoder — it does not. The correct chain is:

  1. Traffic & Scheduling (Intent): The traffic or scheduling system defines what should happen and when — for example, a 2-minute ad break starting at 20:12:30. This is modelled as a secondary event (an avail, a break marker, or a placement opportunity) attached to the primary programme schedule. The traffic system expresses intent; it does not communicate directly with encoders or playout hardware.
  2. Automation & Playout (Execution): The playout automation system reads the schedule and, at the appropriate time, executes the secondary event. It is the automation system — not the traffic system — that sends a SCTE-104 message over IP to the encoder or mux, typically 4–10 seconds before the splice point. The automation system may simultaneously trigger playout servers, graphics systems, GPI outputs, and other downstream components.
  3. Encoder / Mux (SCTE-35 Generation): Upon receiving the SCTE-104 instruction, the encoder or multiplexer converts it into a SCTE-35 binary cue message and embeds it in the MPEG-2 Transport Stream at the correct PTS (Presentation Timestamp). This is the point at which the signal enters the compressed distribution domain.
  4. Distribution: The TS travels through the distribution chain (satellite uplink, CDN ingest, packager). Each system may re-encode or pass through the SCTE-35 data.
  5. Packaging: The packager (e.g. Elemental MediaPackage, Unified Origin, Evrideo) reads the SCTE-35 cue and writes the appropriate manifest tags for HLS/DASH delivery.
  6. Ad Insertion: The SSAI system reads the manifest tags, fires an ad decision request, and stitches the returned ad creative into the stream at the splice point.

A useful mental model: Traffic schedules the intent. Automation executes the trigger. The encoder emits the SCTE-35.

3. The SCTE-35 Binary Structure

A SCTE-35 message is a binary data structure. Understanding its layout is essential for debugging, building parsers, and verifying that your encoder or packager is generating correct messages.

The top-level structure is the splice_info_section:

splice_info_section() {
  table_id                         8 bits   // Always 0xFC
  section_syntax_indicator         1 bit    // Always 0
  private_indicator                1 bit    // Always 0
  reserved                         2 bits
  section_length                  12 bits   // Bytes following this field
  protocol_version                 8 bits   // Always 0x00
  encrypted_packet                 1 bit    // 1 if payload is encrypted
  encryption_algorithm             6 bits   // 0x00 = no encryption
  pts_adjustment                  33 bits   // Added to all PTS values in message
  cw_index                         8 bits   // Encryption key index
  tier                            12 bits   // Addressability tier
  splice_command_length           12 bits   // Length of splice command
  splice_command_type              8 bits   // Identifies the command (see below)
  [splice_command()]                        // Variable-length command payload
  descriptor_loop_length          16 bits   // Total length of all descriptors
  [splice_descriptor()]*                    // Zero or more descriptors
  [alignment_stuffing]*                     // Padding to align to byte boundary
  [E_CRC_32]                      32 bits   // CRC of encrypted portion (if encrypted)
  CRC_32                          32 bits   // CRC-32 of entire section
}

Key Fields Explained

table_id (0xFC): This is always 0xFC (252 decimal). It is the MPEG-2 private section table identifier for SCTE-35. Any parser can identify a SCTE-35 section by this byte.

pts_adjustment: A 33-bit value added to every PTS timestamp within the message. This is used when the stream has undergone PTS wrapping or discontinuities. In most normal streams it is zero, but it must be accounted for in any compliant parser.

tier: A 12-bit addressability field. The value 0xFFF means the message applies to all tiers (the default for most deployments). Non-default tiers are used in cable head-end environments where different subscriber groups receive different ad content.

splice_command_type: The single most important byte in the message. It tells the receiving system what kind of event this is. See Section 4 for the complete command type table.

4. Splice Command Types

The splice_command_type field identifies which command structure follows. The SCTE-35 standard defines six command types:

HexDecimalCommand NameDescription
0x000splice_null()A heartbeat / keep-alive message. Contains no timing or event data. Used to maintain SCTE-35 PID presence in the stream.
0x044splice_schedule()Schedules one or more future splice events using wall-clock UTC time (UTC_splice_time). Rarely used in modern deployments; superseded by splice_insert and time_signal.
0x055splice_insert()The original ad insertion command. Signals a single splice-in or splice-out event with a PTS-based timing. Widely used in cable and satellite broadcast.
0x066time_signal()A generic timing message that carries no implicit meaning on its own. Its meaning is defined entirely by the segmentation_descriptor(s) attached to it. The preferred command type for OTT and streaming workflows.
0x077bandwidth_reservation()Reserves bandwidth in the SCTE-35 PID for future messages. Rarely seen in practice.
0xFF255private_command()A vendor-specific command identified by a 32-bit identifier. Not interoperable without prior agreement between sender and receiver.

splice_insert() in Detail

splice_insert() is the original SCTE-35 command and remains heavily used in cable and satellite environments. Its structure is:

splice_insert() {
  splice_event_id                 32 bits   // Unique event identifier
  splice_event_cancel_indicator    1 bit    // 1 = cancel a previously sent event
  reserved                         7 bits
  if (splice_event_cancel_indicator == 0) {
    out_of_network_indicator       1 bit    // 1 = splice OUT (to ad), 0 = splice IN (back to content)
    program_splice_flag            1 bit    // 1 = applies to all components
    duration_flag                  1 bit    // 1 = break_duration() follows
    splice_immediate_flag          1 bit    // 1 = splice at next opportunity
    reserved                       4 bits
    if (program_splice_flag == 1 && splice_immediate_flag == 0) {
      splice_time()                         // PTS of the splice point
    }
    if (duration_flag == 1) {
      break_duration()                      // Expected duration of the break
    }
    unique_program_id             16 bits   // Programme identifier
    avail_num                      8 bits   // Avail number within the break
    avails_expected                8 bits   // Total avails in this break
  }
}

The out_of_network_indicator is the key field: 1 means "leave the network content and go to an ad" (splice OUT), while 0 means "return to network content" (splice IN). A complete ad break requires both a splice OUT and a splice IN message.

time_signal() in Detail

time_signal() is simpler — it carries only a PTS timestamp:

time_signal() {
  splice_time()   // PTS of the event
}

splice_time() {
  time_specified_flag              1 bit    // 1 = PTS follows
  if (time_specified_flag == 1) {
    reserved                       6 bits
    pts_time                      33 bits   // 90kHz PTS clock
  } else {
    reserved                       7 bits
  }
}

On its own, a time_signal() does nothing. Its meaning comes entirely from the segmentation_descriptor() attached in the descriptor loop. This is why time_signal() + segmentation_descriptor() has become the standard pattern for modern OTT and streaming deployments — it is far more expressive than splice_insert().

5. Splice Descriptors

Descriptors are optional extensions attached to any splice command. They carry additional metadata beyond what the command itself provides. The descriptor_loop_length field in the splice_info_section indicates the total byte length of all descriptors that follow.

Each descriptor begins with a common header:

splice_descriptor() {
  splice_descriptor_tag            8 bits   // Identifies descriptor type
  descriptor_length                8 bits   // Length of descriptor data
  identifier                      32 bits   // Always 0x43554549 ("CUEI" in ASCII)
  [descriptor-specific data]
}

The identifier field 0x43554549 is the ASCII encoding of "CUEI" — the SCTE registration identifier. Any descriptor with a different identifier is a private/vendor extension.

The SCTE-35 standard defines four descriptor types:

TagNamePurpose
0x00avail_descriptor()Provides additional avail (ad slot) information, including a provider avail ID.
0x01DTMF_descriptor()Carries DTMF tone sequences for legacy analogue insertion systems.
0x02segmentation_descriptor()The most important descriptor. Defines content boundaries, segment types, and content identifiers. See Section 6.
0x03time_descriptor()Provides TAI (International Atomic Time) and UTC offset for precise wall-clock synchronisation.

6. The segmentation_descriptor() — The Heart of Modern SCTE-35

The segmentation_descriptor() (tag 0x02) is by far the most important and most complex part of the SCTE-35 standard. It is what transforms a generic timing signal into a meaningful content event. Understanding it in full is essential for anyone building or debugging SSAI, packaging, or content marking systems.

segmentation_descriptor() Structure

segmentation_descriptor() {
  splice_descriptor_tag            8 bits   // 0x02
  descriptor_length                8 bits
  identifier                      32 bits   // 0x43554549 ("CUEI")
  segmentation_event_id           32 bits   // Unique event identifier
  segmentation_event_cancel_indicator  1 bit
  reserved                         7 bits
  if (segmentation_event_cancel_indicator == 0) {
    program_segmentation_flag      1 bit
    segmentation_duration_flag     1 bit    // 1 = duration field follows
    delivery_not_restricted_flag   1 bit    // 1 = no delivery restrictions
    if (delivery_not_restricted_flag == 0) {
      web_delivery_allowed_flag    1 bit
      no_regional_blackout_flag    1 bit
      archive_allowed_flag         1 bit
      device_restrictions          2 bits   // 0x3 = none
    } else {
      reserved                     5 bits
    }
    if (program_segmentation_flag == 0) {
      component_count              8 bits
      for (i = 0; i < component_count; i++) {
        component_tag              8 bits
        reserved                   7 bits
        pts_offset                33 bits
      }
    }
    if (segmentation_duration_flag == 1) {
      segmentation_duration       40 bits   // Duration in 90kHz ticks
    }
    segmentation_upid_type         8 bits   // Type of UPID (see Section 7)
    segmentation_upid_length       8 bits   // Length of UPID in bytes
    segmentation_upid()                     // The UPID data
    segmentation_type_id           8 bits   // What kind of segment this is (see Section 8)
    segment_num                    8 bits   // Segment number within a group
    segments_expected              8 bits   // Total segments expected
    if (segmentation_type_id == 0x34 || segmentation_type_id == 0x36 ||
        segmentation_type_id == 0x38 || segmentation_type_id == 0x3A) {
      sub_segment_num              8 bits
      sub_segments_expected        8 bits
    }
  }
}

Delivery Restriction Flags

When delivery_not_restricted_flag is 0, the descriptor carries four delivery restriction flags that downstream systems must honour:

  • web_delivery_allowed_flag: 0 = do not deliver this segment over the web (internet). Used for content with internet rights restrictions.
  • no_regional_blackout_flag: 0 = apply regional blackout rules. Used for sports rights where out-of-market viewers must be blacked out.
  • archive_allowed_flag: 0 = do not archive this segment. Used for live-only content rights.
  • device_restrictions: A 2-bit field restricting delivery to specific device classes (0x0 = restricted devices only, 0x3 = no restriction).

segmentation_duration

When segmentation_duration_flag is set, the 40-bit segmentation_duration field carries the expected duration of the segment in 90kHz clock ticks. To convert to seconds, divide by 90,000. For example, a 30-second ad break would be signalled as 2,700,000 ticks (0x0029ACA0).

This field is advisory — it tells downstream systems how long the break is expected to last, but the actual end is signalled by the corresponding "End" type_id message. SSAI systems use this value to pre-fetch ad creative of the correct duration.

7. segmentation_upid_type — The Complete Table

The UPID (Unique Programme Identifier) is a content identifier carried within the segmentation_descriptor. The segmentation_upid_type field defines the format of the identifier. This is one of the most important fields for content identification, rights management, and targeted advertising.

HexDecNameFormat & Description
0x000Not UsedNo UPID. segmentation_upid_length shall be 0.
0x011User DefinedDeprecated. Operator-defined format. No interoperability guarantee.
0x022ISCIIndustry Standard Commercial Identifier. 8-character ASCII code used in North American TV advertising. Deprecated in favour of Ad-ID.
0x033Ad-IDAdvertising Digital Identification. 12-character ASCII identifier for ad creative assets, maintained by the Ad-ID registry. The standard identifier for North American TV and digital advertising.
0x044UMIDSMPTE Unique Material Identifier (SMPTE 330M). A 32-byte binary identifier for audio-visual material, widely used in professional broadcast asset management systems.
0x055ISANInternational Standard Audiovisual Number (ISO 15706). Deprecated in favour of V-ISAN (0x06). A 12-byte binary identifier for audiovisual works.
0x066V-ISANVersioned ISAN (ISO 15706-2). A 12-byte binary identifier that extends ISAN with a version component, allowing identification of specific versions (e.g. director's cut, dubbed version) of an audiovisual work.
0x077TIDTribune Media Systems Program Identifier. A 12-character ASCII identifier from the Tribune (now Gracenote) programme data service. Widely used in North American EPG and rights management systems.
0x088TIAiringID (formerly Turner Identifier). A variable-length ASCII string used by Turner Broadcasting to identify programme airings. Used in conjunction with the Turner ad decisioning infrastructure.
0x099ADICableLabs ADI (Asset Distribution Interface) identifier. A variable-length ASCII string in the format PAID:asset_id, used in cable VOD workflows.
0x0A10EIDREntertainment Identifier Registry (EIDR). A 12-byte binary representation of an EIDR DOI (Digital Object Identifier). EIDR is a global content registry used for film, TV series, and episodes.
0x0B11ATSC Content IdentifierATSC A/57B content identifier. A variable-length structure containing a TSID (Transport Stream ID) and a content ID string, used in ATSC broadcast environments.
0x0C12MPU()Managed Private UPID. A structured format allowing private/proprietary identifiers to be carried in a standardised way. Contains a 32-bit format identifier (registered with SMPTE) followed by private data.
0x0D13MID()Multiple Identifier. A list of UPIDs of different types, allowing a single segmentation event to carry multiple identifiers simultaneously (e.g. both an Ad-ID and an EIDR for the same asset).
0x0E14ADS InformationAdvertising Information. A variable-length ASCII string carrying ad decisioning metadata. Format is operator-defined.
0x0F15URIUniform Resource Identifier. A variable-length ASCII URI string. The most flexible and widely used UPID type in modern OTT and streaming deployments. Can carry any URI scheme (urn:uuid:, http:, etc.).
0x1016UUIDUniversally Unique Identifier (RFC 4122). A 16-byte binary UUID. Introduced in SCTE-35 2020. Provides a compact, globally unique identifier without the overhead of a full URI string.
0x1117SCRSubscriber Consent Response. Used in addressable advertising workflows to carry consent signals. Introduced in SCTE-35 2022.

The URI UPID in Practice

The URI UPID (0x0F) is the most commonly used type in modern streaming deployments. Its flexibility makes it suitable for a wide range of use cases:

  • Programme identification: urn:uuid:c7451a50-e053-4b2a-ca37-bae1df2cf1c8 — a UUID-based URN uniquely identifying a programme or episode.
  • Ad decisioning: urn:adid:ABCD1234EFGH — an Ad-ID carried as a URN.
  • Custom content marking: Operators may define their own URI schemes for proprietary workflows, as described in Section 9.

8. segmentation_type_id — The Complete Table

The segmentation_type_id is the field that defines what kind of content boundary this message represents. It is the primary field that SSAI systems, packagers, and players use to decide what action to take. The SCTE-35 standard defines a comprehensive set of type IDs organised into logical groups.

HexDecSegmentation MessageNotes
0x000Not IndicatedNo specific segmentation type. Used when the type is conveyed by other means.
0x011Content IdentificationCarries a content identifier (UPID) without signalling a boundary. Used for content marking and rights identification. Commonly used with custom UPID formats for proprietary content marking workflows.
0x1016Program StartMarks the beginning of a programme. Paired with Program End (0x11).
0x1117Program EndMarks the end of a programme.
0x1218Program Early TerminationSignals that a programme is ending earlier than scheduled. Used in live events (e.g. a sporting event that ends before the scheduled time).
0x1319Program BreakawaySignals a temporary departure from the scheduled programme (e.g. for a breaking news bulletin).
0x1420Program ResumptionSignals the return to the scheduled programme after a breakaway.
0x1521Program Runover PlannedSignals that the current programme will run longer than scheduled, and the overrun was planned in advance.
0x1622Program Runover UnplannedSignals an unplanned programme overrun (e.g. a live event running long).
0x1723Program Overlap StartSignals the start of a programme that overlaps with the preceding programme.
0x1824Program Blackout OverrideOverrides a previously signalled blackout restriction, allowing delivery to proceed.
0x1925Program Start — In ProgressSignals that a programme has already started (used when joining a stream mid-programme, e.g. after a channel change or stream join).
0x2032Chapter StartMarks the beginning of a chapter within a programme. Chapters are subdivisions of a programme.
0x2133Chapter EndMarks the end of a chapter.
0x2234Break StartMarks the start of a content break (not necessarily an ad break — could be a station break, news update, etc.).
0x2335Break EndMarks the end of a content break.
0x2436Opening Credit StartMarks the start of opening credits. Introduced in SCTE-35 2020.
0x2537Opening Credit EndMarks the end of opening credits.
0x2638Closing Credit StartMarks the start of closing credits.
0x2739Closing Credit EndMarks the end of closing credits.
0x3048Provider Advertisement StartMarks the start of a provider-controlled ad avail. The content provider (network) controls the ad inventory in this slot.
0x3149Provider Advertisement EndMarks the end of a provider-controlled ad avail.
0x3250Distributor Advertisement StartMarks the start of a distributor-controlled ad avail. The distributor (cable operator, MVPD) controls the inventory.
0x3351Distributor Advertisement EndMarks the end of a distributor-controlled ad avail.
0x3452Provider Placement Opportunity StartMarks the start of a provider placement opportunity (PO). More granular than an advertisement avail — used for dynamic ad insertion in streaming.
0x3553Provider Placement Opportunity EndMarks the end of a provider placement opportunity.
0x3654Distributor Placement Opportunity StartMarks the start of a distributor placement opportunity.
0x3755Distributor Placement Opportunity EndMarks the end of a distributor placement opportunity.
0x3856Provider Overlay Placement Opportunity StartMarks the start of a provider overlay ad opportunity (e.g. a banner or L-band overlay).
0x3957Provider Overlay Placement Opportunity EndMarks the end of a provider overlay opportunity.
0x3A58Distributor Overlay Placement Opportunity StartMarks the start of a distributor overlay ad opportunity.
0x3B59Distributor Overlay Placement Opportunity EndMarks the end of a distributor overlay opportunity.
0x3C60Provider Promo StartMarks the start of a provider-controlled promotional segment. Introduced in SCTE-35 2022.
0x3D61Provider Promo EndMarks the end of a provider promotional segment.
0x3E62Distributor Promo StartMarks the start of a distributor promotional segment.
0x3F63Distributor Promo EndMarks the end of a distributor promotional segment.
0x4064Unscheduled Event StartMarks the start of an unscheduled live event (e.g. breaking news, emergency broadcast).
0x4165Unscheduled Event EndMarks the end of an unscheduled event.
0x4266Alternate Content Opportunity StartMarks the start of a window where alternative content may be substituted. Introduced in SCTE-35 2020.
0x4367Alternate Content Opportunity EndMarks the end of an alternate content opportunity.
0x4468Provider Ad Block StartMarks the start of a provider-controlled ad block. Introduced in SCTE-35 2022.
0x4569Provider Ad Block EndMarks the end of a provider ad block.
0x4670Distributor Ad Block StartMarks the start of a distributor-controlled ad block.
0x4771Distributor Ad Block EndMarks the end of a distributor ad block.
0x5080Network StartMarks the start of a network segment (the highest-level boundary in the hierarchy).
0x5181Network EndMarks the end of a network segment.

The Provider vs. Distributor Distinction

One of the most important conceptual distinctions in the type_id table is between Provider and Distributor events. This reflects the two-tier commercial structure of the broadcast advertising market:

  • Provider (e.g. a TV network like NBC, CNN, or Sky) controls national/network advertising inventory. Provider ad avails are sold by the network and are consistent across all distribution platforms.
  • Distributor (e.g. a cable operator, MVPD, or OTT platform) controls local/regional advertising inventory. Distributor ad avails are sold by the distributor and may be substituted with locally-targeted ads.

SSAI systems use this distinction to determine which ad decisioning system to call and whose inventory to fill. A distributor SSAI platform will typically only act on Distributor type IDs, leaving Provider avails for the network's own ad insertion system.

9. UPID Formatting in Practice: Custom Content Marking

Beyond the standard UPID types, many operators implement custom content marking workflows using the URI UPID (0x0F) with segmentation_type_id = 0x01 (Content Identification). This is the pattern described in the example at the top of this article.

The general approach is to encode a structured payload as a URI or comma-separated string within the UPID field. Because the UPID field is limited to 255 bytes, JSON is often impractical, and comma-separated or slash-delimited formats are preferred.

Example: Custom Content Boundary Marking

A common pattern used by content providers to signal content boundaries within a SCTE-35 marker uses a comma-separated UPID with three components:

  1. Data Type Identifier: A fixed magic number identifying the format (e.g. 0x01010101), allowing receivers to distinguish this custom format from other URI UPIDs.
  2. Primary Event ID: A URN or UUID identifying the programme or production (e.g. urn:uuid:c7451a50-e053-4b2a-ca37-bae1df2cf1c8).
  3. Boundary Tag List: A slash-delimited list of hex tag values identifying what content boundaries this marker represents.

A complete example:

0x01010101,urn:uuid:c7451a50-e053-4b2a-ca37-bae1df2cf1c8,10/31

This reads as: "For programme c7451a50-e053-4b2a-ca37-bae1df2cf1c8, this marker represents a Bumper Start (0x10) and a Sponsorship End (0x31)."

The boundary tag values are defined by the operator. A typical tag table might look like this:

Tag NameHex ValueDecimal
Bumper Start0x1016
Bumper End0x1117
Promo Start0x2032
Promo End0x2133
Sponsorship Start0x3048
Sponsorship End0x3149
Programme Start0x4064
Programme End0x4165
Chapter Start0x5080
Chapter End0x5181
Break Start0x6096
Break End0x6197
Spot Start0x70112
Spot End0x71113

Why Multiple Tags in One Marker?

A single point in the content timeline can represent multiple simultaneous boundaries. For example, the moment a bumper ends is also the moment a sponsorship segment ends — both events happen at the same frame. Encoding both tags in a single SCTE-35 message is more efficient than sending two separate messages at the same PTS, and avoids race conditions in downstream systems.

10. SCTE-35 in HLS: Manifest Tags

When a packager converts a TS stream to HLS, it must translate SCTE-35 binary messages into HLS manifest tags. There are three main approaches, and different platforms support different ones.

#EXT-X-CUE-OUT / #EXT-X-CUE-IN

The simplest and most widely supported approach. The packager writes a #EXT-X-CUE-OUT tag with the break duration before the first segment of the ad break, and a #EXT-X-CUE-IN tag after the last segment:

#EXT-X-CUE-OUT:DURATION=30.0
#EXTINF:6.006,
segment_0042.ts
#EXTINF:6.006,
segment_0043.ts
...
#EXT-X-CUE-IN

#EXT-X-DATERANGE

The SCTE-35 data is carried as a base64-encoded attribute within an #EXT-X-DATERANGE tag, preserving the full binary payload for downstream parsers:

#EXT-X-DATERANGE:ID="splice-6FFFFFF0",START-DATE="2026-05-21T14:00:00.000Z",
  PLANNED-DURATION=30.0,SCTE35-OUT=0xFC002F0000000000FF...

#EXT-X-SCTE35

A non-standard but widely used extension that carries the raw base64-encoded SCTE-35 binary directly:

#EXT-X-SCTE35:CUE="/DAlAAAAAAAAAAP/wFAVIAACPf+/wgAFQAABQAABQAAAAAAAA=="

11. SCTE-35 in DASH: EventStream and emsg

In DASH, SCTE-35 events are carried in two places:

  • MPD EventStream: The SCTE-35 data is base64-encoded and placed in an <Event> element within an <EventStream> in the MPD. The schemeIdUri is urn:scte:scte35:2014:xml+bin for binary-encoded events.
  • emsg boxes: SCTE-35 events are carried as ISO BMFF Event Message boxes (emsg) within the media segments themselves. The scheme_id_uri is urn:scte:scte35:2013:bin for binary or urn:scte:scte35:2014:xml+bin for the newer format.

12. SCTE-104: The Upstream Signalling Protocol

SCTE-35 defines the message format in the compressed transport stream. SCTE-104 defines how those messages are requested upstream — specifically, the IP interface between the playout automation system and the encoder or mux that generates the SCTE-35 messages.

Understanding where SCTE-104 sits in the chain is important. The traffic/scheduling system defines secondary events (avails, break markers, placement opportunities) as part of the programme schedule — these are expressions of intent, not execution commands. The automation/playout system reads those scheduled events and, at the correct time, fires a SCTE-104 message to the encoder, typically 4–10 seconds before the splice point. The traffic system never communicates directly with the encoder.

In cloud playout environments, the role of the traditional hardware encoder is often replaced by a software mux or origin packager, but the logical relationship remains the same: automation triggers SCTE-104 (or an equivalent API command), and the encoder/packager emits SCTE-35.

A SCTE-104 message is sent over UDP/IP from the automation system to the encoder. The encoder converts the SCTE-104 instruction into a SCTE-35 binary message and inserts it into the transport stream at the correct PTS.

The key SCTE-104 message types are:

  • splice_request_data: Requests a splice_insert() at a specified time.
  • segmentation_descriptor_request_data: Requests a time_signal() + segmentation_descriptor() with specified parameters.
  • insert_descriptor_request_data: Requests insertion of a specific descriptor.

13. Common Implementation Mistakes

After years of working with SCTE-35 across dozens of broadcast and streaming deployments, these are the most common errors we encounter:

Forgetting the PTS Adjustment

The pts_adjustment field must be added to all PTS values in the message. Many parsers ignore this field and use the raw PTS values, which causes timing errors when the stream has undergone PTS manipulation (e.g. after a TS remux or after a PTS discontinuity).

Mismatched splice_event_id

The splice_event_id in a splice_insert() or the segmentation_event_id in a segmentation_descriptor() must match between the "out" and "in" messages for the same event. Mismatches cause SSAI systems to treat the "in" as a new event rather than the close of the "out", resulting in runaway ad breaks.

Missing the segmentation_duration on "Start" messages

SSAI systems use the segmentation_duration field to pre-fetch ad creative. If it is absent from a Provider/Distributor Placement Opportunity Start message, many ad decisioning systems will not fire a request, resulting in an unfilled avail.

Sending only "Out" without "In"

Every splice OUT or segment Start must be paired with a corresponding splice IN or segment End. Unpaired messages leave downstream systems in an undefined state. Some SSAI platforms will time out after the expected duration, but this is not guaranteed.

Incorrect UPID type for the payload

Sending a UUID as a URI UPID (0x0F) when it should be a UUID UPID (0x10), or sending a URN string as a binary UMID, causes parsing failures in downstream systems that validate the UPID type against the payload format.

CRC errors from re-muxing

When a transport stream is re-muxed (e.g. during transcoding or packaging), the SCTE-35 sections must be re-CRC'd. Passing through the original CRC after any modification to the section will cause receivers to discard the message as corrupt.

14. Debugging SCTE-35

When SCTE-35 messages are not behaving as expected, these are the primary tools and techniques:

  • SCTE-35 parsers: Tools like python-scte35, threefive, and scte35-js can decode raw base64 or hex SCTE-35 payloads into human-readable structures. Essential for verifying encoder output.
  • HLS manifest inspection: Use curl or a manifest parser to inspect the live HLS manifest and verify that cue tags are present, correctly formatted, and at the right segment boundaries.
  • Wireshark: For SCTE-104 debugging, capture the UDP traffic between the automation system and the encoder and decode the SCTE-104 messages.
  • SSAI debug mode: Most SSAI platforms provide a debug or passthrough mode that logs every ad decision request and response, making it possible to trace exactly which SCTE-35 event triggered which ad request.
  • PTS timeline analysis: Tools like tsduck and ffprobe can extract the PTS values of SCTE-35 sections from a TS file, allowing you to verify that messages are arriving at the correct time relative to the video frames.

Conclusion

SCTE-35 is a deceptively deep standard. On the surface, it looks like a simple signalling mechanism — a flag in the stream that says "ad break here." In practice, it is a rich, hierarchical content metadata system that underpins billions of dollars of advertising revenue across broadcast and streaming every year.

Understanding the full structure — from the splice_info_section binary layout through to the nuances of UPID formatting and the Provider/Distributor type_id distinction — is what separates teams that reliably monetise their content from those that lose revenue to missed avails, mismatched events, and unfilled breaks.

At Evrideo, SCTE-35 handling is built into the core of our playout, packaging, and SSAI infrastructure. If you are working through a SCTE-35 integration challenge or want to understand how Evrideo handles ad signalling in your specific workflow, get in touch with our team.

Back to Blog