SCTE Plugins

Live Slicer plugins are designed to support the Society of Cable Telecommunications Engineers (SCTE) standards directly, ensuring compatibility with industry best practices for content signaling and dynamic ad workflows. By leveraging these plugins, operators can automate precise splice points for program starts, ad insertion, enforce regional blackout rules, and manage live or scheduled content transitions seamlessly—key requirements for monetizing and controlling digital video streams at scale.

The Live Slicer is compatible with both SCTE-104 and SCTE-35 signal types. SCTE-104 packets are converted to SCTE35 and are processed by the Live Slicer for unified handling of SCTE.

Baseline SCTE Plugin

The baseline SCTE plugin provides SCTE signal processing functionality to the Live Slicer.

Note: The scte_baseline.py file is included in the Live Slicer download package (within the pluginsfolder).

To leverage this functionality, it must be enabled within your Live Slicer configuration file by adding the following lines:

scte_type: python
scte_module: scte_baseline.py
import slicer, time, syslog, os, traceback

'''Function calls should complete in under 250ms. Longer than that may result in dropped frames!'''

'''This function MUST be present and return slicer.initialize()'''
def Initialize():
    syslog.syslog(syslog.LOG_ALERT, "[upl-py] init: ")
    return slicer.Initialize()

STATE_INITIAL = -1
STATE_BLACKOUT = 0
STATE_SLICING = 1
STATE_AD = 2

state_desc = { \
    -1: "initial", \
    0: "blackout", \
    1: "slicing", \
    2: "ad" \
}

state = STATE_INITIAL
lastmod = 0

'''
splice_insert out_of_network indicator controls blackout.
time_signal segmentation_descriptor type 52 for ad starts, type 53 for ad ends.
'''
def Process35(slice_info):
    global state, lastmod, state_desc
    syslog.syslog(syslog.LOG_ALERT, "[upl-py] trigger: " + str(slice_info))

    try:
        initial = state

        #Reset ad state if we used an implicit ad end
        if state == STATE_AD and slicer.GetState() == 'slicing' and time.time() - lastmod > 30:
            state = STATE_SLICING

        if slicer.GetState() == 'start_blackout' and state == STATE_INITIAL:
            state = STATE_BLACKOUT #Initialize to blackout if the slicer started in blackout
        elif slicer.GetState() == 'slicing' and state == STATE_INITIAL:
            state = STATE_SLICING #Do this so later checks for going into ad break work

        if slice_info["splice_command_type"] == 5:
            if slice_info["command"]["out_of_network_indicator"] == 1 and state != STATE_BLACKOUT:
                slicer.Blackout(int(slice_info["pts_time"]))
                state = STATE_BLACKOUT
            elif slice_info["command"]["out_of_network_indicator"] == 0 and state != STATE_SLICING:
                slicer.ContentStart(int(slice_info["pts_time"]))
                state = STATE_SLICING
            #else Ignore state change, we are already in that state
        elif slice_info["splice_command_type"] == 6:
            if slice_info["descriptors"]:
                for descriptor in slice_info["descriptors"]:
                    if 'segmentation_type_id' in descriptor:
                        #Placement Opportunity Start
                        if descriptor["segmentation_type_id"] == 52 and state == STATE_SLICING:
                            if descriptor["segmentation_duration"] == 0:
                                slicer.AdStart(int(slice_info['pts_time']))
                            else:
                                slicer.AdStart(int(slice_info['pts_time']),int(descriptor["segmentation_duration"]*48000//90000))
                            state = STATE_AD
                        #Placement Opportunity End
                        elif descriptor["segmentation_type_id"] == 53 and state == STATE_AD:
                            slicer.AdEnd(int(slice_info['pts_time']))
                            state = STATE_SLICING
                        #Program Start
                        elif descriptor["segmentation_type_id"] == 0x10:
                            slicer.ContentStart(int(slice_info["pts_time"]))
                            state = STATE_SLICING

        if initial != state:
            syslog.syslog(syslog.LOG_ALERT, "[baseline plugin] State changed from %s -> %s"  % (state_desc[initial], state_desc[state]))
            lastmod = time.time()
    except Exception as e:
        for line in traceback.format_exc().split('\n'):
            print(line)
            syslog.syslog(syslog.LOG_ALERT, line)

    return int(0)

Ad Insertion

Ad insertion is managed by time_signals with a segmentation descriptor having a segmentation_type_id of 0x34 (PLACEMENT_OPPORTUNITY_START). You can end an ad explicitly by sending a time_signal with a segmentation descriptor having a segmentation_type_id of 0x35 (PLACEMENT_OPPORTUNITY_END).

  • If segmentation_duration is 0 when starting an ad, the ad break will last until the next content_start, blackout, or explicit PLACEMENT_OPPORTUNITY_END is sent.
  • If segmentation_duration is set, the ad will last for the specified duration and then return automatically.
  • You can always end an ad preemptively by explicitly sending a PLACEMENT_OPPORTUNITY_END.

Blackout

The Baseline SCTE plugin triggers blackout based on the splice_insert's out_of_network_indicator field.

To achieve the desired blackout effect, set the out_of_network_indicator field to:

  • 1: Enter blackout.
  • 2: Leave blackout.

Content Start

In addition to a splice_insert with out_of_network_indicator set to 0, you can also start a new program by sending a time_signal with a segmentation descriptor where segmentation_type_id is set to 0x10 (PROGRAM_START).

Sample SCTE Message Payload

Here is a sample of a SCTE payload message. In this case, a type 6 (Time Signal), type 16 (Content Start) is being passed:

{'base64': b'/DAzAAAAAAAA///wBQb/+SORKAAdAhtDVUVJAAAAAH+/AQwxMjI4NzYzMjU0NzIQAQCmbExp', 'splice_command_type': 6, 'pts_adjustment': 0, 'pts_time': 206229733, 'command': {}, 'descriptors': [{'segmentation_event_id': 0, 'segmentation_event_cancel_indicator': 0, 'delivery_not_restricted_flag': 1, 'segmentation_duration': 0, 'segmentation_upid_type': 1, 'segmentation_upid_length': 12, 'segmentation_upid': b'122876325472', 'segmentation_type_id': 16, 'segment_num': 1, 'segments_expected': 0, 'identifier': 1129661769, 'descriptor_length': 27, 'splice_descriptor_tag': 2}]}

Python SCTE Plugin

Use the Python SCTE plugin to process your SCTE 35/104 signal with a custom Python file. Your code should contain specific functions that hook into SCTE processing and control the state of the Live Slicer.

Python

Python and its libraries must be installed in your system’s shared library path.

Note: Python 2.7 reached end-of-life in January 2020 and is no longer supported. The Live Slicer previously defaulted to Python 2.7 on older systems such as macOS and Ubuntu 14.04 LTS or lower.

For Live Slicer version 23090500 (https://cms.uplynk.com/static/cms/news.html?tag=slicer) or higher, it is strongly recommended to use Python 3.6 or newer. You can configure the Live Slicer to use Python 3.x by adding the following to your Live Slicer configuration file:

  • Syntax (Python 3.6):

    scte_python_version: 3.6
    
  • Syntax (Python 3.6 installed with pymalloc support):

    scte_python_version: 3.6m
    
  • Syntax (Python 3.8)

    scte_python_version: 3.8
    

Set up a Live Slicer

Set up a Live Slicer to use your Python SCTE plugin by adding the following settings to your Live Slicer configuration file:

scte_type: python
scte_module: my_scte_plugin.py
scte_python_version: 3.x

If you have not installed Python 3.6 with pymalloc support, then you should update the scte_python_version setting with the correct version.

Copy your Python plugin to the plugins subfolder of your Live Slicer's installation directory and then replace my_scte_plugin.py with your Python plugin's file name.

Sample Directory Tree

├── home
│   ├── liveslicer
│   │   ├── plugins
│   │   │   ├── my_scte_plugin.py

See information on the SCTE parameters in Live Slicer Configuration File Settings.

Plugin Functions

The most basic plugin, which does nothing, must appear as:

import slicer

def Initialize():
    return slicer.Initialize()

def Notify():
    return int(0)

This function is necessary to establish proper communication between the plugin and the slicer. The second, optional, function is where you perform SCTE logic:

def Process35(slice_info):
    slicer.Blackout(int(0))
    return int(0)

This function receives the SCTE-35 packets and inside you may inspect the slice_info object containing the SCTE packet or call slicer module functions to control the slicer. SCTE-104 packets are converted to SCTE-35 and passed to this function for unified handling of SCTE.

Slicer Module

The slicer module provides a way for your plugin to call functions and manipulate slicer state.

The available functions are:

FunctionDescription
AdEndExplicitly ends an ad break.
AdMetaAdds metadata to an ad break.
AdStartStarts an ad break.
BlackoutInitiates blackout mode.
ContentStartStarts a new asset.
EndBoundaryEnds an ad boundary.
FlushBreakMetaDefines the presentation timestamp (PTS) at which the metadata defined via the MetaMetadata function will be applied.
GetStateIndicates the current Live Slicer state.
GetStatusReturns Live Slicer status and configuration information.
InitializeInitializes the slicer module.
MetadataAdds metadata to the asset currently being sliced.
MetaMetadataAdds metadata to the asset associated with the next segment.
SlicerLoggerLogs error conditions, informational messages, and debug messages.
StartBoundaryStarts an ad boundary.
TimedMetaAdds metadata as an ID3 tag at the presentation timestamp (PTS).

*Note: PTS referred to in the following sections is the presentation time stamp

Initialize

Initializes the slicer module. Call this function via the Initialize() function as shown above.

def Initialize():
    return slicer.Initialize()
def Notify():
    return int(0)

GetState

Returns a string that indicates the current Live Slicer state.

Valid values are:

  • start_blackout: Indicates that the Live Slicer started up and remains in blackout mode.
  • slicing: Indicates that the Live Slicer is currently slicing.
  • adbreak: Indicates that the Live Slicer is in an ad break.
  • replace: Indicates that the Live Slicer is in replace content mode.
  • blackout: Indicates that the Live Slicer is in blackout mode.

Sample request:

slicer.GetState()

GetStatus

Returns Live Slicer status and configuration information. The response for this function may contain the following parameters:

ParameterTypeDescription
statusObjectContains Live Slicer status information. This object is similar to the status object returned by the status endpoint from the Live Slicer API.
configObjectContains Live Slicer configuration file settings.
slicerIDStringIdentifies the Live Slicer's ID as defined in the Live Slicer configuration file.
externalIDStringIndicates the external ID that will be assigned to the CMS asset created from the live stream.

Sample request:

slicer.GetStatus()

Blackout

Initiates blackout mode at the specified PTS.

Sample request:

slicer.Blackout(int(pts))

ContentStart

Starts a new asset at the specified PTS. Description and External ID arguments are optional.

Sample request:

slicer.ContentStart(int(pts), "Description", "External ID")

AdStart

Starts an ad break at the specified PTS. Duration argument is optional. If omitted, you must call AdEnd() to end the ad break.

Sample request:

slicer.AdStart(int(pts), int(duration))

AdEnd

Explicitly ends an ad break at the specified PTS.

Sample request:

slicer.AdEnd(int(pts))

AdMeta

Adds metadata to a specific ad break. Call AdMeta before starting an ad break.

This function only adds metadata to a specific ad break. It is not applied globally across all ad breaks.

Sample request:

slicer.AdMeta("key", "value")

FlushBreakMeta

Defines the presentation timestamp (PTS) at which the metadata defined via the MetaMetadata function will be applied.

Sample request:

slicer.FlushBreakMeta(int(pts))

Metadata

Adds metadata to the asset currently being sliced as determined by the next video frame.

Metadata may be incorrectly applied to an asset under certain conditions. For example, metatadata may be applied to the previous asset when this function is called directly after a content_start. The recommended method for setting metadata is via the MetaMetadata and FlushBreakMeta functions.

View the metadata associated with an asset from within the CMS.

Sample request:

slicer.Metadata("key", "value")

MetaMetadata

Adds metadata to the asset associated with the next segment as determined by content start, ad start, etc.

The recommended method for associating metadata with an asset is to set it via the MetaMetadata function and then setting the presentation timestamp (PTS) at which it will be applied to an asset via the FlushBreakMeta function.

View the metadata associated with an asset from within the CMS.

Sample request: print slicer.MetaMetadata("key", "value")

slicer.MetaMetadata("key", "value")

SlicerLogger

Logs error conditions, informational messages, and debug messages to the terminal and syslog.

Learn more about logging.

Valid log levels are:

  • error: Logs error conditions.
  • info: Logs informational messages.
  • debug: Logs debug-level messages that may be used to troubleshoot an issue.

Syntax: print slicer.SlicerLogger("{Log Level}", "{Log Message}")

Sample request:

slicer.SlicerLogger("info", "Started an asset boundary.")

StartBoundary

Starts an asset boundary. If a boundary is already active, this is ignored. Duration argument is optional. If omitted, call EndBoundary() to explicitly end an asset boundary.

Sample request:

slicer.StartBoundary(int(pts), name, int(duration))

EndBoundary

Ends an asset boundary. This function is ignored when there isn't an active boundary.

Sample request:

slicer.EndBoundary(int(pts))

TimedMeta

Adds metadata as an ID3 tag at the presentation timestamp (PTS).

Syntax: print slicer.TimedMeta({Presentation Timestamp}, {Metadata Key}, {Metadata Value})

Sample request:

slicer.TimedMeta(int(pts), "key", "value")