How to Inject SCTE-35 Cues in FFmpeg

This article provides a practical guide on how to inject and manage SCTE-35 ad insertion cues in video streams using FFmpeg. You will learn how to configure the MPEG-TS muxer, allocate SCTE-35 Program Map Table (PMT) descriptors, and preserve or inject ad markers into your live and file-based video workflows.

Understanding SCTE-35 in FFmpeg

SCTE-35 is the industry standard for signaling cue tones, local ad insertion opportunities, and program splice points in MPEG Transport Streams (MPEG-TS).

FFmpeg does not feature a dedicated video filter (like -vf) to dynamically generate SCTE-35 messages on the fly. Instead, SCTE-35 is treated as a metadata or data stream. FFmpeg handles SCTE-35 cues primarily at the muxer and demuxer level using the mpegts container format.

To work with SCTE-35 in FFmpeg, you generally use one of three approaches: passing through existing cues, allocating an SCTE-35 PID for external injectors, or using timed metadata.


Method 1: Passing Through Existing SCTE-35 Cues

If your input stream (such as an SDI capture, UDP, or SRT stream) already contains SCTE-35 cues, you must instruct FFmpeg to copy the data stream to the output. By default, FFmpeg only maps video and audio streams.

To preserve and pass through existing SCTE-35 cues, use the stream copy (-c:s copy or -c:d copy) and mapping flags:

ffmpeg -i input.ts -map 0:v -map 0:a -map 0:s? -c:v libx264 -c:a aac -c:s copy -f mpegts output.ts

Method 2: Allocating an SCTE-35 PID in the MPEG-TS Muxer

If you are broadcasting a live stream to an downstream hardware or software splicer (like an edge transcoder or ad-splicer), you must reserve a specific Program Identifier (PID) for SCTE-35 cues in the PMT of the output MPEG-TS.

Use the -scte35_pid parameter inside the mpegts muxer:

ffmpeg -i input.mp4 -c:v libx264 -c:a aac -f mpegts -scte35_pid 0x104 output.ts

Downstream ad injectors can now target this specific PID to insert splicing commands (splice_info_section) directly into your stream.


Method 3: Injecting SCTE-35 via HLS (HTTP Live Streaming)

If your workflow involves packaging live streams into HLS (HTTP Live Streaming) for OTT delivery, SCTE-35 cues must be translated into HLS playlist tags, such as #EXT-X-DATERANGE or #EXT-OATR-SCTE35.

FFmpeg’s HLS muxer can accept input SCTE-35 cues and convert them into standard HLS tags using the -hls_flags parameter.

ffmpeg -i input_with_scte35.ts \
  -c:v libx264 -c:a aac \
  -f hls \
  -hls_time 6 \
  -hls_playlist_type event \
  -hls_flags scte35_byteout+independent_segments \
  playlist.m3u8

Verifying SCTE-35 Cue Injection

To verify that your output stream contains the correct SCTE-35 PID and data, use ffprobe to inspect the stream structure:

ffprobe -i output.ts -show_streams

Look for a stream entry with: * codec_type=data * codec_name=scte_35

This confirms that the SCTE-35 stream is successfully provisioned and ready for your ad insertion workflow.