Live Encoding with HLS, SCTE-35 and SSAI

This is a Live Encoding tutorial about Bitmovin’s SCTE-35 support with examples. It also involves supported Server Side Ad Insertion (SSAI) services.


This tutorial details how Bitmovin's Live Encoder supports SCTE-35. It explains how splice decisions are made and what options can be configured. There is a full Java code example for starting a Live Encoding with SCTE-35 tags in a live HLS manifest. Additionally, there are examples of how to integrate with Server Side Ad Insertion (SSAI) services.


  1. Encoder Version: v2.186.0 or higher
  2. Bitmovin Encoding API SDK Version: v1.183.2 or higher
  3. Input Source
    1. Example in this tutorial: srt-live-transmit, pv (pipe viewer)
  4. SSAI Service
    1. Example in this tutorial: AWS Mediatailor
  5. Ad decision server
    1. Example in this tutorial: simple VAST response XML


For a comprehensive overview of the The Society of Cable and Telecommunications Engineers and the SCTE-35 standard, you can read our blog post The Essential Guide to SCTE-35

What is SCTE-35

SCTE-35 signals are used to identify national and local ad breaks as well as program content like intro/outro credits, chapters, blackouts, and extensions when a live program like a sporting event runs long. For modern streaming applications, they are usually included within an MPEG-2 transport stream PID and then converted into metadata that is embedded in HLS and MPEG-DASH manifests.

SCTE-35 markers and their applications for streaming video

While SCTE-35 markers are primarily used for ad insertion in OTT workflows, they can also signal many other events that allow an automation system to tailor the program output for compliance with local restrictions or to improve the viewing experience. Let’s take a look at some common use cases and benefits of using SCTE-35 markers.

Ad Insertion

As mentioned above, inserting advertisements into a video stream is the main use case for SCTE-35 markers. They provide seamless splice points for national, local and individually targeted dynamic ad replacement. This allows for increased monetization opportunities for broadcasters and content providers by enabling segmenting of viewers into specific demographics and geographic locations. When ad content can be tailored for a particular audience, advertisers are willing to pay more, leading to higher revenue for content providers and distributors.

Program boundary markers

Another common use case is to signal a variety of program boundaries. This includes the start and end of programs, chapters, ad breaks and unexpected interruptions or extensions. Many of these become particularly useful in Live-to-VOD scenarios. Ad break start/end markers can be used as edit points in a post-production workflow to automate the removal of ads for viewers with ad-free subscriptions. A program end marker can be used to trigger the next episode in a series for binge viewing sessions if so desired. All of these markers open new possibilities for improving the user experience and keeping your audience happy and engaged.

Blackouts and alternate content

Another less common, but important use case is to signal blackouts, when a piece of content should be replaced or omitted from a broadcast. This often applies to regional blackouts for sporting events. Respecting blackout restrictions is crucial for avoiding fines and loss of access to future events. Using SCTE-35 allows your automation system to take control and ensure you are compliant.

Bitmovin and SCTE-35

Bitmovin supports the parsing of SCTE-35 triggers from MPEG-TS input streams for Live Encodings, the triggers are shown below as splice decisions. The triggers are then mapped to HLS manifest tags.

Splice Decisions

Certain SCTE-35 triggers signal that an advertisement or break (to from the original content starts or ends. The following table describes how the Live Encoder treats SCTE-35 trigger types and SCTE-35 Segmentation Descriptor types as splice decision points, and the compatibility of those types with the different command types, Spice Insert and Time Signal.

Segmentation UPID Type(Start/End)Descriptor Type NameSPLICE_INSERTTIME_SIGNAL
0x10, 0x11PROGRAM
0x20, 0x21CHAPTER
0x22, 0x23BREAK
0x50, 0x51NETWORK

HLS - Ad Marker Settings

The Live Encoder supports a range of different HLS tags that are written when SCTE-35 triggers are parsed from the MPEG-TS input stream. Multiple marker types can be enabled for each HLS manifest. Which marker types to use depends on the consumer of the HLS manifest. An example consumer would be a Server Side Ad Insertion (SSAI) service. They usually state in their documentation which HLS tags they support for signaling SCTE-35 triggers.

  • EXT_X_CUE_OUT_IN: Ad markers will be inserted using #EXT-X-CUE-OUT and #EXT-X-CUE-IN tags.
  • EXT_OATCLS_SCTE35: Ad markers will be inserted using #EXT-OATCLS-SCTE35 tags. They contain the base64 encoded raw bytes of the original SCTE-35 trigger.
  • EXT_X_SPLICEPOINT_SCTE35: Ad markers will be inserted using #EXT-X-SPLICEPOINT-SCTE35 tags. They contain the base64 encoded raw bytes of the original SCTE-35 trigger.
  • EXT_X_SCTE35: Ad markers will be inserted using #EXT-X-SCTE35 tags. They contain the base64 encoded raw bytes of the original SCTE-35 trigger. This type requires Encoder Version: v2.188.0 or higher and Bitmovin Encoding API SDK Version: v1.183.2or higher.
  • EXT_X_DATERANGE: Ad markers will be inserted using #EXT-X-DATERANGE tags as specified in the HLS specification. They contain the ID, start timestamp, and hex-encoded raw bytes of the original SCTE-35 trigger. (Note: This type requires Encoder Version: v2.188.0 or higher and Bitmovin Encoding API SDK Version: v1.183.2 or higher.

To enable HLS ad markers for the Live Encoder, they need to be configured for the Live Encoding start call. For an object of the hlsManifests the adMarkerSettings can be added. A code example can be found in the section Live Encoder Configuration below.

Input Preparation

The Live Encoder supports parsing of SCTE-35 triggers that are present in an MPEG-TS input stream. This stream can be ingested using SRT or Zixi. The incoming MPEG-TS program must have a SCTE-35 data stream with the MPEG-TS Packetized Elementary Stream (PES) type 0x86. ffprobe can be used to verify if the MPEG-TS input contains an SCTE-35 stream like this:

ffprobe srt://<source-ip>:2088 or ffprobe livestream.ts

The following stream should show up in the command line output:
Data: scte_35

Even though the stream is present, it could still be the case that there are no SCTE-35 triggers present or being sent. This depends on the MPEG-TS source provider and the insertion of SCTE-35 triggers might be triggered manually.

Example ingest using srt-live-transmit

The Live Encoder currently supports a live MPEG-TS ingest stream via SRT or Zixi input protocols. If you already have access to an SRT or Zixi source with a SCTE-35 stream, it can be used for this example. Alternatively, the srt-live-transmit tool in combination with pv (pipe viewer) can be used in combination with an MPEG-TS file, to ingest the file to the Live Encoder via SRT in real-time. These tools are available for Mac OS and most Linux distributions.

You need to following information for the command pipeline:

  • Path of the local MPEG-TS file: <mpeg-ts-input-file>
  • The overall bitrate of the file (in kiloBytes/s): <bitrate>
  • The SRT ingest URI: <srt-ingest-uri>

The command:

cat <mpeg-ts-input-file> | pv -L <bitrate> | srt-live-transmit file://con <srt-ingest-uri>

Sample ingest with a demo file

This example uses a demo MPEG-TS file with SPLICE-INSERT SCTE-35 triggers. The triggers signal a 60-second ad break every two minutes. The overall bitrate of the file is determined using ffprobe. To reproduce this on Ubuntu, follow these commands:

  1. Install srt-live-transmit and pv: sudo apt-get update && sudo apt-get install -y pv srt-tools
  2. Download the demo file:
  3. Start the Live Encoder with SRT Input with SrtMode.LISTENER (more details in the Live Encoder Configuration section) and wait for the Live Encoder to get into the state WAITING_FOR_FIRST_CONNECT. Obtain the <srt-ingest-uri> from the Bitmovin Dashboard or the Bimtovin API. (e.g. srt://341.140.55.228:2088)
  4. Execute the command pipeline:
    cat scte35_spliceInsert_2hour_demo.ts | pv -L 19K | srt-live-transmit file://con srt://341.140.55.228:2088

If you are using your own MPEG-TS file for this test, make sure to update the pv -L xxxK part with the kiloBytes/s rate of your MPEG-TS file. You can use ffprobe to get the kiloBit/s from the file and then divide it by 8. This method works best with assets that have a constant bitrate.

Live Encoder Configuration

A full Java code example can be found here:

To set up SCTE-35 support for your Live Encoder, it's essential to activate the relevant marker types within your LiveHlsManifest configuration. For instance, incorporating both markers involves the following steps:

LiveHlsManifest liveHlsManifest = new LiveHlsManifest();
HlsManifestAdMarkerSettings scteMarkerSettings = new HlsManifestAdMarkerSettings();

Once you enable at least one marker type, the SCTE35 pipeline becomes active.

The forthcoming marker type, #EXT-X-DATERANGE, will necessitate ProgramDateTime activation and configuration. This is crucial for synchronization with a universal time source. To configure it using our local system clock as the reference, utilize the following snippet:

var pdts = new ProgramDateTimeSettings();

Most SSAI services only support TsMuxing. Make sure your Live Encoding uses this for the live HLS manifest.

Verification in the dashboard

The following methods show how to check the Bitmovin Dashboard if the SCTE-35 processing is working correctly.

Dashboard Encoding Log

To verify if SCTE-35 triggers have been parsed from the input stream and converted into HLS tags can be seen in the Encoding Log section of the Live Encoding dashboard.

Example of a processed SCTE-35 trigger in the Bitmovin Dashboard

Example of a processed SCTE-35 trigger in the Bitmovin Dashboard

The Base64 or Hex-encoded SCTE-35 triggers can be parsed and inspected by an online tool like Video Tools.

Dashboard Input Monitoring

In the “Input Monitoring” tab of the Live Encoding dashboard, there is a graph for “Bits SCTE35” and “Samples SCTE35”. Use this graph to verify the presence of an SCTE-35 stream in the ingested MPEG-TS stream and if samples have been processed.

Example of the Bits per second graph for a SCTE-35 stream in the Bitmovin Dashboard

Example of the Bits per second graph for a SCTE-35 stream in the Input tab of a running live encoding, in the Bitmovin Dashboard

Verification in the HLS manifest

One way to verify if the SCTE-35 parsing is working is to check the HLS variant playlist’s content.

E.g.: The content of video_0.m3u8:
This example signals the end of an ad segment.

  • #EXT-X-SPLICEPOINT-SCTE35:/DAqAAAAA3XwAP/wDwUAAABvf0/+B7mKAAABEv8ACgAIQ1VFSQAAABJdRndD is inserted by using the EXT_X_SPLICEPOINT_SCTE35 HlsManifestAdMarkerType.
  • #EXT-X-CUE-IN is inserted by using the EXT_X_CUE_OUT_IN HlsManifestAdMarkerType.

Supported SSAI (Server Side Ad Insertion) services

The following table maps the Bitmovin API HlsManifestAdMarkerTypes to SSAI services.

  • ✓ - tested and supported
  • O - not compatible
  • empty - unknown
TagsAWS Elemental MediaTailorGoogle

How to guides

AWS MediaTailor

Live Encoding with AWS Elemental MediaTailor

Live Encoding with

Live Encoding with YoSpace


If there are no HLS tags in the manifest or no ads are being inserted by the SSAI there are several parameters you can look into.

Wrong Muxing Type

Make sure to set the correct muxing type for the Live Encoder. This depends on the consumer of the output HLS manifest, e.g. an SSAI service. The most common muxing types are TsMuxing and Fmp4Muxing.

No or wrong SCTE-35 stream in MPEG-TS

The Live Encoder supports parsing of SCTE-35 triggers that are present in an MPEG-TS input stream. The incoming MPEG-TS program must have a SCTE-35 data stream with the MPEG-TS Packetized Elementary Stream (PES) type 0x86. Make sure that the stream is present by checking the MPEG-TS stream with ffprobe. The Live Monitoring in the Live Encoder Dashboard can also be used to check if the SCTE-35 stream is present.

No SCTE-35 triggers can be parsed

The SCTE-35 stream is present, but there are no SCTE-35 triggers being parsed (no SCTE-35 events in the Encoding Log). The Live Monitoring graph in the Live Encoder Dashboard shows if there are SCTE-35 samples read from the input. If no samples are showing up for the graph this means that there were no SCTE-35 triggers found in the input stream yet. Sometimes manual triggers are needed for an MPEG-TS source to insert SCTE-35 events. If SCTE-35 samples are showing in the graph but no SCTE-35 events are logged, please contact Bitmovin Support.

No Ads are shown in the SSAI’s output manifest

Make sure that the configured HLS ad marker tags are present in the Live Encoder output HLS manifest. Which HLS ad markers are supported by the SSAI varies. SSAI can also have different support of SCTE-35 triggers that are carried in the HLS tags as Base64 or Hex format. Consult the SSAI service’s documentation. Try out different supported HLS ad marker types.

The issue with ingesting SRT/MPEG-TS using ffmpeg

FFmpeg does not support SCTE-35 streams. When transmuxing an SCTE-35 stream with ffmpeg it is converted to a binary data (bin_data) stream. The Live Encoder does not support SCTE-35 triggers that are signaled using a bin_data stream. Alternatively, use the workflow from the section “Sample ingest with a demo file” to stream an MPEG-TS file via SRT to the Live Encoder.

Useful tools and resources

This section provides links to tools and other resources that help with working with SCTE-35.

SSAI (Server-Side Ad Insertion): What Is It? - Blogpost about SSAI

Video Tools - Base64, Hex parser for SCTE-35 trigger

Middleman Software, Inc. - Tools - SCTE 35 Parser - Base64, Hex parser for SCTE-35 trigger

scte35-threefive - SCTE-35 parser, encoder and decoder tools and libraries